@arkade-os/sdk 0.4.19 → 0.4.21

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 (61) hide show
  1. package/dist/cjs/contracts/contractWatcher.js +33 -3
  2. package/dist/cjs/contracts/handlers/default.js +10 -3
  3. package/dist/cjs/contracts/handlers/helpers.js +47 -5
  4. package/dist/cjs/contracts/handlers/vhtlc.js +4 -2
  5. package/dist/cjs/identity/descriptor.js +98 -0
  6. package/dist/cjs/identity/descriptorProvider.js +2 -0
  7. package/dist/cjs/identity/index.js +15 -1
  8. package/dist/cjs/identity/seedIdentity.js +91 -6
  9. package/dist/cjs/identity/serialize.js +166 -0
  10. package/dist/cjs/identity/staticDescriptorProvider.js +65 -0
  11. package/dist/cjs/index.js +6 -3
  12. package/dist/cjs/providers/ark.js +71 -46
  13. package/dist/cjs/providers/electrum.js +663 -0
  14. package/dist/cjs/providers/indexer.js +60 -43
  15. package/dist/cjs/providers/utils.js +62 -12
  16. package/dist/cjs/wallet/ramps.js +1 -1
  17. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +10 -0
  18. package/dist/cjs/wallet/serviceWorker/wallet.js +137 -91
  19. package/dist/cjs/wallet/vtxo-manager.js +56 -8
  20. package/dist/cjs/wallet/wallet.js +130 -156
  21. package/dist/cjs/worker/messageBus.js +200 -56
  22. package/dist/esm/contracts/contractWatcher.js +33 -3
  23. package/dist/esm/contracts/handlers/default.js +10 -3
  24. package/dist/esm/contracts/handlers/helpers.js +47 -5
  25. package/dist/esm/contracts/handlers/vhtlc.js +4 -2
  26. package/dist/esm/identity/descriptor.js +92 -0
  27. package/dist/esm/identity/descriptorProvider.js +1 -0
  28. package/dist/esm/identity/index.js +6 -1
  29. package/dist/esm/identity/seedIdentity.js +89 -6
  30. package/dist/esm/identity/serialize.js +159 -0
  31. package/dist/esm/identity/staticDescriptorProvider.js +61 -0
  32. package/dist/esm/index.js +2 -1
  33. package/dist/esm/providers/ark.js +72 -47
  34. package/dist/esm/providers/electrum.js +658 -0
  35. package/dist/esm/providers/indexer.js +61 -44
  36. package/dist/esm/providers/utils.js +61 -12
  37. package/dist/esm/wallet/ramps.js +1 -1
  38. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +10 -0
  39. package/dist/esm/wallet/serviceWorker/wallet.js +137 -91
  40. package/dist/esm/wallet/vtxo-manager.js +56 -8
  41. package/dist/esm/wallet/wallet.js +130 -156
  42. package/dist/esm/worker/messageBus.js +201 -57
  43. package/dist/types/contracts/contractWatcher.d.ts +3 -0
  44. package/dist/types/contracts/handlers/default.d.ts +1 -1
  45. package/dist/types/contracts/handlers/helpers.d.ts +1 -1
  46. package/dist/types/contracts/types.d.ts +11 -3
  47. package/dist/types/identity/descriptor.d.ts +35 -0
  48. package/dist/types/identity/descriptorProvider.d.ts +28 -0
  49. package/dist/types/identity/index.d.ts +7 -1
  50. package/dist/types/identity/seedIdentity.d.ts +41 -4
  51. package/dist/types/identity/serialize.d.ts +84 -0
  52. package/dist/types/identity/staticDescriptorProvider.d.ts +18 -0
  53. package/dist/types/index.d.ts +4 -2
  54. package/dist/types/providers/electrum.d.ts +212 -0
  55. package/dist/types/providers/utils.d.ts +10 -5
  56. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +11 -2
  57. package/dist/types/wallet/serviceWorker/wallet.d.ts +27 -10
  58. package/dist/types/wallet/vtxo-manager.d.ts +2 -0
  59. package/dist/types/wallet/wallet.d.ts +7 -6
  60. package/dist/types/worker/messageBus.d.ts +68 -8
  61. package/package.json +3 -2
@@ -5,6 +5,8 @@ import { hex } from "@scure/base";
5
5
  import { getSequence } from '../script/base.js';
6
6
  import { Transaction } from '../utils/transaction.js';
7
7
  import { TxWeightEstimator } from '../utils/txSizeEstimator.js';
8
+ import { Estimator } from '../arkfee/index.js';
9
+ import { ArkAddress } from '../script/address.js';
8
10
  /**
9
11
  * Return whether a wallet exposes the properties required for boarding input sweep operations.
10
12
  *
@@ -14,6 +16,7 @@ import { TxWeightEstimator } from '../utils/txSizeEstimator.js';
14
16
  function isSweepCapable(wallet) {
15
17
  return ("boardingTapscript" in wallet &&
16
18
  "onchainProvider" in wallet &&
19
+ "arkProvider" in wallet &&
17
20
  "network" in wallet);
18
21
  }
19
22
  /**
@@ -24,7 +27,7 @@ function isSweepCapable(wallet) {
24
27
  */
25
28
  function assertSweepCapable(wallet) {
26
29
  if (!isSweepCapable(wallet)) {
27
- throw new Error("Boarding UTXO sweep requires a Wallet instance with boardingTapscript, onchainProvider, and network");
30
+ throw new Error("Boarding UTXO sweep requires a Wallet instance with boardingTapscript, onchainProvider, arkProvider, and network");
28
31
  }
29
32
  }
30
33
  /**
@@ -622,6 +625,10 @@ export class VtxoManager {
622
625
  getOnchainProvider() {
623
626
  return this.getSweepWallet().onchainProvider;
624
627
  }
628
+ /** Returns the Ark provider for intent fee and server info lookups. */
629
+ getArkProvider() {
630
+ return this.getSweepWallet().arkProvider;
631
+ }
625
632
  /** Returns the Bitcoin network configuration from the wallet. */
626
633
  getNetwork() {
627
634
  return this.getSweepWallet().network;
@@ -891,13 +898,54 @@ export class VtxoManager {
891
898
  return;
892
899
  }
893
900
  const dustAmount = getDustAmount(this.wallet);
894
- const boardingTotal = unsettledBoarding.reduce((sum, u) => sum + BigInt(u.value), 0n);
895
- const vtxoTotal = expiringVtxos.reduce((sum, v) => sum + BigInt(v.value), 0n);
896
- const totalAmount = boardingTotal + vtxoTotal;
897
- if (totalAmount < dustAmount)
901
+ // Fetch server intent-fee config so each input/output can be priced.
902
+ // Without this, settle sends `outputAmount = sum(inputs)` and the
903
+ // server rejects with INTENT_INSUFFICIENT_FEE whenever the operator
904
+ // charges non-zero intent fees.
905
+ const { fees } = await this.getArkProvider().getInfo();
906
+ const estimator = new Estimator(fees.intentFee);
907
+ let totalAmount = 0n;
908
+ const filteredBoarding = [];
909
+ for (const u of unsettledBoarding) {
910
+ const inputFee = estimator.evalOnchainInput({
911
+ amount: BigInt(u.value),
912
+ });
913
+ if (inputFee.value >= BigInt(u.value)) {
914
+ // Fee exceeds input value — including it would drain the output.
915
+ continue;
916
+ }
917
+ filteredBoarding.push(u);
918
+ totalAmount += BigInt(u.value) - BigInt(inputFee.satoshis);
919
+ }
920
+ const filteredVtxos = [];
921
+ for (const v of expiringVtxos) {
922
+ const inputFee = estimator.evalOffchainInput({
923
+ amount: BigInt(v.value),
924
+ type: v.virtualStatus.state === "swept" ? "recoverable" : "vtxo",
925
+ weight: 0,
926
+ birth: v.createdAt,
927
+ expiry: v.virtualStatus.batchExpiry
928
+ ? new Date(v.virtualStatus.batchExpiry)
929
+ : undefined,
930
+ });
931
+ if (inputFee.satoshis >= v.value) {
932
+ continue;
933
+ }
934
+ filteredVtxos.push(v);
935
+ totalAmount += BigInt(v.value) - BigInt(inputFee.satoshis);
936
+ }
937
+ if (filteredBoarding.length === 0 && filteredVtxos.length === 0) {
898
938
  return;
939
+ }
899
940
  const arkAddress = await this.wallet.getAddress();
900
- const includesVtxos = expiringVtxos.length > 0;
941
+ const outputFee = estimator.evalOffchainOutput({
942
+ amount: totalAmount,
943
+ script: hex.encode(ArkAddress.decode(arkAddress).pkScript),
944
+ });
945
+ totalAmount -= BigInt(outputFee.satoshis);
946
+ if (totalAmount < dustAmount)
947
+ return;
948
+ const includesVtxos = filteredVtxos.length > 0;
901
949
  // Block the event-driven renewal path while this settle is in flight
902
950
  // when VTXOs are part of the intent. Mirrors renewVtxos()'s guard so
903
951
  // the two paths can't race on the same VTXO inputs.
@@ -909,11 +957,11 @@ export class VtxoManager {
909
957
  try {
910
958
  try {
911
959
  await this.wallet.settle({
912
- inputs: [...unsettledBoarding, ...expiringVtxos],
960
+ inputs: [...filteredBoarding, ...filteredVtxos],
913
961
  outputs: [{ address: arkAddress, amount: totalAmount }],
914
962
  });
915
963
  // Mark boarding inputs as known only after successful settle.
916
- for (const u of unsettledBoarding) {
964
+ for (const u of filteredBoarding) {
917
965
  this.knownBoardingUtxos.add(`${u.txid}:${u.vout}`);
918
966
  }
919
967
  success = true;
@@ -34,10 +34,28 @@ 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 { clearSyncCursor, 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.
37
+ // Historical unilateral exit delay for mainnet (~7 days in seconds).
38
+ // Kept so existing wallets can still discover and spend VTXOs sent to the
39
+ // legacy address after arkd starts advertising a different delay.
40
40
  const MAINNET_UNILATERAL_EXIT_DELAY = 605184n;
41
+ function delayToTimelock(delay) {
42
+ return {
43
+ value: delay,
44
+ type: delay < 512n ? "blocks" : "seconds",
45
+ };
46
+ }
47
+ function dedupeTimelocks(timelocks) {
48
+ const seen = new Set();
49
+ const deduped = [];
50
+ for (const timelock of timelocks) {
51
+ const sequence = timelockToSequence(timelock).toString();
52
+ if (seen.has(sequence))
53
+ continue;
54
+ seen.add(sequence);
55
+ deduped.push(timelock);
56
+ }
57
+ return deduped;
58
+ }
41
59
  /**
42
60
  * Type guard function to check if an identity has a toReadonly method.
43
61
  */
@@ -51,7 +69,7 @@ export class ReadonlyWallet {
51
69
  get assetManager() {
52
70
  return this._assetManager;
53
71
  }
54
- constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig) {
72
+ constructor(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig, walletContractTimelocks) {
55
73
  this.identity = identity;
56
74
  this.network = network;
57
75
  this.onchainProvider = onchainProvider;
@@ -84,6 +102,15 @@ export class ReadonlyWallet {
84
102
  }
85
103
  this.watcherConfig = watcherConfig;
86
104
  this._assetManager = new ReadonlyAssetManager(this.indexerProvider);
105
+ // Defensive for direct-construction callers; setupWalletConfig already
106
+ // passes a deduped list through the public create() factories.
107
+ this.walletContractTimelocks =
108
+ walletContractTimelocks && walletContractTimelocks.length > 0
109
+ ? dedupeTimelocks(walletContractTimelocks)
110
+ : [
111
+ this.offchainTapscript.options.csvTimelock ??
112
+ DefaultVtxo.Script.DEFAULT_TIMELOCK,
113
+ ];
87
114
  }
88
115
  /**
89
116
  * Protected helper to set up shared wallet configuration.
@@ -139,17 +166,17 @@ export class ReadonlyWallet {
139
166
  throw new Error("invalid exitTimelock");
140
167
  }
141
168
  }
142
- // On mainnet, pin the unilateral exit delay to the historical value so
143
- // that addresses derived by existing wallets remain stable even if the
144
- // server starts advertising a shorter delay.
145
- const unilateralExitDelay = info.network === "bitcoin"
146
- ? MAINNET_UNILATERAL_EXIT_DELAY
147
- : info.unilateralExitDelay;
169
+ const arkdExitTimelock = delayToTimelock(info.unilateralExitDelay);
148
170
  // create unilateral exit timelock
149
- const exitTimelock = config.exitTimelock ?? {
150
- value: unilateralExitDelay,
151
- type: unilateralExitDelay < 512n ? "blocks" : "seconds",
152
- };
171
+ const exitTimelock = config.exitTimelock ?? arkdExitTimelock;
172
+ const walletContractTimelocks = config.exitTimelock
173
+ ? [exitTimelock]
174
+ : dedupeTimelocks([
175
+ arkdExitTimelock,
176
+ ...(info.network === "bitcoin"
177
+ ? [delayToTimelock(MAINNET_UNILATERAL_EXIT_DELAY)]
178
+ : []),
179
+ ]);
153
180
  // validate boarding timelock passed in config if any
154
181
  if (config.boardingTimelock) {
155
182
  const { value, type } = config.boardingTimelock;
@@ -199,6 +226,7 @@ export class ReadonlyWallet {
199
226
  contractRepository,
200
227
  info,
201
228
  delegatorProvider: config.delegatorProvider,
229
+ walletContractTimelocks,
202
230
  };
203
231
  }
204
232
  /**
@@ -213,14 +241,14 @@ export class ReadonlyWallet {
213
241
  throw new Error("Invalid configured public key");
214
242
  }
215
243
  const setup = await ReadonlyWallet.setupWalletConfig(config, pubkey);
216
- return new ReadonlyWallet(config.identity, setup.network, setup.onchainProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, setup.dustAmount, setup.walletRepository, setup.contractRepository, setup.delegatorProvider, config.watcherConfig);
244
+ return new ReadonlyWallet(config.identity, setup.network, setup.onchainProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, setup.dustAmount, setup.walletRepository, setup.contractRepository, setup.delegatorProvider, config.watcherConfig, setup.walletContractTimelocks);
217
245
  }
218
246
  get arkAddress() {
219
247
  return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
220
248
  }
221
249
  /**
222
- * Get the contract script for the wallet's default address.
223
- * This is the pkScript hex, used to identify the wallet in ContractManager.
250
+ * Get the pkScript hex for the wallet's primary offchain address.
251
+ * For the full wallet-owned script set registered in ContractManager, use getWalletScripts().
224
252
  */
225
253
  get defaultContractScript() {
226
254
  return hex.encode(this.offchainTapscript.pkScript);
@@ -464,56 +492,39 @@ export class ReadonlyWallet {
464
492
  });
465
493
  }
466
494
  if (this.indexerProvider && arkAddress) {
467
- const walletScripts = await this.getWalletScripts();
468
- const subscriptionId = await this.indexerProvider.subscribeForScripts(walletScripts);
469
- const abortController = new AbortController();
470
- const subscription = this.indexerProvider.getSubscription(subscriptionId, abortController.signal);
471
- indexerStopFunc = async () => {
472
- abortController.abort();
473
- await this.indexerProvider?.unsubscribeForScripts(subscriptionId);
474
- };
475
- // Handle subscription updates asynchronously without blocking.
476
- // Subscription covers all wallet scripts (default + delegate) plus
477
- // any additional registered contracts. Virtual outputs carry a
478
- // `script` field from the indexer which the contract manager
479
- // resolves to the owning contract so the extension uses the
480
- // correct forfeit/intent tapscripts.
481
- (async () => {
482
- try {
483
- const cm = await this.getContractManager();
484
- for await (const update of subscription) {
485
- if (update.newVtxos?.length === 0 &&
486
- update.spentVtxos?.length === 0) {
487
- continue;
488
- }
489
- // Isolate per-update annotation failures (e.g. a VTXO
490
- // arriving for a contract we haven't registered yet).
491
- // Without this a single bad update would kill the
492
- // for-await loop and silently drop every subsequent
493
- // subscription event for the session.
494
- try {
495
- // Default to `[]` so a one-sided update (e.g.
496
- // only `newVtxos`) doesn't pass `undefined` into
497
- // annotateVtxos and throw on `.length`.
498
- const [newVtxos, spentVtxos] = await Promise.all([
499
- cm.annotateVtxos(update.newVtxos ?? []),
500
- cm.annotateVtxos(update.spentVtxos ?? []),
501
- ]);
502
- eventCallback({
503
- type: "vtxo",
504
- newVtxos,
505
- spentVtxos,
506
- });
507
- }
508
- catch (error) {
509
- console.warn("Dropping subscription update after annotation failed; next sync will reconcile:", error);
510
- }
511
- }
495
+ // Share the ContractWatcher's single subscription instead of
496
+ // opening a second SSE stream.
497
+ const cm = await this.getContractManager();
498
+ // Serialize annotation+notification: parallel `annotateVtxos`
499
+ // awaits could resolve out of order and deliver eventCallback
500
+ // calls in the wrong sequence (e.g. `vtxo_spent` before its
501
+ // matching `vtxo_received`).
502
+ let annotationQueue = Promise.resolve();
503
+ indexerStopFunc = cm.onContractEvent((event) => {
504
+ if (event.type !== "vtxo_received" &&
505
+ event.type !== "vtxo_spent") {
506
+ return;
512
507
  }
513
- catch (error) {
514
- console.error("Subscription error:", error);
508
+ if (event.contract.type !== "default" &&
509
+ event.contract.type !== "delegate") {
510
+ return;
515
511
  }
516
- })();
512
+ // `event.vtxos` carries placeholder tapscript fields from
513
+ // the watcher; `annotateVtxos` fills them in.
514
+ annotationQueue = annotationQueue.then(async () => {
515
+ try {
516
+ const annotated = await cm.annotateVtxos(event.vtxos);
517
+ eventCallback({
518
+ type: "vtxo",
519
+ newVtxos: event.type === "vtxo_received" ? annotated : [],
520
+ spentVtxos: event.type === "vtxo_spent" ? annotated : [],
521
+ });
522
+ }
523
+ catch (error) {
524
+ console.warn("Dropping subscription update after annotation failed; next sync will reconcile:", error);
525
+ }
526
+ });
527
+ });
517
528
  }
518
529
  const stopFunc = () => {
519
530
  onchainStopFunc?.();
@@ -540,27 +551,13 @@ export class ReadonlyWallet {
540
551
  /**
541
552
  * Get all pkScript hex strings for the wallet's own addresses
542
553
  * (both delegate and non-delegate, current and historical).
543
- * Falls back to only the current script if ContractManager is not yet initialized.
544
554
  */
545
555
  async getWalletScripts() {
546
- // Only use the contract manager if it's already initialized or
547
- // currently initializing never trigger initialization here to
548
- // avoid blocking callers that don't need it.
549
- if (this._contractManager || this._contractManagerInitializing) {
550
- try {
551
- const manager = await this.getContractManager();
552
- const contracts = await manager.getContracts({
553
- type: ["default", "delegate"],
554
- });
555
- if (contracts.length > 0) {
556
- return contracts.map((c) => c.script);
557
- }
558
- }
559
- catch {
560
- // fall through to current script only
561
- }
562
- }
563
- return [hex.encode(this.offchainTapscript.pkScript)];
556
+ const manager = await this.getContractManager();
557
+ const contracts = await manager.getContracts({
558
+ type: ["default", "delegate"],
559
+ });
560
+ return contracts.map((c) => c.script);
564
561
  }
565
562
  /**
566
563
  * Build a map of scriptHex → VtxoScript for all wallet contracts,
@@ -568,26 +565,17 @@ export class ReadonlyWallet {
568
565
  */
569
566
  async getScriptMap() {
570
567
  const map = new Map();
571
- // Always include the current script
572
- const currentScriptHex = hex.encode(this.offchainTapscript.pkScript);
573
- map.set(currentScriptHex, this.offchainTapscript);
574
- if (this._contractManager) {
575
- try {
576
- const contracts = await this._contractManager.getContracts({
577
- type: ["default", "delegate"],
578
- });
579
- for (const contract of contracts) {
580
- if (map.has(contract.script))
581
- continue;
582
- const handler = contractHandlers.get(contract.type);
583
- if (handler) {
584
- const script = handler.createScript(contract.params);
585
- map.set(contract.script, script);
586
- }
587
- }
588
- }
589
- catch {
590
- // ContractManager error — only current script in map
568
+ const manager = await this.getContractManager();
569
+ const contracts = await manager.getContracts({
570
+ type: ["default", "delegate"],
571
+ });
572
+ for (const contract of contracts) {
573
+ if (map.has(contract.script))
574
+ continue;
575
+ const handler = contractHandlers.get(contract.type);
576
+ if (handler) {
577
+ const script = handler.createScript(contract.params);
578
+ map.set(contract.script, script);
591
579
  }
592
580
  }
593
581
  return map;
@@ -655,62 +643,48 @@ export class ReadonlyWallet {
655
643
  walletRepository: this.walletRepository,
656
644
  watcherConfig: this.watcherConfig,
657
645
  });
658
- // Register the wallet's current address as a contract
659
- const csvTimelock = this.offchainTapscript.options.csvTimelock ??
660
- DefaultVtxo.Script.DEFAULT_TIMELOCK;
661
- const csvTimelockStr = timelockToSequence(csvTimelock).toString();
662
- const isDelegateScript = this.offchainTapscript instanceof DelegateVtxo.Script;
663
- if (isDelegateScript) {
664
- const delegateScript = this
665
- .offchainTapscript;
666
- // Register the delegate contract (current address)
667
- await manager.createContract({
668
- type: "delegate",
669
- params: {
670
- pubKey: hex.encode(delegateScript.options.pubKey),
671
- serverPubKey: hex.encode(delegateScript.options.serverPubKey),
672
- delegatePubKey: hex.encode(delegateScript.options.delegatePubKey),
673
- csvTimelock: csvTimelockStr,
674
- },
675
- script: this.defaultContractScript,
676
- address: await this.getAddress(),
677
- state: "active",
678
- });
679
- // Also register the non-delegate version so old virtual outputs remain visible
680
- const nonDelegateScript = new DefaultVtxo.Script({
681
- pubKey: delegateScript.options.pubKey,
682
- serverPubKey: delegateScript.options.serverPubKey,
646
+ for (const csvTimelock of this.walletContractTimelocks) {
647
+ const csvTimelockStr = timelockToSequence(csvTimelock).toString();
648
+ const defaultScript = new DefaultVtxo.Script({
649
+ pubKey: this.offchainTapscript.options.pubKey,
650
+ serverPubKey: this.offchainTapscript.options.serverPubKey,
683
651
  csvTimelock,
684
652
  });
685
653
  await manager.createContract({
686
654
  type: "default",
687
655
  params: {
688
- pubKey: hex.encode(delegateScript.options.pubKey),
689
- serverPubKey: hex.encode(delegateScript.options.serverPubKey),
656
+ pubKey: hex.encode(defaultScript.options.pubKey),
657
+ serverPubKey: hex.encode(defaultScript.options.serverPubKey),
690
658
  csvTimelock: csvTimelockStr,
691
659
  },
692
- script: hex.encode(nonDelegateScript.pkScript),
693
- address: nonDelegateScript
660
+ script: hex.encode(defaultScript.pkScript),
661
+ address: defaultScript
694
662
  .address(this.network.hrp, this.arkServerPublicKey)
695
663
  .encode(),
696
664
  state: "active",
697
665
  });
698
- }
699
- else {
700
- // Register the default contract (current address)
701
- await manager.createContract({
702
- type: "default",
703
- params: {
704
- pubKey: hex.encode(this.offchainTapscript.options.pubKey),
705
- serverPubKey: hex.encode(this.offchainTapscript.options.serverPubKey),
706
- csvTimelock: csvTimelockStr,
707
- },
708
- script: this.defaultContractScript,
709
- address: await this.getAddress(),
710
- state: "active",
711
- });
712
- // Any old "delegate" contract from a prior wallet incarnation
713
- // is already loaded by ContractManager.initialize() from ContractRepository
666
+ if (this.offchainTapscript instanceof DelegateVtxo.Script) {
667
+ const delegateScript = new DelegateVtxo.Script({
668
+ pubKey: this.offchainTapscript.options.pubKey,
669
+ serverPubKey: this.offchainTapscript.options.serverPubKey,
670
+ delegatePubKey: this.offchainTapscript.options.delegatePubKey,
671
+ csvTimelock,
672
+ });
673
+ await manager.createContract({
674
+ type: "delegate",
675
+ params: {
676
+ pubKey: hex.encode(delegateScript.options.pubKey),
677
+ serverPubKey: hex.encode(delegateScript.options.serverPubKey),
678
+ delegatePubKey: hex.encode(delegateScript.options.delegatePubKey),
679
+ csvTimelock: csvTimelockStr,
680
+ },
681
+ script: hex.encode(delegateScript.pkScript),
682
+ address: delegateScript
683
+ .address(this.network.hrp, this.arkServerPublicKey)
684
+ .encode(),
685
+ state: "active",
686
+ });
687
+ }
714
688
  }
715
689
  return manager;
716
690
  }
@@ -793,8 +767,8 @@ export class Wallet extends ReadonlyWallet {
793
767
  }
794
768
  constructor(identity, network, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository,
795
769
  /** @deprecated Use settlementConfig */
796
- renewalConfig, delegatorProvider, watcherConfig, settlementConfig) {
797
- super(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig);
770
+ renewalConfig, delegatorProvider, watcherConfig, settlementConfig, walletContractTimelocks) {
771
+ super(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig, walletContractTimelocks);
798
772
  this.arkProvider = arkProvider;
799
773
  this.serverUnrollScript = serverUnrollScript;
800
774
  this.forfeitOutputScript = forfeitOutputScript;
@@ -914,7 +888,7 @@ export class Wallet extends ReadonlyWallet {
914
888
  const forfeitPubkey = hex.decode(setup.info.forfeitPubkey).slice(1);
915
889
  const forfeitAddress = Address(setup.network).decode(setup.info.forfeitAddress);
916
890
  const forfeitOutputScript = OutScript.encode(forfeitAddress);
917
- const wallet = new Wallet(config.identity, setup.network, setup.onchainProvider, setup.arkProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, setup.dustAmount, setup.walletRepository, setup.contractRepository, config.renewalConfig, config.delegatorProvider, config.watcherConfig, config.settlementConfig);
891
+ const wallet = new Wallet(config.identity, setup.network, setup.onchainProvider, setup.arkProvider, setup.indexerProvider, setup.serverPubKey, setup.offchainTapscript, setup.boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, setup.dustAmount, setup.walletRepository, setup.contractRepository, config.renewalConfig, config.delegatorProvider, config.watcherConfig, config.settlementConfig, setup.walletContractTimelocks);
918
892
  await wallet.getVtxoManager();
919
893
  return wallet;
920
894
  }
@@ -940,7 +914,7 @@ export class Wallet extends ReadonlyWallet {
940
914
  const readonlyIdentity = hasToReadonly(this.identity)
941
915
  ? await this.identity.toReadonly()
942
916
  : this.identity; // Identity extends ReadonlyIdentity, so this is safe
943
- 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);
917
+ 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, this.walletContractTimelocks);
944
918
  }
945
919
  /** Returns the delegator manager when delegation support is configured. */
946
920
  async getDelegatorManager() {
@@ -1071,10 +1045,10 @@ export class Wallet extends ReadonlyWallet {
1071
1045
  weight: 0,
1072
1046
  birth: vtxo.createdAt,
1073
1047
  expiry: vtxo.virtualStatus.batchExpiry
1074
- ? new Date(vtxo.virtualStatus.batchExpiry * 1000)
1075
- : new Date(),
1048
+ ? new Date(vtxo.virtualStatus.batchExpiry)
1049
+ : undefined,
1076
1050
  });
1077
- if (inputFee.value >= vtxo.value) {
1051
+ if (inputFee.satoshis >= vtxo.value) {
1078
1052
  // skip if fees are greater than the virtual output value
1079
1053
  continue;
1080
1054
  }