@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0

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 (109) hide show
  1. package/README.md +99 -14
  2. package/dist/cjs/adapters/expo.js +8 -0
  3. package/dist/cjs/arknote/index.js +3 -3
  4. package/dist/cjs/forfeit.js +2 -2
  5. package/dist/cjs/identity/singleKey.js +8 -8
  6. package/dist/cjs/index.js +14 -5
  7. package/dist/cjs/{bip322 → intent}/index.js +38 -61
  8. package/dist/cjs/musig2/index.js +2 -1
  9. package/dist/cjs/musig2/nonces.js +4 -0
  10. package/dist/cjs/providers/ark.js +76 -45
  11. package/dist/cjs/providers/errors.js +59 -0
  12. package/dist/cjs/providers/expoArk.js +82 -0
  13. package/dist/cjs/providers/expoIndexer.js +105 -0
  14. package/dist/cjs/providers/expoUtils.js +124 -0
  15. package/dist/cjs/providers/indexer.js +3 -1
  16. package/dist/cjs/providers/onchain.js +19 -20
  17. package/dist/cjs/repositories/walletRepository.js +64 -28
  18. package/dist/cjs/script/base.js +15 -7
  19. package/dist/cjs/script/tapscript.js +20 -21
  20. package/dist/cjs/script/vhtlc.js +2 -2
  21. package/dist/cjs/tree/signingSession.js +44 -11
  22. package/dist/cjs/tree/txTree.js +3 -4
  23. package/dist/cjs/tree/validation.js +2 -3
  24. package/dist/cjs/utils/arkTransaction.js +118 -15
  25. package/dist/cjs/utils/transaction.js +28 -0
  26. package/dist/cjs/utils/unknownFields.js +7 -7
  27. package/dist/cjs/wallet/index.js +1 -1
  28. package/dist/cjs/wallet/onchain.js +6 -7
  29. package/dist/cjs/wallet/serviceWorker/response.js +32 -0
  30. package/dist/cjs/wallet/serviceWorker/utils.js +2 -9
  31. package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
  32. package/dist/cjs/wallet/serviceWorker/worker.js +48 -32
  33. package/dist/cjs/wallet/unroll.js +7 -9
  34. package/dist/cjs/wallet/utils.js +20 -0
  35. package/dist/cjs/wallet/vtxo-manager.js +323 -0
  36. package/dist/cjs/wallet/wallet.js +165 -174
  37. package/dist/esm/adapters/expo.js +3 -0
  38. package/dist/esm/arknote/index.js +2 -2
  39. package/dist/esm/forfeit.js +1 -1
  40. package/dist/esm/identity/singleKey.js +9 -9
  41. package/dist/esm/index.js +14 -10
  42. package/dist/esm/{bip322 → intent}/index.js +32 -54
  43. package/dist/esm/musig2/index.js +1 -1
  44. package/dist/esm/musig2/nonces.js +3 -0
  45. package/dist/esm/providers/ark.js +76 -45
  46. package/dist/esm/providers/errors.js +54 -0
  47. package/dist/esm/providers/expoArk.js +78 -0
  48. package/dist/esm/providers/expoIndexer.js +101 -0
  49. package/dist/esm/providers/expoUtils.js +87 -0
  50. package/dist/esm/providers/indexer.js +3 -1
  51. package/dist/esm/providers/onchain.js +19 -20
  52. package/dist/esm/repositories/walletRepository.js +64 -28
  53. package/dist/esm/script/base.js +12 -4
  54. package/dist/esm/script/tapscript.js +1 -2
  55. package/dist/esm/script/vhtlc.js +1 -1
  56. package/dist/esm/tree/signingSession.js +45 -12
  57. package/dist/esm/tree/txTree.js +3 -4
  58. package/dist/esm/tree/validation.js +2 -3
  59. package/dist/esm/utils/arkTransaction.js +110 -9
  60. package/dist/esm/utils/transaction.js +24 -0
  61. package/dist/esm/utils/unknownFields.js +3 -3
  62. package/dist/esm/wallet/index.js +1 -1
  63. package/dist/esm/wallet/onchain.js +3 -4
  64. package/dist/esm/wallet/serviceWorker/response.js +32 -0
  65. package/dist/esm/wallet/serviceWorker/utils.js +1 -8
  66. package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
  67. package/dist/esm/wallet/serviceWorker/worker.js +49 -33
  68. package/dist/esm/wallet/unroll.js +5 -7
  69. package/dist/esm/wallet/utils.js +16 -0
  70. package/dist/esm/wallet/vtxo-manager.js +317 -0
  71. package/dist/esm/wallet/wallet.js +159 -168
  72. package/dist/types/adapters/expo.d.ts +4 -0
  73. package/dist/types/arknote/index.d.ts +1 -1
  74. package/dist/types/forfeit.d.ts +2 -2
  75. package/dist/types/identity/index.d.ts +2 -2
  76. package/dist/types/identity/singleKey.d.ts +2 -2
  77. package/dist/types/index.d.ts +11 -9
  78. package/dist/types/intent/index.d.ts +41 -0
  79. package/dist/types/musig2/index.d.ts +1 -1
  80. package/dist/types/musig2/nonces.d.ts +1 -0
  81. package/dist/types/providers/ark.d.ts +197 -27
  82. package/dist/types/providers/errors.d.ts +13 -0
  83. package/dist/types/providers/expoArk.d.ts +22 -0
  84. package/dist/types/providers/expoIndexer.d.ts +18 -0
  85. package/dist/types/providers/expoUtils.d.ts +18 -0
  86. package/dist/types/providers/indexer.d.ts +8 -8
  87. package/dist/types/providers/onchain.d.ts +6 -2
  88. package/dist/types/repositories/walletRepository.d.ts +9 -5
  89. package/dist/types/script/base.d.ts +5 -2
  90. package/dist/types/tree/signingSession.d.ts +16 -11
  91. package/dist/types/utils/anchor.d.ts +2 -2
  92. package/dist/types/utils/arkTransaction.d.ts +15 -5
  93. package/dist/types/utils/transaction.d.ts +13 -0
  94. package/dist/types/utils/unknownFields.d.ts +4 -4
  95. package/dist/types/wallet/index.d.ts +47 -7
  96. package/dist/types/wallet/onchain.d.ts +1 -1
  97. package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
  98. package/dist/types/wallet/serviceWorker/utils.d.ts +1 -2
  99. package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
  100. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
  101. package/dist/types/wallet/unroll.d.ts +1 -1
  102. package/dist/types/wallet/utils.d.ts +3 -0
  103. package/dist/types/wallet/vtxo-manager.d.ts +179 -0
  104. package/dist/types/wallet/wallet.d.ts +17 -5
  105. package/package.json +11 -3
  106. package/dist/cjs/bip322/errors.js +0 -13
  107. package/dist/esm/bip322/errors.js +0 -9
  108. package/dist/types/bip322/errors.d.ts +0 -6
  109. package/dist/types/bip322/index.d.ts +0 -57
@@ -0,0 +1,323 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VtxoManager = exports.DEFAULT_RENEWAL_CONFIG = void 0;
4
+ exports.isVtxoExpiringSoon = isVtxoExpiringSoon;
5
+ exports.getExpiringAndRecoverableVtxos = getExpiringAndRecoverableVtxos;
6
+ const _1 = require(".");
7
+ /**
8
+ * Default renewal configuration values
9
+ */
10
+ exports.DEFAULT_RENEWAL_CONFIG = {
11
+ thresholdPercentage: 10,
12
+ };
13
+ function getDustAmount(wallet) {
14
+ return "dustAmount" in wallet ? wallet.dustAmount : 330n;
15
+ }
16
+ /**
17
+ * Filter VTXOs that are recoverable (swept and still spendable, or preconfirmed subdust)
18
+ *
19
+ * Recovery strategy:
20
+ * - Always recover swept VTXOs (they've been taken by the server)
21
+ * - Only recover subdust preconfirmed VTXOs (to avoid locking liquidity on settled VTXOs with long expiry)
22
+ *
23
+ * @param vtxos - Array of virtual coins to check
24
+ * @param dustAmount - Dust threshold to identify subdust
25
+ * @returns Array of recoverable VTXOs
26
+ */
27
+ function getRecoverableVtxos(vtxos, dustAmount) {
28
+ return vtxos.filter((vtxo) => {
29
+ // Always recover swept VTXOs
30
+ if ((0, _1.isRecoverable)(vtxo)) {
31
+ return true;
32
+ }
33
+ // Recover preconfirmed subdust to consolidate small amounts
34
+ if (vtxo.virtualStatus.state === "preconfirmed" &&
35
+ (0, _1.isSubdust)(vtxo, dustAmount)) {
36
+ return true;
37
+ }
38
+ return false;
39
+ });
40
+ }
41
+ /**
42
+ * Get recoverable VTXOs including subdust coins if the total value exceeds dust threshold.
43
+ *
44
+ * Decision is based on the combined total of ALL recoverable VTXOs (regular + subdust),
45
+ * not just the subdust portion alone.
46
+ *
47
+ * @param vtxos - Array of virtual coins to check
48
+ * @param dustAmount - Dust threshold amount in satoshis
49
+ * @returns Object containing recoverable VTXOs and whether subdust should be included
50
+ */
51
+ function getRecoverableWithSubdust(vtxos, dustAmount) {
52
+ const recoverableVtxos = getRecoverableVtxos(vtxos, dustAmount);
53
+ // Separate subdust from regular recoverable
54
+ const subdust = [];
55
+ const regular = [];
56
+ for (const vtxo of recoverableVtxos) {
57
+ if ((0, _1.isSubdust)(vtxo, dustAmount)) {
58
+ subdust.push(vtxo);
59
+ }
60
+ else {
61
+ regular.push(vtxo);
62
+ }
63
+ }
64
+ // Calculate totals
65
+ const regularTotal = regular.reduce((sum, vtxo) => sum + BigInt(vtxo.value), 0n);
66
+ const subdustTotal = subdust.reduce((sum, vtxo) => sum + BigInt(vtxo.value), 0n);
67
+ const combinedTotal = regularTotal + subdustTotal;
68
+ // Include subdust only if the combined total exceeds dust threshold
69
+ const shouldIncludeSubdust = combinedTotal >= dustAmount;
70
+ const vtxosToRecover = shouldIncludeSubdust ? recoverableVtxos : regular;
71
+ const totalAmount = vtxosToRecover.reduce((sum, vtxo) => sum + BigInt(vtxo.value), 0n);
72
+ return {
73
+ vtxosToRecover,
74
+ includesSubdust: shouldIncludeSubdust,
75
+ totalAmount,
76
+ };
77
+ }
78
+ /**
79
+ * Check if a VTXO is expiring soon based on threshold
80
+ *
81
+ * @param vtxo - The virtual coin to check
82
+ * @param thresholdMs - Threshold in milliseconds from now
83
+ * @returns true if VTXO expires within threshold, false otherwise
84
+ */
85
+ function isVtxoExpiringSoon(vtxo, percentage) {
86
+ const { batchExpiry } = vtxo.virtualStatus;
87
+ if (!batchExpiry) {
88
+ return false; // it doesn't expire
89
+ }
90
+ const now = Date.now();
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;
101
+ }
102
+ /**
103
+ * Filter VTXOs that are expiring soon or are recoverable/subdust
104
+ *
105
+ * @param vtxos - Array of virtual coins to check
106
+ * @param thresholdMs - Threshold in milliseconds from now
107
+ * @param dustAmount - Dust threshold amount in satoshis
108
+ * @returns Array of VTXOs expiring within threshold
109
+ */
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));
114
+ }
115
+ /**
116
+ * VtxoManager is a unified class for managing VTXO lifecycle operations including
117
+ * recovery of swept/expired VTXOs and renewal to prevent expiration.
118
+ *
119
+ * Key Features:
120
+ * - **Recovery**: Reclaim swept or expired VTXOs back to the wallet
121
+ * - **Renewal**: Refresh VTXO expiration time before they expire
122
+ * - **Smart subdust handling**: Automatically includes subdust VTXOs when economically viable
123
+ * - **Expiry monitoring**: Check for VTXOs that are expiring soon
124
+ *
125
+ * VTXOs become recoverable when:
126
+ * - The Ark server sweeps them (virtualStatus.state === "swept") and they remain spendable
127
+ * - They are preconfirmed subdust (to consolidate small amounts without locking liquidity on settled VTXOs)
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * // Initialize with renewal config
132
+ * const manager = new VtxoManager(wallet, {
133
+ * enabled: true,
134
+ * thresholdPercentage: 10
135
+ * });
136
+ *
137
+ * // Check recoverable balance
138
+ * const balance = await manager.getRecoverableBalance();
139
+ * if (balance.recoverable > 0n) {
140
+ * console.log(`Can recover ${balance.recoverable} sats`);
141
+ * const txid = await manager.recoverVtxos();
142
+ * }
143
+ *
144
+ * // Check for expiring VTXOs
145
+ * const expiring = await manager.getExpiringVtxos();
146
+ * if (expiring.length > 0) {
147
+ * console.log(`${expiring.length} VTXOs expiring soon`);
148
+ * const txid = await manager.renewVtxos();
149
+ * }
150
+ * ```
151
+ */
152
+ class VtxoManager {
153
+ constructor(wallet, renewalConfig) {
154
+ this.wallet = wallet;
155
+ this.renewalConfig = renewalConfig;
156
+ }
157
+ // ========== Recovery Methods ==========
158
+ /**
159
+ * Recover swept/expired VTXOs by settling them back to the wallet's Ark address.
160
+ *
161
+ * This method:
162
+ * 1. Fetches all VTXOs (including recoverable ones)
163
+ * 2. Filters for swept but still spendable VTXOs and preconfirmed subdust
164
+ * 3. Includes subdust VTXOs if the total value >= dust threshold
165
+ * 4. Settles everything back to the wallet's Ark address
166
+ *
167
+ * Note: Settled VTXOs with long expiry are NOT recovered to avoid locking liquidity unnecessarily.
168
+ * Only preconfirmed subdust is recovered to consolidate small amounts.
169
+ *
170
+ * @param eventCallback - Optional callback to receive settlement events
171
+ * @returns Settlement transaction ID
172
+ * @throws Error if no recoverable VTXOs found
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const manager = new VtxoManager(wallet);
177
+ *
178
+ * // Simple recovery
179
+ * const txid = await manager.recoverVtxos();
180
+ *
181
+ * // With event callback
182
+ * const txid = await manager.recoverVtxos((event) => {
183
+ * console.log('Settlement event:', event.type);
184
+ * });
185
+ * ```
186
+ */
187
+ async recoverVtxos(eventCallback) {
188
+ // Get all VTXOs including recoverable ones
189
+ const allVtxos = await this.wallet.getVtxos({
190
+ withRecoverable: true,
191
+ withUnrolled: false,
192
+ });
193
+ // Get dust amount from wallet
194
+ const dustAmount = getDustAmount(this.wallet);
195
+ // Filter recoverable VTXOs and handle subdust logic
196
+ const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
197
+ if (vtxosToRecover.length === 0) {
198
+ throw new Error("No recoverable VTXOs found");
199
+ }
200
+ const arkAddress = await this.wallet.getAddress();
201
+ // Settle all recoverable VTXOs back to the wallet
202
+ return this.wallet.settle({
203
+ inputs: vtxosToRecover,
204
+ outputs: [
205
+ {
206
+ address: arkAddress,
207
+ amount: totalAmount,
208
+ },
209
+ ],
210
+ }, eventCallback);
211
+ }
212
+ /**
213
+ * Get information about recoverable balance without executing recovery.
214
+ *
215
+ * Useful for displaying to users before they decide to recover funds.
216
+ *
217
+ * @returns Object containing recoverable amounts and subdust information
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * const manager = new VtxoManager(wallet);
222
+ * const balance = await manager.getRecoverableBalance();
223
+ *
224
+ * if (balance.recoverable > 0n) {
225
+ * console.log(`You can recover ${balance.recoverable} sats`);
226
+ * if (balance.includesSubdust) {
227
+ * console.log(`This includes ${balance.subdust} sats from subdust VTXOs`);
228
+ * }
229
+ * }
230
+ * ```
231
+ */
232
+ async getRecoverableBalance() {
233
+ const allVtxos = await this.wallet.getVtxos({
234
+ withRecoverable: true,
235
+ withUnrolled: false,
236
+ });
237
+ const dustAmount = getDustAmount(this.wallet);
238
+ const { vtxosToRecover, includesSubdust, totalAmount } = getRecoverableWithSubdust(allVtxos, dustAmount);
239
+ // Calculate subdust amount separately for reporting
240
+ const subdustAmount = vtxosToRecover
241
+ .filter((v) => BigInt(v.value) < dustAmount)
242
+ .reduce((sum, v) => sum + BigInt(v.value), 0n);
243
+ return {
244
+ recoverable: totalAmount,
245
+ subdust: subdustAmount,
246
+ includesSubdust,
247
+ vtxoCount: vtxosToRecover.length,
248
+ };
249
+ }
250
+ // ========== Renewal Methods ==========
251
+ /**
252
+ * Get VTXOs that are expiring soon based on renewal configuration
253
+ *
254
+ * @param thresholdPercentage - Optional override for threshold percentage (0-100)
255
+ * @returns Array of expiring VTXOs, empty array if renewal is disabled or no VTXOs expiring
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const manager = new VtxoManager(wallet, { enabled: true, thresholdPercentage: 10 });
260
+ * const expiringVtxos = await manager.getExpiringVtxos();
261
+ * if (expiringVtxos.length > 0) {
262
+ * console.log(`${expiringVtxos.length} VTXOs expiring soon`);
263
+ * }
264
+ * ```
265
+ */
266
+ async getExpiringVtxos(thresholdPercentage) {
267
+ const vtxos = await this.wallet.getVtxos({ withRecoverable: true });
268
+ const percentage = thresholdPercentage ??
269
+ this.renewalConfig?.thresholdPercentage ??
270
+ exports.DEFAULT_RENEWAL_CONFIG.thresholdPercentage;
271
+ return getExpiringAndRecoverableVtxos(vtxos, percentage, getDustAmount(this.wallet));
272
+ }
273
+ /**
274
+ * Renew expiring VTXOs by settling them back to the wallet's address
275
+ *
276
+ * This method collects all expiring spendable VTXOs (including recoverable ones) and settles
277
+ * them back to the wallet, effectively refreshing their expiration time. This is the
278
+ * primary way to prevent VTXOs from expiring.
279
+ *
280
+ * @param eventCallback - Optional callback for settlement events
281
+ * @returns Settlement transaction ID
282
+ * @throws Error if no VTXOs available to renew
283
+ * @throws Error if total amount is below dust threshold
284
+ *
285
+ * @example
286
+ * ```typescript
287
+ * const manager = new VtxoManager(wallet);
288
+ *
289
+ * // Simple renewal
290
+ * const txid = await manager.renewVtxos();
291
+ *
292
+ * // With event callback
293
+ * const txid = await manager.renewVtxos((event) => {
294
+ * console.log('Settlement event:', event.type);
295
+ * });
296
+ * ```
297
+ */
298
+ async renewVtxos(eventCallback) {
299
+ // Get all VTXOs (including recoverable ones)
300
+ const vtxos = await this.getExpiringVtxos();
301
+ if (vtxos.length === 0) {
302
+ throw new Error("No VTXOs available to renew");
303
+ }
304
+ const totalAmount = vtxos.reduce((sum, vtxo) => sum + vtxo.value, 0);
305
+ // Get dust amount from wallet
306
+ const dustAmount = getDustAmount(this.wallet);
307
+ // Check if total amount is above dust threshold
308
+ if (BigInt(totalAmount) < dustAmount) {
309
+ throw new Error(`Total amount ${totalAmount} is below dust threshold ${dustAmount}`);
310
+ }
311
+ const arkAddress = await this.wallet.getAddress();
312
+ return this.wallet.settle({
313
+ inputs: vtxos,
314
+ outputs: [
315
+ {
316
+ address: arkAddress,
317
+ amount: BigInt(totalAmount),
318
+ },
319
+ ],
320
+ }, eventCallback);
321
+ }
322
+ }
323
+ exports.VtxoManager = VtxoManager;