@arkade-os/sdk 0.1.4 → 0.2.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 (114) hide show
  1. package/README.md +156 -174
  2. package/dist/cjs/arknote/index.js +61 -58
  3. package/dist/cjs/bip322/errors.js +13 -0
  4. package/dist/cjs/bip322/index.js +178 -0
  5. package/dist/cjs/forfeit.js +14 -25
  6. package/dist/cjs/identity/singleKey.js +68 -0
  7. package/dist/cjs/index.js +41 -17
  8. package/dist/cjs/providers/ark.js +253 -317
  9. package/dist/cjs/providers/indexer.js +525 -0
  10. package/dist/cjs/providers/onchain.js +193 -15
  11. package/dist/cjs/script/address.js +48 -17
  12. package/dist/cjs/script/base.js +120 -3
  13. package/dist/cjs/script/default.js +18 -4
  14. package/dist/cjs/script/tapscript.js +46 -14
  15. package/dist/cjs/script/vhtlc.js +27 -7
  16. package/dist/cjs/tree/signingSession.js +63 -106
  17. package/dist/cjs/tree/txTree.js +193 -0
  18. package/dist/cjs/tree/validation.js +79 -155
  19. package/dist/cjs/utils/anchor.js +35 -0
  20. package/dist/cjs/utils/arkTransaction.js +108 -0
  21. package/dist/cjs/utils/transactionHistory.js +84 -72
  22. package/dist/cjs/utils/txSizeEstimator.js +12 -0
  23. package/dist/cjs/utils/unknownFields.js +211 -0
  24. package/dist/cjs/wallet/index.js +12 -0
  25. package/dist/cjs/wallet/onchain.js +201 -0
  26. package/dist/cjs/wallet/ramps.js +95 -0
  27. package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  28. package/dist/cjs/wallet/serviceWorker/request.js +15 -12
  29. package/dist/cjs/wallet/serviceWorker/response.js +22 -27
  30. package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
  31. package/dist/cjs/wallet/serviceWorker/wallet.js +58 -34
  32. package/dist/cjs/wallet/serviceWorker/worker.js +117 -108
  33. package/dist/cjs/wallet/unroll.js +270 -0
  34. package/dist/cjs/wallet/wallet.js +701 -454
  35. package/dist/esm/arknote/index.js +61 -57
  36. package/dist/esm/bip322/errors.js +9 -0
  37. package/dist/esm/bip322/index.js +174 -0
  38. package/dist/esm/forfeit.js +15 -26
  39. package/dist/esm/identity/singleKey.js +64 -0
  40. package/dist/esm/index.js +30 -12
  41. package/dist/esm/providers/ark.js +252 -317
  42. package/dist/esm/providers/indexer.js +521 -0
  43. package/dist/esm/providers/onchain.js +193 -15
  44. package/dist/esm/script/address.js +48 -17
  45. package/dist/esm/script/base.js +120 -3
  46. package/dist/esm/script/default.js +18 -4
  47. package/dist/esm/script/tapscript.js +46 -14
  48. package/dist/esm/script/vhtlc.js +27 -7
  49. package/dist/esm/tree/signingSession.js +65 -108
  50. package/dist/esm/tree/txTree.js +189 -0
  51. package/dist/esm/tree/validation.js +75 -152
  52. package/dist/esm/utils/anchor.js +31 -0
  53. package/dist/esm/utils/arkTransaction.js +105 -0
  54. package/dist/esm/utils/transactionHistory.js +84 -72
  55. package/dist/esm/utils/txSizeEstimator.js +12 -0
  56. package/dist/esm/utils/unknownFields.js +173 -0
  57. package/dist/esm/wallet/index.js +9 -0
  58. package/dist/esm/wallet/onchain.js +196 -0
  59. package/dist/esm/wallet/ramps.js +91 -0
  60. package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
  61. package/dist/esm/wallet/serviceWorker/request.js +15 -12
  62. package/dist/esm/wallet/serviceWorker/response.js +22 -27
  63. package/dist/esm/wallet/serviceWorker/utils.js +8 -0
  64. package/dist/esm/wallet/serviceWorker/wallet.js +59 -35
  65. package/dist/esm/wallet/serviceWorker/worker.js +117 -108
  66. package/dist/esm/wallet/unroll.js +267 -0
  67. package/dist/esm/wallet/wallet.js +674 -461
  68. package/dist/types/arknote/index.d.ts +40 -13
  69. package/dist/types/bip322/errors.d.ts +6 -0
  70. package/dist/types/bip322/index.d.ts +57 -0
  71. package/dist/types/forfeit.d.ts +2 -14
  72. package/dist/types/identity/singleKey.d.ts +27 -0
  73. package/dist/types/index.d.ts +23 -12
  74. package/dist/types/providers/ark.d.ts +114 -95
  75. package/dist/types/providers/indexer.d.ts +186 -0
  76. package/dist/types/providers/onchain.d.ts +41 -11
  77. package/dist/types/script/address.d.ts +26 -2
  78. package/dist/types/script/base.d.ts +13 -3
  79. package/dist/types/script/default.d.ts +22 -0
  80. package/dist/types/script/tapscript.d.ts +61 -5
  81. package/dist/types/script/vhtlc.d.ts +27 -0
  82. package/dist/types/tree/signingSession.d.ts +5 -5
  83. package/dist/types/tree/txTree.d.ts +28 -0
  84. package/dist/types/tree/validation.d.ts +15 -22
  85. package/dist/types/utils/anchor.d.ts +19 -0
  86. package/dist/types/utils/arkTransaction.d.ts +27 -0
  87. package/dist/types/utils/transactionHistory.d.ts +7 -1
  88. package/dist/types/utils/txSizeEstimator.d.ts +3 -0
  89. package/dist/types/utils/unknownFields.d.ts +83 -0
  90. package/dist/types/wallet/index.d.ts +51 -50
  91. package/dist/types/wallet/onchain.d.ts +49 -0
  92. package/dist/types/wallet/ramps.d.ts +32 -0
  93. package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
  94. package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
  95. package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
  96. package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
  97. package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
  98. package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
  99. package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
  100. package/dist/types/wallet/unroll.d.ts +102 -0
  101. package/dist/types/wallet/wallet.d.ts +71 -25
  102. package/package.json +14 -15
  103. package/dist/cjs/identity/inMemoryKey.js +0 -40
  104. package/dist/cjs/tree/vtxoTree.js +0 -231
  105. package/dist/cjs/utils/coinselect.js +0 -73
  106. package/dist/cjs/utils/psbt.js +0 -137
  107. package/dist/esm/identity/inMemoryKey.js +0 -36
  108. package/dist/esm/tree/vtxoTree.js +0 -191
  109. package/dist/esm/utils/coinselect.js +0 -69
  110. package/dist/esm/utils/psbt.js +0 -131
  111. package/dist/types/identity/inMemoryKey.d.ts +0 -12
  112. package/dist/types/tree/vtxoTree.d.ts +0 -33
  113. package/dist/types/utils/coinselect.d.ts +0 -21
  114. package/dist/types/utils/psbt.d.ts +0 -11
@@ -1,12 +1,37 @@
1
1
  import { Response } from './response.js';
2
- import { hex } from "@scure/base";
2
+ import { base64, hex } from "@scure/base";
3
+ import { SingleKey } from '../../identity/singleKey.js';
4
+ import { TreeSignerSession } from '../../tree/signingSession.js';
5
+ import { Transaction } from "@scure/btc-signer";
3
6
  class UnexpectedResponseError extends Error {
4
7
  constructor(response) {
5
8
  super(`Unexpected response type. Got: ${JSON.stringify(response, null, 2)}`);
6
9
  this.name = "UnexpectedResponseError";
7
10
  }
8
11
  }
9
- // ServiceWorkerWallet is a wallet that uses a service worker as "backend" to handle the wallet logic
12
+ /**
13
+ * Service Worker-based wallet implementation for browser environments.
14
+ *
15
+ * This wallet uses a service worker as a backend to handle wallet logic,
16
+ * providing secure key storage and transaction signing in web applications.
17
+ * The service worker runs in a separate thread and can persist data between
18
+ * browser sessions.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Create and initialize the service worker wallet
23
+ * const serviceWorker = await setupServiceWorker("/service-worker.js");
24
+ * const wallet = new ServiceWorkerWallet(serviceWorker);
25
+ * await wallet.init({
26
+ * privateKey: 'your_private_key_hex',
27
+ * arkServerUrl: 'https://ark.example.com'
28
+ * });
29
+ *
30
+ * // Use like any other wallet
31
+ * const address = await wallet.getAddress();
32
+ * const balance = await wallet.getBalance();
33
+ * ```
34
+ */
10
35
  export class ServiceWorkerWallet {
11
36
  constructor(serviceWorker) {
12
37
  this.serviceWorker = serviceWorker;
@@ -41,11 +66,14 @@ export class ServiceWorkerWallet {
41
66
  type: "INIT_WALLET",
42
67
  id: getRandomId(),
43
68
  privateKey: config.privateKey,
44
- network: config.network,
45
- arkServerUrl: config.arkServerUrl || "",
69
+ arkServerUrl: config.arkServerUrl,
46
70
  arkServerPublicKey: config.arkServerPublicKey,
47
71
  };
48
72
  await this.sendMessage(message);
73
+ const privKeyBytes = hex.decode(config.privateKey);
74
+ // cache the identity xOnlyPublicKey
75
+ this.cachedXOnlyPublicKey =
76
+ SingleKey.fromPrivateKey(privKeyBytes).xOnlyPublicKey();
49
77
  }
50
78
  async clear() {
51
79
  const message = {
@@ -53,6 +81,8 @@ export class ServiceWorkerWallet {
53
81
  id: getRandomId(),
54
82
  };
55
83
  await this.sendMessage(message);
84
+ // clear the cached xOnlyPublicKey
85
+ this.cachedXOnlyPublicKey = undefined;
56
86
  }
57
87
  // send a message and wait for a response
58
88
  async sendMessage(message) {
@@ -86,7 +116,7 @@ export class ServiceWorkerWallet {
86
116
  try {
87
117
  const response = await this.sendMessage(message);
88
118
  if (Response.isAddress(response)) {
89
- return response.addresses;
119
+ return response.address;
90
120
  }
91
121
  throw new UnexpectedResponseError(response);
92
122
  }
@@ -94,20 +124,20 @@ export class ServiceWorkerWallet {
94
124
  throw new Error(`Failed to get address: ${error}`);
95
125
  }
96
126
  }
97
- async getAddressInfo() {
127
+ async getBoardingAddress() {
98
128
  const message = {
99
- type: "GET_ADDRESS_INFO",
129
+ type: "GET_BOARDING_ADDRESS",
100
130
  id: getRandomId(),
101
131
  };
102
132
  try {
103
133
  const response = await this.sendMessage(message);
104
- if (Response.isAddressInfo(response)) {
105
- return response.addressInfo;
134
+ if (Response.isBoardingAddress(response)) {
135
+ return response.address;
106
136
  }
107
137
  throw new UnexpectedResponseError(response);
108
138
  }
109
139
  catch (error) {
110
- throw new Error(`Failed to get address info: ${error}`);
140
+ throw new Error(`Failed to get boarding address: ${error}`);
111
141
  }
112
142
  }
113
143
  async getBalance() {
@@ -126,26 +156,11 @@ export class ServiceWorkerWallet {
126
156
  throw new Error(`Failed to get balance: ${error}`);
127
157
  }
128
158
  }
129
- async getCoins() {
130
- const message = {
131
- type: "GET_COINS",
132
- id: getRandomId(),
133
- };
134
- try {
135
- const response = await this.sendMessage(message);
136
- if (Response.isCoins(response)) {
137
- return response.coins;
138
- }
139
- throw new UnexpectedResponseError(response);
140
- }
141
- catch (error) {
142
- throw new Error(`Failed to get coins: ${error}`);
143
- }
144
- }
145
- async getVtxos() {
159
+ async getVtxos(filter) {
146
160
  const message = {
147
161
  type: "GET_VTXOS",
148
162
  id: getRandomId(),
163
+ filter,
149
164
  };
150
165
  try {
151
166
  const response = await this.sendMessage(message);
@@ -174,11 +189,10 @@ export class ServiceWorkerWallet {
174
189
  throw new Error(`Failed to get boarding UTXOs: ${error}`);
175
190
  }
176
191
  }
177
- async sendBitcoin(params, zeroFee) {
192
+ async sendBitcoin(params) {
178
193
  const message = {
179
194
  type: "SEND_BITCOIN",
180
195
  params,
181
- zeroFee,
182
196
  id: getRandomId(),
183
197
  };
184
198
  try {
@@ -245,21 +259,31 @@ export class ServiceWorkerWallet {
245
259
  throw new Error(`Failed to get transaction history: ${error}`);
246
260
  }
247
261
  }
248
- async exit(outpoints) {
262
+ xOnlyPublicKey() {
263
+ if (!this.cachedXOnlyPublicKey) {
264
+ throw new Error("Wallet not initialized");
265
+ }
266
+ return this.cachedXOnlyPublicKey;
267
+ }
268
+ signerSession() {
269
+ return TreeSignerSession.random();
270
+ }
271
+ async sign(tx, inputIndexes) {
249
272
  const message = {
250
- type: "EXIT",
251
- outpoints,
273
+ type: "SIGN",
274
+ tx: base64.encode(tx.toPSBT()),
275
+ inputIndexes,
252
276
  id: getRandomId(),
253
277
  };
254
278
  try {
255
279
  const response = await this.sendMessage(message);
256
- if (response.type === "EXIT_SUCCESS") {
257
- return;
280
+ if (Response.isSignSuccess(response)) {
281
+ return Transaction.fromPSBT(base64.decode(response.tx));
258
282
  }
259
283
  throw new UnexpectedResponseError(response);
260
284
  }
261
285
  catch (error) {
262
- throw new Error(`Failed to exit: ${error}`);
286
+ throw new Error(`Failed to sign: ${error}`);
263
287
  }
264
288
  }
265
289
  }
@@ -1,14 +1,19 @@
1
1
  /// <reference lib="webworker" />
2
- import { InMemoryKey } from '../../identity/inMemoryKey.js';
2
+ import { SingleKey } from '../../identity/singleKey.js';
3
+ import { isSpendable, isSubdust } from '../index.js';
3
4
  import { Wallet } from '../wallet.js';
4
5
  import { Request } from './request.js';
5
6
  import { Response } from './response.js';
6
7
  import { RestArkProvider } from '../../providers/ark.js';
7
- import { DefaultVtxo } from '../../script/default.js';
8
8
  import { IndexedDBVtxoRepository } from './db/vtxo/idb.js';
9
9
  import { vtxosToTxs } from '../../utils/transactionHistory.js';
10
- // Worker is a class letting to interact with ServiceWorkerWallet from the client
11
- // it aims to be run in a service worker context
10
+ import { RestIndexerProvider } from '../../providers/indexer.js';
11
+ import { base64, hex } from "@scure/base";
12
+ import { Transaction } from "@scure/btc-signer";
13
+ /**
14
+ * Worker is a class letting to interact with ServiceWorkerWallet from the client
15
+ * it aims to be run in a service worker context
16
+ */
12
17
  export class Worker {
13
18
  constructor(vtxoRepository = new IndexedDBVtxoRepository(), messageCallback = () => { }) {
14
19
  this.vtxoRepository = vtxoRepository;
@@ -36,41 +41,48 @@ export class Worker {
36
41
  await this.vtxoRepository.close();
37
42
  this.wallet = undefined;
38
43
  this.arkProvider = undefined;
44
+ this.indexerProvider = undefined;
39
45
  this.vtxoSubscription = undefined;
40
46
  }
41
47
  async onWalletInitialized() {
42
48
  if (!this.wallet ||
43
49
  !this.arkProvider ||
50
+ !this.indexerProvider ||
44
51
  !this.wallet.offchainTapscript ||
45
52
  !this.wallet.boardingTapscript) {
46
53
  return;
47
54
  }
48
55
  // subscribe to address updates
49
- const addressInfo = await this.wallet.getAddressInfo();
50
- if (!addressInfo.offchain) {
51
- return;
52
- }
53
56
  await this.vtxoRepository.open();
54
- // set the initial vtxos state
55
- const { spendableVtxos, spentVtxos } = await this.arkProvider.getVirtualCoins(addressInfo.offchain.address);
56
57
  const encodedOffchainTapscript = this.wallet.offchainTapscript.encode();
57
58
  const forfeit = this.wallet.offchainTapscript.forfeit();
58
- const vtxos = [...spendableVtxos, ...spentVtxos].map((vtxo) => ({
59
+ const exit = this.wallet.offchainTapscript.exit();
60
+ const script = hex.encode(this.wallet.offchainTapscript.pkScript);
61
+ // set the initial vtxos state
62
+ const response = await this.indexerProvider.getVtxos({
63
+ scripts: [script],
64
+ });
65
+ const vtxos = response.vtxos.map((vtxo) => ({
59
66
  ...vtxo,
60
- tapLeafScript: forfeit,
61
- scripts: encodedOffchainTapscript,
67
+ forfeitTapLeafScript: forfeit,
68
+ intentTapLeafScript: exit,
69
+ tapTree: encodedOffchainTapscript,
62
70
  }));
63
71
  await this.vtxoRepository.addOrUpdate(vtxos);
64
- this.processVtxoSubscription(addressInfo.offchain);
72
+ this.processVtxoSubscription({
73
+ script,
74
+ vtxoScript: this.wallet.offchainTapscript,
75
+ });
65
76
  }
66
- async processVtxoSubscription({ address, scripts, }) {
77
+ async processVtxoSubscription({ script, vtxoScript, }) {
67
78
  try {
68
- const addressScripts = [...scripts.exit, ...scripts.forfeit];
69
- const vtxoScript = DefaultVtxo.Script.decode(addressScripts);
70
- const tapLeafScript = vtxoScript.findLeaf(scripts.forfeit[0]);
79
+ const forfeitTapLeafScript = vtxoScript.forfeit();
80
+ const intentTapLeafScript = vtxoScript.exit();
71
81
  const abortController = new AbortController();
72
- const subscription = this.arkProvider.subscribeForAddress(address, abortController.signal);
82
+ const subscriptionId = await this.indexerProvider.subscribeForScripts([script]);
83
+ const subscription = this.indexerProvider.getSubscription(subscriptionId, abortController.signal);
73
84
  this.vtxoSubscription = abortController;
85
+ const tapTree = vtxoScript.encode();
74
86
  for await (const update of subscription) {
75
87
  const vtxos = [...update.newVtxos, ...update.spentVtxos];
76
88
  if (vtxos.length === 0) {
@@ -78,8 +90,9 @@ export class Worker {
78
90
  }
79
91
  const extendedVtxos = vtxos.map((vtxo) => ({
80
92
  ...vtxo,
81
- tapLeafScript,
82
- scripts: addressScripts,
93
+ forfeitTapLeafScript,
94
+ intentTapLeafScript,
95
+ tapTree,
83
96
  }));
84
97
  await this.vtxoRepository.addOrUpdate(extendedVtxos);
85
98
  }
@@ -103,9 +116,9 @@ export class Worker {
103
116
  }
104
117
  try {
105
118
  this.arkProvider = new RestArkProvider(message.arkServerUrl);
119
+ this.indexerProvider = new RestIndexerProvider(message.arkServerUrl);
106
120
  this.wallet = await Wallet.create({
107
- network: message.network,
108
- identity: InMemoryKey.fromHex(message.privateKey),
121
+ identity: SingleKey.fromHex(message.privateKey),
109
122
  arkServerUrl: message.arkServerUrl,
110
123
  arkServerPublicKey: message.arkServerPublicKey,
111
124
  });
@@ -159,7 +172,7 @@ export class Worker {
159
172
  return;
160
173
  }
161
174
  try {
162
- const txid = await this.wallet.sendBitcoin(message.params, message.zeroFee);
175
+ const txid = await this.wallet.sendBitcoin(message.params);
163
176
  event.source?.postMessage(Response.sendBitcoinSuccess(message.id, txid));
164
177
  }
165
178
  catch (error) {
@@ -183,8 +196,8 @@ export class Worker {
183
196
  return;
184
197
  }
185
198
  try {
186
- const addresses = await this.wallet.getAddress();
187
- event.source?.postMessage(Response.addresses(message.id, addresses));
199
+ const address = await this.wallet.getAddress();
200
+ event.source?.postMessage(Response.address(message.id, address));
188
201
  }
189
202
  catch (error) {
190
203
  console.error("Error getting address:", error);
@@ -194,11 +207,11 @@ export class Worker {
194
207
  event.source?.postMessage(Response.error(message.id, errorMessage));
195
208
  }
196
209
  }
197
- async handleGetAddressInfo(event) {
210
+ async handleGetBoardingAddress(event) {
198
211
  const message = event.data;
199
- if (!Request.isGetAddressInfo(message)) {
200
- console.error("Invalid GET_ADDRESS_INFO message format", message);
201
- event.source?.postMessage(Response.error(message.id, "Invalid GET_ADDRESS_INFO message format"));
212
+ if (!Request.isGetBoardingAddress(message)) {
213
+ console.error("Invalid GET_BOARDING_ADDRESS message format", message);
214
+ event.source?.postMessage(Response.error(message.id, "Invalid GET_BOARDING_ADDRESS message format"));
202
215
  return;
203
216
  }
204
217
  if (!this.wallet) {
@@ -207,11 +220,11 @@ export class Worker {
207
220
  return;
208
221
  }
209
222
  try {
210
- const addressInfo = await this.wallet.getAddressInfo();
211
- event.source?.postMessage(Response.addressInfo(message.id, addressInfo));
223
+ const address = await this.wallet.getBoardingAddress();
224
+ event.source?.postMessage(Response.boardingAddress(message.id, address));
212
225
  }
213
226
  catch (error) {
214
- console.error("Error getting address info:", error);
227
+ console.error("Error getting boarding address:", error);
215
228
  const errorMessage = error instanceof Error
216
229
  ? error.message
217
230
  : "Unknown error occurred";
@@ -231,40 +244,52 @@ export class Worker {
231
244
  return;
232
245
  }
233
246
  try {
234
- const coins = await this.wallet.getCoins();
235
- const onchainConfirmed = coins
236
- .filter((coin) => coin.status.confirmed)
237
- .reduce((sum, coin) => sum + coin.value, 0);
238
- const onchainUnconfirmed = coins
239
- .filter((coin) => !coin.status.confirmed)
240
- .reduce((sum, coin) => sum + coin.value, 0);
241
- const onchainTotal = onchainConfirmed + onchainUnconfirmed;
242
- const spendableVtxos = await this.vtxoRepository.getSpendableVtxos();
243
- const offchainSettledBalance = spendableVtxos.reduce((sum, vtxo) => vtxo.virtualStatus.state === "settled"
244
- ? sum + vtxo.value
245
- : sum, 0);
246
- const offchainPendingBalance = spendableVtxos.reduce((sum, vtxo) => vtxo.virtualStatus.state === "pending"
247
- ? sum + vtxo.value
248
- : sum, 0);
249
- const offchainSweptBalance = spendableVtxos.reduce((sum, vtxo) => vtxo.virtualStatus.state === "swept"
250
- ? sum + vtxo.value
251
- : sum, 0);
252
- const offchainTotal = offchainSettledBalance +
253
- offchainPendingBalance +
254
- offchainSweptBalance;
247
+ const [boardingUtxos, spendableVtxos, sweptVtxos] = await Promise.all([
248
+ this.wallet.getBoardingUtxos(),
249
+ this.vtxoRepository.getSpendableVtxos(),
250
+ this.vtxoRepository.getSweptVtxos(),
251
+ ]);
252
+ // boarding
253
+ let confirmed = 0;
254
+ let unconfirmed = 0;
255
+ for (const utxo of boardingUtxos) {
256
+ if (utxo.status.confirmed) {
257
+ confirmed += utxo.value;
258
+ }
259
+ else {
260
+ unconfirmed += utxo.value;
261
+ }
262
+ }
263
+ // offchain
264
+ let settled = 0;
265
+ let preconfirmed = 0;
266
+ let recoverable = 0;
267
+ for (const vtxo of spendableVtxos) {
268
+ if (vtxo.virtualStatus.state === "settled") {
269
+ settled += vtxo.value;
270
+ }
271
+ else if (vtxo.virtualStatus.state === "preconfirmed") {
272
+ preconfirmed += vtxo.value;
273
+ }
274
+ }
275
+ for (const vtxo of sweptVtxos) {
276
+ if (isSpendable(vtxo)) {
277
+ recoverable += vtxo.value;
278
+ }
279
+ }
280
+ const totalBoarding = confirmed + unconfirmed;
281
+ const totalOffchain = settled + preconfirmed + recoverable;
255
282
  event.source?.postMessage(Response.balance(message.id, {
256
- onchain: {
257
- confirmed: onchainConfirmed,
258
- unconfirmed: onchainUnconfirmed,
259
- total: onchainTotal,
260
- },
261
- offchain: {
262
- swept: offchainSweptBalance,
263
- settled: offchainSettledBalance,
264
- pending: offchainPendingBalance,
265
- total: offchainTotal,
283
+ boarding: {
284
+ confirmed,
285
+ unconfirmed,
286
+ total: totalBoarding,
266
287
  },
267
- total: onchainTotal + offchainTotal,
288
+ settled,
289
+ preconfirmed,
290
+ available: settled + preconfirmed,
291
+ recoverable,
292
+ total: totalBoarding + totalOffchain,
268
293
  }));
269
294
  }
270
295
  catch (error) {
@@ -275,30 +300,6 @@ export class Worker {
275
300
  event.source?.postMessage(Response.error(message.id, errorMessage));
276
301
  }
277
302
  }
278
- async handleGetCoins(event) {
279
- const message = event.data;
280
- if (!Request.isGetCoins(message)) {
281
- console.error("Invalid GET_COINS message format", message);
282
- event.source?.postMessage(Response.error(message.id, "Invalid GET_COINS message format"));
283
- return;
284
- }
285
- if (!this.wallet) {
286
- console.error("Wallet not initialized");
287
- event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
288
- return;
289
- }
290
- try {
291
- const coins = await this.wallet.getCoins();
292
- event.source?.postMessage(Response.coins(message.id, coins));
293
- }
294
- catch (error) {
295
- console.error("Error getting coins:", error);
296
- const errorMessage = error instanceof Error
297
- ? error.message
298
- : "Unknown error occurred";
299
- event.source?.postMessage(Response.error(message.id, errorMessage));
300
- }
301
- }
302
303
  async handleGetVtxos(event) {
303
304
  const message = event.data;
304
305
  if (!Request.isGetVtxos(message)) {
@@ -312,7 +313,18 @@ export class Worker {
312
313
  return;
313
314
  }
314
315
  try {
315
- const vtxos = await this.vtxoRepository.getSpendableVtxos();
316
+ let vtxos = await this.vtxoRepository.getSpendableVtxos();
317
+ if (!message.filter?.withRecoverable) {
318
+ if (!this.wallet)
319
+ throw new Error("Wallet not initialized");
320
+ // exclude subdust is we don't want recoverable
321
+ vtxos = vtxos.filter((v) => !isSubdust(v, this.wallet.dustAmount));
322
+ }
323
+ if (message.filter?.withRecoverable) {
324
+ // get also swept and spendable vtxos
325
+ const sweptVtxos = await this.vtxoRepository.getSweptVtxos();
326
+ vtxos.push(...sweptVtxos.filter(isSpendable));
327
+ }
316
328
  event.source?.postMessage(Response.vtxos(message.id, vtxos));
317
329
  }
318
330
  catch (error) {
@@ -360,7 +372,7 @@ export class Worker {
360
372
  return;
361
373
  }
362
374
  try {
363
- const { boardingTxs, roundsToIgnore } = await this.wallet.getBoardingTxs();
375
+ const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
364
376
  const { spendable, spent } = await this.vtxoRepository.getAllVtxos();
365
377
  // convert VTXOs to offchain transactions
366
378
  const offchainTxs = vtxosToTxs(spendable, spent, roundsToIgnore);
@@ -394,11 +406,11 @@ export class Worker {
394
406
  }
395
407
  event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined));
396
408
  }
397
- async handleExit(event) {
409
+ async handleSign(event) {
398
410
  const message = event.data;
399
- if (!Request.isExit(message)) {
400
- console.error("Invalid EXIT message format", message);
401
- event.source?.postMessage(Response.error(message.id, "Invalid EXIT message format"));
411
+ if (!Request.isSign(message)) {
412
+ console.error("Invalid SIGN message format", message);
413
+ event.source?.postMessage(Response.error(message.id, "Invalid SIGN message format"));
402
414
  return;
403
415
  }
404
416
  if (!this.wallet) {
@@ -407,11 +419,12 @@ export class Worker {
407
419
  return;
408
420
  }
409
421
  try {
410
- await this.wallet.exit(message.outpoints);
411
- event.source?.postMessage(Response.exitSuccess(message.id));
422
+ const tx = Transaction.fromPSBT(base64.decode(message.tx));
423
+ const signedTx = await this.wallet.identity.sign(tx, message.inputIndexes);
424
+ event.source?.postMessage(Response.signSuccess(message.id, base64.encode(signedTx.toPSBT())));
412
425
  }
413
426
  catch (error) {
414
- console.error("Error exiting:", error);
427
+ console.error("Error signing:", error);
415
428
  const errorMessage = error instanceof Error
416
429
  ? error.message
417
430
  : "Unknown error occurred";
@@ -443,18 +456,14 @@ export class Worker {
443
456
  await this.handleGetAddress(event);
444
457
  break;
445
458
  }
446
- case "GET_ADDRESS_INFO": {
447
- await this.handleGetAddressInfo(event);
459
+ case "GET_BOARDING_ADDRESS": {
460
+ await this.handleGetBoardingAddress(event);
448
461
  break;
449
462
  }
450
463
  case "GET_BALANCE": {
451
464
  await this.handleGetBalance(event);
452
465
  break;
453
466
  }
454
- case "GET_COINS": {
455
- await this.handleGetCoins(event);
456
- break;
457
- }
458
467
  case "GET_VTXOS": {
459
468
  await this.handleGetVtxos(event);
460
469
  break;
@@ -471,14 +480,14 @@ export class Worker {
471
480
  await this.handleGetStatus(event);
472
481
  break;
473
482
  }
474
- case "EXIT": {
475
- await this.handleExit(event);
476
- break;
477
- }
478
483
  case "CLEAR": {
479
484
  await this.handleClear(event);
480
485
  break;
481
486
  }
487
+ case "SIGN": {
488
+ await this.handleSign(event);
489
+ break;
490
+ }
482
491
  default:
483
492
  event.source?.postMessage(Response.error(message.id, "Unknown message type"));
484
493
  }