@arkade-os/sdk 0.3.7 → 0.3.9
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 +78 -1
- package/dist/cjs/identity/singleKey.js +33 -1
- package/dist/cjs/index.js +17 -2
- package/dist/cjs/intent/index.js +31 -2
- package/dist/cjs/providers/ark.js +9 -3
- package/dist/cjs/providers/indexer.js +2 -2
- package/dist/cjs/wallet/batch.js +183 -0
- package/dist/cjs/wallet/index.js +15 -0
- package/dist/cjs/wallet/serviceWorker/request.js +0 -2
- package/dist/cjs/wallet/serviceWorker/wallet.js +98 -34
- package/dist/cjs/wallet/serviceWorker/worker.js +169 -69
- package/dist/cjs/wallet/utils.js +2 -2
- package/dist/cjs/wallet/vtxo-manager.js +5 -0
- package/dist/cjs/wallet/wallet.js +399 -356
- package/dist/esm/identity/singleKey.js +31 -0
- package/dist/esm/index.js +12 -7
- package/dist/esm/intent/index.js +31 -2
- package/dist/esm/providers/ark.js +9 -3
- package/dist/esm/providers/indexer.js +2 -2
- package/dist/esm/wallet/batch.js +180 -0
- package/dist/esm/wallet/index.js +14 -0
- package/dist/esm/wallet/serviceWorker/request.js +0 -2
- package/dist/esm/wallet/serviceWorker/wallet.js +96 -33
- package/dist/esm/wallet/serviceWorker/worker.js +171 -71
- package/dist/esm/wallet/utils.js +2 -2
- package/dist/esm/wallet/vtxo-manager.js +6 -1
- package/dist/esm/wallet/wallet.js +400 -359
- package/dist/types/identity/index.d.ts +5 -3
- package/dist/types/identity/singleKey.d.ts +20 -1
- package/dist/types/index.d.ts +11 -8
- package/dist/types/intent/index.d.ts +19 -2
- package/dist/types/providers/ark.d.ts +9 -8
- package/dist/types/providers/indexer.d.ts +2 -2
- package/dist/types/wallet/batch.d.ts +87 -0
- package/dist/types/wallet/index.d.ts +75 -16
- package/dist/types/wallet/serviceWorker/request.d.ts +5 -1
- package/dist/types/wallet/serviceWorker/wallet.d.ts +46 -15
- package/dist/types/wallet/serviceWorker/worker.d.ts +6 -3
- package/dist/types/wallet/utils.d.ts +8 -3
- package/dist/types/wallet/wallet.d.ts +96 -35
- package/package.json +123 -113
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServiceWorkerWallet = void 0;
|
|
3
|
+
exports.ServiceWorkerWallet = exports.ServiceWorkerReadonlyWallet = void 0;
|
|
4
4
|
const response_1 = require("./response");
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
6
|
const indexedDB_1 = require("../../storage/indexedDB");
|
|
@@ -16,7 +16,16 @@ class UnexpectedResponseError extends Error {
|
|
|
16
16
|
this.name = "UnexpectedResponseError";
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
const createCommon = (options) => {
|
|
20
|
+
// Default to IndexedDB for service worker context
|
|
21
|
+
const storage = new indexedDB_1.IndexedDBStorageAdapter(options.dbName || utils_1.DEFAULT_DB_NAME, options.dbVersion);
|
|
22
|
+
// Create repositories
|
|
23
|
+
return {
|
|
24
|
+
walletRepo: new walletRepository_1.WalletRepositoryImpl(storage),
|
|
25
|
+
contractRepo: new contractRepository_1.ContractRepositoryImpl(storage),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
class ServiceWorkerReadonlyWallet {
|
|
20
29
|
constructor(serviceWorker, identity, walletRepository, contractRepository) {
|
|
21
30
|
this.serviceWorker = serviceWorker;
|
|
22
31
|
this.identity = identity;
|
|
@@ -24,27 +33,17 @@ class ServiceWorkerWallet {
|
|
|
24
33
|
this.contractRepository = contractRepository;
|
|
25
34
|
}
|
|
26
35
|
static async create(options) {
|
|
27
|
-
|
|
28
|
-
const storage = new indexedDB_1.IndexedDBStorageAdapter(options.dbName || utils_1.DEFAULT_DB_NAME, options.dbVersion);
|
|
29
|
-
// Create repositories
|
|
30
|
-
const walletRepo = new walletRepository_1.WalletRepositoryImpl(storage);
|
|
31
|
-
const contractRepo = new contractRepository_1.ContractRepositoryImpl(storage);
|
|
32
|
-
// Extract identity and check if it can expose private key
|
|
33
|
-
const identity = isPrivateKeyIdentity(options.identity)
|
|
34
|
-
? options.identity
|
|
35
|
-
: null;
|
|
36
|
-
if (!identity) {
|
|
37
|
-
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose a single private key");
|
|
38
|
-
}
|
|
39
|
-
// Extract private key for service worker initialization
|
|
40
|
-
const privateKey = identity.toHex();
|
|
36
|
+
const { walletRepo, contractRepo } = createCommon(options);
|
|
41
37
|
// Create the wallet instance
|
|
42
|
-
const wallet = new
|
|
38
|
+
const wallet = new ServiceWorkerReadonlyWallet(options.serviceWorker, options.identity, walletRepo, contractRepo);
|
|
39
|
+
const publicKey = await options.identity
|
|
40
|
+
.compressedPublicKey()
|
|
41
|
+
.then(base_1.hex.encode);
|
|
43
42
|
// Initialize the service worker with the config
|
|
44
43
|
const initMessage = {
|
|
45
44
|
type: "INIT_WALLET",
|
|
46
45
|
id: getRandomId(),
|
|
47
|
-
|
|
46
|
+
key: { publicKey },
|
|
48
47
|
arkServerUrl: options.arkServerUrl,
|
|
49
48
|
arkServerPublicKey: options.arkServerPublicKey,
|
|
50
49
|
};
|
|
@@ -59,14 +58,14 @@ class ServiceWorkerWallet {
|
|
|
59
58
|
* @example
|
|
60
59
|
* ```typescript
|
|
61
60
|
* // One-liner setup - handles everything automatically!
|
|
62
|
-
* const wallet = await
|
|
61
|
+
* const wallet = await ServiceWorkerReadonlyWallet.setup({
|
|
63
62
|
* serviceWorkerPath: '/service-worker.js',
|
|
64
63
|
* arkServerUrl: 'https://mutinynet.arkade.sh'
|
|
65
64
|
* });
|
|
66
65
|
*
|
|
67
|
-
* // With custom identity
|
|
68
|
-
* const identity =
|
|
69
|
-
* const wallet = await
|
|
66
|
+
* // With custom readonly identity
|
|
67
|
+
* const identity = ReadonlySingleKey.fromPublicKey('your_public_key_hex');
|
|
68
|
+
* const wallet = await ServiceWorkerReadonlyWallet.setup({
|
|
70
69
|
* serviceWorkerPath: '/service-worker.js',
|
|
71
70
|
* arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
72
71
|
* identity
|
|
@@ -77,7 +76,7 @@ class ServiceWorkerWallet {
|
|
|
77
76
|
// Register and setup the service worker
|
|
78
77
|
const serviceWorker = await (0, utils_1.setupServiceWorker)(options.serviceWorkerPath);
|
|
79
78
|
// Use the existing create method
|
|
80
|
-
return
|
|
79
|
+
return ServiceWorkerReadonlyWallet.create({
|
|
81
80
|
...options,
|
|
82
81
|
serviceWorker,
|
|
83
82
|
});
|
|
@@ -229,6 +228,82 @@ class ServiceWorkerWallet {
|
|
|
229
228
|
throw new Error(`Failed to get vtxos: ${error}`);
|
|
230
229
|
}
|
|
231
230
|
}
|
|
231
|
+
async reload() {
|
|
232
|
+
const message = {
|
|
233
|
+
type: "RELOAD_WALLET",
|
|
234
|
+
id: getRandomId(),
|
|
235
|
+
};
|
|
236
|
+
const response = await this.sendMessage(message);
|
|
237
|
+
if (response_1.Response.isWalletReloaded(response)) {
|
|
238
|
+
return response.success;
|
|
239
|
+
}
|
|
240
|
+
throw new UnexpectedResponseError(response);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
exports.ServiceWorkerReadonlyWallet = ServiceWorkerReadonlyWallet;
|
|
244
|
+
class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
245
|
+
constructor(serviceWorker, identity, walletRepository, contractRepository) {
|
|
246
|
+
super(serviceWorker, identity, walletRepository, contractRepository);
|
|
247
|
+
this.serviceWorker = serviceWorker;
|
|
248
|
+
this.identity = identity;
|
|
249
|
+
this.walletRepository = walletRepository;
|
|
250
|
+
this.contractRepository = contractRepository;
|
|
251
|
+
}
|
|
252
|
+
static async create(options) {
|
|
253
|
+
const { walletRepo, contractRepo } = createCommon(options);
|
|
254
|
+
// Extract identity and check if it can expose private key
|
|
255
|
+
const identity = isPrivateKeyIdentity(options.identity)
|
|
256
|
+
? options.identity
|
|
257
|
+
: null;
|
|
258
|
+
if (!identity) {
|
|
259
|
+
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose a single private key");
|
|
260
|
+
}
|
|
261
|
+
// Extract private key for service worker initialization
|
|
262
|
+
const privateKey = identity.toHex();
|
|
263
|
+
// Create the wallet instance
|
|
264
|
+
const wallet = new ServiceWorkerWallet(options.serviceWorker, identity, walletRepo, contractRepo);
|
|
265
|
+
// Initialize the service worker with the config
|
|
266
|
+
const initMessage = {
|
|
267
|
+
type: "INIT_WALLET",
|
|
268
|
+
id: getRandomId(),
|
|
269
|
+
key: { privateKey },
|
|
270
|
+
arkServerUrl: options.arkServerUrl,
|
|
271
|
+
arkServerPublicKey: options.arkServerPublicKey,
|
|
272
|
+
};
|
|
273
|
+
// Initialize the service worker
|
|
274
|
+
await wallet.sendMessage(initMessage);
|
|
275
|
+
return wallet;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Simplified setup method that handles service worker registration,
|
|
279
|
+
* identity creation, and wallet initialization automatically.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // One-liner setup - handles everything automatically!
|
|
284
|
+
* const wallet = await ServiceWorkerWallet.setup({
|
|
285
|
+
* serviceWorkerPath: '/service-worker.js',
|
|
286
|
+
* arkServerUrl: 'https://mutinynet.arkade.sh'
|
|
287
|
+
* });
|
|
288
|
+
*
|
|
289
|
+
* // With custom identity
|
|
290
|
+
* const identity = SingleKey.fromHex('your_private_key_hex');
|
|
291
|
+
* const wallet = await ServiceWorkerWallet.setup({
|
|
292
|
+
* serviceWorkerPath: '/service-worker.js',
|
|
293
|
+
* arkServerUrl: 'https://mutinynet.arkade.sh',
|
|
294
|
+
* identity
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
static async setup(options) {
|
|
299
|
+
// Register and setup the service worker
|
|
300
|
+
const serviceWorker = await (0, utils_1.setupServiceWorker)(options.serviceWorkerPath);
|
|
301
|
+
// Use the existing create method
|
|
302
|
+
return ServiceWorkerWallet.create({
|
|
303
|
+
...options,
|
|
304
|
+
serviceWorker,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
232
307
|
async sendBitcoin(params) {
|
|
233
308
|
const message = {
|
|
234
309
|
type: "SEND_BITCOIN",
|
|
@@ -286,17 +361,6 @@ class ServiceWorkerWallet {
|
|
|
286
361
|
throw new Error(`Settlement failed: ${error}`);
|
|
287
362
|
}
|
|
288
363
|
}
|
|
289
|
-
async reload() {
|
|
290
|
-
const message = {
|
|
291
|
-
type: "RELOAD_WALLET",
|
|
292
|
-
id: getRandomId(),
|
|
293
|
-
};
|
|
294
|
-
const response = await this.sendMessage(message);
|
|
295
|
-
if (response_1.Response.isWalletReloaded(response)) {
|
|
296
|
-
return response.success;
|
|
297
|
-
}
|
|
298
|
-
throw new UnexpectedResponseError(response);
|
|
299
|
-
}
|
|
300
364
|
}
|
|
301
365
|
exports.ServiceWorkerWallet = ServiceWorkerWallet;
|
|
302
366
|
function getRandomId() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/// <reference lib="webworker" />
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
exports.Worker = void 0;
|
|
4
|
-
/// <reference lib="webworker" />
|
|
5
5
|
const singleKey_1 = require("../../identity/singleKey");
|
|
6
6
|
const __1 = require("..");
|
|
7
7
|
const wallet_1 = require("../wallet");
|
|
@@ -15,9 +15,70 @@ const indexedDB_1 = require("../../storage/indexedDB");
|
|
|
15
15
|
const walletRepository_1 = require("../../repositories/walletRepository");
|
|
16
16
|
const utils_1 = require("../utils");
|
|
17
17
|
const utils_2 = require("./utils");
|
|
18
|
+
class ReadonlyHandler {
|
|
19
|
+
constructor(wallet) {
|
|
20
|
+
this.wallet = wallet;
|
|
21
|
+
}
|
|
22
|
+
get offchainTapscript() {
|
|
23
|
+
return this.wallet.offchainTapscript;
|
|
24
|
+
}
|
|
25
|
+
get boardingTapscript() {
|
|
26
|
+
return this.wallet.boardingTapscript;
|
|
27
|
+
}
|
|
28
|
+
get onchainProvider() {
|
|
29
|
+
return this.wallet.onchainProvider;
|
|
30
|
+
}
|
|
31
|
+
get dustAmount() {
|
|
32
|
+
return this.wallet.dustAmount;
|
|
33
|
+
}
|
|
34
|
+
get identity() {
|
|
35
|
+
return this.wallet.identity;
|
|
36
|
+
}
|
|
37
|
+
notifyIncomingFunds(...args) {
|
|
38
|
+
return this.wallet.notifyIncomingFunds(...args);
|
|
39
|
+
}
|
|
40
|
+
getAddress() {
|
|
41
|
+
return this.wallet.getAddress();
|
|
42
|
+
}
|
|
43
|
+
getBoardingAddress() {
|
|
44
|
+
return this.wallet.getBoardingAddress();
|
|
45
|
+
}
|
|
46
|
+
getBoardingTxs() {
|
|
47
|
+
return this.wallet.getBoardingTxs();
|
|
48
|
+
}
|
|
49
|
+
async handleReload(_) {
|
|
50
|
+
const pending = await this.wallet.fetchPendingTxs();
|
|
51
|
+
return { pending, finalized: [] };
|
|
52
|
+
}
|
|
53
|
+
async handleSettle(..._) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
async handleSendBitcoin(..._) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
class Handler extends ReadonlyHandler {
|
|
61
|
+
constructor(wallet) {
|
|
62
|
+
super(wallet);
|
|
63
|
+
this.wallet = wallet;
|
|
64
|
+
}
|
|
65
|
+
async handleReload(vtxos) {
|
|
66
|
+
return this.wallet.finalizePendingTxs(vtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
|
|
67
|
+
vtxo.virtualStatus.state !== "settled"));
|
|
68
|
+
}
|
|
69
|
+
async handleSettle(...args) {
|
|
70
|
+
return this.wallet.settle(...args);
|
|
71
|
+
}
|
|
72
|
+
async handleSendBitcoin(...args) {
|
|
73
|
+
return this.wallet.sendBitcoin(...args);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
18
76
|
/**
|
|
19
|
-
* Worker is a class letting to interact with ServiceWorkerWallet
|
|
20
|
-
* it aims to be run in a service worker context
|
|
77
|
+
* Worker is a class letting to interact with ServiceWorkerWallet and ServiceWorkerReadonlyWallet from
|
|
78
|
+
* the client; it aims to be run in a service worker context.
|
|
79
|
+
*
|
|
80
|
+
* The messages requiring a Wallet rather than a ReadonlyWallet result in no-op
|
|
81
|
+
* without errors.
|
|
21
82
|
*/
|
|
22
83
|
class Worker {
|
|
23
84
|
constructor(dbName = utils_2.DEFAULT_DB_NAME, dbVersion = 1, messageCallback = () => { }) {
|
|
@@ -31,9 +92,9 @@ class Worker {
|
|
|
31
92
|
* Get spendable vtxos for the current wallet address
|
|
32
93
|
*/
|
|
33
94
|
async getSpendableVtxos() {
|
|
34
|
-
if (!this.
|
|
95
|
+
if (!this.handler)
|
|
35
96
|
return [];
|
|
36
|
-
const address = await this.
|
|
97
|
+
const address = await this.handler.getAddress();
|
|
37
98
|
const allVtxos = await this.walletRepository.getVtxos(address);
|
|
38
99
|
return allVtxos.filter(__1.isSpendable);
|
|
39
100
|
}
|
|
@@ -41,9 +102,9 @@ class Worker {
|
|
|
41
102
|
* Get swept vtxos for the current wallet address
|
|
42
103
|
*/
|
|
43
104
|
async getSweptVtxos() {
|
|
44
|
-
if (!this.
|
|
105
|
+
if (!this.handler)
|
|
45
106
|
return [];
|
|
46
|
-
const address = await this.
|
|
107
|
+
const address = await this.handler.getAddress();
|
|
47
108
|
const allVtxos = await this.walletRepository.getVtxos(address);
|
|
48
109
|
return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept");
|
|
49
110
|
}
|
|
@@ -51,9 +112,9 @@ class Worker {
|
|
|
51
112
|
* Get all vtxos categorized by type
|
|
52
113
|
*/
|
|
53
114
|
async getAllVtxos() {
|
|
54
|
-
if (!this.
|
|
115
|
+
if (!this.handler)
|
|
55
116
|
return { spendable: [], spent: [] };
|
|
56
|
-
const address = await this.
|
|
117
|
+
const address = await this.handler.getAddress();
|
|
57
118
|
const allVtxos = await this.walletRepository.getVtxos(address);
|
|
58
119
|
return {
|
|
59
120
|
spendable: allVtxos.filter(__1.isSpendable),
|
|
@@ -64,17 +125,17 @@ class Worker {
|
|
|
64
125
|
* Get all boarding utxos from wallet repository
|
|
65
126
|
*/
|
|
66
127
|
async getAllBoardingUtxos() {
|
|
67
|
-
if (!this.
|
|
128
|
+
if (!this.handler)
|
|
68
129
|
return [];
|
|
69
|
-
const address = await this.
|
|
130
|
+
const address = await this.handler.getBoardingAddress();
|
|
70
131
|
return await this.walletRepository.getUtxos(address);
|
|
71
132
|
}
|
|
72
133
|
async getTransactionHistory() {
|
|
73
|
-
if (!this.
|
|
134
|
+
if (!this.handler)
|
|
74
135
|
return [];
|
|
75
136
|
let txs = [];
|
|
76
137
|
try {
|
|
77
|
-
const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.
|
|
138
|
+
const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.handler.getBoardingTxs();
|
|
78
139
|
const { spendable, spent } = await this.getAllVtxos();
|
|
79
140
|
// convert VTXOs to offchain transactions
|
|
80
141
|
const offchainTxs = (0, transactionHistory_1.vtxosToTxs)(spendable, spent, roundsToIgnore);
|
|
@@ -117,7 +178,7 @@ class Worker {
|
|
|
117
178
|
await this.storage.clear();
|
|
118
179
|
// Reset in-memory caches by recreating the repository
|
|
119
180
|
this.walletRepository = new walletRepository_1.WalletRepositoryImpl(this.storage);
|
|
120
|
-
this.
|
|
181
|
+
this.handler = undefined;
|
|
121
182
|
this.arkProvider = undefined;
|
|
122
183
|
this.indexerProvider = undefined;
|
|
123
184
|
}
|
|
@@ -125,26 +186,34 @@ class Worker {
|
|
|
125
186
|
await this.onWalletInitialized();
|
|
126
187
|
}
|
|
127
188
|
async onWalletInitialized() {
|
|
128
|
-
if (!this.
|
|
189
|
+
if (!this.handler ||
|
|
129
190
|
!this.arkProvider ||
|
|
130
191
|
!this.indexerProvider ||
|
|
131
|
-
!this.
|
|
132
|
-
!this.
|
|
192
|
+
!this.handler.offchainTapscript ||
|
|
193
|
+
!this.handler.boardingTapscript) {
|
|
133
194
|
return;
|
|
134
195
|
}
|
|
135
196
|
// Get public key script and set the initial vtxos state
|
|
136
|
-
const script = base_1.hex.encode(this.
|
|
197
|
+
const script = base_1.hex.encode(this.handler.offchainTapscript.pkScript);
|
|
137
198
|
const response = await this.indexerProvider.getVtxos({
|
|
138
199
|
scripts: [script],
|
|
139
200
|
});
|
|
140
|
-
const vtxos = response.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.
|
|
201
|
+
const vtxos = response.vtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.handler, vtxo));
|
|
202
|
+
try {
|
|
203
|
+
// recover pending transactions if possible
|
|
204
|
+
const { pending, finalized } = await this.handler.handleReload(vtxos);
|
|
205
|
+
console.info(`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.error("Error recovering pending transactions:", error);
|
|
209
|
+
}
|
|
141
210
|
// Get wallet address and save vtxos using unified repository
|
|
142
|
-
const address = await this.
|
|
211
|
+
const address = await this.handler.getAddress();
|
|
143
212
|
await this.walletRepository.saveVtxos(address, vtxos);
|
|
144
213
|
// Fetch boarding utxos and save using unified repository
|
|
145
|
-
const boardingAddress = await this.
|
|
146
|
-
const coins = await this.
|
|
147
|
-
await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => (0, utils_1.extendCoin)(this.
|
|
214
|
+
const boardingAddress = await this.handler.getBoardingAddress();
|
|
215
|
+
const coins = await this.handler.onchainProvider.getCoins(boardingAddress);
|
|
216
|
+
await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => (0, utils_1.extendCoin)(this.handler, utxo)));
|
|
148
217
|
// Get transaction history to cache boarding txs
|
|
149
218
|
const txs = await this.getTransactionHistory();
|
|
150
219
|
if (txs)
|
|
@@ -153,13 +222,13 @@ class Worker {
|
|
|
153
222
|
if (this.incomingFundsSubscription)
|
|
154
223
|
this.incomingFundsSubscription();
|
|
155
224
|
// subscribe for incoming funds and notify all clients when new funds arrive
|
|
156
|
-
this.incomingFundsSubscription = await this.
|
|
225
|
+
this.incomingFundsSubscription = await this.handler.notifyIncomingFunds(async (funds) => {
|
|
157
226
|
if (funds.type === "vtxo") {
|
|
158
227
|
const newVtxos = funds.newVtxos.length > 0
|
|
159
|
-
? funds.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.
|
|
228
|
+
? funds.newVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.handler, vtxo))
|
|
160
229
|
: [];
|
|
161
230
|
const spentVtxos = funds.spentVtxos.length > 0
|
|
162
|
-
? funds.spentVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.
|
|
231
|
+
? funds.spentVtxos.map((vtxo) => (0, utils_1.extendVirtualCoin)(this.handler, vtxo))
|
|
163
232
|
: [];
|
|
164
233
|
if ([...newVtxos, ...spentVtxos].length === 0)
|
|
165
234
|
return;
|
|
@@ -172,8 +241,8 @@ class Worker {
|
|
|
172
241
|
await this.sendMessageToAllClients(response_1.Response.vtxoUpdate(newVtxos, spentVtxos));
|
|
173
242
|
}
|
|
174
243
|
if (funds.type === "utxo") {
|
|
175
|
-
const utxos = funds.coins.map((utxo) => (0, utils_1.extendCoin)(this.
|
|
176
|
-
const boardingAddress = await this.
|
|
244
|
+
const utxos = funds.coins.map((utxo) => (0, utils_1.extendCoin)(this.handler, utxo));
|
|
245
|
+
const boardingAddress = await this.handler?.getBoardingAddress();
|
|
177
246
|
// save utxos using unified repository
|
|
178
247
|
await this.walletRepository.clearUtxos(boardingAddress);
|
|
179
248
|
await this.walletRepository.saveUtxos(boardingAddress, utxos);
|
|
@@ -189,31 +258,46 @@ class Worker {
|
|
|
189
258
|
}
|
|
190
259
|
}
|
|
191
260
|
async handleInitWallet(event) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
event.source?.postMessage(response_1.Response.error(message.id, "Invalid INIT_WALLET message format"));
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (!message.privateKey) {
|
|
199
|
-
const err = "Missing privateKey";
|
|
200
|
-
event.source?.postMessage(response_1.Response.error(message.id, err));
|
|
201
|
-
console.error(err);
|
|
261
|
+
if (!request_1.Request.isInitWallet(event.data)) {
|
|
262
|
+
console.error("Invalid INIT_WALLET message format", event.data);
|
|
263
|
+
event.source?.postMessage(response_1.Response.error(event.data.id, "Invalid INIT_WALLET message format"));
|
|
202
264
|
return;
|
|
203
265
|
}
|
|
266
|
+
const message = event.data;
|
|
267
|
+
const { arkServerPublicKey, arkServerUrl } = message;
|
|
268
|
+
this.arkProvider = new ark_1.RestArkProvider(arkServerUrl);
|
|
269
|
+
this.indexerProvider = new indexer_1.RestIndexerProvider(arkServerUrl);
|
|
204
270
|
try {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
271
|
+
if ("privateKey" in message.key &&
|
|
272
|
+
typeof message.key.privateKey === "string") {
|
|
273
|
+
const { key: { privateKey }, } = message;
|
|
274
|
+
const identity = singleKey_1.SingleKey.fromHex(privateKey);
|
|
275
|
+
const wallet = await wallet_1.Wallet.create({
|
|
276
|
+
identity,
|
|
277
|
+
arkServerUrl,
|
|
278
|
+
arkServerPublicKey,
|
|
279
|
+
storage: this.storage, // Use unified storage for wallet too
|
|
280
|
+
});
|
|
281
|
+
this.handler = new Handler(wallet);
|
|
282
|
+
}
|
|
283
|
+
else if ("publicKey" in message.key &&
|
|
284
|
+
typeof message.key.publicKey === "string") {
|
|
285
|
+
const { key: { publicKey }, } = message;
|
|
286
|
+
const identity = singleKey_1.ReadonlySingleKey.fromPublicKey(base_1.hex.decode(publicKey));
|
|
287
|
+
const wallet = await wallet_1.ReadonlyWallet.create({
|
|
288
|
+
identity,
|
|
289
|
+
arkServerUrl,
|
|
290
|
+
arkServerPublicKey,
|
|
291
|
+
storage: this.storage, // Use unified storage for wallet too
|
|
292
|
+
});
|
|
293
|
+
this.handler = new ReadonlyHandler(wallet);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const err = "Missing privateKey or publicKey in key object";
|
|
297
|
+
event.source?.postMessage(response_1.Response.error(message.id, err));
|
|
298
|
+
console.error(err);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
217
301
|
}
|
|
218
302
|
catch (error) {
|
|
219
303
|
console.error("Error initializing wallet:", error);
|
|
@@ -221,7 +305,10 @@ class Worker {
|
|
|
221
305
|
? error.message
|
|
222
306
|
: "Unknown error occurred";
|
|
223
307
|
event.source?.postMessage(response_1.Response.error(message.id, errorMessage));
|
|
308
|
+
return;
|
|
224
309
|
}
|
|
310
|
+
event.source?.postMessage(response_1.Response.walletInitialized(message.id));
|
|
311
|
+
await this.onWalletInitialized();
|
|
225
312
|
}
|
|
226
313
|
async handleSettle(event) {
|
|
227
314
|
const message = event.data;
|
|
@@ -231,15 +318,20 @@ class Worker {
|
|
|
231
318
|
return;
|
|
232
319
|
}
|
|
233
320
|
try {
|
|
234
|
-
if (!this.
|
|
321
|
+
if (!this.handler) {
|
|
235
322
|
console.error("Wallet not initialized");
|
|
236
323
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
237
324
|
return;
|
|
238
325
|
}
|
|
239
|
-
const txid = await this.
|
|
326
|
+
const txid = await this.handler.handleSettle(message.params, (e) => {
|
|
240
327
|
event.source?.postMessage(response_1.Response.settleEvent(message.id, e));
|
|
241
328
|
});
|
|
242
|
-
|
|
329
|
+
if (txid) {
|
|
330
|
+
event.source?.postMessage(response_1.Response.settleSuccess(message.id, txid));
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
event.source?.postMessage(response_1.Response.error(message.id, "Operation not supported in readonly mode"));
|
|
334
|
+
}
|
|
243
335
|
}
|
|
244
336
|
catch (error) {
|
|
245
337
|
console.error("Error settling:", error);
|
|
@@ -256,14 +348,19 @@ class Worker {
|
|
|
256
348
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid SEND_BITCOIN message format"));
|
|
257
349
|
return;
|
|
258
350
|
}
|
|
259
|
-
if (!this.
|
|
351
|
+
if (!this.handler) {
|
|
260
352
|
console.error("Wallet not initialized");
|
|
261
353
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
262
354
|
return;
|
|
263
355
|
}
|
|
264
356
|
try {
|
|
265
|
-
const txid = await this.
|
|
266
|
-
|
|
357
|
+
const txid = await this.handler.handleSendBitcoin(message.params);
|
|
358
|
+
if (txid) {
|
|
359
|
+
event.source?.postMessage(response_1.Response.sendBitcoinSuccess(message.id, txid));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
event.source?.postMessage(response_1.Response.error(message.id, "Operation not supported in readonly mode"));
|
|
363
|
+
}
|
|
267
364
|
}
|
|
268
365
|
catch (error) {
|
|
269
366
|
console.error("Error sending bitcoin:", error);
|
|
@@ -280,13 +377,13 @@ class Worker {
|
|
|
280
377
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_ADDRESS message format"));
|
|
281
378
|
return;
|
|
282
379
|
}
|
|
283
|
-
if (!this.
|
|
380
|
+
if (!this.handler) {
|
|
284
381
|
console.error("Wallet not initialized");
|
|
285
382
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
286
383
|
return;
|
|
287
384
|
}
|
|
288
385
|
try {
|
|
289
|
-
const address = await this.
|
|
386
|
+
const address = await this.handler.getAddress();
|
|
290
387
|
event.source?.postMessage(response_1.Response.address(message.id, address));
|
|
291
388
|
}
|
|
292
389
|
catch (error) {
|
|
@@ -304,13 +401,13 @@ class Worker {
|
|
|
304
401
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_BOARDING_ADDRESS message format"));
|
|
305
402
|
return;
|
|
306
403
|
}
|
|
307
|
-
if (!this.
|
|
404
|
+
if (!this.handler) {
|
|
308
405
|
console.error("Wallet not initialized");
|
|
309
406
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
310
407
|
return;
|
|
311
408
|
}
|
|
312
409
|
try {
|
|
313
|
-
const address = await this.
|
|
410
|
+
const address = await this.handler.getBoardingAddress();
|
|
314
411
|
event.source?.postMessage(response_1.Response.boardingAddress(message.id, address));
|
|
315
412
|
}
|
|
316
413
|
catch (error) {
|
|
@@ -328,7 +425,7 @@ class Worker {
|
|
|
328
425
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_BALANCE message format"));
|
|
329
426
|
return;
|
|
330
427
|
}
|
|
331
|
-
if (!this.
|
|
428
|
+
if (!this.handler) {
|
|
332
429
|
console.error("Wallet not initialized");
|
|
333
430
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
334
431
|
return;
|
|
@@ -397,14 +494,14 @@ class Worker {
|
|
|
397
494
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_VTXOS message format"));
|
|
398
495
|
return;
|
|
399
496
|
}
|
|
400
|
-
if (!this.
|
|
497
|
+
if (!this.handler) {
|
|
401
498
|
console.error("Wallet not initialized");
|
|
402
499
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
403
500
|
return;
|
|
404
501
|
}
|
|
405
502
|
try {
|
|
406
503
|
const vtxos = await this.getSpendableVtxos();
|
|
407
|
-
const dustAmount = this.
|
|
504
|
+
const dustAmount = this.handler.dustAmount;
|
|
408
505
|
const includeRecoverable = message.filter?.withRecoverable ?? false;
|
|
409
506
|
const filteredVtxos = includeRecoverable
|
|
410
507
|
? vtxos
|
|
@@ -415,6 +512,9 @@ class Worker {
|
|
|
415
512
|
if ((0, __1.isRecoverable)(v)) {
|
|
416
513
|
return false;
|
|
417
514
|
}
|
|
515
|
+
if ((0, __1.isExpired)(v)) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
418
518
|
return true;
|
|
419
519
|
});
|
|
420
520
|
event.source?.postMessage(response_1.Response.vtxos(message.id, filteredVtxos));
|
|
@@ -434,7 +534,7 @@ class Worker {
|
|
|
434
534
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_BOARDING_UTXOS message format"));
|
|
435
535
|
return;
|
|
436
536
|
}
|
|
437
|
-
if (!this.
|
|
537
|
+
if (!this.handler) {
|
|
438
538
|
console.error("Wallet not initialized");
|
|
439
539
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
440
540
|
return;
|
|
@@ -458,7 +558,7 @@ class Worker {
|
|
|
458
558
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_TRANSACTION_HISTORY message format"));
|
|
459
559
|
return;
|
|
460
560
|
}
|
|
461
|
-
if (!this.
|
|
561
|
+
if (!this.handler) {
|
|
462
562
|
console.error("Wallet not initialized");
|
|
463
563
|
event.source?.postMessage(response_1.Response.error(message.id, "Wallet not initialized"));
|
|
464
564
|
return;
|
|
@@ -482,10 +582,10 @@ class Worker {
|
|
|
482
582
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid GET_STATUS message format"));
|
|
483
583
|
return;
|
|
484
584
|
}
|
|
485
|
-
const pubKey = this.
|
|
486
|
-
? await this.
|
|
585
|
+
const pubKey = this.handler
|
|
586
|
+
? await this.handler.identity.xOnlyPublicKey()
|
|
487
587
|
: undefined;
|
|
488
|
-
event.source?.postMessage(response_1.Response.walletStatus(message.id, this.
|
|
588
|
+
event.source?.postMessage(response_1.Response.walletStatus(message.id, this.handler !== undefined, pubKey));
|
|
489
589
|
}
|
|
490
590
|
async handleMessage(event) {
|
|
491
591
|
this.messageCallback(event);
|
|
@@ -564,7 +664,7 @@ class Worker {
|
|
|
564
664
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid RELOAD_WALLET message format"));
|
|
565
665
|
return;
|
|
566
666
|
}
|
|
567
|
-
if (!this.
|
|
667
|
+
if (!this.handler) {
|
|
568
668
|
console.error("Wallet not initialized");
|
|
569
669
|
event.source?.postMessage(response_1.Response.walletReloaded(message.id, false));
|
|
570
670
|
return;
|
package/dist/cjs/wallet/utils.js
CHANGED
|
@@ -6,7 +6,7 @@ function extendVirtualCoin(wallet, vtxo) {
|
|
|
6
6
|
return {
|
|
7
7
|
...vtxo,
|
|
8
8
|
forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
|
|
9
|
-
intentTapLeafScript: wallet.offchainTapscript.
|
|
9
|
+
intentTapLeafScript: wallet.offchainTapscript.forfeit(),
|
|
10
10
|
tapTree: wallet.offchainTapscript.encode(),
|
|
11
11
|
};
|
|
12
12
|
}
|
|
@@ -14,7 +14,7 @@ function extendCoin(wallet, utxo) {
|
|
|
14
14
|
return {
|
|
15
15
|
...utxo,
|
|
16
16
|
forfeitTapLeafScript: wallet.boardingTapscript.forfeit(),
|
|
17
|
-
intentTapLeafScript: wallet.boardingTapscript.
|
|
17
|
+
intentTapLeafScript: wallet.boardingTapscript.forfeit(),
|
|
18
18
|
tapTree: wallet.boardingTapscript.encode(),
|
|
19
19
|
};
|
|
20
20
|
}
|
|
@@ -31,6 +31,10 @@ function getRecoverableVtxos(vtxos, dustAmount) {
|
|
|
31
31
|
if ((0, _1.isRecoverable)(vtxo)) {
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
34
|
+
// also include vtxos that are not swept but expired
|
|
35
|
+
if ((0, _1.isSpendable)(vtxo) && (0, _1.isExpired)(vtxo)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
34
38
|
// Recover preconfirmed subdust to consolidate small amounts
|
|
35
39
|
if (vtxo.virtualStatus.state === "preconfirmed" &&
|
|
36
40
|
(0, _1.isSubdust)(vtxo, dustAmount)) {
|
|
@@ -105,6 +109,7 @@ function isVtxoExpiringSoon(vtxo, thresholdMs // in milliseconds
|
|
|
105
109
|
function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount) {
|
|
106
110
|
return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs) ||
|
|
107
111
|
(0, _1.isRecoverable)(vtxo) ||
|
|
112
|
+
((0, _1.isSpendable)(vtxo) && (0, _1.isExpired)(vtxo)) ||
|
|
108
113
|
(0, _1.isSubdust)(vtxo, dustAmount));
|
|
109
114
|
}
|
|
110
115
|
/**
|