@midnight-ntwrk/wallet-sdk-facade 4.0.0 → 4.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.
package/README.md CHANGED
@@ -1,3 +1,10 @@
1
+ > [!IMPORTANT]
2
+ > **This package has moved.** The `@midnight-ntwrk` scope is published only
3
+ > during the migration window and will stop receiving updates. Please migrate to
4
+ > [`@midnightntwrk/wallet-sdk-facade`](https://www.npmjs.com/package/@midnightntwrk/wallet-sdk-facade).
5
+
6
+ ---
7
+
1
8
  # @midnight-ntwrk/wallet-sdk-facade
2
9
 
3
10
  > **Note:** It is recommended to use the [`@midnight-ntwrk/wallet-sdk`](../wallet-sdk/README.md) barrel package, which
@@ -24,6 +31,11 @@ The Wallet Facade provides a high-level unified interface that aggregates the fu
24
31
 
25
32
  ## Usage
26
33
 
34
+ More detailed and complete examples can be found at [docs snippets](../docs-snippets/src/snippets) (always up-to-date
35
+ with the recent changes) or at the
36
+ [SDK documentation site](https://docs.midnight.network/sdks/official/wallet-developer-guide) (aligned with the recent
37
+ release)
38
+
27
39
  ### Initializing the Facade
28
40
 
29
41
  ```typescript
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { type AnyTransaction, type CoinsAndBalances as DustCoinsAndBalances } fr
6
6
  import { type DefaultShieldedConfiguration, type ShieldedWalletAPI, type ShieldedWalletState } from '@midnight-ntwrk/wallet-sdk-shielded';
7
7
  import type { DefaultUnshieldedConfiguration, UnshieldedWalletAPI } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
8
8
  import { type UnshieldedWalletState } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
9
+ import { Clock } from '@midnight-ntwrk/wallet-sdk-utilities';
9
10
  import { Schema } from 'effect';
10
11
  import { TransactionHistoryStorage } from '@midnight-ntwrk/wallet-sdk-abstractions';
11
12
  import { type Observable } from 'rxjs';
@@ -137,14 +138,16 @@ export declare class FacadeState {
137
138
  constructor(shielded: ShieldedWalletState, unshielded: UnshieldedWalletState, dust: DustWalletState, pending: PendingTransactions.PendingTransactions<ledger.FinalizedTransaction>);
138
139
  }
139
140
  /**
140
- * A clock abstraction for obtaining the current time. By default, the facade uses the system clock. For testing with a
141
- * simulator, inject a custom clock (e.g., one backed by the simulator's time).
141
+ * Clock abstraction for obtaining the current time. By default, the facade uses the system clock
142
+ * ({@link Clock.systemClock}); for testing with a simulator, inject a custom clock (e.g. one backed by the simulator's
143
+ * time).
144
+ *
145
+ * Re-exported from `@midnight-ntwrk/wallet-sdk-utilities` as a namespace so the type is `Clock.Clock` and the default is
146
+ * `Clock.systemClock`. Forwarding the same symbol — rather than re-declaring its members individually — keeps the
147
+ * umbrella `wallet-sdk` package's star-exports unambiguous and lets lower-level packages (e.g. dust-wallet) share it
148
+ * without a circular dependency.
142
149
  */
143
- export type Clock = {
144
- readonly now: () => Date;
145
- };
146
- /** Default clock using real system time. */
147
- export declare const systemClock: Clock;
150
+ export { Clock };
148
151
  /**
149
152
  * The Terms and Conditions returned by the indexer, containing a URL for display and a SHA-256 hash for content
150
153
  * verification.
@@ -168,10 +171,16 @@ export type FetchTermsAndConditionsConfiguration = {
168
171
  };
169
172
  export type DefaultConfiguration = DefaultUnshieldedConfiguration & DefaultShieldedConfiguration & DefaultDustConfiguration & DefaultSubmissionConfiguration & DefaultPendingTransactionsServiceConfiguration & Partial<DefaultProvingConfiguration>;
170
173
  type MaybePromise<T> = T | Promise<T>;
174
+ /**
175
+ * Parameters object for {@link WalletFacade.init}. It features configuration and bunch of initializers for the wallets
176
+ * and services, all of them are in a form of a function that takes the configuration and returns proper implementation,
177
+ * either synchronously or wrapped in a Promise. Services are optional to provide ({@link WalletFacade.init} will provide
178
+ * default implementations), but all 3 wallets: shielded, unshielded and Dust one need to be present
179
+ */
171
180
  export type InitParams<TConfig extends DefaultConfiguration> = {
172
181
  configuration: TConfig;
173
182
  /** Optional factory for the clock abstraction. Defaults to system clock (`() => new Date()`). */
174
- clock?: (config: TConfig) => MaybePromise<Clock>;
183
+ clock?: (config: TConfig) => MaybePromise<Clock.Clock>;
175
184
  submissionService?: (config: TConfig) => MaybePromise<SubmissionService<ledger.FinalizedTransaction>>;
176
185
  pendingTransactionsService?: (config: TConfig) => MaybePromise<PendingTransactionsService<ledger.FinalizedTransaction>>;
177
186
  provingService?: (config: TConfig) => MaybePromise<ProvingService<UnboundTransaction>>;
@@ -200,6 +209,27 @@ export declare class WalletFacade {
200
209
  * been set on the network yet.
201
210
  */
202
211
  static fetchTermsAndConditions(configuration: FetchTermsAndConditionsConfiguration): Promise<TermsAndConditions>;
212
+ /**
213
+ * Default initialization for {@link WalletFacade}. It is a static method, which takes an object holding configuration
214
+ * and initialization of necessary components. Specifically - it requires following fields:
215
+ *
216
+ * - `configuration` - holding a configuration, which needs to extend {@link DefaultConfiguration} - this way allows to
217
+ * convey use-case-specific settings in the same way, as the SDK works by default
218
+ * - `shielded` - a function taking the configuration and returning shielded wallet (or a promise with such)
219
+ * implementing {@link ShieldedWalletAPI}
220
+ * - `unshielded` - a function taking the configuration and returning unshielded wallet (or a promise with such)
221
+ * implementing {@link UnshieldedWalletAPI}
222
+ * - `dust` - a function taking the configuration and returning Dust wallet (or a promise with such) implementing
223
+ * {@link DustWalletAPI} There are some optional services/abstractions to provide, too. If not provided - default
224
+ * implementations will be used, each of them is initialized by a function taking the configuration and returning
225
+ * proper implementation (wrapped in a {@link Promise} or not).
226
+ * - `submissionService` - needs to implement {@link SubmissionService} for a {@link ledger.FinalizedTransaction} to
227
+ * submit transactions to the network, default uses Node RPC connection
228
+ * - `pendingTransactionsService` - needs to implement {@link PendingTransactionsService} for a
229
+ * {@link ledger.FinalizedTransaction} to keep track of pending transactions, default uses in-memory implementation
230
+ * - `provingService` - needs to implement {@link ProvingService} to prove it, default uses proving server
231
+ * - `clock` - needs to implement {@link Clock.Clock} for getting current time, default uses system clock
232
+ */
203
233
  static init<TConfig extends DefaultConfiguration>(initParams: InitParams<TConfig>): Promise<WalletFacade>;
204
234
  readonly shielded: ShieldedWalletAPI;
205
235
  readonly unshielded: UnshieldedWalletAPI;
@@ -207,7 +237,13 @@ export declare class WalletFacade {
207
237
  readonly submissionService: SubmissionService<ledger.FinalizedTransaction>;
208
238
  readonly pendingTransactionsService: PendingTransactionsService<ledger.FinalizedTransaction>;
209
239
  readonly provingService: ProvingService<UnboundTransaction>;
210
- readonly clock: Clock;
240
+ readonly clock: Clock.Clock;
241
+ /**
242
+ * Constructor is private on purpose - much of initialization of the facade is potentially asynchronous, and adding
243
+ * new parameters is a breaking change to the users Use {@link WalletFacade.init} instead
244
+ *
245
+ * @private
246
+ */
211
247
  private constructor();
212
248
  private defaultTtl;
213
249
  private mergeUnprovenTransactions;
@@ -274,6 +310,19 @@ export declare class WalletFacade {
274
310
  payFees?: boolean;
275
311
  }): Promise<UnprovenTransactionRecipe>;
276
312
  registerNightUtxosForDustGeneration(nightUtxos: readonly UtxoWithMeta[], nightVerifyingKey: ledger.SignatureVerifyingKey, signDustRegistration: (payload: Uint8Array) => ledger.Signature, dustReceiverAddress?: DustAddress): Promise<UnprovenTransactionRecipe>;
313
+ /**
314
+ * Waits until the dust projected to be generated by the given Night UTxOs reaches `requiredAmount`, re-checking every
315
+ * second. Pair with {@link estimateRegistration} to pick `requiredAmount`, then call before
316
+ * {@link registerNightUtxosForDustGeneration} so the registration covers its own fee.
317
+ *
318
+ * @param nightUtxos - Night UTxOs to project generation for; the same set passed to the registration.
319
+ * @param requiredAmount - Dust threshold to wait for. Resolves immediately if `<= 0n`.
320
+ * @param opts.timeoutMs - Deadline, in ms, for the threshold to be reached. Rejects otherwise. Default `300_000`.
321
+ * @throws If `nightUtxos` is empty, or if `requiredAmount` is not reached within `opts.timeoutMs`.
322
+ */
323
+ waitForGeneratedDust(nightUtxos: readonly UtxoWithMeta[], requiredAmount: bigint, opts?: {
324
+ timeoutMs?: number;
325
+ }): Promise<void>;
277
326
  deregisterFromDustGeneration(nightUtxos: UtxoWithMeta[], nightVerifyingKey: ledger.SignatureVerifyingKey, signDustRegistration: (payload: Uint8Array) => ledger.Signature): Promise<UnprovenTransactionRecipe>;
278
327
  revert(txOrRecipe: AnyTransaction | BalancingRecipe): Promise<void>;
279
328
  revertTransaction(tx: AnyTransaction): Promise<void>;
@@ -282,4 +331,3 @@ export declare class WalletFacade {
282
331
  queryTxHistoryByHash(hash: TransactionHistoryStorage.TransactionHash): Promise<WalletEntry | undefined>;
283
332
  getAllFromTxHistory(): Promise<WalletEntry[]>;
284
333
  }
285
- export {};
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { makeDefaultProvingService, } from '@midnight-ntwrk/wallet-sdk-capabilit
16
16
  import { ShieldedSectionSchema, mergeShieldedSections, } from '@midnight-ntwrk/wallet-sdk-shielded';
17
17
  import { UnshieldedSectionSchema } from '@midnight-ntwrk/wallet-sdk-unshielded-wallet';
18
18
  import { DustSectionSchema, mergeDustSections } from '@midnight-ntwrk/wallet-sdk-dust-wallet';
19
+ import { Clock } from '@midnight-ntwrk/wallet-sdk-utilities';
19
20
  import { FetchTermsAndConditions as FetchTermsAndConditionsQuery } from '@midnight-ntwrk/wallet-sdk-indexer-client';
20
21
  import { QueryRunner } from '@midnight-ntwrk/wallet-sdk-indexer-client/effect';
21
22
  import { Array as Arr, pipe, Schema } from 'effect';
@@ -98,8 +99,17 @@ export class FacadeState {
98
99
  }
99
100
  }
100
101
  const DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour
101
- /** Default clock using real system time. */
102
- export const systemClock = { now: () => new Date() };
102
+ /**
103
+ * Clock abstraction for obtaining the current time. By default, the facade uses the system clock
104
+ * ({@link Clock.systemClock}); for testing with a simulator, inject a custom clock (e.g. one backed by the simulator's
105
+ * time).
106
+ *
107
+ * Re-exported from `@midnight-ntwrk/wallet-sdk-utilities` as a namespace so the type is `Clock.Clock` and the default is
108
+ * `Clock.systemClock`. Forwarding the same symbol — rather than re-declaring its members individually — keeps the
109
+ * umbrella `wallet-sdk` package's star-exports unambiguous and lets lower-level packages (e.g. dust-wallet) share it
110
+ * without a circular dependency.
111
+ */
112
+ export { Clock };
103
113
  export class WalletFacade {
104
114
  static makeDefaultSubmissionService(config) {
105
115
  return makeDefaultSubmissionService(config);
@@ -145,6 +155,27 @@ export class WalletFacade {
145
155
  }
146
156
  return tc;
147
157
  }
158
+ /**
159
+ * Default initialization for {@link WalletFacade}. It is a static method, which takes an object holding configuration
160
+ * and initialization of necessary components. Specifically - it requires following fields:
161
+ *
162
+ * - `configuration` - holding a configuration, which needs to extend {@link DefaultConfiguration} - this way allows to
163
+ * convey use-case-specific settings in the same way, as the SDK works by default
164
+ * - `shielded` - a function taking the configuration and returning shielded wallet (or a promise with such)
165
+ * implementing {@link ShieldedWalletAPI}
166
+ * - `unshielded` - a function taking the configuration and returning unshielded wallet (or a promise with such)
167
+ * implementing {@link UnshieldedWalletAPI}
168
+ * - `dust` - a function taking the configuration and returning Dust wallet (or a promise with such) implementing
169
+ * {@link DustWalletAPI} There are some optional services/abstractions to provide, too. If not provided - default
170
+ * implementations will be used, each of them is initialized by a function taking the configuration and returning
171
+ * proper implementation (wrapped in a {@link Promise} or not).
172
+ * - `submissionService` - needs to implement {@link SubmissionService} for a {@link ledger.FinalizedTransaction} to
173
+ * submit transactions to the network, default uses Node RPC connection
174
+ * - `pendingTransactionsService` - needs to implement {@link PendingTransactionsService} for a
175
+ * {@link ledger.FinalizedTransaction} to keep track of pending transactions, default uses in-memory implementation
176
+ * - `provingService` - needs to implement {@link ProvingService} to prove it, default uses proving server
177
+ * - `clock` - needs to implement {@link Clock.Clock} for getting current time, default uses system clock
178
+ */
148
179
  static async init(initParams) {
149
180
  const submissionService = await Promise.resolve(initParams.submissionService
150
181
  ? initParams.submissionService(initParams.configuration)
@@ -158,7 +189,7 @@ export class WalletFacade {
158
189
  const shielded = await Promise.resolve(initParams.shielded(initParams.configuration));
159
190
  const unshielded = await Promise.resolve(initParams.unshielded(initParams.configuration));
160
191
  const dust = await Promise.resolve(initParams.dust(initParams.configuration));
161
- const clock = await Promise.resolve(initParams.clock ? initParams.clock(initParams.configuration) : systemClock);
192
+ const clock = await Promise.resolve(initParams.clock ? initParams.clock(initParams.configuration) : Clock.systemClock);
162
193
  return new WalletFacade(shielded, unshielded, dust, submissionService, pendingTransactionsService, provingService, initParams.configuration.txHistoryStorage, clock);
163
194
  }
164
195
  shielded;
@@ -170,7 +201,13 @@ export class WalletFacade {
170
201
  #txHistoryStorage;
171
202
  clock;
172
203
  #pendingSubscription;
173
- constructor(shieldedWallet, unshieldedWallet, dustWallet, submissionService, pendingTransactionsService, provingService, txHistoryStorage, clock = systemClock) {
204
+ /**
205
+ * Constructor is private on purpose - much of initialization of the facade is potentially asynchronous, and adding
206
+ * new parameters is a breaking change to the users Use {@link WalletFacade.init} instead
207
+ *
208
+ * @private
209
+ */
210
+ constructor(shieldedWallet, unshieldedWallet, dustWallet, submissionService, pendingTransactionsService, provingService, txHistoryStorage, clock = Clock.systemClock) {
174
211
  this.shielded = shieldedWallet;
175
212
  this.unshielded = unshieldedWallet;
176
213
  this.dust = dustWallet;
@@ -194,18 +231,72 @@ export class WalletFacade {
194
231
  }
195
232
  async createDustActionTransaction(action, nightUtxos, nightVerifyingKey, signDustRegistration) {
196
233
  const ttl = this.defaultTtl();
197
- const transaction = await this.dust.createDustGenerationTransaction(undefined, ttl, nightUtxos.map(({ utxo, meta }) => ({
234
+ const now = this.clock.now();
235
+ const isRegistration = action.type === 'registration';
236
+ const dustReceiverAddress = isRegistration ? action.dustReceiverAddress : undefined;
237
+ // Step 1 — Dust decides which Night UTxO belongs in the guaranteed slot (the one whose dust
238
+ // generation can pay the fee) and computes the fee-payment allowance.
239
+ const split = await this.dust.splitNightUtxosForDustRegistration(now, nightUtxos.map(({ utxo, meta }) => ({
198
240
  ...utxo,
199
241
  ctime: meta.ctime,
200
242
  registeredForDustGeneration: meta.registeredForDustGeneration,
201
- })), nightVerifyingKey, action.type === 'registration' ? action.dustReceiverAddress : undefined);
202
- const intent = transaction.intents?.get(1);
203
- if (!intent) {
204
- throw Error('Dust generation transaction is missing intent segment 1.');
243
+ })), isRegistration);
244
+ const toUnshieldedUtxoWithMeta = (u) => ({
245
+ utxo: {
246
+ value: u.utxo.value,
247
+ type: u.utxo.type,
248
+ owner: u.utxo.owner,
249
+ intentHash: u.utxo.intentHash,
250
+ outputNo: u.utxo.outputNo,
251
+ },
252
+ meta: {
253
+ ctime: u.utxo.ctime,
254
+ registeredForDustGeneration: u.utxo.registeredForDustGeneration,
255
+ },
256
+ });
257
+ const guaranteedForUnshielded = split.guaranteedUtxos.map(toUnshieldedUtxoWithMeta);
258
+ const fallibleForUnshielded = split.fallibleUtxos.map(toUnshieldedUtxoWithMeta);
259
+ // Step 2 — Unshielded books the Night UTxOs (move available -> pending) and builds the intent
260
+ // with the two offers. After this point, a concurrent build call that wants any of these UTxOs
261
+ // will fail fast.
262
+ const txWithOffers = await this.unshielded.rotateUtxos(guaranteedForUnshielded, fallibleForUnshielded, nightVerifyingKey, ttl);
263
+ // Step 3 — Dust attaches its DustActions onto the intent the unshielded wallet just built.
264
+ // If this fails we must unbook the UTxOs so the caller can retry.
265
+ let txWithDustActions;
266
+ try {
267
+ txWithDustActions = await this.dust.attachDustRegistration(txWithOffers, now, nightVerifyingKey, dustReceiverAddress, split.feePayment);
268
+ }
269
+ catch (error) {
270
+ await this.unshielded.revertTransaction(txWithOffers);
271
+ throw error;
272
+ }
273
+ // Step 4 (first-time registration only) — Fail fast if the dust generated so far by the
274
+ // unregistered guaranteed UTxOs is below the registration's own fee. Submitting would fail
275
+ // on-chain with BalanceCheckOverspend. Skip for re-registration (all guaranteed UTxOs already
276
+ // registered) since `feePayment` is 0 by design and the caller is expected to balance the fee
277
+ // externally via `balanceUnprovenTransaction({ tokenKindsToBalance: ['dust'] })`.
278
+ const hasUnregisteredGuaranteed = split.guaranteedUtxos.some((u) => !u.utxo.registeredForDustGeneration);
279
+ if (isRegistration && hasUnregisteredGuaranteed) {
280
+ const fee = await this.dust.calculateFee([txWithDustActions]);
281
+ if (split.feePayment < fee) {
282
+ await this.unshielded.revertTransaction(txWithOffers);
283
+ throw Error(`Insufficient generated dust to cover registration fee (have ${split.feePayment}, need ${fee}). ` +
284
+ `Use WalletFacade.waitForGeneratedDust(utxos, ${fee}) before retrying.`);
285
+ }
286
+ }
287
+ // Step 5 — Sign via the standard signRecipe pathway, which now stamps both the unshielded
288
+ // offers and the dust registration. Signing failures also need to release the booking.
289
+ try {
290
+ const signedRecipe = await this.signRecipe({ type: 'UNPROVEN_TRANSACTION', transaction: txWithDustActions }, signDustRegistration);
291
+ if (signedRecipe.type !== 'UNPROVEN_TRANSACTION') {
292
+ throw Error('signRecipe returned unexpected recipe type for dust action transaction.');
293
+ }
294
+ return signedRecipe.transaction;
295
+ }
296
+ catch (error) {
297
+ await this.unshielded.revertTransaction(txWithOffers);
298
+ throw error;
205
299
  }
206
- const signatureData = intent.signatureData(1);
207
- const signature = await Promise.resolve(signDustRegistration(signatureData));
208
- return await this.dust.addDustGenerationSignature(transaction, signature);
209
300
  }
210
301
  state() {
211
302
  return combineLatest([
@@ -349,15 +440,16 @@ export class WalletFacade {
349
440
  switch (recipe.type) {
350
441
  case 'FINALIZED_TRANSACTION': {
351
442
  const signedBalancingTx = await this.signUnprovenTransaction(recipe.balancingTransaction, signSegment);
443
+ const withDustSig = await this.#signDustRegistrationIfPresent(signedBalancingTx, signSegment);
352
444
  return {
353
445
  type: 'FINALIZED_TRANSACTION',
354
446
  originalTransaction: recipe.originalTransaction,
355
- balancingTransaction: signedBalancingTx,
447
+ balancingTransaction: withDustSig,
356
448
  };
357
449
  }
358
450
  case 'UNBOUND_TRANSACTION': {
359
451
  const signedBalancingTx = recipe.balancingTransaction
360
- ? await this.signUnprovenTransaction(recipe.balancingTransaction, signSegment)
452
+ ? await this.signUnprovenTransaction(recipe.balancingTransaction, signSegment).then((tx) => this.#signDustRegistrationIfPresent(tx, signSegment))
361
453
  : undefined;
362
454
  const signedBaseTx = await this.signUnboundTransaction(recipe.baseTransaction, signSegment);
363
455
  return {
@@ -368,13 +460,23 @@ export class WalletFacade {
368
460
  }
369
461
  case 'UNPROVEN_TRANSACTION': {
370
462
  const signedTx = await this.signUnprovenTransaction(recipe.transaction, signSegment);
463
+ const withDustSig = await this.#signDustRegistrationIfPresent(signedTx, signSegment);
371
464
  return {
372
465
  type: 'UNPROVEN_TRANSACTION',
373
- transaction: signedTx,
466
+ transaction: withDustSig,
374
467
  };
375
468
  }
376
469
  }
377
470
  }
471
+ async #signDustRegistrationIfPresent(tx, signSegment) {
472
+ const intent = tx.intents?.get(1);
473
+ const registrations = intent?.dustActions?.registrations ?? [];
474
+ if (!intent || registrations.length === 0) {
475
+ return tx;
476
+ }
477
+ const signature = signSegment(intent.signatureData(1));
478
+ return await this.dust.addDustRegistrationSignature(tx, signature);
479
+ }
378
480
  async signUnprovenTransaction(tx, signSegment) {
379
481
  return await this.unshielded.signUnprovenTransaction(tx, signSegment);
380
482
  }
@@ -446,8 +548,23 @@ export class WalletFacade {
446
548
  })), (utxosWithMeta) => dustState.estimateDustGeneration(utxosWithMeta, now), (estimatedUtxos) => dustState.capabilities.coinsAndBalances.splitNightUtxos(estimatedUtxos), (split) => split.guaranteed);
447
549
  const fakeSigningKey = ledger.sampleSigningKey();
448
550
  const fakeVerifyingKey = ledger.signatureVerifyingKey(fakeSigningKey);
449
- const fakeRegistrationRecipe = await this.registerNightUtxosForDustGeneration(nightUtxos, fakeVerifyingKey, (payload) => ledger.signData(fakeSigningKey, payload), dustState.address);
450
- const finalizedFakeTx = fakeRegistrationRecipe.transaction.mockProve().bind();
551
+ // Use the legacy dust-only construction path here so estimation does NOT book real UTxOs in the
552
+ // unshielded wallet state. (The race-fix path in createDustActionTransaction books on purpose;
553
+ // estimation is meant to be observation-only.)
554
+ const ttl = this.defaultTtl();
555
+ const fakeUnsignedTx = await this.dust.createDustGenerationTransaction(undefined, ttl, nightUtxos.map(({ utxo, meta }) => ({
556
+ ...utxo,
557
+ ctime: meta.ctime,
558
+ registeredForDustGeneration: meta.registeredForDustGeneration,
559
+ })), fakeVerifyingKey, dustState.address);
560
+ const intent = fakeUnsignedTx.intents?.get(1);
561
+ if (!intent) {
562
+ throw Error('Dust generation transaction is missing intent segment 1.');
563
+ }
564
+ const signatureData = intent.signatureData(1);
565
+ const signature = ledger.signData(fakeSigningKey, signatureData);
566
+ const fakeSignedTx = await this.dust.addDustGenerationSignature(fakeUnsignedTx, signature);
567
+ const finalizedFakeTx = fakeSignedTx.mockProve().bind();
451
568
  const fee = await this.calculateTransactionFee(finalizedFakeTx);
452
569
  return {
453
570
  fee,
@@ -497,6 +614,23 @@ export class WalletFacade {
497
614
  transaction: dustRegistrationTx,
498
615
  };
499
616
  }
617
+ /**
618
+ * Waits until the dust projected to be generated by the given Night UTxOs reaches `requiredAmount`, re-checking every
619
+ * second. Pair with {@link estimateRegistration} to pick `requiredAmount`, then call before
620
+ * {@link registerNightUtxosForDustGeneration} so the registration covers its own fee.
621
+ *
622
+ * @param nightUtxos - Night UTxOs to project generation for; the same set passed to the registration.
623
+ * @param requiredAmount - Dust threshold to wait for. Resolves immediately if `<= 0n`.
624
+ * @param opts.timeoutMs - Deadline, in ms, for the threshold to be reached. Rejects otherwise. Default `300_000`.
625
+ * @throws If `nightUtxos` is empty, or if `requiredAmount` is not reached within `opts.timeoutMs`.
626
+ */
627
+ async waitForGeneratedDust(nightUtxos, requiredAmount, opts) {
628
+ await this.dust.waitForGeneratedDust(nightUtxos.map(({ utxo, meta }) => ({
629
+ ...utxo,
630
+ ctime: meta.ctime,
631
+ registeredForDustGeneration: meta.registeredForDustGeneration,
632
+ })), requiredAmount, this.clock, opts);
633
+ }
500
634
  async deregisterFromDustGeneration(nightUtxos, nightVerifyingKey, signDustRegistration) {
501
635
  const dustDeregistrationTx = await this.createDustActionTransaction({ type: 'deregistration' }, nightUtxos, nightVerifyingKey, signDustRegistration);
502
636
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midnight-ntwrk/wallet-sdk-facade",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -8,14 +8,16 @@
8
8
  "author": "Midnight Foundation",
9
9
  "license": "Apache-2.0",
10
10
  "publishConfig": {
11
- "registry": "https://npm.pkg.github.com/"
11
+ "registry": "https://registry.npmjs.org/",
12
+ "access": "public"
12
13
  },
13
14
  "files": [
14
15
  "dist/"
15
16
  ],
16
17
  "repository": {
17
18
  "type": "git",
18
- "url": "git+https://github.com/midnight-ntwrk/artifacts.git"
19
+ "url": "git+https://github.com/midnightntwrk/midnight-wallet.git",
20
+ "directory": "packages/facade"
19
21
  },
20
22
  "exports": {
21
23
  ".": {
@@ -24,19 +26,19 @@
24
26
  }
25
27
  },
26
28
  "dependencies": {
27
- "@midnight-ntwrk/ledger-v8": "^8.0.3",
28
- "@midnight-ntwrk/wallet-sdk-address-format": "^3.1.1",
29
- "@midnight-ntwrk/wallet-sdk-capabilities": "^3.3.0",
30
- "@midnight-ntwrk/wallet-sdk-dust-wallet": "^4.0.0",
31
- "@midnight-ntwrk/wallet-sdk-indexer-client": "^1.2.1",
32
- "@midnight-ntwrk/wallet-sdk-shielded": "^3.0.0",
33
- "@midnight-ntwrk/wallet-sdk-unshielded-wallet": "^3.0.0",
29
+ "@midnight-ntwrk/ledger-v8": "^8.1.0",
30
+ "@midnight-ntwrk/wallet-sdk-abstractions": "^2.1.0",
31
+ "@midnight-ntwrk/wallet-sdk-address-format": "^3.1.2",
32
+ "@midnight-ntwrk/wallet-sdk-capabilities": "^3.3.1",
33
+ "@midnight-ntwrk/wallet-sdk-dust-wallet": "^4.2.0",
34
+ "@midnight-ntwrk/wallet-sdk-indexer-client": "^1.2.3",
35
+ "@midnight-ntwrk/wallet-sdk-shielded": "^3.0.2",
36
+ "@midnight-ntwrk/wallet-sdk-unshielded-wallet": "^3.1.0",
34
37
  "rxjs": "^7.8.2"
35
38
  },
36
39
  "devDependencies": {
37
- "@midnight-ntwrk/wallet-sdk-abstractions": "^2.1.0",
38
- "@midnight-ntwrk/wallet-sdk-hd": "^3.0.2",
39
- "@midnight-ntwrk/wallet-sdk-utilities": "1.1.1"
40
+ "@midnight-ntwrk/wallet-sdk-hd": "^3.0.3",
41
+ "@midnight-ntwrk/wallet-sdk-utilities": "^1.2.0"
40
42
  },
41
43
  "scripts": {
42
44
  "typecheck": "tsc -b ./tsconfig.json --noEmit --force",