@arkade-os/sdk 0.4.15 → 0.4.17
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 +102 -96
- package/dist/cjs/arkfee/estimator.js +1 -1
- package/dist/cjs/arkfee/types.js +2 -1
- package/dist/cjs/arknote/index.js +43 -4
- package/dist/cjs/bip322/index.js +1 -1
- package/dist/cjs/contracts/arkcontract.js +1 -1
- package/dist/cjs/contracts/contractManager.js +60 -28
- package/dist/cjs/contracts/contractWatcher.js +29 -22
- package/dist/cjs/contracts/handlers/default.js +1 -1
- package/dist/cjs/contracts/handlers/delegate.js +1 -1
- package/dist/cjs/contracts/handlers/helpers.js +1 -1
- package/dist/cjs/extension/asset/assetGroup.js +92 -5
- package/dist/cjs/extension/asset/assetId.js +67 -3
- package/dist/cjs/extension/asset/assetInput.js +18 -0
- package/dist/cjs/extension/asset/assetOutput.js +15 -0
- package/dist/cjs/extension/asset/assetRef.js +66 -0
- package/dist/cjs/extension/asset/metadata.js +15 -0
- package/dist/cjs/extension/asset/packet.js +4 -1
- package/dist/cjs/extension/index.js +1 -1
- package/dist/cjs/forfeit.js +14 -0
- package/dist/cjs/identity/seedIdentity.js +2 -2
- package/dist/cjs/identity/singleKey.js +4 -0
- package/dist/cjs/intent/index.js +28 -12
- package/dist/cjs/providers/ark.js +3 -2
- package/dist/cjs/providers/delegator.js +20 -1
- package/dist/cjs/providers/expoArk.js +2 -2
- package/dist/cjs/providers/indexer.js +2 -2
- package/dist/cjs/providers/onchain.js +2 -1
- package/dist/cjs/repositories/realm/schemas.js +2 -2
- package/dist/cjs/repositories/realm/types.js +1 -1
- package/dist/cjs/script/address.js +37 -6
- package/dist/cjs/script/base.js +70 -1
- package/dist/cjs/script/default.js +3 -0
- package/dist/cjs/script/delegate.js +4 -0
- package/dist/cjs/script/tapscript.js +17 -2
- package/dist/cjs/script/vhtlc.js +35 -27
- package/dist/cjs/storage/fileSystem.js +1 -1
- package/dist/cjs/storage/inMemory.js +1 -1
- package/dist/cjs/storage/indexedDB.js +1 -1
- package/dist/cjs/storage/localStorage.js +1 -1
- package/dist/cjs/tree/validation.js +1 -1
- package/dist/cjs/utils/arkTransaction.js +5 -5
- package/dist/cjs/utils/bip21.js +16 -3
- package/dist/cjs/utils/syncCursors.js +4 -4
- package/dist/cjs/utils/transaction.js +1 -1
- package/dist/cjs/utils/transactionHistory.js +11 -11
- package/dist/cjs/utils/unknownFields.js +3 -3
- package/dist/cjs/wallet/asset-manager.js +4 -4
- package/dist/cjs/wallet/batch.js +5 -5
- package/dist/cjs/wallet/delegator.js +9 -8
- package/dist/cjs/wallet/expo/background.js +3 -3
- package/dist/cjs/wallet/expo/wallet.js +7 -7
- package/dist/cjs/wallet/index.js +43 -0
- package/dist/cjs/wallet/onchain.js +43 -5
- package/dist/cjs/wallet/ramps.js +44 -14
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +22 -22
- package/dist/cjs/wallet/serviceWorker/wallet.js +28 -24
- package/dist/cjs/wallet/unroll.js +12 -8
- package/dist/cjs/wallet/utils.js +1 -1
- package/dist/cjs/wallet/vtxo-manager.js +122 -82
- package/dist/cjs/wallet/wallet.js +140 -77
- package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +1 -1
- package/dist/cjs/worker/expo/processors/contractPollProcessor.js +2 -2
- package/dist/cjs/worker/expo/taskRunner.js +3 -3
- package/dist/cjs/worker/messageBus.js +3 -0
- package/dist/esm/arkfee/estimator.js +1 -1
- package/dist/esm/arkfee/types.js +2 -1
- package/dist/esm/arknote/index.js +43 -4
- package/dist/esm/bip322/index.js +1 -1
- package/dist/esm/contracts/arkcontract.js +1 -1
- package/dist/esm/contracts/contractManager.js +60 -28
- package/dist/esm/contracts/contractWatcher.js +29 -22
- package/dist/esm/contracts/handlers/default.js +1 -1
- package/dist/esm/contracts/handlers/delegate.js +1 -1
- package/dist/esm/contracts/handlers/helpers.js +1 -1
- package/dist/esm/extension/asset/assetGroup.js +92 -5
- package/dist/esm/extension/asset/assetId.js +67 -3
- package/dist/esm/extension/asset/assetInput.js +18 -0
- package/dist/esm/extension/asset/assetOutput.js +15 -0
- package/dist/esm/extension/asset/assetRef.js +66 -0
- package/dist/esm/extension/asset/metadata.js +15 -0
- package/dist/esm/extension/asset/packet.js +4 -1
- package/dist/esm/extension/index.js +1 -1
- package/dist/esm/forfeit.js +14 -0
- package/dist/esm/identity/seedIdentity.js +2 -2
- package/dist/esm/identity/singleKey.js +4 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/intent/index.js +28 -12
- package/dist/esm/providers/ark.js +3 -2
- package/dist/esm/providers/delegator.js +20 -1
- package/dist/esm/providers/expoArk.js +2 -2
- package/dist/esm/providers/indexer.js +2 -2
- package/dist/esm/providers/onchain.js +2 -1
- package/dist/esm/repositories/realm/schemas.js +2 -2
- package/dist/esm/repositories/realm/types.js +1 -1
- package/dist/esm/script/address.js +37 -6
- package/dist/esm/script/base.js +70 -1
- package/dist/esm/script/default.js +3 -0
- package/dist/esm/script/delegate.js +4 -0
- package/dist/esm/script/tapscript.js +17 -2
- package/dist/esm/script/vhtlc.js +35 -27
- package/dist/esm/storage/fileSystem.js +1 -1
- package/dist/esm/storage/inMemory.js +1 -1
- package/dist/esm/storage/indexedDB.js +1 -1
- package/dist/esm/storage/localStorage.js +1 -1
- package/dist/esm/tree/validation.js +1 -1
- package/dist/esm/utils/arkTransaction.js +5 -5
- package/dist/esm/utils/bip21.js +16 -3
- package/dist/esm/utils/syncCursors.js +4 -4
- package/dist/esm/utils/transaction.js +1 -1
- package/dist/esm/utils/transactionHistory.js +11 -11
- package/dist/esm/utils/unknownFields.js +3 -3
- package/dist/esm/wallet/asset-manager.js +4 -4
- package/dist/esm/wallet/batch.js +5 -5
- package/dist/esm/wallet/delegator.js +9 -8
- package/dist/esm/wallet/expo/background.js +3 -3
- package/dist/esm/wallet/expo/wallet.js +7 -7
- package/dist/esm/wallet/index.js +43 -0
- package/dist/esm/wallet/onchain.js +43 -5
- package/dist/esm/wallet/ramps.js +44 -14
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +22 -22
- package/dist/esm/wallet/serviceWorker/wallet.js +28 -24
- package/dist/esm/wallet/unroll.js +12 -8
- package/dist/esm/wallet/utils.js +1 -1
- package/dist/esm/wallet/vtxo-manager.js +121 -81
- package/dist/esm/wallet/wallet.js +140 -77
- package/dist/esm/worker/expo/asyncStorageTaskQueue.js +1 -1
- package/dist/esm/worker/expo/processors/contractPollProcessor.js +2 -2
- package/dist/esm/worker/expo/taskRunner.js +3 -3
- package/dist/esm/worker/messageBus.js +3 -0
- package/dist/types/arkfee/estimator.d.ts +1 -1
- package/dist/types/arkfee/types.d.ts +2 -1
- package/dist/types/arknote/index.d.ts +44 -4
- package/dist/types/bip322/index.d.ts +1 -1
- package/dist/types/contracts/arkcontract.d.ts +1 -1
- package/dist/types/contracts/contractManager.d.ts +39 -65
- package/dist/types/contracts/contractWatcher.d.ts +39 -18
- package/dist/types/contracts/handlers/default.d.ts +1 -1
- package/dist/types/contracts/handlers/delegate.d.ts +1 -1
- package/dist/types/contracts/handlers/helpers.d.ts +1 -1
- package/dist/types/contracts/types.d.ts +36 -26
- package/dist/types/extension/asset/assetGroup.d.ts +92 -1
- package/dist/types/extension/asset/assetId.d.ts +67 -3
- package/dist/types/extension/asset/assetInput.d.ts +18 -0
- package/dist/types/extension/asset/assetOutput.d.ts +15 -0
- package/dist/types/extension/asset/assetRef.d.ts +66 -0
- package/dist/types/extension/asset/metadata.d.ts +15 -0
- package/dist/types/extension/asset/packet.d.ts +4 -1
- package/dist/types/extension/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +14 -0
- package/dist/types/identity/index.d.ts +16 -0
- package/dist/types/identity/seedIdentity.d.ts +8 -6
- package/dist/types/identity/singleKey.d.ts +4 -0
- package/dist/types/intent/index.d.ts +19 -6
- package/dist/types/providers/ark.d.ts +40 -2
- package/dist/types/providers/delegator.d.ts +54 -1
- package/dist/types/providers/expoArk.d.ts +2 -2
- package/dist/types/providers/indexer.d.ts +105 -2
- package/dist/types/providers/onchain.d.ts +62 -1
- package/dist/types/repositories/realm/schemas.d.ts +2 -2
- package/dist/types/repositories/realm/types.d.ts +2 -2
- package/dist/types/repositories/walletRepository.d.ts +16 -0
- package/dist/types/script/address.d.ts +35 -2
- package/dist/types/script/base.d.ts +66 -1
- package/dist/types/script/default.d.ts +3 -0
- package/dist/types/script/delegate.d.ts +4 -0
- package/dist/types/script/tapscript.d.ts +17 -2
- package/dist/types/script/vhtlc.d.ts +35 -27
- package/dist/types/storage/fileSystem.d.ts +1 -1
- package/dist/types/storage/inMemory.d.ts +1 -1
- package/dist/types/storage/index.d.ts +1 -1
- package/dist/types/storage/indexedDB.d.ts +1 -1
- package/dist/types/storage/localStorage.d.ts +1 -1
- package/dist/types/utils/arkTransaction.d.ts +3 -3
- package/dist/types/utils/bip21.d.ts +17 -0
- package/dist/types/utils/syncCursors.d.ts +4 -4
- package/dist/types/utils/transaction.d.ts +1 -1
- package/dist/types/utils/transactionHistory.d.ts +3 -3
- package/dist/types/utils/unknownFields.d.ts +5 -5
- package/dist/types/wallet/asset-manager.d.ts +3 -3
- package/dist/types/wallet/batch.d.ts +27 -7
- package/dist/types/wallet/delegator.d.ts +10 -0
- package/dist/types/wallet/expo/background.d.ts +4 -4
- package/dist/types/wallet/expo/wallet.d.ts +10 -10
- package/dist/types/wallet/index.d.ts +457 -25
- package/dist/types/wallet/onchain.d.ts +42 -4
- package/dist/types/wallet/ramps.d.ts +40 -10
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +4 -4
- package/dist/types/wallet/serviceWorker/wallet.d.ts +71 -33
- package/dist/types/wallet/unroll.d.ts +8 -6
- package/dist/types/wallet/vtxo-manager.d.ts +146 -93
- package/dist/types/wallet/wallet.d.ts +91 -33
- package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +1 -1
- package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +1 -1
- package/dist/types/worker/expo/taskRunner.d.ts +6 -6
- package/dist/types/worker/messageBus.d.ts +5 -3
- package/package.json +1 -1
|
@@ -34,6 +34,10 @@ import { ContractManager } from '../contracts/contractManager.js';
|
|
|
34
34
|
import { contractHandlers } from '../contracts/handlers/index.js';
|
|
35
35
|
import { timelockToSequence } from '../contracts/handlers/helpers.js';
|
|
36
36
|
import { advanceSyncCursors, clearSyncCursors, computeSyncWindow, cursorCutoff, getAllSyncCursors, updateWalletState, } from '../utils/syncCursors.js';
|
|
37
|
+
// Hardcoded unilateral exit delay for mainnet (~7 days in seconds).
|
|
38
|
+
// Pinned here so that address derivation stays stable for existing mainnet
|
|
39
|
+
// wallets even after the server lowers the delay it advertises.
|
|
40
|
+
const MAINNET_UNILATERAL_EXIT_DELAY = 605184n;
|
|
37
41
|
/**
|
|
38
42
|
* Type guard function to check if an identity has a toReadonly method.
|
|
39
43
|
*/
|
|
@@ -108,12 +112,12 @@ export class ReadonlyWallet {
|
|
|
108
112
|
const serverIsMainnet = info.network === "bitcoin";
|
|
109
113
|
if (identityIsMainnet && !serverIsMainnet) {
|
|
110
114
|
throw new Error(`Network mismatch: identity uses mainnet derivation (coin type 0) ` +
|
|
111
|
-
`but
|
|
115
|
+
`but the Arkade server is on ${info.network}. ` +
|
|
112
116
|
`Create identity with { isMainnet: false } to use testnet derivation.`);
|
|
113
117
|
}
|
|
114
118
|
if (!identityIsMainnet && serverIsMainnet) {
|
|
115
119
|
throw new Error(`Network mismatch: identity uses testnet derivation (coin type 1) ` +
|
|
116
|
-
`but
|
|
120
|
+
`but the Arkade server is on mainnet. ` +
|
|
117
121
|
`Create identity with { isMainnet: true } or omit isMainnet (defaults to mainnet).`);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
@@ -129,10 +133,16 @@ export class ReadonlyWallet {
|
|
|
129
133
|
throw new Error("invalid exitTimelock");
|
|
130
134
|
}
|
|
131
135
|
}
|
|
136
|
+
// On mainnet, pin the unilateral exit delay to the historical value so
|
|
137
|
+
// that addresses derived by existing wallets remain stable even if the
|
|
138
|
+
// server starts advertising a shorter delay.
|
|
139
|
+
const unilateralExitDelay = info.network === "bitcoin"
|
|
140
|
+
? MAINNET_UNILATERAL_EXIT_DELAY
|
|
141
|
+
: info.unilateralExitDelay;
|
|
132
142
|
// create unilateral exit timelock
|
|
133
143
|
const exitTimelock = config.exitTimelock ?? {
|
|
134
|
-
value:
|
|
135
|
-
type:
|
|
144
|
+
value: unilateralExitDelay,
|
|
145
|
+
type: unilateralExitDelay < 512n ? "blocks" : "seconds",
|
|
136
146
|
};
|
|
137
147
|
// validate boarding timelock passed in config if any
|
|
138
148
|
if (config.boardingTimelock) {
|
|
@@ -185,6 +195,12 @@ export class ReadonlyWallet {
|
|
|
185
195
|
delegatorProvider: config.delegatorProvider,
|
|
186
196
|
};
|
|
187
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Create a readonly wallet for querying balances, addresses, and history.
|
|
200
|
+
*
|
|
201
|
+
* @param config - Readonly wallet configuration
|
|
202
|
+
* @returns A readonly wallet instance
|
|
203
|
+
*/
|
|
188
204
|
static async create(config) {
|
|
189
205
|
const pubkey = await config.identity.xOnlyPublicKey();
|
|
190
206
|
if (!pubkey) {
|
|
@@ -203,12 +219,17 @@ export class ReadonlyWallet {
|
|
|
203
219
|
get defaultContractScript() {
|
|
204
220
|
return hex.encode(this.offchainTapscript.pkScript);
|
|
205
221
|
}
|
|
222
|
+
/** Returns the wallet's Arkade address. */
|
|
206
223
|
async getAddress() {
|
|
207
224
|
return this.arkAddress.encode();
|
|
208
225
|
}
|
|
226
|
+
/** Returns the onchain boarding address used to move funds into Arkade. */
|
|
209
227
|
async getBoardingAddress() {
|
|
210
228
|
return this.boardingTapscript.onchainAddress(this.network);
|
|
211
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Return the wallet's combined onchain and offchain balances.
|
|
232
|
+
*/
|
|
212
233
|
async getBalance() {
|
|
213
234
|
const [boardingUtxos, vtxos] = await Promise.all([
|
|
214
235
|
this.getBoardingUtxos(),
|
|
@@ -240,7 +261,7 @@ export class ReadonlyWallet {
|
|
|
240
261
|
.reduce((sum, coin) => sum + coin.value, 0);
|
|
241
262
|
const totalBoarding = confirmed + unconfirmed;
|
|
242
263
|
const totalOffchain = settled + preconfirmed + recoverable;
|
|
243
|
-
// aggregate asset balances from spendable
|
|
264
|
+
// aggregate asset balances from spendable virtual outputs
|
|
244
265
|
const assetBalances = new Map();
|
|
245
266
|
for (const vtxo of vtxos) {
|
|
246
267
|
if (!isSpendable(vtxo))
|
|
@@ -270,15 +291,16 @@ export class ReadonlyWallet {
|
|
|
270
291
|
assets,
|
|
271
292
|
};
|
|
272
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Return virtual outputs tracked by the wallet.
|
|
296
|
+
*
|
|
297
|
+
* @param filter - Optional flags controlling whether recoverable or unrolled VTXOs are included
|
|
298
|
+
*/
|
|
273
299
|
async getVtxos(filter) {
|
|
274
|
-
const { isDelta, fetchedExtended, address } = await this.syncVtxos();
|
|
275
300
|
const f = filter ?? { withRecoverable: true, withUnrolled: false };
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
? await this.walletRepository.getVtxos(address)
|
|
280
|
-
: fetchedExtended;
|
|
281
|
-
return vtxos.filter((vtxo) => {
|
|
301
|
+
const contractManager = await this.getContractManager();
|
|
302
|
+
const contractsWithVtxos = await contractManager.getContractsWithVtxos();
|
|
303
|
+
return contractsWithVtxos.flatMap(({ vtxos }) => vtxos.filter((vtxo) => {
|
|
282
304
|
if (isSpendable(vtxo)) {
|
|
283
305
|
if (!f.withRecoverable &&
|
|
284
306
|
(isRecoverable(vtxo) || isExpired(vtxo))) {
|
|
@@ -287,10 +309,13 @@ export class ReadonlyWallet {
|
|
|
287
309
|
return true;
|
|
288
310
|
}
|
|
289
311
|
return !!(f.withUnrolled && vtxo.isUnrolled);
|
|
290
|
-
});
|
|
312
|
+
}));
|
|
291
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Return wallet transaction history derived from Arkade state and boarding transactions.
|
|
316
|
+
*/
|
|
292
317
|
async getTransactionHistory() {
|
|
293
|
-
// Delta-sync
|
|
318
|
+
// Delta-sync virtual outputs into cache, then build history from the cache.
|
|
294
319
|
const { isDelta, fetchedExtended, address } = await this.syncVtxos();
|
|
295
320
|
const allVtxos = isDelta
|
|
296
321
|
? await this.walletRepository.getVtxos(address)
|
|
@@ -302,7 +327,7 @@ export class ReadonlyWallet {
|
|
|
302
327
|
return buildTransactionHistory(allVtxos, boardingTxs, commitmentsToIgnore, getTxCreatedAt);
|
|
303
328
|
}
|
|
304
329
|
/**
|
|
305
|
-
* Delta-sync wallet
|
|
330
|
+
* Delta-sync wallet virtual outputs: fetch only changed virtual outputs since the last
|
|
306
331
|
* cursor, or do a full bootstrap when no cursor exists. Upserts
|
|
307
332
|
* the result into the cache and advances the sync cursors.
|
|
308
333
|
*
|
|
@@ -375,21 +400,21 @@ export class ReadonlyWallet {
|
|
|
375
400
|
allVtxos.push(...response.vtxos);
|
|
376
401
|
}
|
|
377
402
|
}
|
|
378
|
-
// Extend every fetched
|
|
403
|
+
// Extend every fetched virtual output and upsert into the cache.
|
|
379
404
|
const fetchedExtended = [];
|
|
380
405
|
for (const vtxo of allVtxos) {
|
|
381
406
|
const extended = extendWithScript(vtxo);
|
|
382
407
|
if (extended)
|
|
383
408
|
fetchedExtended.push(extended);
|
|
384
409
|
}
|
|
385
|
-
// Save
|
|
410
|
+
// Save virtual outputs first, then advance cursors only on success.
|
|
386
411
|
const cutoff = cursorCutoff(requestStartedAt);
|
|
387
412
|
await this.walletRepository.saveVtxos(address, fetchedExtended);
|
|
388
413
|
await advanceSyncCursors(this.walletRepository, Object.fromEntries(allScripts.map((s) => [s, cutoff])));
|
|
389
414
|
// Delta-sync reconciliation: full re-fetch for delta scripts.
|
|
390
415
|
//
|
|
391
|
-
// The delta fetch (above) only returns
|
|
392
|
-
// cursor, so it can miss preconfirmed
|
|
416
|
+
// The delta fetch (above) only returns virtual outputs changed after the
|
|
417
|
+
// cursor, so it can miss preconfirmed virtual outputs that were consumed
|
|
393
418
|
// by a round between syncs. Rather than layering targeted
|
|
394
419
|
// queries (pendingOnly, spendableOnly) with pagination guards
|
|
395
420
|
// and set algebra, we perform a single unfiltered re-fetch for
|
|
@@ -397,8 +422,8 @@ export class ReadonlyWallet {
|
|
|
397
422
|
// gives us complete, authoritative state in one call and keeps
|
|
398
423
|
// the reconciliation logic simple.
|
|
399
424
|
//
|
|
400
|
-
// Any cached non-spent
|
|
401
|
-
// result set is marked spent; any
|
|
425
|
+
// Any cached non-spent virtual output that is absent from the full
|
|
426
|
+
// result set is marked spent; any virtual output whose state changed
|
|
402
427
|
// (e.g. preconfirmed → settled) is updated in place.
|
|
403
428
|
if (hasDelta) {
|
|
404
429
|
const { vtxos: fullVtxos, page: fullPage } = await this.indexerProvider.getVtxos({
|
|
@@ -407,7 +432,7 @@ export class ReadonlyWallet {
|
|
|
407
432
|
// Reconciliation is best-effort: if the response is
|
|
408
433
|
// paginated we don't have a complete picture, so we skip
|
|
409
434
|
// rather than act on partial data. Wallets with enough
|
|
410
|
-
//
|
|
435
|
+
// virtual outputs to exceed a single page rely solely on the
|
|
411
436
|
// cursor-based delta mechanism for state updates.
|
|
412
437
|
const fullSetComplete = !fullPage || fullPage.total <= 1;
|
|
413
438
|
if (fullSetComplete) {
|
|
@@ -424,7 +449,7 @@ export class ReadonlyWallet {
|
|
|
424
449
|
const outpoint = `${cached.txid}:${cached.vout}`;
|
|
425
450
|
const fresh = fullOutpoints.get(outpoint);
|
|
426
451
|
if (!fresh) {
|
|
427
|
-
// Server no longer knows about this
|
|
452
|
+
// Server no longer knows about this virtual output —
|
|
428
453
|
// it was spent between syncs.
|
|
429
454
|
reconciledExtended.push({
|
|
430
455
|
...cached,
|
|
@@ -457,12 +482,15 @@ export class ReadonlyWallet {
|
|
|
457
482
|
};
|
|
458
483
|
}
|
|
459
484
|
/**
|
|
460
|
-
* Clear all
|
|
485
|
+
* Clear all virtual output sync cursors, forcing a full re-bootstrap on next sync.
|
|
461
486
|
* Useful for recovery after indexer reprocessing or debugging.
|
|
462
487
|
*/
|
|
463
488
|
async clearSyncCursors() {
|
|
464
489
|
await clearSyncCursors(this.walletRepository);
|
|
465
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Build a transaction history view for the wallet's boarding address.
|
|
493
|
+
*/
|
|
466
494
|
async getBoardingTxs() {
|
|
467
495
|
const utxos = [];
|
|
468
496
|
const commitmentsToIgnore = new Set();
|
|
@@ -533,16 +561,25 @@ export class ReadonlyWallet {
|
|
|
533
561
|
commitmentsToIgnore,
|
|
534
562
|
};
|
|
535
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* Fetch and cache onchain inputs (UTXOs) received at the boarding address.
|
|
566
|
+
*/
|
|
536
567
|
async getBoardingUtxos() {
|
|
537
568
|
const boardingAddress = await this.getBoardingAddress();
|
|
538
569
|
const boardingUtxos = await this.onchainProvider.getCoins(boardingAddress);
|
|
539
570
|
const utxos = boardingUtxos.map((utxo) => {
|
|
540
571
|
return extendCoin(this, utxo);
|
|
541
572
|
});
|
|
542
|
-
// Save
|
|
573
|
+
// Save boarding inputs using unified repository
|
|
543
574
|
await this.walletRepository.saveUtxos(boardingAddress, utxos);
|
|
544
575
|
return utxos;
|
|
545
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Subscribe to onchain and offchain notifications for newly received funds.
|
|
579
|
+
*
|
|
580
|
+
* @param eventCallback - Callback invoked when matching funds are detected
|
|
581
|
+
* @returns A function that stops the subscriptions
|
|
582
|
+
*/
|
|
546
583
|
async notifyIncomingFunds(eventCallback) {
|
|
547
584
|
const arkAddress = await this.getAddress();
|
|
548
585
|
const boardingAddress = await this.getBoardingAddress();
|
|
@@ -553,11 +590,11 @@ export class ReadonlyWallet {
|
|
|
553
590
|
return tx.vout.findIndex((v) => v.scriptpubkey_address === boardingAddress);
|
|
554
591
|
};
|
|
555
592
|
onchainStopFunc = await this.onchainProvider.watchAddresses([boardingAddress], (txs) => {
|
|
556
|
-
// find all
|
|
593
|
+
// find all onchain outputs belonging to our boarding address
|
|
557
594
|
const coins = txs
|
|
558
595
|
// filter txs where address is in output
|
|
559
596
|
.filter((tx) => findVoutOnTx(tx) !== -1)
|
|
560
|
-
// return
|
|
597
|
+
// return boarding input as Coin
|
|
561
598
|
.map((tx) => {
|
|
562
599
|
const { txid, status } = tx;
|
|
563
600
|
const vout = findVoutOnTx(tx);
|
|
@@ -582,8 +619,8 @@ export class ReadonlyWallet {
|
|
|
582
619
|
};
|
|
583
620
|
// Handle subscription updates asynchronously without blocking.
|
|
584
621
|
// Note: subscription covers all wallet scripts (default + delegate),
|
|
585
|
-
// but we can't determine which script each
|
|
586
|
-
// subscription event.
|
|
622
|
+
// but we can't determine which script each virtual output belongs to from the
|
|
623
|
+
// subscription event. Virtual outputs are extended with the current offchainTapscript;
|
|
587
624
|
// this is for notification/display only — not for spending.
|
|
588
625
|
// For correct extension metadata, use getVtxos() which queries per-script.
|
|
589
626
|
(async () => {
|
|
@@ -610,8 +647,9 @@ export class ReadonlyWallet {
|
|
|
610
647
|
};
|
|
611
648
|
return stopFunc;
|
|
612
649
|
}
|
|
650
|
+
/** Fetch Arkade transaction ids that are still pending final settlement. */
|
|
613
651
|
async fetchPendingTxs() {
|
|
614
|
-
// get non-swept
|
|
652
|
+
// get non-swept virtual outputs, rely on the indexer only in case DB doesn't have the right state
|
|
615
653
|
const scripts = await this.getWalletScripts();
|
|
616
654
|
let { vtxos } = await this.indexerProvider.getVtxos({
|
|
617
655
|
scripts,
|
|
@@ -652,7 +690,7 @@ export class ReadonlyWallet {
|
|
|
652
690
|
}
|
|
653
691
|
/**
|
|
654
692
|
* Build a map of scriptHex → VtxoScript for all wallet contracts,
|
|
655
|
-
* so
|
|
693
|
+
* so virtual outputs can be extended with the correct tapscript per contract.
|
|
656
694
|
*/
|
|
657
695
|
async getScriptMap() {
|
|
658
696
|
const map = new Map();
|
|
@@ -741,7 +779,6 @@ export class ReadonlyWallet {
|
|
|
741
779
|
indexerProvider: this.indexerProvider,
|
|
742
780
|
contractRepository: this.contractRepository,
|
|
743
781
|
walletRepository: this.walletRepository,
|
|
744
|
-
getDefaultAddress: () => this.getAddress(),
|
|
745
782
|
watcherConfig: this.watcherConfig,
|
|
746
783
|
});
|
|
747
784
|
// Register the wallet's current address as a contract
|
|
@@ -765,7 +802,7 @@ export class ReadonlyWallet {
|
|
|
765
802
|
address: await this.getAddress(),
|
|
766
803
|
state: "active",
|
|
767
804
|
});
|
|
768
|
-
// Also register the non-delegate version so old
|
|
805
|
+
// Also register the non-delegate version so old virtual outputs remain visible
|
|
769
806
|
const nonDelegateScript = new DefaultVtxo.Script({
|
|
770
807
|
pubKey: delegateScript.options.pubKey,
|
|
771
808
|
serverPubKey: delegateScript.options.serverPubKey,
|
|
@@ -803,6 +840,7 @@ export class ReadonlyWallet {
|
|
|
803
840
|
}
|
|
804
841
|
return manager;
|
|
805
842
|
}
|
|
843
|
+
/** Dispose wallet-owned managers and release background resources. */
|
|
806
844
|
async dispose() {
|
|
807
845
|
const manager = this._contractManager ??
|
|
808
846
|
(this._contractManagerInitializing
|
|
@@ -812,29 +850,30 @@ export class ReadonlyWallet {
|
|
|
812
850
|
this._contractManager = undefined;
|
|
813
851
|
this._contractManagerInitializing = undefined;
|
|
814
852
|
}
|
|
853
|
+
/** Async-dispose hook that forwards to `dispose()`. */
|
|
815
854
|
async [Symbol.asyncDispose]() {
|
|
816
855
|
await this.dispose();
|
|
817
856
|
}
|
|
818
857
|
}
|
|
819
858
|
/**
|
|
820
|
-
* Main wallet implementation for Bitcoin transactions with
|
|
821
|
-
* The wallet does not store any data locally and relies on
|
|
822
|
-
* providers to fetch
|
|
859
|
+
* Main wallet implementation for Bitcoin transactions with Arkade protocol support.
|
|
860
|
+
* The wallet does not store any data locally and relies on Arkade and onchain
|
|
861
|
+
* providers to fetch onchain and virtual outputs.
|
|
823
862
|
*
|
|
824
863
|
* @example
|
|
825
864
|
* ```typescript
|
|
826
865
|
* // Create a wallet with URL configuration
|
|
827
866
|
* const wallet = await Wallet.create({
|
|
828
|
-
* identity:
|
|
829
|
-
* arkServerUrl: 'https://
|
|
867
|
+
* identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
868
|
+
* arkServerUrl: 'https://arkade.computer',
|
|
830
869
|
* esploraUrl: 'https://mempool.space/api'
|
|
831
870
|
* });
|
|
832
871
|
*
|
|
833
872
|
* // Or with custom provider instances (e.g., for Expo/React Native)
|
|
834
873
|
* const wallet = await Wallet.create({
|
|
835
|
-
* identity:
|
|
836
|
-
* arkProvider: new ExpoArkProvider('https://
|
|
837
|
-
* indexerProvider: new ExpoIndexerProvider('https://
|
|
874
|
+
* identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
|
|
875
|
+
* arkProvider: new ExpoArkProvider('https://arkade.computer'),
|
|
876
|
+
* indexerProvider: new ExpoIndexerProvider('https://arkade.computer'),
|
|
838
877
|
* esploraUrl: 'https://mempool.space/api'
|
|
839
878
|
* });
|
|
840
879
|
*
|
|
@@ -843,9 +882,9 @@ export class ReadonlyWallet {
|
|
|
843
882
|
* const boardingAddress = await wallet.getBoardingAddress();
|
|
844
883
|
*
|
|
845
884
|
* // Send bitcoin
|
|
846
|
-
* const txid = await wallet.
|
|
847
|
-
* address: '
|
|
848
|
-
* amount: 50000
|
|
885
|
+
* const txid = await wallet.send({
|
|
886
|
+
* address: 'ark1q...',
|
|
887
|
+
* amount: 50000,
|
|
849
888
|
* });
|
|
850
889
|
* ```
|
|
851
890
|
*/
|
|
@@ -874,7 +913,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
874
913
|
this.forfeitOutputScript = forfeitOutputScript;
|
|
875
914
|
this.forfeitPubkey = forfeitPubkey;
|
|
876
915
|
/**
|
|
877
|
-
* Async mutex that serializes all operations submitting VTXOs to the
|
|
916
|
+
* Async mutex that serializes all operations submitting VTXOs to the Arkade
|
|
878
917
|
* server (`settle`, `send`, `sendBitcoin`). This prevents VtxoManager's
|
|
879
918
|
* background renewal from racing with user-initiated transactions for the
|
|
880
919
|
* same VTXO inputs.
|
|
@@ -954,6 +993,19 @@ export class Wallet extends ReadonlyWallet {
|
|
|
954
993
|
await super.dispose();
|
|
955
994
|
}
|
|
956
995
|
}
|
|
996
|
+
/**
|
|
997
|
+
* Create a full wallet and initialize its background managers.
|
|
998
|
+
*
|
|
999
|
+
* @param config - Wallet configuration
|
|
1000
|
+
* @returns A wallet ready to query balances and send transactions
|
|
1001
|
+
* @example
|
|
1002
|
+
* ```typescript
|
|
1003
|
+
* const wallet = await Wallet.create({
|
|
1004
|
+
* identity,
|
|
1005
|
+
* arkServerUrl: 'https://arkade.computer',
|
|
1006
|
+
* });
|
|
1007
|
+
* ```
|
|
1008
|
+
*/
|
|
957
1009
|
static async create(config) {
|
|
958
1010
|
const pubkey = await config.identity.xOnlyPublicKey();
|
|
959
1011
|
if (!pubkey) {
|
|
@@ -985,7 +1037,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
985
1037
|
* @returns A readonly wallet with the same configuration but readonly identity
|
|
986
1038
|
* @example
|
|
987
1039
|
* ```typescript
|
|
988
|
-
* const wallet = await Wallet.create({ identity:
|
|
1040
|
+
* const wallet = await Wallet.create({ identity: MnemonicIdentity.fromMnemonic('abandon abandon...'), ... });
|
|
989
1041
|
* const readonlyWallet = await wallet.toReadonly();
|
|
990
1042
|
*
|
|
991
1043
|
* // Can query balance and addresses
|
|
@@ -993,7 +1045,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
993
1045
|
* const address = await readonlyWallet.getAddress();
|
|
994
1046
|
*
|
|
995
1047
|
* // But cannot send transactions (type error)
|
|
996
|
-
* // readonlyWallet.
|
|
1048
|
+
* // readonlyWallet.send(...); // TypeScript error
|
|
997
1049
|
* ```
|
|
998
1050
|
*/
|
|
999
1051
|
async toReadonly() {
|
|
@@ -1003,19 +1055,22 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1003
1055
|
: this.identity; // Identity extends ReadonlyIdentity, so this is safe
|
|
1004
1056
|
return new ReadonlyWallet(readonlyIdentity, this.network, this.onchainProvider, this.indexerProvider, this.arkServerPublicKey, this.offchainTapscript, this.boardingTapscript, this.dustAmount, this.walletRepository, this.contractRepository, this.delegatorProvider, this.watcherConfig);
|
|
1005
1057
|
}
|
|
1058
|
+
/** Returns the delegator manager when delegation support is configured. */
|
|
1006
1059
|
async getDelegatorManager() {
|
|
1007
1060
|
return this._delegatorManager;
|
|
1008
1061
|
}
|
|
1009
1062
|
/**
|
|
1010
|
-
*
|
|
1011
|
-
*
|
|
1063
|
+
* Send bitcoin to an Arkade address.
|
|
1064
|
+
*
|
|
1065
|
+
* @deprecated Use `send`.
|
|
1066
|
+
* @param params - Send parameters
|
|
1012
1067
|
*/
|
|
1013
1068
|
async sendBitcoin(params) {
|
|
1014
1069
|
if (params.amount <= 0) {
|
|
1015
1070
|
throw new Error("Amount must be positive");
|
|
1016
1071
|
}
|
|
1017
1072
|
if (!isValidArkAddress(params.address)) {
|
|
1018
|
-
throw new Error("Invalid
|
|
1073
|
+
throw new Error("Invalid Arkade address " + params.address);
|
|
1019
1074
|
}
|
|
1020
1075
|
if (params.selectedVtxos && params.selectedVtxos.length > 0) {
|
|
1021
1076
|
return this._withTxLock(async () => {
|
|
@@ -1060,6 +1115,13 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1060
1115
|
amount: params.amount,
|
|
1061
1116
|
});
|
|
1062
1117
|
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Settle boarding inputs and/or virtual outputs into a finalized mainnet transaction.
|
|
1120
|
+
*
|
|
1121
|
+
* @param params - Optional settlement inputs and outputs. When omitted, the wallet settles all eligible funds.
|
|
1122
|
+
* @param eventCallback - Optional callback invoked for settlement stream events.
|
|
1123
|
+
* @returns The finalized Arkade transaction id
|
|
1124
|
+
*/
|
|
1063
1125
|
async settle(params, eventCallback) {
|
|
1064
1126
|
return this._withTxLock(() => this._settleImpl(params, eventCallback));
|
|
1065
1127
|
}
|
|
@@ -1077,7 +1139,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1077
1139
|
}
|
|
1078
1140
|
}
|
|
1079
1141
|
}
|
|
1080
|
-
// if no params are provided, use all non
|
|
1142
|
+
// if no params are provided, use all non-expired boarding inputs and offchain virtual outputs as inputs
|
|
1081
1143
|
// and send all to the offchain address
|
|
1082
1144
|
if (!params) {
|
|
1083
1145
|
const { fees } = await this.arkProvider.getInfo();
|
|
@@ -1098,7 +1160,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1098
1160
|
amount: BigInt(utxo.value),
|
|
1099
1161
|
});
|
|
1100
1162
|
if (inputFee.value >= utxo.value) {
|
|
1101
|
-
// skip if fees are greater than the
|
|
1163
|
+
// skip if fees are greater than the boarding input value
|
|
1102
1164
|
continue;
|
|
1103
1165
|
}
|
|
1104
1166
|
filteredBoardingUtxos.push(utxo);
|
|
@@ -1119,7 +1181,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1119
1181
|
: new Date(),
|
|
1120
1182
|
});
|
|
1121
1183
|
if (inputFee.value >= vtxo.value) {
|
|
1122
|
-
// skip if fees are greater than the
|
|
1184
|
+
// skip if fees are greater than the virtual output value
|
|
1123
1185
|
continue;
|
|
1124
1186
|
}
|
|
1125
1187
|
filteredVtxos.push(vtxo);
|
|
@@ -1210,7 +1272,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1210
1272
|
const assetPacket = createAssetPacket(assetInputs, recipients);
|
|
1211
1273
|
outputs.push(Extension.create([assetPacket]).txOut());
|
|
1212
1274
|
}
|
|
1213
|
-
// session holds the state of the musig2 signing process of the
|
|
1275
|
+
// session holds the state of the musig2 signing process of the virtual output tree
|
|
1214
1276
|
let session;
|
|
1215
1277
|
const signingPublicKeys = [];
|
|
1216
1278
|
if (hasOffchainOutputs) {
|
|
@@ -1261,7 +1323,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1261
1323
|
for (const input of inputs) {
|
|
1262
1324
|
// check if the input is an offchain "virtual" coin
|
|
1263
1325
|
const vtxo = vtxos.find((vtxo) => vtxo.txid === input.txid && vtxo.vout === input.vout);
|
|
1264
|
-
// boarding
|
|
1326
|
+
// boarding input, we need to sign the settlement tx
|
|
1265
1327
|
if (!vtxo) {
|
|
1266
1328
|
for (let i = 0; i < settlementPsbt.inputsLength; i++) {
|
|
1267
1329
|
const settlementInput = settlementPsbt.getInput(i);
|
|
@@ -1339,11 +1401,12 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1339
1401
|
}
|
|
1340
1402
|
}
|
|
1341
1403
|
/**
|
|
1342
|
-
*
|
|
1404
|
+
* Create a batch event handler for settlement flows.
|
|
1405
|
+
*
|
|
1343
1406
|
* @param intentId - The intent ID.
|
|
1344
|
-
* @param inputs -
|
|
1345
|
-
* @param
|
|
1346
|
-
* @param
|
|
1407
|
+
* @param inputs - Inputs used by the intent.
|
|
1408
|
+
* @param expectedRecipients - Expected recipients to validate in the virtual output tree.
|
|
1409
|
+
* @param session - Optional musig2 signing session. When omitted, signing steps are skipped.
|
|
1347
1410
|
*/
|
|
1348
1411
|
createBatchHandler(intentId, inputs, expectedRecipients, session) {
|
|
1349
1412
|
let sweepTapTreeRoot;
|
|
@@ -1357,7 +1420,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1357
1420
|
for (const idHash of event.intentIdHashes) {
|
|
1358
1421
|
if (idHash === intentIdHashStr) {
|
|
1359
1422
|
if (!this.arkProvider) {
|
|
1360
|
-
throw new Error("
|
|
1423
|
+
throw new Error("Arkade provider not configured");
|
|
1361
1424
|
}
|
|
1362
1425
|
await this.arkProvider.confirmRegistration(intentId);
|
|
1363
1426
|
skip = false;
|
|
@@ -1390,10 +1453,10 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1390
1453
|
// not a cosigner, skip the signing
|
|
1391
1454
|
return { skip: true };
|
|
1392
1455
|
}
|
|
1393
|
-
// validate the unsigned
|
|
1456
|
+
// validate the unsigned virtual output tree
|
|
1394
1457
|
const commitmentTx = Transaction.fromPSBT(base64.decode(event.unsignedCommitmentTx));
|
|
1395
1458
|
validateVtxoTxGraph(vtxoTree, commitmentTx, sweepTapTreeRoot);
|
|
1396
|
-
// validate that all expected receivers are in the
|
|
1459
|
+
// validate that all expected receivers are in the virtual output tree with correct amounts and assets
|
|
1397
1460
|
if (expectedRecipients && expectedRecipients.length > 0) {
|
|
1398
1461
|
validateBatchRecipients(commitmentTx, vtxoTree.leaves(), expectedRecipients, this.network);
|
|
1399
1462
|
}
|
|
@@ -1494,7 +1557,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1494
1557
|
/**
|
|
1495
1558
|
* Finalizes pending transactions by retrieving them from the server and finalizing each one.
|
|
1496
1559
|
* Skips the server check entirely when no send was interrupted (no pending tx flag set).
|
|
1497
|
-
* @param vtxos - Optional list of
|
|
1560
|
+
* @param vtxos - Optional list of virtual outputs to use instead of retrieving them from the server
|
|
1498
1561
|
* @returns Array of transaction IDs that were finalized
|
|
1499
1562
|
*/
|
|
1500
1563
|
async finalizePendingTxs(vtxos) {
|
|
@@ -1593,13 +1656,13 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1593
1656
|
/**
|
|
1594
1657
|
* Send BTC and/or assets to one or more recipients.
|
|
1595
1658
|
*
|
|
1596
|
-
* @param
|
|
1597
|
-
* @returns Promise resolving to the
|
|
1659
|
+
* @param args - Recipients with their addresses, BTC amounts, and assets
|
|
1660
|
+
* @returns Promise resolving to the Arkade transaction ID
|
|
1598
1661
|
*
|
|
1599
1662
|
* @example
|
|
1600
1663
|
* ```typescript
|
|
1601
1664
|
* const txid = await wallet.send({
|
|
1602
|
-
* address: '
|
|
1665
|
+
* address: 'ark1q...',
|
|
1603
1666
|
* amount: 1000, // (optional, default to dust) btc amount to send to the output
|
|
1604
1667
|
* assets: [{ assetId: 'abc123...', amount: 50 }] // (optional) list of assets to send
|
|
1605
1668
|
* });
|
|
@@ -1747,8 +1810,8 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1747
1810
|
}
|
|
1748
1811
|
/**
|
|
1749
1812
|
* Build an offchain transaction from the given inputs and outputs,
|
|
1750
|
-
* sign it, submit to the
|
|
1751
|
-
* @returns The
|
|
1813
|
+
* sign it, submit to the Arkade provider, and finalize.
|
|
1814
|
+
* @returns The Arkade transaction id and server-signed checkpoint PSBTs (for bookkeeping)
|
|
1752
1815
|
*/
|
|
1753
1816
|
async buildAndSubmitOffchainTx(inputs, outputs) {
|
|
1754
1817
|
const offchainTx = buildOffchainTx(inputs.map((input) => {
|
|
@@ -1807,7 +1870,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1807
1870
|
}
|
|
1808
1871
|
return { arkTxid, signedCheckpointTxs };
|
|
1809
1872
|
}
|
|
1810
|
-
// mark
|
|
1873
|
+
// mark virtual outputs as spent, save change outputs if any
|
|
1811
1874
|
async updateDbAfterOffchainTx(inputs, arkTxid, signedCheckpointTxs, sentAmount, changeAmount, changeVout, changeAssets) {
|
|
1812
1875
|
try {
|
|
1813
1876
|
const spentVtxos = [];
|
|
@@ -1855,7 +1918,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1855
1918
|
}
|
|
1856
1919
|
const createdAt = Date.now();
|
|
1857
1920
|
const addr = this.arkAddress.encode();
|
|
1858
|
-
// Only save a change
|
|
1921
|
+
// Only save a change virtual output for preconfirmed coins (those with a batchExpiry).
|
|
1859
1922
|
// Inputs without a batchExpiry are already settled/unrolled and don't need tracking.
|
|
1860
1923
|
let changeVtxo;
|
|
1861
1924
|
if (changeAmount > 0n && batchExpiry !== Number.MAX_SAFE_INTEGER) {
|
|
@@ -1899,7 +1962,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1899
1962
|
console.warn("error saving offchain tx to repository", e);
|
|
1900
1963
|
}
|
|
1901
1964
|
}
|
|
1902
|
-
// mark
|
|
1965
|
+
// mark virtual outputs as spent/settled, remove boarding inputs
|
|
1903
1966
|
async updateDbAfterSettle(inputs, commitmentTxid) {
|
|
1904
1967
|
try {
|
|
1905
1968
|
const addr = this.arkAddress.encode();
|
|
@@ -1910,7 +1973,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1910
1973
|
const isVtxo = (input) => "virtualStatus" in input;
|
|
1911
1974
|
for (const input of inputs) {
|
|
1912
1975
|
if (isVtxo(input)) {
|
|
1913
|
-
//
|
|
1976
|
+
// virtual output = mark it settled
|
|
1914
1977
|
const vtxo = extendVirtualCoin(this, input);
|
|
1915
1978
|
if (vtxo.arkTxId) {
|
|
1916
1979
|
inputArkTxIds.add(vtxo.arkTxId);
|
|
@@ -1926,7 +1989,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1926
1989
|
});
|
|
1927
1990
|
}
|
|
1928
1991
|
else {
|
|
1929
|
-
// boarding
|
|
1992
|
+
// boarding input = remove it
|
|
1930
1993
|
boardingUtxoToRemove.add(`${input.txid}:${input.vout}`);
|
|
1931
1994
|
}
|
|
1932
1995
|
}
|
|
@@ -1950,13 +2013,13 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1950
2013
|
}
|
|
1951
2014
|
Wallet.MIN_FEE_RATE = 1; // sats/vbyte
|
|
1952
2015
|
/**
|
|
1953
|
-
* Select virtual
|
|
1954
|
-
* @param coins List of virtual
|
|
2016
|
+
* Select virtual outputs to reach a target amount, prioritizing those closer to expiry
|
|
2017
|
+
* @param coins List of virtual outputs to select from
|
|
1955
2018
|
* @param targetAmount Target amount to reach in satoshis
|
|
1956
|
-
* @returns Selected
|
|
2019
|
+
* @returns Selected virtual outputs and change amount
|
|
1957
2020
|
*/
|
|
1958
2021
|
export function selectVirtualCoins(coins, targetAmount) {
|
|
1959
|
-
// Sort
|
|
2022
|
+
// Sort virtual outputs by expiry (ascending) and amount (descending)
|
|
1960
2023
|
const sortedCoins = [...coins].sort((a, b) => {
|
|
1961
2024
|
// First sort by expiry if available
|
|
1962
2025
|
const expiryA = a.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
|
|
@@ -49,7 +49,7 @@ export class AsyncStorageTaskQueue {
|
|
|
49
49
|
// ── Config persistence (for background handler rehydration) ──────
|
|
50
50
|
/**
|
|
51
51
|
* Persist a config blob alongside the queue data.
|
|
52
|
-
* Used by
|
|
52
|
+
* Used by @see ExpoWallet.setup to store the wallet parameters
|
|
53
53
|
* that the background handler needs to reconstruct providers.
|
|
54
54
|
*/
|
|
55
55
|
async persistConfig(config) {
|
|
@@ -3,7 +3,7 @@ export const CONTRACT_POLL_TASK_TYPE = "contract-poll";
|
|
|
3
3
|
* Polls the indexer for the latest VTXO state of every contract and
|
|
4
4
|
* persists the results to the wallet repository.
|
|
5
5
|
*
|
|
6
|
-
* Replicates the polling subset of
|
|
6
|
+
* Replicates the polling subset of @see ContractManager.initialize:
|
|
7
7
|
* 1. Load all contracts from the contract repository.
|
|
8
8
|
* 2. Mark expired active contracts as inactive.
|
|
9
9
|
* 3. Paginated fetch of spendable VTXOs from the indexer.
|
|
@@ -26,7 +26,7 @@ export const contractPollProcessor = {
|
|
|
26
26
|
contract.state = "inactive";
|
|
27
27
|
await contractRepository.saveContract(contract);
|
|
28
28
|
}
|
|
29
|
-
// Paginated fetch of spendable
|
|
29
|
+
// Paginated fetch of spendable virtual outputs
|
|
30
30
|
const pageSize = 100;
|
|
31
31
|
let pageIndex = 0;
|
|
32
32
|
let hasMore = true;
|
|
@@ -4,7 +4,7 @@ import { getRandomId, extendVirtualCoin, extendVtxoFromContract, } from '../../w
|
|
|
4
4
|
*
|
|
5
5
|
* For each task in the inbox:
|
|
6
6
|
* 1. Find the processor whose `taskType` matches `task.type`.
|
|
7
|
-
* 2. Execute it, producing a
|
|
7
|
+
* 2. Execute it, producing a @see TaskResult.
|
|
8
8
|
* 3. Push the result to the outbox and remove the task from the inbox.
|
|
9
9
|
*
|
|
10
10
|
* Tasks with no matching processor produce a `"noop"` result.
|
|
@@ -53,8 +53,8 @@ export async function runTasks(queue, processors, deps) {
|
|
|
53
53
|
return results;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* Build the
|
|
57
|
-
* (e.g.
|
|
56
|
+
* Build the @see TaskDependencies needed by task processors
|
|
57
|
+
* (e.g. `src/worker/expo/processors/contractPollProcessor.ts`)
|
|
58
58
|
*
|
|
59
59
|
* This is the same construction that `defineExpoBackgroundTask` does
|
|
60
60
|
* internally, extracted so that consumers with custom schedulers
|