@arkade-os/sdk 0.4.5 → 0.4.7

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.
@@ -15,7 +15,7 @@ import { createAssetPacket, selectedCoinsToAssetInputs, selectCoinsWithAsset, }
15
15
  import { VtxoScript } from '../script/base.js';
16
16
  import { CSVMultisigTapscript } from '../script/tapscript.js';
17
17
  import { buildOffchainTx, hasBoardingTxExpired, isValidArkAddress, } from '../utils/arkTransaction.js';
18
- import { DEFAULT_RENEWAL_CONFIG } from './vtxo-manager.js';
18
+ import { DEFAULT_RENEWAL_CONFIG, DEFAULT_SETTLEMENT_CONFIG, VtxoManager, } from './vtxo-manager.js';
19
19
  import { ArkNote } from '../arknote/index.js';
20
20
  import { Intent } from '../intent/index.js';
21
21
  import { RestIndexerProvider } from '../providers/indexer.js';
@@ -611,6 +611,18 @@ export class ReadonlyWallet {
611
611
  }
612
612
  return manager;
613
613
  }
614
+ async dispose() {
615
+ const manager = this._contractManager ??
616
+ (this._contractManagerInitializing
617
+ ? await this._contractManagerInitializing.catch(() => undefined)
618
+ : undefined);
619
+ manager?.dispose();
620
+ this._contractManager = undefined;
621
+ this._contractManagerInitializing = undefined;
622
+ }
623
+ async [Symbol.asyncDispose]() {
624
+ await this.dispose();
625
+ }
614
626
  }
615
627
  /**
616
628
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
@@ -646,7 +658,9 @@ export class ReadonlyWallet {
646
658
  * ```
647
659
  */
648
660
  export class Wallet extends ReadonlyWallet {
649
- constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository, renewalConfig, delegatorProvider, watcherConfig) {
661
+ constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository,
662
+ /** @deprecated Use settlementConfig */
663
+ renewalConfig, delegatorProvider, watcherConfig, settlementConfig) {
650
664
  super(identity, network, onchainProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, dustAmount, walletRepository, contractRepository, delegatorProvider, watcherConfig);
651
665
  this.networkName = networkName;
652
666
  this.arkProvider = arkProvider;
@@ -654,11 +668,31 @@ export class Wallet extends ReadonlyWallet {
654
668
  this.forfeitOutputScript = forfeitOutputScript;
655
669
  this.forfeitPubkey = forfeitPubkey;
656
670
  this.identity = identity;
671
+ // Backwards-compatible: keep renewalConfig populated for any code reading it
657
672
  this.renewalConfig = {
658
673
  enabled: renewalConfig?.enabled ?? false,
659
674
  ...DEFAULT_RENEWAL_CONFIG,
660
675
  ...renewalConfig,
661
676
  };
677
+ // Normalize: prefer settlementConfig, fall back to renewalConfig, default to enabled
678
+ if (settlementConfig !== undefined) {
679
+ this.settlementConfig = settlementConfig;
680
+ }
681
+ else if (renewalConfig && this.renewalConfig.enabled) {
682
+ this.settlementConfig = {
683
+ vtxoThreshold: renewalConfig.thresholdMs
684
+ ? renewalConfig.thresholdMs / 1000
685
+ : undefined,
686
+ };
687
+ }
688
+ else if (renewalConfig) {
689
+ // renewalConfig provided but not enabled → disabled
690
+ this.settlementConfig = false;
691
+ }
692
+ else {
693
+ // No config at all → enabled by default
694
+ this.settlementConfig = { ...DEFAULT_SETTLEMENT_CONFIG };
695
+ }
662
696
  this._delegatorManager = delegatorProvider
663
697
  ? new DelegatorManagerImpl(delegatorProvider, arkProvider, identity)
664
698
  : undefined;
@@ -667,6 +701,46 @@ export class Wallet extends ReadonlyWallet {
667
701
  this._walletAssetManager ?? (this._walletAssetManager = new AssetManager(this));
668
702
  return this._walletAssetManager;
669
703
  }
704
+ async getVtxoManager() {
705
+ if (this._vtxoManager) {
706
+ return this._vtxoManager;
707
+ }
708
+ if (this._vtxoManagerInitializing) {
709
+ return this._vtxoManagerInitializing;
710
+ }
711
+ this._vtxoManagerInitializing = Promise.resolve(new VtxoManager(this, this.renewalConfig, this.settlementConfig));
712
+ try {
713
+ const manager = await this._vtxoManagerInitializing;
714
+ this._vtxoManager = manager;
715
+ return manager;
716
+ }
717
+ catch (error) {
718
+ this._vtxoManagerInitializing = undefined;
719
+ throw error;
720
+ }
721
+ finally {
722
+ this._vtxoManagerInitializing = undefined;
723
+ }
724
+ }
725
+ async dispose() {
726
+ const manager = this._vtxoManager ??
727
+ (this._vtxoManagerInitializing
728
+ ? await this._vtxoManagerInitializing.catch(() => undefined)
729
+ : undefined);
730
+ try {
731
+ if (manager) {
732
+ await manager.dispose();
733
+ }
734
+ }
735
+ catch {
736
+ // best-effort teardown; ensure super.dispose() still runs
737
+ }
738
+ finally {
739
+ this._vtxoManager = undefined;
740
+ this._vtxoManagerInitializing = undefined;
741
+ await super.dispose();
742
+ }
743
+ }
670
744
  static async create(config) {
671
745
  const pubkey = await config.identity.xOnlyPublicKey();
672
746
  if (!pubkey) {
@@ -688,7 +762,9 @@ export class Wallet extends ReadonlyWallet {
688
762
  const forfeitPubkey = hex.decode(setup.info.forfeitPubkey).slice(1);
689
763
  const forfeitAddress = Address(setup.network).decode(setup.info.forfeitAddress);
690
764
  const forfeitOutputScript = OutScript.encode(forfeitAddress);
691
- return new Wallet(config.identity, setup.network, setup.networkName, 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);
765
+ const wallet = new Wallet(config.identity, setup.network, setup.networkName, 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);
766
+ await wallet.getVtxoManager();
767
+ return wallet;
692
768
  }
693
769
  /**
694
770
  * Convert this wallet to a readonly wallet.
@@ -791,7 +867,13 @@ export class Wallet extends ReadonlyWallet {
791
867
  let amount = 0;
792
868
  const exitScript = CSVMultisigTapscript.decode(hex.decode(this.boardingTapscript.exitScript));
793
869
  const boardingTimelock = exitScript.params.timelock;
794
- const boardingUtxos = (await this.getBoardingUtxos()).filter((utxo) => !hasBoardingTxExpired(utxo, boardingTimelock));
870
+ // For block-based timelocks, fetch the chain tip height
871
+ let chainTipHeight;
872
+ if (boardingTimelock.type === "blocks") {
873
+ const tip = await this.onchainProvider.getChainTip();
874
+ chainTipHeight = tip.height;
875
+ }
876
+ const boardingUtxos = (await this.getBoardingUtxos()).filter((utxo) => !hasBoardingTxExpired(utxo, boardingTimelock, chainTipHeight));
795
877
  const filteredBoardingUtxos = [];
796
878
  for (const utxo of boardingUtxos) {
797
879
  const inputFee = estimator.evalOnchainInput({
@@ -16,6 +16,7 @@ import { TxTree, TxTreeNode } from "./tree/txTree";
16
16
  import { SignerSession, TreeNonces, TreePartialSigs } from "./tree/signingSession";
17
17
  import { Ramps } from "./wallet/ramps";
18
18
  import { isVtxoExpiringSoon, VtxoManager } from "./wallet/vtxo-manager";
19
+ import type { SettlementConfig } from "./wallet/vtxo-manager";
19
20
  import { ServiceWorkerWallet, ServiceWorkerReadonlyWallet } from "./wallet/serviceWorker/wallet";
20
21
  import { OnchainWallet } from "./wallet/onchain";
21
22
  import { setupServiceWorker } from "./worker/browser/utils";
@@ -48,4 +49,4 @@ import { IContractManager } from "./contracts/contractManager";
48
49
  import { closeDatabase, openDatabase } from "./repositories/indexedDB/manager";
49
50
  import { WalletMessageHandler } from "./wallet/serviceWorker/wallet-message-handler";
50
51
  export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, MessageBus, WalletMessageHandler, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs, isVtxoExpiringSoon, isValidArkAddress, ArkNote, networks, closeDatabase, openDatabase, IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletRepository, InMemoryContractRepository, MIGRATION_KEY, migrateWalletRepository, requiresMigration, getMigrationStatus, rollbackMigration, WalletRepositoryImpl, ContractRepositoryImpl, Intent, BIP322, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, Batch, validateVtxoTxGraph, validateConnectorsTxGraph, buildForfeitTx, isRecoverable, isSpendable, isSubdust, isExpired, getSequence, ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, };
51
- export type { Identity, ReadonlyIdentity, IWallet, IReadonlyWallet, BaseWalletConfig, WalletConfig, ReadonlyWalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, SeedIdentityOptions, MnemonicOptions, NetworkOptions, DescriptorOptions, IndexerProvider, PageResponse, BatchInfo, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, FeeInfo, ArkInfo, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, Asset, Recipient, IssuanceParams, IssuanceResult, ReissuanceParams, BurnParams, AssetDetails, AssetMetadata, KnownMetadata, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, StorageConfig, Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, IContractManager, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams, MessageHandler, RequestEnvelope, ResponseEnvelope, IDelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
52
+ export type { Identity, ReadonlyIdentity, IWallet, IReadonlyWallet, BaseWalletConfig, WalletConfig, ReadonlyWalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, SeedIdentityOptions, MnemonicOptions, NetworkOptions, DescriptorOptions, IndexerProvider, PageResponse, BatchInfo, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, FeeInfo, ArkInfo, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, SettlementConfig, Asset, Recipient, IssuanceParams, IssuanceResult, ReissuanceParams, BurnParams, AssetDetails, AssetMetadata, KnownMetadata, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, StorageConfig, Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, IContractManager, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams, MessageHandler, RequestEnvelope, ResponseEnvelope, IDelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
@@ -24,7 +24,7 @@ export type OffchainTx = {
24
24
  * @returns Object containing the virtual transaction and checkpoint transactions
25
25
  */
26
26
  export declare function buildOffchainTx(inputs: ArkTxInput[], outputs: TransactionOutput[], serverUnrollScript: CSVMultisigTapscript.Type): OffchainTx;
27
- export declare function hasBoardingTxExpired(coin: ExtendedCoin, boardingTimelock: RelativeTimelock): boolean;
27
+ export declare function hasBoardingTxExpired(coin: ExtendedCoin, boardingTimelock: RelativeTimelock, chainTipHeight?: number): boolean;
28
28
  /**
29
29
  * Verify tapscript signatures on a transaction input
30
30
  * @param tx Transaction to verify
@@ -3,7 +3,7 @@ import { ArkProvider, Output, SettlementEvent } from "../providers/ark";
3
3
  import { Identity, ReadonlyIdentity } from "../identity";
4
4
  import { RelativeTimelock } from "../script/tapscript";
5
5
  import { EncodedVtxoScript, TapLeafScript } from "../script/base";
6
- import { RenewalConfig } from "./vtxo-manager";
6
+ import { RenewalConfig, SettlementConfig } from "./vtxo-manager";
7
7
  import { IndexerProvider } from "../providers/indexer";
8
8
  import { OnchainProvider } from "../providers/onchain";
9
9
  import { ContractWatcherConfig } from "../contracts/contractWatcher";
@@ -96,20 +96,26 @@ export interface ReadonlyWalletConfig extends BaseWalletConfig {
96
96
  * onchainProvider: new EsploraProvider('https://mempool.space/api')
97
97
  * });
98
98
  *
99
- * // With renewal configuration
99
+ * // With settlement configuration
100
100
  * const wallet = await Wallet.create({
101
101
  * identity: SingleKey.fromHex('...'),
102
102
  * arkServerUrl: 'https://ark.example.com',
103
- * renewalConfig: {
104
- * enabled: true,
105
- * thresholdMs: 86400000, // 24 hours
106
- * }
103
+ * settlementConfig: {
104
+ * vtxoThreshold: 86400, // 24 hours in seconds
105
+ * boardingUtxoSweep: true,
106
+ * },
107
107
  * });
108
108
  * ```
109
109
  */
110
110
  export interface WalletConfig extends ReadonlyWalletConfig {
111
111
  identity: Identity;
112
+ /** @deprecated Use settlementConfig instead */
112
113
  renewalConfig?: RenewalConfig;
114
+ /**
115
+ * Configuration for automatic settlement and renewal.
116
+ * `false` = explicitly disabled, `undefined` = enabled by default, `{}` = enabled with defaults.
117
+ */
118
+ settlementConfig?: SettlementConfig | false;
113
119
  }
114
120
  export type StorageConfig = {
115
121
  walletRepository: WalletRepository;
@@ -1,8 +1,10 @@
1
- import { ExtendedVirtualCoin, IWallet } from ".";
1
+ import { ExtendedCoin, ExtendedVirtualCoin, IWallet } from ".";
2
2
  import { SettlementEvent } from "../providers/ark";
3
3
  export declare const DEFAULT_THRESHOLD_MS: number;
4
+ export declare const DEFAULT_THRESHOLD_SECONDS: number;
4
5
  /**
5
6
  * Configuration options for automatic VTXO renewal
7
+ * @deprecated Use SettlementConfig instead
6
8
  */
7
9
  export interface RenewalConfig {
8
10
  /**
@@ -14,13 +16,81 @@ export interface RenewalConfig {
14
16
  * Threshold in milliseconds to use as threshold for renewal
15
17
  * E.g., 86400000 means renew when 24 hours until expiry remains
16
18
  * @default 86400000 (24 hours)
19
+ * @deprecated Use SettlementConfig.vtxoThreshold (in seconds) instead
17
20
  */
18
21
  thresholdMs?: number;
19
22
  }
23
+ /**
24
+ * Configuration for automatic settlement and renewal.
25
+ *
26
+ * Controls two behaviors:
27
+ * 1. **VTXO renewal**: Automatically renew VTXOs that are close to expiry
28
+ * 2. **Boarding UTXO sweep**: Sweep expired boarding UTXOs back to a fresh boarding address
29
+ * via the unilateral exit path (on-chain self-spend to restart the timelock)
30
+ *
31
+ * Enabled by default when no config is provided.
32
+ * Pass `false` to explicitly disable all settlement behavior.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // Default behavior: VTXO renewal at 3 days + boarding sweep enabled
37
+ * const wallet = await Wallet.create({
38
+ * identity: SingleKey.fromHex('...'),
39
+ * arkServerUrl: 'https://ark.example.com',
40
+ * });
41
+ *
42
+ * // Custom threshold
43
+ * const wallet = await Wallet.create({
44
+ * identity: SingleKey.fromHex('...'),
45
+ * arkServerUrl: 'https://ark.example.com',
46
+ * settlementConfig: {
47
+ * vtxoThreshold: 86400, // 24 hours in seconds
48
+ * },
49
+ * });
50
+ *
51
+ * // Explicitly disable
52
+ * const wallet = await Wallet.create({
53
+ * identity: SingleKey.fromHex('...'),
54
+ * arkServerUrl: 'https://ark.example.com',
55
+ * settlementConfig: false,
56
+ * });
57
+ * ```
58
+ */
59
+ export interface SettlementConfig {
60
+ /**
61
+ * Seconds before VTXO expiry to trigger renewal.
62
+ * @default 259200 (3 days)
63
+ */
64
+ vtxoThreshold?: number;
65
+ /**
66
+ * Sweep expired boarding UTXOs back to a fresh boarding address
67
+ * via the unilateral exit path (on-chain self-spend to restart the timelock).
68
+ *
69
+ * When enabled, expired boarding UTXOs are batched into a single on-chain transaction
70
+ * with multiple inputs and one output. A dust check ensures the sweep is only
71
+ * performed when the output after fees is above dust.
72
+ *
73
+ * @default true
74
+ */
75
+ boardingUtxoSweep?: boolean;
76
+ /**
77
+ * Polling interval in milliseconds for checking boarding UTXOs.
78
+ * The poll loop auto-settles new boarding UTXOs into Ark and
79
+ * sweeps expired ones (when boardingUtxoSweep is enabled).
80
+ *
81
+ * @default 60000 (1 minute)
82
+ */
83
+ pollIntervalMs?: number;
84
+ }
20
85
  /**
21
86
  * Default renewal configuration values
87
+ * @deprecated Use DEFAULT_SETTLEMENT_CONFIG instead
22
88
  */
23
89
  export declare const DEFAULT_RENEWAL_CONFIG: Required<Omit<RenewalConfig, "enabled">>;
90
+ /**
91
+ * Default settlement configuration values
92
+ */
93
+ export declare const DEFAULT_SETTLEMENT_CONFIG: Required<SettlementConfig>;
24
94
  /**
25
95
  * Check if a VTXO is expiring soon based on threshold
26
96
  *
@@ -75,10 +145,21 @@ export declare function getExpiringAndRecoverableVtxos(vtxos: ExtendedVirtualCoi
75
145
  * }
76
146
  * ```
77
147
  */
78
- export declare class VtxoManager {
148
+ export declare class VtxoManager implements AsyncDisposable {
79
149
  readonly wallet: IWallet;
150
+ /** @deprecated Use settlementConfig instead */
80
151
  readonly renewalConfig?: RenewalConfig | undefined;
81
- constructor(wallet: IWallet, renewalConfig?: RenewalConfig | undefined);
152
+ readonly settlementConfig: SettlementConfig | false;
153
+ private contractEventsSubscription?;
154
+ private readonly contractEventsSubscriptionReady;
155
+ private disposePromise?;
156
+ private pollIntervalId?;
157
+ private knownBoardingUtxos;
158
+ private sweptBoardingUtxos;
159
+ private pollInProgress;
160
+ constructor(wallet: IWallet,
161
+ /** @deprecated Use settlementConfig instead */
162
+ renewalConfig?: RenewalConfig | undefined, settlementConfig?: SettlementConfig | false);
82
163
  /**
83
164
  * Recover swept/expired VTXOs by settling them back to the wallet's Ark address.
84
165
  *
@@ -177,4 +258,86 @@ export declare class VtxoManager {
177
258
  * ```
178
259
  */
179
260
  renewVtxos(eventCallback?: (event: SettlementEvent) => void): Promise<string>;
261
+ /**
262
+ * Get boarding UTXOs whose timelock has expired.
263
+ *
264
+ * These UTXOs can no longer be onboarded cooperatively via `settle()` and
265
+ * must be swept back to a fresh boarding address using the unilateral exit path.
266
+ *
267
+ * @returns Array of expired boarding UTXOs
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const manager = new VtxoManager(wallet);
272
+ * const expired = await manager.getExpiredBoardingUtxos();
273
+ * if (expired.length > 0) {
274
+ * console.log(`${expired.length} expired boarding UTXOs to sweep`);
275
+ * }
276
+ * ```
277
+ */
278
+ getExpiredBoardingUtxos(): Promise<ExtendedCoin[]>;
279
+ /**
280
+ * Sweep expired boarding UTXOs back to a fresh boarding address via
281
+ * the unilateral exit path (on-chain self-spend).
282
+ *
283
+ * This builds a raw on-chain transaction that:
284
+ * - Uses all expired boarding UTXOs as inputs (spent via the CSV exit script path)
285
+ * - Has a single output to the wallet's boarding address (restarts the timelock)
286
+ * - Batches multiple expired UTXOs into one transaction
287
+ * - Skips the sweep if the output after fees would be below dust
288
+ *
289
+ * No Ark server involvement is needed — this is a pure on-chain transaction.
290
+ *
291
+ * @returns The broadcast transaction ID
292
+ * @throws Error if no expired boarding UTXOs found
293
+ * @throws Error if output after fees is below dust (not economical to sweep)
294
+ * @throws Error if boarding UTXO sweep is not enabled in settlementConfig
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * const manager = new VtxoManager(wallet, undefined, {
299
+ * boardingUtxoSweep: true,
300
+ * });
301
+ *
302
+ * try {
303
+ * const txid = await manager.sweepExpiredBoardingUtxos();
304
+ * console.log('Swept expired boarding UTXOs:', txid);
305
+ * } catch (e) {
306
+ * console.log('No sweep needed or not economical');
307
+ * }
308
+ * ```
309
+ */
310
+ sweepExpiredBoardingUtxos(): Promise<string>;
311
+ /** Asserts sweep capability and returns the typed wallet. */
312
+ private getSweepWallet;
313
+ /** Decodes the boarding tapscript exit path to extract the CSV timelock. */
314
+ private getBoardingTimelock;
315
+ /** Returns the TapLeafScript for the boarding tapscript's exit (CSV) path. */
316
+ private getBoardingExitLeaf;
317
+ /** Returns the pkScript (output script) of the boarding tapscript. */
318
+ private getBoardingOutputScript;
319
+ /** Returns the on-chain provider for fee estimation and broadcasting. */
320
+ private getOnchainProvider;
321
+ /** Returns the Bitcoin network configuration from the wallet. */
322
+ private getNetwork;
323
+ /** Returns the wallet's identity for transaction signing. */
324
+ private getIdentity;
325
+ private initializeSubscription;
326
+ /**
327
+ * Starts a polling loop that:
328
+ * 1. Auto-settles new boarding UTXOs into Ark
329
+ * 2. Sweeps expired boarding UTXOs (when boardingUtxoSweep is enabled)
330
+ */
331
+ private startBoardingUtxoPoll;
332
+ private pollBoardingUtxos;
333
+ /**
334
+ * Auto-settle new (unexpired) boarding UTXOs into the Ark.
335
+ * Skips UTXOs that are already expired (those are handled by sweep).
336
+ * Only settles UTXOs not already in-flight (tracked in knownBoardingUtxos).
337
+ * UTXOs are marked as known only after a successful settle, so failed
338
+ * attempts will be retried on the next poll.
339
+ */
340
+ private settleBoardingUtxos;
341
+ dispose(): Promise<void>;
342
+ [Symbol.asyncDispose](): Promise<void>;
180
343
  }
@@ -9,6 +9,7 @@ import { SignerSession } from "../tree/signingSession";
9
9
  import { Identity, ReadonlyIdentity } from "../identity";
10
10
  import { ArkTransaction, Recipient, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IReadonlyWallet, IWallet, ReadonlyWalletConfig, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig, IAssetManager, IReadonlyAssetManager } from ".";
11
11
  import { CSVMultisigTapscript } from "../script/tapscript";
12
+ import { SettlementConfig, VtxoManager } from "./vtxo-manager";
12
13
  import { Intent } from "../intent";
13
14
  import { IndexerProvider } from "../providers/indexer";
14
15
  import { WalletRepository } from "../repositories/walletRepository";
@@ -122,6 +123,8 @@ export declare class ReadonlyWallet implements IReadonlyWallet {
122
123
  */
123
124
  getContractManager(): Promise<ContractManager>;
124
125
  private initializeContractManager;
126
+ dispose(): Promise<void>;
127
+ [Symbol.asyncDispose](): Promise<void>;
125
128
  }
126
129
  /**
127
130
  * Main wallet implementation for Bitcoin transactions with Ark protocol support.
@@ -165,13 +168,21 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
165
168
  static MIN_FEE_RATE: number;
166
169
  readonly identity: Identity;
167
170
  private readonly _delegatorManager?;
171
+ private _vtxoManager?;
172
+ private _vtxoManagerInitializing?;
168
173
  private _walletAssetManager?;
174
+ /** @deprecated Use settlementConfig instead */
169
175
  readonly renewalConfig: Required<Omit<WalletConfig["renewalConfig"], "enabled">> & {
170
176
  enabled: boolean;
171
177
  thresholdMs: number;
172
178
  };
173
- protected constructor(identity: Identity, network: Network, networkName: NetworkName, onchainProvider: OnchainProvider, arkProvider: ArkProvider, indexerProvider: IndexerProvider, arkServerPublicKey: Bytes, offchainTapscript: DefaultVtxo.Script | DelegateVtxo.Script, boardingTapscript: DefaultVtxo.Script, serverUnrollScript: CSVMultisigTapscript.Type, forfeitOutputScript: Bytes, forfeitPubkey: Bytes, dustAmount: bigint, walletRepository: WalletRepository, contractRepository: ContractRepository, renewalConfig?: WalletConfig["renewalConfig"], delegatorProvider?: DelegatorProvider, watcherConfig?: WalletConfig["watcherConfig"]);
179
+ readonly settlementConfig: SettlementConfig | false;
180
+ protected constructor(identity: Identity, network: Network, networkName: NetworkName, onchainProvider: OnchainProvider, arkProvider: ArkProvider, indexerProvider: IndexerProvider, arkServerPublicKey: Bytes, offchainTapscript: DefaultVtxo.Script | DelegateVtxo.Script, boardingTapscript: DefaultVtxo.Script, serverUnrollScript: CSVMultisigTapscript.Type, forfeitOutputScript: Bytes, forfeitPubkey: Bytes, dustAmount: bigint, walletRepository: WalletRepository, contractRepository: ContractRepository,
181
+ /** @deprecated Use settlementConfig */
182
+ renewalConfig?: WalletConfig["renewalConfig"], delegatorProvider?: DelegatorProvider, watcherConfig?: WalletConfig["watcherConfig"], settlementConfig?: WalletConfig["settlementConfig"]);
174
183
  get assetManager(): IAssetManager;
184
+ getVtxoManager(): Promise<VtxoManager>;
185
+ dispose(): Promise<void>;
175
186
  static create(config: WalletConfig): Promise<Wallet>;
176
187
  /**
177
188
  * Convert this wallet to a readonly wallet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",