@arkade-os/sdk 0.3.1-alpha.5 → 0.3.1-alpha.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.
package/README.md CHANGED
@@ -127,7 +127,8 @@ const manager = new VtxoManager(wallet, {
127
127
 
128
128
  #### Renewal: Prevent Expiration
129
129
 
130
- Renew VTXOs before they expire to keep your liquidity accessible. This settles all VTXOs (including recoverable ones) back to your wallet with a fresh expiration time.
130
+ Renew VTXOs before they expire to retain unilateral control of funds.
131
+ This settles expiring and recoverable VTXOs back to your wallet, refreshing their expiration time.
131
132
 
132
133
  ```typescript
133
134
  // Renew all VTXOs to prevent expiration
@@ -2,10 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VtxoManager = exports.DEFAULT_RENEWAL_CONFIG = void 0;
4
4
  exports.isVtxoExpiringSoon = isVtxoExpiringSoon;
5
- exports.getExpiringVtxos = getExpiringVtxos;
6
- exports.calculateExpiryThreshold = calculateExpiryThreshold;
7
- exports.getMinimumExpiry = getMinimumExpiry;
8
- exports.calculateDynamicThreshold = calculateDynamicThreshold;
5
+ exports.getExpiringAndRecoverableVtxos = getExpiringAndRecoverableVtxos;
9
6
  const _1 = require(".");
10
7
  /**
11
8
  * Default renewal configuration values
@@ -13,6 +10,9 @@ const _1 = require(".");
13
10
  exports.DEFAULT_RENEWAL_CONFIG = {
14
11
  thresholdPercentage: 10,
15
12
  };
13
+ function getDustAmount(wallet) {
14
+ return "dustAmount" in wallet ? wallet.dustAmount : 330n;
15
+ }
16
16
  /**
17
17
  * Filter VTXOs that are recoverable (swept and still spendable, or preconfirmed subdust)
18
18
  *
@@ -82,80 +82,35 @@ function getRecoverableWithSubdust(vtxos, dustAmount) {
82
82
  * @param thresholdMs - Threshold in milliseconds from now
83
83
  * @returns true if VTXO expires within threshold, false otherwise
84
84
  */
85
- function isVtxoExpiringSoon(vtxo, thresholdMs) {
85
+ function isVtxoExpiringSoon(vtxo, percentage) {
86
86
  const { batchExpiry } = vtxo.virtualStatus;
87
- // No expiry set means it doesn't expire
88
87
  if (!batchExpiry) {
89
- return false;
88
+ return false; // it doesn't expire
90
89
  }
91
90
  const now = Date.now();
92
- const timeUntilExpiry = batchExpiry - now;
93
- return timeUntilExpiry > 0 && timeUntilExpiry <= thresholdMs;
91
+ if (batchExpiry <= now) {
92
+ return false; // already expired
93
+ }
94
+ // It shouldn't happen, but let's be safe
95
+ if (!vtxo.createdAt) {
96
+ return false;
97
+ }
98
+ const duration = batchExpiry - vtxo.createdAt.getTime();
99
+ const softExpiry = batchExpiry - (duration * percentage) / 100;
100
+ return softExpiry > 0 && softExpiry <= now;
94
101
  }
95
102
  /**
96
- * Filter VTXOs that are expiring soon
103
+ * Filter VTXOs that are expiring soon or are recoverable/subdust
97
104
  *
98
105
  * @param vtxos - Array of virtual coins to check
99
106
  * @param thresholdMs - Threshold in milliseconds from now
107
+ * @param dustAmount - Dust threshold amount in satoshis
100
108
  * @returns Array of VTXOs expiring within threshold
101
109
  */
102
- function getExpiringVtxos(vtxos, thresholdMs) {
103
- return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs));
104
- }
105
- /**
106
- * Calculate expiry threshold in milliseconds based on batch expiry and percentage
107
- *
108
- * @param batchExpiry - Batch expiry timestamp in milliseconds
109
- * @param percentage - Percentage of total time (0-100)
110
- * @returns Threshold timestamp in milliseconds from now
111
- *
112
- * @example
113
- * // VTXO expires in 10 days, threshold is 10%
114
- * const expiry = Date.now() + 10 * 24 * 60 * 60 * 1000;
115
- * const threshold = calculateExpiryThreshold(expiry, 10);
116
- * // Returns 1 day in milliseconds (10% of 10 days)
117
- */
118
- function calculateExpiryThreshold(batchExpiry, percentage) {
119
- if (percentage < 0 || percentage > 100) {
120
- throw new Error("Percentage must be between 0 and 100");
121
- }
122
- const now = Date.now();
123
- const totalTime = batchExpiry - now;
124
- if (totalTime <= 0) {
125
- // Already expired
126
- return 0;
127
- }
128
- // Calculate threshold as percentage of total time
129
- return Math.floor((totalTime * percentage) / 100);
130
- }
131
- /**
132
- * Get the minimum expiry time from a list of VTXOs
133
- *
134
- * @param vtxos - Array of virtual coins
135
- * @returns Minimum batch expiry timestamp, or undefined if no VTXOs have expiry
136
- */
137
- function getMinimumExpiry(vtxos) {
138
- const expiries = vtxos
139
- .map((v) => v.virtualStatus.batchExpiry)
140
- .filter((e) => e !== undefined);
141
- if (expiries.length === 0) {
142
- return undefined;
143
- }
144
- return Math.min(...expiries);
145
- }
146
- /**
147
- * Calculate dynamic threshold based on the earliest expiring VTXO
148
- *
149
- * @param vtxos - Array of virtual coins
150
- * @param percentage - Percentage of time until expiry (0-100)
151
- * @returns Threshold in milliseconds, or undefined if no VTXOs have expiry
152
- */
153
- function calculateDynamicThreshold(vtxos, percentage) {
154
- const minExpiry = getMinimumExpiry(vtxos);
155
- if (!minExpiry) {
156
- return undefined;
157
- }
158
- return calculateExpiryThreshold(minExpiry, percentage);
110
+ function getExpiringAndRecoverableVtxos(vtxos, percentage, dustAmount) {
111
+ return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, percentage) ||
112
+ (0, _1.isRecoverable)(vtxo) ||
113
+ (0, _1.isSubdust)(vtxo, dustAmount));
159
114
  }
160
115
  /**
161
116
  * VtxoManager is a unified class for managing VTXO lifecycle operations including
@@ -236,9 +191,7 @@ class VtxoManager {
236
191
  withUnrolled: false,
237
192
  });
238
193
  // Get dust amount from wallet
239
- const dustAmount = "dustAmount" in this.wallet
240
- ? this.wallet.dustAmount
241
- : 1000n;
194
+ const dustAmount = getDustAmount(this.wallet);
242
195
  // Filter recoverable VTXOs and handle subdust logic
243
196
  const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
244
197
  if (vtxosToRecover.length === 0) {
@@ -281,9 +234,7 @@ class VtxoManager {
281
234
  withRecoverable: true,
282
235
  withUnrolled: false,
283
236
  });
284
- const dustAmount = "dustAmount" in this.wallet
285
- ? this.wallet.dustAmount
286
- : 1000n;
237
+ const dustAmount = getDustAmount(this.wallet);
287
238
  const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
288
239
  // Calculate subdust amount separately for reporting
289
240
  const subdustAmount = vtxosToRecover
@@ -313,23 +264,16 @@ class VtxoManager {
313
264
  * ```
314
265
  */
315
266
  async getExpiringVtxos(thresholdPercentage) {
316
- if (!this.renewalConfig?.enabled) {
317
- return [];
318
- }
319
- const vtxos = await this.wallet.getVtxos();
267
+ const vtxos = await this.wallet.getVtxos({ withRecoverable: true });
320
268
  const percentage = thresholdPercentage ??
321
- this.renewalConfig.thresholdPercentage ??
269
+ this.renewalConfig?.thresholdPercentage ??
322
270
  exports.DEFAULT_RENEWAL_CONFIG.thresholdPercentage;
323
- const threshold = calculateDynamicThreshold(vtxos, percentage);
324
- if (!threshold) {
325
- return [];
326
- }
327
- return getExpiringVtxos(vtxos, threshold);
271
+ return getExpiringAndRecoverableVtxos(vtxos, percentage, getDustAmount(this.wallet));
328
272
  }
329
273
  /**
330
- * Renew VTXOs by settling them back to the wallet's address
274
+ * Renew expiring VTXOs by settling them back to the wallet's address
331
275
  *
332
- * This method collects all spendable VTXOs (including recoverable ones) and settles
276
+ * This method collects all expiring spendable VTXOs (including recoverable ones) and settles
333
277
  * them back to the wallet, effectively refreshing their expiration time. This is the
334
278
  * primary way to prevent VTXOs from expiring.
335
279
  *
@@ -353,15 +297,13 @@ class VtxoManager {
353
297
  */
354
298
  async renewVtxos(eventCallback) {
355
299
  // Get all VTXOs (including recoverable ones)
356
- const vtxos = await this.wallet.getVtxos({ withRecoverable: true });
300
+ const vtxos = await this.getExpiringVtxos();
357
301
  if (vtxos.length === 0) {
358
302
  throw new Error("No VTXOs available to renew");
359
303
  }
360
304
  const totalAmount = vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
361
305
  // Get dust amount from wallet
362
- const dustAmount = "dustAmount" in this.wallet
363
- ? this.wallet.dustAmount
364
- : 1000n;
306
+ const dustAmount = getDustAmount(this.wallet);
365
307
  // Check if total amount is above dust threshold
366
308
  if (BigInt(totalAmount) < dustAmount) {
367
309
  throw new Error(`Total amount ${totalAmount} is below dust threshold ${dustAmount}`);
@@ -5,6 +5,9 @@ import { isRecoverable, isSubdust } from './index.js';
5
5
  export const DEFAULT_RENEWAL_CONFIG = {
6
6
  thresholdPercentage: 10,
7
7
  };
8
+ function getDustAmount(wallet) {
9
+ return "dustAmount" in wallet ? wallet.dustAmount : 330n;
10
+ }
8
11
  /**
9
12
  * Filter VTXOs that are recoverable (swept and still spendable, or preconfirmed subdust)
10
13
  *
@@ -74,80 +77,35 @@ function getRecoverableWithSubdust(vtxos, dustAmount) {
74
77
  * @param thresholdMs - Threshold in milliseconds from now
75
78
  * @returns true if VTXO expires within threshold, false otherwise
76
79
  */
77
- export function isVtxoExpiringSoon(vtxo, thresholdMs) {
80
+ export function isVtxoExpiringSoon(vtxo, percentage) {
78
81
  const { batchExpiry } = vtxo.virtualStatus;
79
- // No expiry set means it doesn't expire
80
82
  if (!batchExpiry) {
81
- return false;
83
+ return false; // it doesn't expire
82
84
  }
83
85
  const now = Date.now();
84
- const timeUntilExpiry = batchExpiry - now;
85
- return timeUntilExpiry > 0 && timeUntilExpiry <= thresholdMs;
86
+ if (batchExpiry <= now) {
87
+ return false; // already expired
88
+ }
89
+ // It shouldn't happen, but let's be safe
90
+ if (!vtxo.createdAt) {
91
+ return false;
92
+ }
93
+ const duration = batchExpiry - vtxo.createdAt.getTime();
94
+ const softExpiry = batchExpiry - (duration * percentage) / 100;
95
+ return softExpiry > 0 && softExpiry <= now;
86
96
  }
87
97
  /**
88
- * Filter VTXOs that are expiring soon
98
+ * Filter VTXOs that are expiring soon or are recoverable/subdust
89
99
  *
90
100
  * @param vtxos - Array of virtual coins to check
91
101
  * @param thresholdMs - Threshold in milliseconds from now
102
+ * @param dustAmount - Dust threshold amount in satoshis
92
103
  * @returns Array of VTXOs expiring within threshold
93
104
  */
94
- export function getExpiringVtxos(vtxos, thresholdMs) {
95
- return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs));
96
- }
97
- /**
98
- * Calculate expiry threshold in milliseconds based on batch expiry and percentage
99
- *
100
- * @param batchExpiry - Batch expiry timestamp in milliseconds
101
- * @param percentage - Percentage of total time (0-100)
102
- * @returns Threshold timestamp in milliseconds from now
103
- *
104
- * @example
105
- * // VTXO expires in 10 days, threshold is 10%
106
- * const expiry = Date.now() + 10 * 24 * 60 * 60 * 1000;
107
- * const threshold = calculateExpiryThreshold(expiry, 10);
108
- * // Returns 1 day in milliseconds (10% of 10 days)
109
- */
110
- export function calculateExpiryThreshold(batchExpiry, percentage) {
111
- if (percentage < 0 || percentage > 100) {
112
- throw new Error("Percentage must be between 0 and 100");
113
- }
114
- const now = Date.now();
115
- const totalTime = batchExpiry - now;
116
- if (totalTime <= 0) {
117
- // Already expired
118
- return 0;
119
- }
120
- // Calculate threshold as percentage of total time
121
- return Math.floor((totalTime * percentage) / 100);
122
- }
123
- /**
124
- * Get the minimum expiry time from a list of VTXOs
125
- *
126
- * @param vtxos - Array of virtual coins
127
- * @returns Minimum batch expiry timestamp, or undefined if no VTXOs have expiry
128
- */
129
- export function getMinimumExpiry(vtxos) {
130
- const expiries = vtxos
131
- .map((v) => v.virtualStatus.batchExpiry)
132
- .filter((e) => e !== undefined);
133
- if (expiries.length === 0) {
134
- return undefined;
135
- }
136
- return Math.min(...expiries);
137
- }
138
- /**
139
- * Calculate dynamic threshold based on the earliest expiring VTXO
140
- *
141
- * @param vtxos - Array of virtual coins
142
- * @param percentage - Percentage of time until expiry (0-100)
143
- * @returns Threshold in milliseconds, or undefined if no VTXOs have expiry
144
- */
145
- export function calculateDynamicThreshold(vtxos, percentage) {
146
- const minExpiry = getMinimumExpiry(vtxos);
147
- if (!minExpiry) {
148
- return undefined;
149
- }
150
- return calculateExpiryThreshold(minExpiry, percentage);
105
+ export function getExpiringAndRecoverableVtxos(vtxos, percentage, dustAmount) {
106
+ return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, percentage) ||
107
+ isRecoverable(vtxo) ||
108
+ isSubdust(vtxo, dustAmount));
151
109
  }
152
110
  /**
153
111
  * VtxoManager is a unified class for managing VTXO lifecycle operations including
@@ -228,9 +186,7 @@ export class VtxoManager {
228
186
  withUnrolled: false,
229
187
  });
230
188
  // Get dust amount from wallet
231
- const dustAmount = "dustAmount" in this.wallet
232
- ? this.wallet.dustAmount
233
- : 1000n;
189
+ const dustAmount = getDustAmount(this.wallet);
234
190
  // Filter recoverable VTXOs and handle subdust logic
235
191
  const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
236
192
  if (vtxosToRecover.length === 0) {
@@ -273,9 +229,7 @@ export class VtxoManager {
273
229
  withRecoverable: true,
274
230
  withUnrolled: false,
275
231
  });
276
- const dustAmount = "dustAmount" in this.wallet
277
- ? this.wallet.dustAmount
278
- : 1000n;
232
+ const dustAmount = getDustAmount(this.wallet);
279
233
  const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
280
234
  // Calculate subdust amount separately for reporting
281
235
  const subdustAmount = vtxosToRecover
@@ -305,23 +259,16 @@ export class VtxoManager {
305
259
  * ```
306
260
  */
307
261
  async getExpiringVtxos(thresholdPercentage) {
308
- if (!this.renewalConfig?.enabled) {
309
- return [];
310
- }
311
- const vtxos = await this.wallet.getVtxos();
262
+ const vtxos = await this.wallet.getVtxos({ withRecoverable: true });
312
263
  const percentage = thresholdPercentage ??
313
- this.renewalConfig.thresholdPercentage ??
264
+ this.renewalConfig?.thresholdPercentage ??
314
265
  DEFAULT_RENEWAL_CONFIG.thresholdPercentage;
315
- const threshold = calculateDynamicThreshold(vtxos, percentage);
316
- if (!threshold) {
317
- return [];
318
- }
319
- return getExpiringVtxos(vtxos, threshold);
266
+ return getExpiringAndRecoverableVtxos(vtxos, percentage, getDustAmount(this.wallet));
320
267
  }
321
268
  /**
322
- * Renew VTXOs by settling them back to the wallet's address
269
+ * Renew expiring VTXOs by settling them back to the wallet's address
323
270
  *
324
- * This method collects all spendable VTXOs (including recoverable ones) and settles
271
+ * This method collects all expiring spendable VTXOs (including recoverable ones) and settles
325
272
  * them back to the wallet, effectively refreshing their expiration time. This is the
326
273
  * primary way to prevent VTXOs from expiring.
327
274
  *
@@ -345,15 +292,13 @@ export class VtxoManager {
345
292
  */
346
293
  async renewVtxos(eventCallback) {
347
294
  // Get all VTXOs (including recoverable ones)
348
- const vtxos = await this.wallet.getVtxos({ withRecoverable: true });
295
+ const vtxos = await this.getExpiringVtxos();
349
296
  if (vtxos.length === 0) {
350
297
  throw new Error("No VTXOs available to renew");
351
298
  }
352
299
  const totalAmount = vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
353
300
  // Get dust amount from wallet
354
- const dustAmount = "dustAmount" in this.wallet
355
- ? this.wallet.dustAmount
356
- : 1000n;
301
+ const dustAmount = getDustAmount(this.wallet);
357
302
  // Check if total amount is above dust threshold
358
303
  if (BigInt(totalAmount) < dustAmount) {
359
304
  throw new Error(`Total amount ${totalAmount} is below dust threshold ${dustAmount}`);
@@ -8,7 +8,7 @@ export interface RenewalConfig {
8
8
  * Enable automatic renewal monitoring
9
9
  * @default false
10
10
  */
11
- enabled: boolean;
11
+ enabled?: boolean;
12
12
  /**
13
13
  * Percentage of expiry time to use as threshold (0-100)
14
14
  * E.g., 10 means renew when 10% of time until expiry remains
@@ -27,44 +27,16 @@ export declare const DEFAULT_RENEWAL_CONFIG: Required<Omit<RenewalConfig, "enabl
27
27
  * @param thresholdMs - Threshold in milliseconds from now
28
28
  * @returns true if VTXO expires within threshold, false otherwise
29
29
  */
30
- export declare function isVtxoExpiringSoon(vtxo: ExtendedVirtualCoin, thresholdMs: number): boolean;
30
+ export declare function isVtxoExpiringSoon(vtxo: ExtendedVirtualCoin, percentage: number): boolean;
31
31
  /**
32
- * Filter VTXOs that are expiring soon
32
+ * Filter VTXOs that are expiring soon or are recoverable/subdust
33
33
  *
34
34
  * @param vtxos - Array of virtual coins to check
35
35
  * @param thresholdMs - Threshold in milliseconds from now
36
+ * @param dustAmount - Dust threshold amount in satoshis
36
37
  * @returns Array of VTXOs expiring within threshold
37
38
  */
38
- export declare function getExpiringVtxos(vtxos: ExtendedVirtualCoin[], thresholdMs: number): ExtendedVirtualCoin[];
39
- /**
40
- * Calculate expiry threshold in milliseconds based on batch expiry and percentage
41
- *
42
- * @param batchExpiry - Batch expiry timestamp in milliseconds
43
- * @param percentage - Percentage of total time (0-100)
44
- * @returns Threshold timestamp in milliseconds from now
45
- *
46
- * @example
47
- * // VTXO expires in 10 days, threshold is 10%
48
- * const expiry = Date.now() + 10 * 24 * 60 * 60 * 1000;
49
- * const threshold = calculateExpiryThreshold(expiry, 10);
50
- * // Returns 1 day in milliseconds (10% of 10 days)
51
- */
52
- export declare function calculateExpiryThreshold(batchExpiry: number, percentage: number): number;
53
- /**
54
- * Get the minimum expiry time from a list of VTXOs
55
- *
56
- * @param vtxos - Array of virtual coins
57
- * @returns Minimum batch expiry timestamp, or undefined if no VTXOs have expiry
58
- */
59
- export declare function getMinimumExpiry(vtxos: ExtendedVirtualCoin[]): number | undefined;
60
- /**
61
- * Calculate dynamic threshold based on the earliest expiring VTXO
62
- *
63
- * @param vtxos - Array of virtual coins
64
- * @param percentage - Percentage of time until expiry (0-100)
65
- * @returns Threshold in milliseconds, or undefined if no VTXOs have expiry
66
- */
67
- export declare function calculateDynamicThreshold(vtxos: ExtendedVirtualCoin[], percentage: number): number | undefined;
39
+ export declare function getExpiringAndRecoverableVtxos(vtxos: ExtendedVirtualCoin[], percentage: number, dustAmount: bigint): ExtendedVirtualCoin[];
68
40
  /**
69
41
  * VtxoManager is a unified class for managing VTXO lifecycle operations including
70
42
  * recovery of swept/expired VTXOs and renewal to prevent expiration.
@@ -179,9 +151,9 @@ export declare class VtxoManager {
179
151
  */
180
152
  getExpiringVtxos(thresholdPercentage?: number): Promise<ExtendedVirtualCoin[]>;
181
153
  /**
182
- * Renew VTXOs by settling them back to the wallet's address
154
+ * Renew expiring VTXOs by settling them back to the wallet's address
183
155
  *
184
- * This method collects all spendable VTXOs (including recoverable ones) and settles
156
+ * This method collects all expiring spendable VTXOs (including recoverable ones) and settles
185
157
  * them back to the wallet, effectively refreshing their expiration time. This is the
186
158
  * primary way to prevent VTXOs from expiring.
187
159
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/sdk",
3
- "version": "0.3.1-alpha.5",
3
+ "version": "0.3.1-alpha.7",
4
4
  "description": "Bitcoin wallet SDK with Taproot and Ark integration",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",