@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 +12 -0
- package/dist/index.d.ts +58 -10
- package/dist/index.js +151 -17
- package/package.json +15 -13
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
|
-
*
|
|
141
|
-
* simulator, inject a custom clock (e.g
|
|
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
|
|
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
|
-
/**
|
|
102
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
})),
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
450
|
-
|
|
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.
|
|
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://
|
|
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-
|
|
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
|
|
28
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
29
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
30
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
31
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
32
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
33
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
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-
|
|
38
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
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",
|