@arkade-os/sdk 0.4.26 → 0.4.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +5 -25
  2. package/dist/cjs/contracts/contractManager.js +31 -11
  3. package/dist/cjs/contracts/contractWatcher.js +2 -2
  4. package/dist/cjs/identity/hdCapableIdentity.js +18 -0
  5. package/dist/cjs/identity/index.js +3 -1
  6. package/dist/cjs/identity/seedIdentity.js +16 -0
  7. package/dist/cjs/index.js +4 -2
  8. package/dist/cjs/wallet/delegator.js +10 -4
  9. package/dist/cjs/wallet/hdDescriptorProvider.js +29 -0
  10. package/dist/cjs/wallet/inputSignerRouter.js +98 -0
  11. package/dist/cjs/wallet/serviceWorker/wallet.js +1 -0
  12. package/dist/cjs/wallet/signingErrors.js +32 -0
  13. package/dist/cjs/wallet/unroll.js +5 -1
  14. package/dist/cjs/wallet/wallet.js +232 -86
  15. package/dist/cjs/wallet/walletReceiveRotator.js +547 -0
  16. package/dist/cjs/worker/messageBus.js +1 -0
  17. package/dist/esm/contracts/contractManager.js +31 -11
  18. package/dist/esm/contracts/contractWatcher.js +2 -2
  19. package/dist/esm/identity/hdCapableIdentity.js +17 -1
  20. package/dist/esm/identity/index.js +1 -0
  21. package/dist/esm/identity/seedIdentity.js +16 -0
  22. package/dist/esm/index.js +2 -2
  23. package/dist/esm/wallet/delegator.js +10 -4
  24. package/dist/esm/wallet/hdDescriptorProvider.js +29 -0
  25. package/dist/esm/wallet/inputSignerRouter.js +94 -0
  26. package/dist/esm/wallet/serviceWorker/wallet.js +1 -0
  27. package/dist/esm/wallet/signingErrors.js +27 -0
  28. package/dist/esm/wallet/unroll.js +5 -1
  29. package/dist/esm/wallet/wallet.js +231 -86
  30. package/dist/esm/wallet/walletReceiveRotator.js +540 -0
  31. package/dist/esm/worker/messageBus.js +1 -0
  32. package/dist/types/contracts/contractManager.d.ts +33 -3
  33. package/dist/types/contracts/types.d.ts +19 -2
  34. package/dist/types/identity/descriptorProvider.d.ts +7 -0
  35. package/dist/types/identity/hdCapableIdentity.d.ts +30 -3
  36. package/dist/types/identity/index.d.ts +1 -0
  37. package/dist/types/identity/seedIdentity.d.ts +16 -0
  38. package/dist/types/index.d.ts +6 -6
  39. package/dist/types/wallet/hdDescriptorProvider.d.ts +22 -1
  40. package/dist/types/wallet/index.d.ts +34 -0
  41. package/dist/types/wallet/inputSignerRouter.d.ts +35 -0
  42. package/dist/types/wallet/serviceWorker/wallet.d.ts +10 -0
  43. package/dist/types/wallet/signingErrors.d.ts +19 -0
  44. package/dist/types/wallet/wallet.d.ts +51 -2
  45. package/dist/types/wallet/walletReceiveRotator.d.ts +306 -0
  46. package/dist/types/worker/messageBus.d.ts +1 -0
  47. package/package.json +1 -1
package/README.md CHANGED
@@ -28,12 +28,11 @@ const mnemonic = generateMnemonic(wordlist)
28
28
  const identity = MnemonicIdentity.fromMnemonic(mnemonic)
29
29
 
30
30
  // Create a wallet with Arkade support
31
- const wallet = await Wallet.create({
32
- identity,
33
- arkServerUrl: 'https://arkade.computer',
34
- })
31
+ const wallet = await Wallet.create({ identity }) // defaults to mainnet
35
32
  ```
36
33
 
34
+ To use a different network, pass `arkServerUrl` option.
35
+
37
36
  ### Read-Only Wallets (Watch-Only)
38
37
 
39
38
  The SDK supports read-only wallets that allow you to query wallet state without exposing private keys. This is useful for:
@@ -55,7 +54,6 @@ const readonlyIdentity = ReadonlySingleKey.fromPublicKey(publicKey)
55
54
  // Create a read-only wallet
56
55
  const readonlyWallet = await ReadonlyWallet.create({
57
56
  identity: readonlyIdentity,
58
- arkServerUrl: 'https://arkade.computer'
59
57
  })
60
58
 
61
59
  // Query operations work normally
@@ -75,10 +73,7 @@ import { Wallet, MnemonicIdentity } from '@arkade-os/sdk'
75
73
 
76
74
  // Create a full wallet
77
75
  const identity = MnemonicIdentity.fromMnemonic('abandon abandon...')
78
- const wallet = await Wallet.create({
79
- identity,
80
- arkServerUrl: 'https://arkade.computer'
81
- })
76
+ const wallet = await Wallet.create({ identity })
82
77
 
83
78
  // Convert to read-only wallet (safe to share)
84
79
  const readonlyWallet = await wallet.toReadonly()
@@ -101,7 +96,6 @@ const readonlyIdentity = await identity.toReadonly()
101
96
  // Use in read-only wallet
102
97
  const readonlyWallet = await ReadonlyWallet.create({
103
98
  identity: readonlyIdentity,
104
- arkServerUrl: 'https://arkade.computer'
105
99
  })
106
100
  ```
107
101
 
@@ -132,7 +126,6 @@ const identityWithPassphrase = MnemonicIdentity.fromMnemonic(mnemonic, {
132
126
  // Create wallet as usual
133
127
  const wallet = await Wallet.create({
134
128
  identity: identityWithPassphrase,
135
- arkServerUrl: 'https://arkade.computer'
136
129
  })
137
130
  ```
138
131
 
@@ -177,7 +170,6 @@ const readonlyFromTemplate = ReadonlyDescriptorIdentity.fromDescriptor(template)
177
170
  // Use in a watch-only wallet
178
171
  const readonlyWallet = await ReadonlyWallet.create({
179
172
  identity: readonly,
180
- arkServerUrl: 'https://arkade.computer'
181
173
  })
182
174
 
183
175
  // Can query but not sign
@@ -219,7 +211,7 @@ const identity = new MyBrowserWallet()
219
211
  console.log(isBatchSignable(identity)) // true
220
212
 
221
213
  // Wallet.send() uses one popup instead of N+1
222
- const wallet = await Wallet.create({ identity, arkServerUrl: 'https://arkade.computer' })
214
+ const wallet = await Wallet.create({ identity })
223
215
  await wallet.send({ address: 'ark1q...', amount: 1000 })
224
216
  ```
225
217
 
@@ -456,7 +448,6 @@ Virtual output renewal at 3 days and boarding input sweep enabled.
456
448
  ```typescript
457
449
  const wallet = await Wallet.create({
458
450
  identity,
459
- arkServerUrl: 'https://arkade.computer',
460
451
  // Enable settlement with defaults explicitly:
461
452
  settlementConfig: {
462
453
  // Seconds before virtual output expiry to trigger renewal
@@ -473,7 +464,6 @@ const wallet = await Wallet.create({
473
464
  // Enable both virtual output renewal and boarding input sweep
474
465
  const wallet = await Wallet.create({
475
466
  identity,
476
- arkServerUrl: 'https://arkade.computer',
477
467
  settlementConfig: {
478
468
  vtxoThreshold: 60 * 60 * 24, // renew when 24 hours remain (in seconds)
479
469
  boardingUtxoSweep: true, // sweep expired boarding inputs
@@ -485,7 +475,6 @@ const wallet = await Wallet.create({
485
475
  // Explicitly disable all settlement
486
476
  const wallet = await Wallet.create({
487
477
  identity,
488
- arkServerUrl: 'https://arkade.computer',
489
478
  settlementConfig: false,
490
479
  })
491
480
  ```
@@ -567,7 +556,6 @@ import { Wallet, MnemonicIdentity, RestDelegatorProvider } from '@arkade-os/sdk'
567
556
 
568
557
  const wallet = await Wallet.create({
569
558
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
570
- arkServerUrl: 'https://arkade.computer',
571
559
  delegatorProvider: new RestDelegatorProvider('http://localhost:7001'),
572
560
  })
573
561
  ```
@@ -612,7 +600,6 @@ import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
612
600
 
613
601
  const wallet = await ServiceWorkerWallet.setup({
614
602
  serviceWorkerPath: '/service-worker.js',
615
- arkServerUrl: 'https://arkade.computer',
616
603
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
617
604
  delegatorUrl: 'http://localhost:7001',
618
605
  })
@@ -686,7 +673,6 @@ import { Wallet, MnemonicIdentity, Ramps } from '@arkade-os/sdk'
686
673
 
687
674
  const wallet = await Wallet.create({
688
675
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
689
- arkServerUrl: 'https://arkade.computer'
690
676
  })
691
677
 
692
678
  // Get fee information from the server
@@ -828,7 +814,6 @@ import { ServiceWorkerWallet, MnemonicIdentity } from '@arkade-os/sdk'
828
814
  // One-liner: registers the SW, initializes the MessageBus, and creates the wallet
829
815
  const wallet = await ServiceWorkerWallet.setup({
830
816
  serviceWorkerPath: '/service-worker.js',
831
- arkServerUrl: 'https://arkade.computer',
832
817
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
833
818
  })
834
819
 
@@ -968,7 +953,6 @@ const executor: SQLExecutor = {
968
953
 
969
954
  const wallet = await Wallet.create({
970
955
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
971
- arkServerUrl: 'https://arkade.computer',
972
956
  storage: {
973
957
  walletRepository: new SQLiteWalletRepository(executor),
974
958
  contractRepository: new SQLiteContractRepository(executor),
@@ -999,7 +983,6 @@ const realm = await Realm.open({
999
983
  })
1000
984
  const wallet = await Wallet.create({
1001
985
  identity,
1002
- arkServerUrl: 'https://arkade.computer',
1003
986
  storage: {
1004
987
  walletRepository: new RealmWalletRepository(realm),
1005
988
  contractRepository: new RealmContractRepository(realm),
@@ -1017,7 +1000,6 @@ import { MnemonicIdentity, Wallet } from '@arkade-os/sdk'
1017
1000
 
1018
1001
  const wallet = await Wallet.create({
1019
1002
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
1020
- arkServerUrl: 'https://arkade.computer',
1021
1003
  // Uses IndexedDB by default in the browser
1022
1004
  })
1023
1005
  ```
@@ -1037,7 +1019,6 @@ import {
1037
1019
 
1038
1020
  const wallet = await Wallet.create({
1039
1021
  identity: MnemonicIdentity.fromMnemonic('abandon abandon...'),
1040
- arkServerUrl: 'https://arkade.computer',
1041
1022
  storage: {
1042
1023
  walletRepository: new InMemoryWalletRepository(),
1043
1024
  contractRepository: new InMemoryContractRepository()
@@ -1121,7 +1102,6 @@ const executor = {
1121
1102
 
1122
1103
  const wallet = await Wallet.create({
1123
1104
  identity,
1124
- arkServerUrl: 'https://arkade.computer',
1125
1105
  arkProvider: new ExpoArkProvider('https://arkade.computer'),
1126
1106
  indexerProvider: new ExpoIndexerProvider('https://arkade.computer'),
1127
1107
  storage: {
@@ -350,9 +350,22 @@ class ContractManager {
350
350
  /**
351
351
  * Force refresh virtual outputs from the indexer.
352
352
  *
353
- * Without options, re-fetches every contract and advances the global cursor.
354
- * With options, narrows the refresh to specific scripts and/or a time window.
355
- * Subset refreshes (scripts filter) intentionally do not advance the cursor.
353
+ * Without options, re-fetches every contract in the watcher's
354
+ * watched set and advances the global cursor.
355
+ *
356
+ * `scripts` narrows the refresh to a specific list (subset query —
357
+ * cursor is not advanced because contracts outside the list may
358
+ * have data we'd skip).
359
+ *
360
+ * `includeInactive: true` (and no `scripts`) widens the refresh to
361
+ * every contract in the repository, including ones marked
362
+ * `inactive` and ones that have dropped out of the watcher's
363
+ * active set. This is a *superset* of the watched set, so the
364
+ * cursor invariant still holds and the cursor advances normally.
365
+ *
366
+ * `after` / `before` apply a caller-supplied time window. The
367
+ * cursor never advances on a windowed query because the window
368
+ * may skip data outside its bounds.
356
369
  */
357
370
  async refreshVtxos(opts) {
358
371
  const contracts = opts?.scripts
@@ -368,6 +381,9 @@ class ContractManager {
368
381
  const hasExplicitWindow = opts?.after !== undefined || opts?.before !== undefined;
369
382
  await this.syncContracts({
370
383
  contracts,
384
+ // Scope-only widener; never set together with explicit
385
+ // `contracts` because `scripts` already names the exact set.
386
+ includeInactive: contracts ? false : opts?.includeInactive,
371
387
  window: hasExplicitWindow
372
388
  ? { after: opts?.after, before: opts?.before }
373
389
  : undefined,
@@ -472,17 +488,21 @@ class ContractManager {
472
488
  async syncContracts(options) {
473
489
  const cursor = await (0, syncCursors_1.getSyncCursor)(this.config.walletRepository);
474
490
  const window = options.window ?? (0, syncCursors_1.computeSyncWindow)(cursor);
475
- // Advance the global cursor only on full-scope, cursor-derived delta
476
- // syncs. A caller-supplied window is targeted (e.g. `refreshVtxos`)
477
- // and must not move the cursor — it may skip data outside its bounds.
478
- // `<=` lets the bootstrap case (cursor=0, window.after=0) write the
479
- // migration marker on first boot; otherwise the marker would never
480
- // be written and every subsequent boot would treat the cursor as
481
- // legacy and re-bootstrap.
491
+ // Advance the global cursor only on cursor-derived delta syncs
492
+ // whose contract scope covers at least the watcher's watched
493
+ // set. Targeted subset queries (caller-supplied `contracts`) and
494
+ // bounded-window queries must not move the cursor they may
495
+ // skip data outside their bounds. `includeInactive` (with no
496
+ // `contracts`) widens the scope rather than narrowing it, so it
497
+ // is cursor-safe. `<=` lets the bootstrap case (cursor=0,
498
+ // window.after=0) write the migration marker on first boot.
482
499
  const mustUpdateCursor = options.contracts === undefined &&
483
500
  options.window === undefined &&
484
501
  (window.after ?? 0) <= cursor;
485
- const contracts = options.contracts ?? this.watcher.getWatchedContracts();
502
+ const contracts = options.contracts ??
503
+ (options.includeInactive
504
+ ? await this.config.contractRepository.getContracts({})
505
+ : this.watcher.getWatchedContracts());
486
506
  const requestStartedAt = Date.now();
487
507
  const result = await this.fetchContractVxosFromIndexer(contracts, options.pageSize, window);
488
508
  if (mustUpdateCursor) {
@@ -566,8 +566,8 @@ class ContractWatcher {
566
566
  const extendedVtxo = (0, utils_1.extendVirtualCoinForContract)(v, state.contract);
567
567
  extended.push({ ...extendedVtxo, contractScript });
568
568
  }
569
- catch {
570
- console.warn("failed to extend vtxo: ", v);
569
+ catch (err) {
570
+ console.warn(`failed to extend vtxo ${v.txid}:${v.vout}`, err);
571
571
  extended.push({ ...v, contractScript });
572
572
  }
573
573
  }
@@ -1,2 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isHDCapableIdentity = isHDCapableIdentity;
4
+ /**
5
+ * Structural type guard for {@link HDCapableIdentity}. Returns `true`
6
+ * when the value exposes the four members the HD wallet flow relies on:
7
+ * `descriptor`, `isOurs`, `signWithDescriptor`, and
8
+ * `signMessageWithDescriptor`. Used by callers that need to opt into
9
+ * the HD path (e.g. installing an `HDDescriptorProvider`) without
10
+ * coupling to a concrete identity class.
11
+ */
12
+ function isHDCapableIdentity(value) {
13
+ if (typeof value !== "object" || value === null)
14
+ return false;
15
+ const v = value;
16
+ return (typeof v.descriptor === "string" &&
17
+ typeof v.isOurs === "function" &&
18
+ typeof v.signWithDescriptor === "function" &&
19
+ typeof v.signMessageWithDescriptor === "function");
20
+ }
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.StaticDescriptorProvider = exports.parseHDDescriptor = exports.extractPubKey = exports.normalizeToDescriptor = exports.isDescriptor = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = void 0;
17
+ exports.StaticDescriptorProvider = exports.isHDCapableIdentity = exports.parseHDDescriptor = exports.extractPubKey = exports.normalizeToDescriptor = exports.isDescriptor = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = void 0;
18
18
  exports.isBatchSignable = isBatchSignable;
19
19
  /** Type guard for identities that support batch signing. */
20
20
  function isBatchSignable(identity) {
@@ -33,6 +33,8 @@ Object.defineProperty(exports, "isDescriptor", { enumerable: true, get: function
33
33
  Object.defineProperty(exports, "normalizeToDescriptor", { enumerable: true, get: function () { return descriptor_1.normalizeToDescriptor; } });
34
34
  Object.defineProperty(exports, "extractPubKey", { enumerable: true, get: function () { return descriptor_1.extractPubKey; } });
35
35
  Object.defineProperty(exports, "parseHDDescriptor", { enumerable: true, get: function () { return descriptor_1.parseHDDescriptor; } });
36
+ var hdCapableIdentity_1 = require("./hdCapableIdentity");
37
+ Object.defineProperty(exports, "isHDCapableIdentity", { enumerable: true, get: function () { return hdCapableIdentity_1.isHDCapableIdentity; } });
36
38
  // Static descriptor provider (wrapper for legacy Identity)
37
39
  var staticDescriptorProvider_1 = require("./staticDescriptorProvider");
38
40
  Object.defineProperty(exports, "StaticDescriptorProvider", { enumerable: true, get: function () { return staticDescriptorProvider_1.StaticDescriptorProvider; } });
@@ -185,6 +185,10 @@ class SeedIdentity {
185
185
  * Returns true when `descriptor` is derived from this identity's seed.
186
186
  * HD descriptors match by account xpub; bare `tr(pubkey)` descriptors
187
187
  * match by raw pubkey. See {@link descriptorIsOurs}.
188
+ *
189
+ * @deprecated Prefer `DescriptorProvider.isOurs()` via
190
+ * `HDDescriptorProvider` for rotating HD wallets or
191
+ * `StaticDescriptorProvider` for legacy single-key wallets.
188
192
  */
189
193
  isOurs(descriptor) {
190
194
  return (0, descriptor_1.descriptorIsOurs)(descriptor, this.descriptor, (0, utils_js_1.pubSchnorr)(this.derivedKey));
@@ -192,6 +196,10 @@ class SeedIdentity {
192
196
  /**
193
197
  * Signs each request with the key derived from its descriptor.
194
198
  * Each descriptor must share this identity's seed ({@link isOurs}).
199
+ *
200
+ * @deprecated Prefer `DescriptorProvider.signWithDescriptor()` via
201
+ * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep
202
+ * this method only as backing implementation for descriptor providers.
195
203
  */
196
204
  async signWithDescriptor(requests) {
197
205
  return requests.map((request) => {
@@ -204,6 +212,10 @@ class SeedIdentity {
204
212
  }
205
213
  /**
206
214
  * Signs a message with the key derived from `descriptor`.
215
+ *
216
+ * @deprecated Prefer `DescriptorProvider.signMessageWithDescriptor()` via
217
+ * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep
218
+ * this method only as backing implementation for descriptor providers.
207
219
  */
208
220
  async signMessageWithDescriptor(descriptor, message, signatureType = "schnorr") {
209
221
  if (!this.isOurs(descriptor)) {
@@ -386,6 +398,10 @@ class ReadonlyDescriptorIdentity {
386
398
  * HD descriptors match by account xpub; bare `tr(pubkey)` descriptors
387
399
  * fall back to comparing against the index-0 x-only pubkey. See
388
400
  * {@link descriptorIsOurs}.
401
+ *
402
+ * @deprecated Prefer `DescriptorProvider.isOurs()` via
403
+ * `HDDescriptorProvider` for rotating HD wallets or
404
+ * `StaticDescriptorProvider` for legacy single-key wallets.
389
405
  */
390
406
  isOurs(descriptor) {
391
407
  return (0, descriptor_1.descriptorIsOurs)(descriptor, this.descriptor, this.indexZero.pubkey);
package/dist/cjs/index.js CHANGED
@@ -37,8 +37,8 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.DEFAULT_MESSAGE_TIMEOUTS = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.ServiceWorkerTimeoutError = exports.MessageBusNotInitializedError = exports.MESSAGE_BUS_NOT_INITIALIZED = exports.DelegatorNotConfiguredError = exports.ReadonlyWalletError = exports.WalletNotInitializedError = exports.WalletMessageHandler = exports.MessageBus = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DelegateVtxo = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.WsElectrumChainSource = exports.ElectrumOnchainProvider = exports.ELECTRUM_TCP_HOST = exports.ELECTRUM_WS_URL = exports.EsploraProvider = exports.ESPLORA_URL = exports.RestDelegatorProvider = exports.DelegatorManagerImpl = exports.HDDescriptorProvider = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.isBatchSignable = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = exports.asset = void 0;
40
- exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.sequenceToTimelock = exports.timelockToSequence = exports.TxWeightEstimator = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = void 0;
41
- exports.isArkContract = exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = void 0;
40
+ exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.MissingSigningDescriptorError = exports.DescriptorSigningProviderMissingError = exports.maybeArkError = exports.ArkError = exports.sequenceToTimelock = exports.timelockToSequence = exports.TxWeightEstimator = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = void 0;
41
+ exports.isArkContract = exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = void 0;
42
42
  const transaction_1 = require("./utils/transaction");
43
43
  Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
44
44
  const singleKey_1 = require("./identity/singleKey");
@@ -76,6 +76,8 @@ const wallet_2 = require("./wallet/wallet");
76
76
  Object.defineProperty(exports, "Wallet", { enumerable: true, get: function () { return wallet_2.Wallet; } });
77
77
  Object.defineProperty(exports, "ReadonlyWallet", { enumerable: true, get: function () { return wallet_2.ReadonlyWallet; } });
78
78
  Object.defineProperty(exports, "waitForIncomingFunds", { enumerable: true, get: function () { return wallet_2.waitForIncomingFunds; } });
79
+ Object.defineProperty(exports, "DescriptorSigningProviderMissingError", { enumerable: true, get: function () { return wallet_2.DescriptorSigningProviderMissingError; } });
80
+ Object.defineProperty(exports, "MissingSigningDescriptorError", { enumerable: true, get: function () { return wallet_2.MissingSigningDescriptorError; } });
79
81
  const txTree_1 = require("./tree/txTree");
80
82
  Object.defineProperty(exports, "TxTree", { enumerable: true, get: function () { return txTree_1.TxTree; } });
81
83
  const ramps_1 = require("./wallet/ramps");
@@ -29,10 +29,11 @@ class DelegatorManagerImpl {
29
29
  // fetch server and delegator info once, shared across all groups
30
30
  const arkInfo = await this.arkInfoProvider.getInfo();
31
31
  const delegateInfo = await this.delegatorProvider.getDelegateInfo();
32
- // keep only vtxos that can be signed by the delegate
33
- const eligible = vtxos
34
- .filter((v) => findDelegateTapLeaf(v, delegateInfo.pubkey) !== undefined)
35
- .map((v) => v);
32
+ // keep only vtxos that can be signed by the delegate. The guard
33
+ // narrows ContractVtxo (with optional taproot fields) to the
34
+ // ExtendedVirtualCoin shape required by makeDelegateForfeitTx.
35
+ const eligible = vtxos.filter((v) => isAnnotated(v) &&
36
+ findDelegateTapLeaf(v, delegateInfo.pubkey) !== undefined);
36
37
  if (eligible.length === 0) {
37
38
  return { delegated: [], failed: [] };
38
39
  }
@@ -300,3 +301,8 @@ function findDelegateTapLeaf(vtxo, delegatePubkey) {
300
301
  return arkTapscript.params.pubkeys.map(base_1.hex.encode).includes(pk);
301
302
  });
302
303
  }
304
+ function isAnnotated(v) {
305
+ return (v.tapTree !== undefined &&
306
+ v.forfeitTapLeafScript !== undefined &&
307
+ v.intentTapLeafScript !== undefined);
308
+ }
@@ -4,6 +4,7 @@ exports.HDDescriptorProvider = void 0;
4
4
  const descriptors_scure_1 = require("@bitcoinerlab/descriptors-scure");
5
5
  const descriptor_1 = require("../identity/descriptor");
6
6
  const syncCursors_1 = require("../utils/syncCursors");
7
+ const walletReceiveRotator_1 = require("./walletReceiveRotator");
7
8
  /** Settings key under {@link WalletState.settings} where HD state lives. */
8
9
  const HD_SETTINGS_KEY = "hd";
9
10
  /**
@@ -63,6 +64,25 @@ class HDDescriptorProvider {
63
64
  return this.materializeAt(next);
64
65
  });
65
66
  }
67
+ /**
68
+ * Re-derive the descriptor at the most recently allocated index
69
+ * WITHOUT advancing — i.e. read the same descriptor
70
+ * `getNextSigningDescriptor` last returned. Returns `undefined`
71
+ * when no descriptor has ever been allocated on this repo.
72
+ *
73
+ * Used by the boot path to keep the wallet's display address
74
+ * stable across restarts: when no tagged display contract exists
75
+ * (e.g. a fresh wallet that hasn't rotated yet, or a wallet whose
76
+ * baseline-only repo carries no rotation history), the boot should
77
+ * re-derive the existing index rather than burn a new one.
78
+ */
79
+ async getCurrentSigningDescriptor() {
80
+ const state = await this.walletRepository.getWalletState();
81
+ const settings = this.parseSettings(state ?? {});
82
+ if (settings.lastIndexUsed === undefined)
83
+ return undefined;
84
+ return this.materializeAt(settings.lastIndexUsed);
85
+ }
66
86
  /**
67
87
  * Returns true when the given descriptor is derivable from this wallet's
68
88
  * seed. Delegates to the underlying identity, which handles both HD and
@@ -83,6 +103,15 @@ class HDDescriptorProvider {
83
103
  async signMessageWithDescriptor(descriptor, message, signatureType = "schnorr") {
84
104
  return this.identity.signMessageWithDescriptor(descriptor, message, signatureType);
85
105
  }
106
+ /**
107
+ * HD providers participate in receive rotation. The default
108
+ * factory boot (contract-repo lookup → allocate fresh descriptor)
109
+ * is exactly what we want, so this just delegates to
110
+ * {@link WalletReceiveRotator.defaultBoot}.
111
+ */
112
+ async createReceiveRotator(opts) {
113
+ return walletReceiveRotator_1.WalletReceiveRotator.defaultBoot(this, opts);
114
+ }
86
115
  // ── internals ────────────────────────────────────────────────────
87
116
  /**
88
117
  * Substitute the wildcard in the identity's account-descriptor template
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InputSignerRouter = void 0;
4
+ const base_1 = require("@scure/base");
5
+ const signingErrors_1 = require("./signingErrors");
6
+ const DESCRIPTOR_CAPABLE_CONTRACT_TYPES = new Set(["default", "delegate"]);
7
+ /**
8
+ * Routes PSBT inputs to the correct signer based on the owning contract.
9
+ * Inputs whose script matches a `default`/`delegate` contract with a
10
+ * non-baseline owner are sent to {@link DescriptorProvider}; everything
11
+ * else (baseline-owned contracts, non-default/non-delegate contracts,
12
+ * and the boarding script) is sent to {@link Identity}. Inputs with no
13
+ * matching contract and no boarding match are silently skipped, matching
14
+ * how the wallet historically handled cosigner/connector inputs.
15
+ */
16
+ class InputSignerRouter {
17
+ constructor(deps) {
18
+ this.deps = deps;
19
+ }
20
+ async sign(tx, jobs) {
21
+ if (jobs.length === 0)
22
+ return tx;
23
+ const distinctScripts = Array.from(new Set(jobs.map((j) => base_1.hex.encode(j.lookupScript))));
24
+ const contracts = await this.deps.contractRepository.getContracts({
25
+ script: distinctScripts,
26
+ });
27
+ // Repo may yield duplicates if seeded oddly; keep the first one
28
+ // for each script to match the wallet's historical behaviour.
29
+ const scriptToContract = new Map();
30
+ for (const contract of contracts) {
31
+ if (!scriptToContract.has(contract.script)) {
32
+ scriptToContract.set(contract.script, contract);
33
+ }
34
+ }
35
+ const baselinePubKeyHex = base_1.hex.encode(await this.deps.identity.xOnlyPublicKey());
36
+ const boardingScriptHex = base_1.hex.encode(this.deps.boardingPkScript);
37
+ const identityIndexes = [];
38
+ const descriptorGroups = new Map();
39
+ for (const job of jobs) {
40
+ const scriptHex = base_1.hex.encode(job.lookupScript);
41
+ const contract = scriptToContract.get(scriptHex);
42
+ if (!contract) {
43
+ if (scriptHex === boardingScriptHex) {
44
+ identityIndexes.push(job.index);
45
+ }
46
+ continue;
47
+ }
48
+ if (!DESCRIPTOR_CAPABLE_CONTRACT_TYPES.has(contract.type)) {
49
+ identityIndexes.push(job.index);
50
+ continue;
51
+ }
52
+ // `baselinePubKeyHex` is freshly produced by `hex.encode`,
53
+ // so it is already lowercase. `contract.params.pubKey` is
54
+ // persisted data: a migration or custom repository adapter
55
+ // could legitimately store it uppercase, so canonicalize
56
+ // before comparing to match the legacy router behaviour.
57
+ const ownerPubKeyHex = contract.params.pubKey?.toLowerCase();
58
+ if (ownerPubKeyHex && ownerPubKeyHex === baselinePubKeyHex) {
59
+ identityIndexes.push(job.index);
60
+ continue;
61
+ }
62
+ const descriptor = contract.metadata?.signingDescriptor;
63
+ if (typeof descriptor !== "string" || descriptor.length === 0) {
64
+ throw new signingErrors_1.MissingSigningDescriptorError(contract.script, contract.type);
65
+ }
66
+ const bucket = descriptorGroups.get(descriptor);
67
+ if (bucket) {
68
+ bucket.push(job.index);
69
+ }
70
+ else {
71
+ descriptorGroups.set(descriptor, [job.index]);
72
+ }
73
+ }
74
+ let signed = tx;
75
+ if (identityIndexes.length > 0) {
76
+ signed = await this.deps.identity.sign(signed, identityIndexes);
77
+ }
78
+ if (descriptorGroups.size > 0) {
79
+ if (!this.deps.descriptorProvider) {
80
+ throw new signingErrors_1.DescriptorSigningProviderMissingError();
81
+ }
82
+ const sortedDescriptors = Array.from(descriptorGroups.keys()).sort();
83
+ for (const descriptor of sortedDescriptors) {
84
+ const indexes = descriptorGroups.get(descriptor);
85
+ const [next] = await this.deps.descriptorProvider.signWithDescriptor([
86
+ {
87
+ tx: signed,
88
+ descriptor,
89
+ inputIndexes: indexes,
90
+ },
91
+ ]);
92
+ signed = next;
93
+ }
94
+ }
95
+ return signed;
96
+ }
97
+ }
98
+ exports.InputSignerRouter = InputSignerRouter;
@@ -921,6 +921,7 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
921
921
  indexerUrl: options.indexerUrl,
922
922
  esploraUrl: options.esploraUrl,
923
923
  settlementConfig: options.settlementConfig,
924
+ walletMode: options.walletMode,
924
925
  watcherConfig: options.watcherConfig,
925
926
  messageTimeouts,
926
927
  };
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DescriptorSigningProviderMissingError = exports.MissingSigningDescriptorError = void 0;
4
+ /**
5
+ * Thrown when a rotated contract (default or delegate) is missing the
6
+ * metadata.signingDescriptor required to route it to a descriptor-aware
7
+ * signer.
8
+ */
9
+ class MissingSigningDescriptorError extends Error {
10
+ constructor(contractScript, contractType) {
11
+ super(`Cannot sign input for ${contractType} contract ${contractScript}: ` +
12
+ `metadata.signingDescriptor is missing. This wallet was rotated ` +
13
+ `on an earlier build that did not persist signing descriptors. ` +
14
+ `Manually set metadata.signingDescriptor on the contract record, ` +
15
+ `or restore from a pre-rotation snapshot.`);
16
+ this.contractScript = contractScript;
17
+ this.contractType = contractType;
18
+ this.name = "MissingSigningDescriptorError";
19
+ }
20
+ }
21
+ exports.MissingSigningDescriptorError = MissingSigningDescriptorError;
22
+ /**
23
+ * Thrown when an input needs descriptor-aware signing but no
24
+ * DescriptorProvider was wired into the wallet.
25
+ */
26
+ class DescriptorSigningProviderMissingError extends Error {
27
+ constructor() {
28
+ super("Descriptor signing requested but no DescriptorProvider was wired into this wallet");
29
+ this.name = "DescriptorSigningProviderMissingError";
30
+ }
31
+ }
32
+ exports.DescriptorSigningProviderMissingError = DescriptorSigningProviderMissingError;
@@ -231,7 +231,11 @@ async function prepareUnrollTransaction(wallet, vtxoTxIds, outputAddress) {
231
231
  if (!feeRate || feeRate < wallet_1.Wallet.MIN_FEE_RATE) {
232
232
  feeRate = wallet_1.Wallet.MIN_FEE_RATE;
233
233
  }
234
- const feeAmount = txWeightEstimator.vsize().fee(BigInt(feeRate));
234
+ // Esplora returns a `number` and bitcoind regtest sometimes reports
235
+ // fractional sat/vB (e.g. 1.006). `BigInt(1.006)` throws RangeError
236
+ // — round up so we always pay AT LEAST the advertised rate and
237
+ // satisfy BigInt's integer requirement.
238
+ const feeAmount = txWeightEstimator.vsize().fee(BigInt(Math.ceil(feeRate)));
235
239
  if (feeAmount > totalAmount) {
236
240
  throw new Error("fee amount is greater than the total amount");
237
241
  }