@arkade-os/sdk 0.3.0-alpha.1 → 0.3.0-alpha.4
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/dist/cjs/identity/singleKey.js +3 -0
- package/dist/cjs/providers/ark.js +53 -71
- package/dist/cjs/providers/indexer.js +45 -51
- package/dist/cjs/providers/utils.js +60 -0
- package/dist/cjs/repositories/walletRepository.js +34 -4
- package/dist/cjs/script/tapscript.js +8 -2
- package/dist/cjs/tree/signingSession.js +3 -3
- package/dist/cjs/tree/txTree.js +3 -3
- package/dist/cjs/tree/validation.js +1 -1
- package/dist/cjs/wallet/serviceWorker/utils.js +5 -5
- package/dist/cjs/wallet/wallet.js +5 -6
- package/dist/esm/identity/singleKey.js +4 -1
- package/dist/esm/providers/ark.js +53 -71
- package/dist/esm/providers/indexer.js +45 -51
- package/dist/esm/providers/utils.js +57 -0
- package/dist/esm/repositories/walletRepository.js +34 -4
- package/dist/esm/script/tapscript.js +8 -2
- package/dist/esm/tree/signingSession.js +3 -3
- package/dist/esm/tree/txTree.js +3 -3
- package/dist/esm/tree/validation.js +1 -1
- package/dist/esm/wallet/serviceWorker/utils.js +5 -5
- package/dist/esm/wallet/wallet.js +5 -6
- package/dist/types/identity/index.d.ts +3 -2
- package/dist/types/identity/singleKey.d.ts +1 -0
- package/dist/types/providers/ark.d.ts +1 -0
- package/dist/types/providers/utils.d.ts +1 -0
- package/dist/types/tree/txTree.d.ts +2 -2
- package/package.json +2 -1
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RestArkProvider = exports.SettlementEventType = void 0;
|
|
4
4
|
exports.isFetchTimeoutError = isFetchTimeoutError;
|
|
5
5
|
const base_1 = require("@scure/base");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
6
7
|
var SettlementEventType;
|
|
7
8
|
(function (SettlementEventType) {
|
|
8
9
|
SettlementEventType["BatchStarted"] = "batch_started";
|
|
@@ -45,6 +46,7 @@ class RestArkProvider {
|
|
|
45
46
|
vtxoMinAmount: BigInt(fromServer.vtxoMinAmount ?? 0),
|
|
46
47
|
vtxoMaxAmount: BigInt(fromServer.vtxoMaxAmount ?? -1),
|
|
47
48
|
boardingExitDelay: BigInt(fromServer.boardingExitDelay ?? 0),
|
|
49
|
+
checkpointExitClosure: fromServer.checkpointTapscript ?? "",
|
|
48
50
|
marketHour: "marketHour" in fromServer && fromServer.marketHour != null
|
|
49
51
|
? {
|
|
50
52
|
nextStartTime: BigInt(fromServer.marketHour.nextStartTime ?? 0),
|
|
@@ -219,39 +221,21 @@ class RestArkProvider {
|
|
|
219
221
|
: "";
|
|
220
222
|
while (!signal?.aborted) {
|
|
221
223
|
try {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
throw new Error("Response body is null");
|
|
233
|
-
}
|
|
234
|
-
const reader = response.body.getReader();
|
|
235
|
-
const decoder = new TextDecoder();
|
|
236
|
-
let buffer = "";
|
|
237
|
-
while (!signal?.aborted) {
|
|
238
|
-
const { done, value } = await reader.read();
|
|
239
|
-
if (done) {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
// Append new data to buffer and split by newlines
|
|
243
|
-
buffer += decoder.decode(value, { stream: true });
|
|
244
|
-
const lines = buffer.split("\n");
|
|
245
|
-
// Process all complete lines
|
|
246
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
247
|
-
const line = lines[i].trim();
|
|
248
|
-
if (!line)
|
|
249
|
-
continue;
|
|
224
|
+
const eventSource = new EventSource(url + queryParams);
|
|
225
|
+
// Set up abort handling
|
|
226
|
+
const abortHandler = () => {
|
|
227
|
+
eventSource.close();
|
|
228
|
+
};
|
|
229
|
+
signal?.addEventListener("abort", abortHandler);
|
|
230
|
+
try {
|
|
231
|
+
for await (const event of (0, utils_1.eventSourceIterator)(eventSource)) {
|
|
232
|
+
if (signal?.aborted)
|
|
233
|
+
break;
|
|
250
234
|
try {
|
|
251
|
-
const data = JSON.parse(
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
254
|
-
yield
|
|
235
|
+
const data = JSON.parse(event.data);
|
|
236
|
+
const settlementEvent = this.parseSettlementEvent(data);
|
|
237
|
+
if (settlementEvent) {
|
|
238
|
+
yield settlementEvent;
|
|
255
239
|
}
|
|
256
240
|
}
|
|
257
241
|
catch (err) {
|
|
@@ -259,8 +243,10 @@ class RestArkProvider {
|
|
|
259
243
|
throw err;
|
|
260
244
|
}
|
|
261
245
|
}
|
|
262
|
-
|
|
263
|
-
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
249
|
+
eventSource.close();
|
|
264
250
|
}
|
|
265
251
|
}
|
|
266
252
|
catch (error) {
|
|
@@ -268,7 +254,6 @@ class RestArkProvider {
|
|
|
268
254
|
break;
|
|
269
255
|
}
|
|
270
256
|
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
271
|
-
// these timeouts are set by builtin fetch function
|
|
272
257
|
if (isFetchTimeoutError(error)) {
|
|
273
258
|
console.debug("Timeout error ignored");
|
|
274
259
|
continue;
|
|
@@ -282,42 +267,32 @@ class RestArkProvider {
|
|
|
282
267
|
const url = `${this.serverUrl}/v1/txs`;
|
|
283
268
|
while (!signal?.aborted) {
|
|
284
269
|
try {
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
// Append new data to buffer and split by newlines
|
|
306
|
-
buffer += decoder.decode(value, { stream: true });
|
|
307
|
-
const lines = buffer.split("\n");
|
|
308
|
-
// Process all complete lines
|
|
309
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
310
|
-
const line = lines[i].trim();
|
|
311
|
-
if (!line)
|
|
312
|
-
continue;
|
|
313
|
-
const data = JSON.parse(line);
|
|
314
|
-
const txNotification = this.parseTransactionNotification(data.result);
|
|
315
|
-
if (txNotification) {
|
|
316
|
-
yield txNotification;
|
|
270
|
+
const eventSource = new EventSource(url);
|
|
271
|
+
// Set up abort handling
|
|
272
|
+
const abortHandler = () => {
|
|
273
|
+
eventSource.close();
|
|
274
|
+
};
|
|
275
|
+
signal?.addEventListener("abort", abortHandler);
|
|
276
|
+
try {
|
|
277
|
+
for await (const event of (0, utils_1.eventSourceIterator)(eventSource)) {
|
|
278
|
+
if (signal?.aborted)
|
|
279
|
+
break;
|
|
280
|
+
try {
|
|
281
|
+
const data = JSON.parse(event.data);
|
|
282
|
+
const txNotification = this.parseTransactionNotification(data);
|
|
283
|
+
if (txNotification) {
|
|
284
|
+
yield txNotification;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
console.error("Failed to parse transaction notification:", err);
|
|
289
|
+
throw err;
|
|
317
290
|
}
|
|
318
291
|
}
|
|
319
|
-
|
|
320
|
-
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
signal?.removeEventListener("abort", abortHandler);
|
|
295
|
+
eventSource.close();
|
|
321
296
|
}
|
|
322
297
|
}
|
|
323
298
|
catch (error) {
|
|
@@ -325,12 +300,11 @@ class RestArkProvider {
|
|
|
325
300
|
break;
|
|
326
301
|
}
|
|
327
302
|
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
328
|
-
// these timeouts are set by builtin fetch function
|
|
329
303
|
if (isFetchTimeoutError(error)) {
|
|
330
304
|
console.debug("Timeout error ignored");
|
|
331
305
|
continue;
|
|
332
306
|
}
|
|
333
|
-
console.error("
|
|
307
|
+
console.error("Transaction stream error:", error);
|
|
334
308
|
throw error;
|
|
335
309
|
}
|
|
336
310
|
}
|
|
@@ -413,6 +387,10 @@ class RestArkProvider {
|
|
|
413
387
|
signature: data.treeSignature.signature,
|
|
414
388
|
};
|
|
415
389
|
}
|
|
390
|
+
// Skip heartbeat events
|
|
391
|
+
if (data.heartbeat) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
416
394
|
console.warn("Unknown event type:", data);
|
|
417
395
|
return null;
|
|
418
396
|
}
|
|
@@ -439,6 +417,10 @@ class RestArkProvider {
|
|
|
439
417
|
},
|
|
440
418
|
};
|
|
441
419
|
}
|
|
420
|
+
// Skip heartbeat events
|
|
421
|
+
if (data.heartbeat) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
442
424
|
console.warn("Unknown transaction notification type:", data);
|
|
443
425
|
return null;
|
|
444
426
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RestIndexerProvider = exports.ChainTxType = exports.IndexerTxType = void 0;
|
|
4
4
|
const ark_1 = require("./ark");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
5
6
|
var IndexerTxType;
|
|
6
7
|
(function (IndexerTxType) {
|
|
7
8
|
IndexerTxType[IndexerTxType["INDEXER_TX_TYPE_UNSPECIFIED"] = 0] = "INDEXER_TX_TYPE_UNSPECIFIED";
|
|
@@ -30,7 +31,7 @@ class RestIndexerProvider {
|
|
|
30
31
|
this.serverUrl = serverUrl;
|
|
31
32
|
}
|
|
32
33
|
async getVtxoTree(batchOutpoint, opts) {
|
|
33
|
-
let url = `${this.serverUrl}/v1/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/tree`;
|
|
34
|
+
let url = `${this.serverUrl}/v1/indexer/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/tree`;
|
|
34
35
|
const params = new URLSearchParams();
|
|
35
36
|
if (opts) {
|
|
36
37
|
if (opts.pageIndex !== undefined)
|
|
@@ -58,7 +59,7 @@ class RestIndexerProvider {
|
|
|
58
59
|
return data;
|
|
59
60
|
}
|
|
60
61
|
async getVtxoTreeLeaves(batchOutpoint, opts) {
|
|
61
|
-
let url = `${this.serverUrl}/v1/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/tree/leaves`;
|
|
62
|
+
let url = `${this.serverUrl}/v1/indexer/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/tree/leaves`;
|
|
62
63
|
const params = new URLSearchParams();
|
|
63
64
|
if (opts) {
|
|
64
65
|
if (opts.pageIndex !== undefined)
|
|
@@ -80,7 +81,7 @@ class RestIndexerProvider {
|
|
|
80
81
|
return data;
|
|
81
82
|
}
|
|
82
83
|
async getBatchSweepTransactions(batchOutpoint) {
|
|
83
|
-
const url = `${this.serverUrl}/v1/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/sweepTxs`;
|
|
84
|
+
const url = `${this.serverUrl}/v1/indexer/batch/${batchOutpoint.txid}/${batchOutpoint.vout}/sweepTxs`;
|
|
84
85
|
const res = await fetch(url);
|
|
85
86
|
if (!res.ok) {
|
|
86
87
|
throw new Error(`Failed to fetch batch sweep transactions: ${res.statusText}`);
|
|
@@ -92,7 +93,7 @@ class RestIndexerProvider {
|
|
|
92
93
|
return data;
|
|
93
94
|
}
|
|
94
95
|
async getCommitmentTx(txid) {
|
|
95
|
-
const url = `${this.serverUrl}/v1/commitmentTx/${txid}`;
|
|
96
|
+
const url = `${this.serverUrl}/v1/indexer/commitmentTx/${txid}`;
|
|
96
97
|
const res = await fetch(url);
|
|
97
98
|
if (!res.ok) {
|
|
98
99
|
throw new Error(`Failed to fetch commitment tx: ${res.statusText}`);
|
|
@@ -104,7 +105,7 @@ class RestIndexerProvider {
|
|
|
104
105
|
return data;
|
|
105
106
|
}
|
|
106
107
|
async getCommitmentTxConnectors(txid, opts) {
|
|
107
|
-
let url = `${this.serverUrl}/v1/commitmentTx/${txid}/connectors`;
|
|
108
|
+
let url = `${this.serverUrl}/v1/indexer/commitmentTx/${txid}/connectors`;
|
|
108
109
|
const params = new URLSearchParams();
|
|
109
110
|
if (opts) {
|
|
110
111
|
if (opts.pageIndex !== undefined)
|
|
@@ -132,7 +133,7 @@ class RestIndexerProvider {
|
|
|
132
133
|
return data;
|
|
133
134
|
}
|
|
134
135
|
async getCommitmentTxForfeitTxs(txid, opts) {
|
|
135
|
-
let url = `${this.serverUrl}/v1/commitmentTx/${txid}/forfeitTxs`;
|
|
136
|
+
let url = `${this.serverUrl}/v1/indexer/commitmentTx/${txid}/forfeitTxs`;
|
|
136
137
|
const params = new URLSearchParams();
|
|
137
138
|
if (opts) {
|
|
138
139
|
if (opts.pageIndex !== undefined)
|
|
@@ -154,47 +155,41 @@ class RestIndexerProvider {
|
|
|
154
155
|
return data;
|
|
155
156
|
}
|
|
156
157
|
async *getSubscription(subscriptionId, abortSignal) {
|
|
157
|
-
const url = `${this.serverUrl}/v1/script/subscription/${subscriptionId}`;
|
|
158
|
-
while (!abortSignal
|
|
158
|
+
const url = `${this.serverUrl}/v1/indexer/script/subscription/${subscriptionId}`;
|
|
159
|
+
while (!abortSignal?.aborted) {
|
|
159
160
|
try {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if ("result" in data) {
|
|
187
|
-
yield {
|
|
188
|
-
txid: data.result.txid,
|
|
189
|
-
scripts: data.result.scripts || [],
|
|
190
|
-
newVtxos: (data.result.newVtxos || []).map(convertVtxo),
|
|
191
|
-
spentVtxos: (data.result.spentVtxos || []).map(convertVtxo),
|
|
192
|
-
tx: data.result.tx,
|
|
193
|
-
checkpointTxs: data.result.checkpointTxs,
|
|
194
|
-
};
|
|
161
|
+
const eventSource = new EventSource(url);
|
|
162
|
+
// Set up abort handling
|
|
163
|
+
const abortHandler = () => {
|
|
164
|
+
eventSource.close();
|
|
165
|
+
};
|
|
166
|
+
abortSignal?.addEventListener("abort", abortHandler);
|
|
167
|
+
try {
|
|
168
|
+
for await (const event of (0, utils_1.eventSourceIterator)(eventSource)) {
|
|
169
|
+
if (abortSignal?.aborted)
|
|
170
|
+
break;
|
|
171
|
+
try {
|
|
172
|
+
const data = JSON.parse(event.data);
|
|
173
|
+
if (data.event) {
|
|
174
|
+
yield {
|
|
175
|
+
txid: data.event.txid,
|
|
176
|
+
scripts: data.event.scripts || [],
|
|
177
|
+
newVtxos: (data.event.newVtxos || []).map(convertVtxo),
|
|
178
|
+
spentVtxos: (data.event.spentVtxos || []).map(convertVtxo),
|
|
179
|
+
tx: data.event.tx,
|
|
180
|
+
checkpointTxs: data.event.checkpointTxs,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
console.error("Failed to parse subscription event:", err);
|
|
186
|
+
throw err;
|
|
195
187
|
}
|
|
196
188
|
}
|
|
197
|
-
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
abortSignal?.removeEventListener("abort", abortHandler);
|
|
192
|
+
eventSource.close();
|
|
198
193
|
}
|
|
199
194
|
}
|
|
200
195
|
catch (error) {
|
|
@@ -202,7 +197,6 @@ class RestIndexerProvider {
|
|
|
202
197
|
break;
|
|
203
198
|
}
|
|
204
199
|
// ignore timeout errors, they're expected when the server is not sending anything for 5 min
|
|
205
|
-
// these timeouts are set by builtin fetch function
|
|
206
200
|
if ((0, ark_1.isFetchTimeoutError)(error)) {
|
|
207
201
|
console.debug("Timeout error ignored");
|
|
208
202
|
continue;
|
|
@@ -213,7 +207,7 @@ class RestIndexerProvider {
|
|
|
213
207
|
}
|
|
214
208
|
}
|
|
215
209
|
async getVirtualTxs(txids, opts) {
|
|
216
|
-
let url = `${this.serverUrl}/v1/virtualTx/${txids.join(",")}`;
|
|
210
|
+
let url = `${this.serverUrl}/v1/indexer/virtualTx/${txids.join(",")}`;
|
|
217
211
|
const params = new URLSearchParams();
|
|
218
212
|
if (opts) {
|
|
219
213
|
if (opts.pageIndex !== undefined)
|
|
@@ -235,7 +229,7 @@ class RestIndexerProvider {
|
|
|
235
229
|
return data;
|
|
236
230
|
}
|
|
237
231
|
async getVtxoChain(vtxoOutpoint, opts) {
|
|
238
|
-
let url = `${this.serverUrl}/v1/vtxo/${vtxoOutpoint.txid}/${vtxoOutpoint.vout}/chain`;
|
|
232
|
+
let url = `${this.serverUrl}/v1/indexer/vtxo/${vtxoOutpoint.txid}/${vtxoOutpoint.vout}/chain`;
|
|
239
233
|
const params = new URLSearchParams();
|
|
240
234
|
if (opts) {
|
|
241
235
|
if (opts.pageIndex !== undefined)
|
|
@@ -264,7 +258,7 @@ class RestIndexerProvider {
|
|
|
264
258
|
if (!opts?.scripts && !opts?.outpoints) {
|
|
265
259
|
throw new Error("Either scripts or outpoints must be provided");
|
|
266
260
|
}
|
|
267
|
-
let url = `${this.serverUrl}/v1/vtxos`;
|
|
261
|
+
let url = `${this.serverUrl}/v1/indexer/vtxos`;
|
|
268
262
|
const params = new URLSearchParams();
|
|
269
263
|
// Handle scripts with multi collection format
|
|
270
264
|
if (opts?.scripts && opts.scripts.length > 0) {
|
|
@@ -307,7 +301,7 @@ class RestIndexerProvider {
|
|
|
307
301
|
};
|
|
308
302
|
}
|
|
309
303
|
async subscribeForScripts(scripts, subscriptionId) {
|
|
310
|
-
const url = `${this.serverUrl}/v1/script/subscribe`;
|
|
304
|
+
const url = `${this.serverUrl}/v1/indexer/script/subscribe`;
|
|
311
305
|
const res = await fetch(url, {
|
|
312
306
|
headers: {
|
|
313
307
|
"Content-Type": "application/json",
|
|
@@ -325,7 +319,7 @@ class RestIndexerProvider {
|
|
|
325
319
|
return data.subscriptionId;
|
|
326
320
|
}
|
|
327
321
|
async unsubscribeForScripts(subscriptionId, scripts) {
|
|
328
|
-
const url = `${this.serverUrl}/v1/script/unsubscribe`;
|
|
322
|
+
const url = `${this.serverUrl}/v1/indexer/script/unsubscribe`;
|
|
329
323
|
const res = await fetch(url, {
|
|
330
324
|
headers: {
|
|
331
325
|
"Content-Type": "application/json",
|
|
@@ -442,7 +436,7 @@ var Response;
|
|
|
442
436
|
return (typeof data === "object" &&
|
|
443
437
|
isOutpoint(data.outpoint) &&
|
|
444
438
|
typeof data.createdAt === "string" &&
|
|
445
|
-
typeof data.expiresAt === "string" &&
|
|
439
|
+
(data.expiresAt === null || typeof data.expiresAt === "string") &&
|
|
446
440
|
typeof data.amount === "string" &&
|
|
447
441
|
typeof data.script === "string" &&
|
|
448
442
|
typeof data.isPreconfirmed === "boolean" &&
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.eventSourceIterator = eventSourceIterator;
|
|
4
|
+
async function* eventSourceIterator(eventSource) {
|
|
5
|
+
const messageQueue = [];
|
|
6
|
+
const errorQueue = [];
|
|
7
|
+
let messageResolve = null;
|
|
8
|
+
let errorResolve = null;
|
|
9
|
+
const messageHandler = (event) => {
|
|
10
|
+
if (messageResolve) {
|
|
11
|
+
messageResolve(event);
|
|
12
|
+
messageResolve = null;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
messageQueue.push(event);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const errorHandler = () => {
|
|
19
|
+
const error = new Error("EventSource error");
|
|
20
|
+
if (errorResolve) {
|
|
21
|
+
errorResolve(error);
|
|
22
|
+
errorResolve = null;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
errorQueue.push(error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
eventSource.addEventListener("message", messageHandler);
|
|
29
|
+
eventSource.addEventListener("error", errorHandler);
|
|
30
|
+
try {
|
|
31
|
+
while (true) {
|
|
32
|
+
// if we have queued messages, yield the first one, remove it from the queue
|
|
33
|
+
if (messageQueue.length > 0) {
|
|
34
|
+
yield messageQueue.shift();
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
// if we have queued errors, throw the first one, remove it from the queue
|
|
38
|
+
if (errorQueue.length > 0) {
|
|
39
|
+
const error = errorQueue.shift();
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
// wait for the next message or error
|
|
43
|
+
const result = await new Promise((resolve, reject) => {
|
|
44
|
+
messageResolve = resolve;
|
|
45
|
+
errorResolve = reject;
|
|
46
|
+
}).finally(() => {
|
|
47
|
+
messageResolve = null;
|
|
48
|
+
errorResolve = null;
|
|
49
|
+
});
|
|
50
|
+
if (result) {
|
|
51
|
+
yield result;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
// clean up
|
|
57
|
+
eventSource.removeEventListener("message", messageHandler);
|
|
58
|
+
eventSource.removeEventListener("error", errorHandler);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WalletRepositoryImpl = void 0;
|
|
4
|
+
const base_1 = require("@scure/base");
|
|
5
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
|
+
// Utility functions for (de)serializing complex structures
|
|
7
|
+
const toHex = (b) => (b ? base_1.hex.encode(b) : undefined);
|
|
8
|
+
const fromHex = (h) => h ? base_1.hex.decode(h) : undefined;
|
|
9
|
+
const serializeTapLeaf = ([cb, s]) => ({
|
|
10
|
+
cb: btc_signer_1.TaprootControlBlock.encode(cb) &&
|
|
11
|
+
base_1.hex.encode(btc_signer_1.TaprootControlBlock.encode(cb)),
|
|
12
|
+
s: base_1.hex.encode(s),
|
|
13
|
+
});
|
|
14
|
+
const serializeVtxo = (v) => ({
|
|
15
|
+
...v,
|
|
16
|
+
tapTree: toHex(v.tapTree),
|
|
17
|
+
forfeitTapLeafScript: serializeTapLeaf(v.forfeitTapLeafScript),
|
|
18
|
+
intentTapLeafScript: serializeTapLeaf(v.intentTapLeafScript),
|
|
19
|
+
extraWitness: v.extraWitness?.map((w) => toHex(w)),
|
|
20
|
+
});
|
|
21
|
+
const deserializeTapLeaf = (t) => {
|
|
22
|
+
const cb = btc_signer_1.TaprootControlBlock.decode(fromHex(t.cb));
|
|
23
|
+
const s = fromHex(t.s);
|
|
24
|
+
return [cb, s];
|
|
25
|
+
};
|
|
26
|
+
const deserializeVtxo = (o) => ({
|
|
27
|
+
...o,
|
|
28
|
+
tapTree: fromHex(o.tapTree),
|
|
29
|
+
forfeitTapLeafScript: deserializeTapLeaf(o.forfeitTapLeafScript),
|
|
30
|
+
intentTapLeafScript: deserializeTapLeaf(o.intentTapLeafScript),
|
|
31
|
+
extraWitness: o.extraWitness?.map((w) => fromHex(w)),
|
|
32
|
+
});
|
|
4
33
|
class WalletRepositoryImpl {
|
|
5
34
|
constructor(storage) {
|
|
6
35
|
this.storage = storage;
|
|
@@ -22,7 +51,8 @@ class WalletRepositoryImpl {
|
|
|
22
51
|
return [];
|
|
23
52
|
}
|
|
24
53
|
try {
|
|
25
|
-
const
|
|
54
|
+
const parsed = JSON.parse(stored);
|
|
55
|
+
const vtxos = parsed.map(deserializeVtxo);
|
|
26
56
|
this.cache.vtxos.set(address, vtxos);
|
|
27
57
|
return vtxos.slice();
|
|
28
58
|
}
|
|
@@ -42,7 +72,7 @@ class WalletRepositoryImpl {
|
|
|
42
72
|
vtxos.push(vtxo);
|
|
43
73
|
}
|
|
44
74
|
this.cache.vtxos.set(address, vtxos);
|
|
45
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos));
|
|
75
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos.map(serializeVtxo)));
|
|
46
76
|
}
|
|
47
77
|
async saveVtxos(address, vtxos) {
|
|
48
78
|
const storedVtxos = await this.getVtxos(address);
|
|
@@ -56,14 +86,14 @@ class WalletRepositoryImpl {
|
|
|
56
86
|
}
|
|
57
87
|
}
|
|
58
88
|
this.cache.vtxos.set(address, storedVtxos);
|
|
59
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos));
|
|
89
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos.map(serializeVtxo)));
|
|
60
90
|
}
|
|
61
91
|
async removeVtxo(address, vtxoId) {
|
|
62
92
|
const vtxos = await this.getVtxos(address);
|
|
63
93
|
const [txid, vout] = vtxoId.split(":");
|
|
64
94
|
const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout)));
|
|
65
95
|
this.cache.vtxos.set(address, filtered);
|
|
66
|
-
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered));
|
|
96
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered.map(serializeVtxo)));
|
|
67
97
|
}
|
|
68
98
|
async clearVtxos(address) {
|
|
69
99
|
this.cache.vtxos.set(address, []);
|
|
@@ -297,7 +297,7 @@ var CSVMultisigTapscript;
|
|
|
297
297
|
throw new Error(`Invalid script: too short (expected at least 3)`);
|
|
298
298
|
}
|
|
299
299
|
const sequence = asm[0];
|
|
300
|
-
if (typeof sequence === "string"
|
|
300
|
+
if (typeof sequence === "string") {
|
|
301
301
|
throw new Error("Invalid script: expected sequence number");
|
|
302
302
|
}
|
|
303
303
|
if (asm[1] !== "CHECKSEQUENCEVERIFY" || asm[2] !== "DROP") {
|
|
@@ -311,7 +311,13 @@ var CSVMultisigTapscript;
|
|
|
311
311
|
catch (error) {
|
|
312
312
|
throw new Error(`Invalid multisig script: ${error instanceof Error ? error.message : String(error)}`);
|
|
313
313
|
}
|
|
314
|
-
|
|
314
|
+
let sequenceNum;
|
|
315
|
+
if (typeof sequence === "number") {
|
|
316
|
+
sequenceNum = sequence;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
sequenceNum = Number(MinimalScriptNum.decode(sequence));
|
|
320
|
+
}
|
|
315
321
|
const decodedTimelock = bip68.decode(sequenceNum);
|
|
316
322
|
const timelock = decodedTimelock.blocks !== undefined
|
|
317
323
|
? { type: "blocks", value: BigInt(decodedTimelock.blocks) }
|
|
@@ -90,7 +90,7 @@ class TreeSignerSession {
|
|
|
90
90
|
if (!this.myNonces)
|
|
91
91
|
throw new Error("nonces not generated");
|
|
92
92
|
const sigs = new Map();
|
|
93
|
-
for (const g of this.graph) {
|
|
93
|
+
for (const g of this.graph.iterator()) {
|
|
94
94
|
const sig = this.signPartial(g);
|
|
95
95
|
sigs.set(g.txid, sig);
|
|
96
96
|
}
|
|
@@ -101,7 +101,7 @@ class TreeSignerSession {
|
|
|
101
101
|
throw exports.ErrMissingVtxoGraph;
|
|
102
102
|
const myNonces = new Map();
|
|
103
103
|
const publicKey = secp256k1_js_1.secp256k1.getPublicKey(this.secretKey);
|
|
104
|
-
for (const g of this.graph) {
|
|
104
|
+
for (const g of this.graph.iterator()) {
|
|
105
105
|
const nonces = musig2.generateNonces(publicKey);
|
|
106
106
|
myNonces.set(g.txid, nonces);
|
|
107
107
|
}
|
|
@@ -144,7 +144,7 @@ TreeSignerSession.NOT_INITIALIZED = new Error("session not initialized, call ini
|
|
|
144
144
|
// Helper function to validate tree signatures
|
|
145
145
|
async function validateTreeSigs(finalAggregatedKey, sharedOutputAmount, vtxoTree) {
|
|
146
146
|
// Iterate through each level of the tree
|
|
147
|
-
for (const g of vtxoTree) {
|
|
147
|
+
for (const g of vtxoTree.iterator()) {
|
|
148
148
|
// Parse the transaction
|
|
149
149
|
const input = g.root.getInput(0);
|
|
150
150
|
// Check if input has signature
|
package/dist/cjs/tree/txTree.js
CHANGED
|
@@ -157,11 +157,11 @@ class TxTree {
|
|
|
157
157
|
}
|
|
158
158
|
throw new Error(`tx not found: ${txid}`);
|
|
159
159
|
}
|
|
160
|
-
*
|
|
161
|
-
yield this;
|
|
160
|
+
*iterator() {
|
|
162
161
|
for (const child of this.children.values()) {
|
|
163
|
-
yield* child;
|
|
162
|
+
yield* child.iterator();
|
|
164
163
|
}
|
|
164
|
+
yield this;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
exports.TxTree = TxTree;
|
|
@@ -81,7 +81,7 @@ function validateVtxoTxGraph(graph, roundTransaction, sweepTapTreeRoot) {
|
|
|
81
81
|
// validate the graph structure
|
|
82
82
|
graph.validate();
|
|
83
83
|
// iterates over all the nodes of the graph to verify that cosigners public keys are corresponding to the parent output
|
|
84
|
-
for (const g of graph) {
|
|
84
|
+
for (const g of graph.iterator()) {
|
|
85
85
|
for (const [childIndex, child] of g.children) {
|
|
86
86
|
const parentOutput = g.root.getOutput(childIndex);
|
|
87
87
|
if (!parentOutput?.script) {
|
|
@@ -17,7 +17,7 @@ async function setupServiceWorker(path) {
|
|
|
17
17
|
// register service worker
|
|
18
18
|
const registration = await navigator.serviceWorker.register(path);
|
|
19
19
|
// force update to ensure the service worker is active
|
|
20
|
-
registration.update();
|
|
20
|
+
await registration.update();
|
|
21
21
|
const serviceWorker = registration.active || registration.waiting || registration.installing;
|
|
22
22
|
if (!serviceWorker) {
|
|
23
23
|
throw new Error("Failed to get service worker instance");
|
|
@@ -39,11 +39,11 @@ async function setupServiceWorker(path) {
|
|
|
39
39
|
reject(new Error("Service worker activation timed out"));
|
|
40
40
|
}, 10000);
|
|
41
41
|
const cleanup = () => {
|
|
42
|
-
serviceWorker.removeEventListener("activate", onActivate);
|
|
43
|
-
serviceWorker.removeEventListener("error", onError);
|
|
42
|
+
navigator.serviceWorker.removeEventListener("activate", onActivate);
|
|
43
|
+
navigator.serviceWorker.removeEventListener("error", onError);
|
|
44
44
|
clearTimeout(timeout);
|
|
45
45
|
};
|
|
46
|
-
serviceWorker.addEventListener("activate", onActivate);
|
|
47
|
-
serviceWorker.addEventListener("error", onError);
|
|
46
|
+
navigator.serviceWorker.addEventListener("activate", onActivate);
|
|
47
|
+
navigator.serviceWorker.addEventListener("error", onError);
|
|
48
48
|
});
|
|
49
49
|
}
|