@arkade-os/boltz-swap 0.3.32 → 0.3.33
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 +22 -26
- package/dist/{arkade-swaps-CS8FZSVL.d.cts → arkade-swaps-9M7FRuq1.d.cts} +57 -5
- package/dist/{arkade-swaps-WiKCanCL.d.ts → arkade-swaps-DNsyWeFr.d.ts} +57 -5
- package/dist/{chunk-NHBWNN6H.js → chunk-HNQDJOLM.js} +8 -27
- package/dist/{chunk-B3Q4TFWT.js → chunk-SJ5SYSMK.js} +551 -779
- package/dist/chunk-SJQJQO7P.js +25 -0
- package/dist/expo/background.cjs +573 -805
- package/dist/expo/background.d.cts +3 -3
- package/dist/expo/background.d.ts +3 -3
- package/dist/expo/background.js +8 -20
- package/dist/expo/index.cjs +574 -783
- package/dist/expo/index.d.cts +7 -5
- package/dist/expo/index.d.ts +7 -5
- package/dist/expo/index.js +17 -20
- package/dist/index.cjs +732 -931
- package/dist/index.d.cts +80 -10
- package/dist/index.d.ts +80 -10
- package/dist/index.js +126 -115
- package/dist/repositories/realm/index.cjs +10 -22
- package/dist/repositories/realm/index.d.cts +7 -5
- package/dist/repositories/realm/index.d.ts +7 -5
- package/dist/repositories/realm/index.js +8 -22
- package/dist/repositories/sqlite/index.cjs +12 -23
- package/dist/repositories/sqlite/index.d.cts +1 -1
- package/dist/repositories/sqlite/index.d.ts +1 -1
- package/dist/repositories/sqlite/index.js +10 -23
- package/dist/{swapsPollProcessor-wYOMzldd.d.ts → swapsPollProcessor-CEgeGlbP.d.ts} +3 -3
- package/dist/{swapsPollProcessor-BF3uTFae.d.cts → swapsPollProcessor-CuITxZie.d.cts} +3 -3
- package/dist/{types-BBI7-KJ0.d.cts → types-CrKkVzBB.d.cts} +9 -3
- package/dist/{types-BBI7-KJ0.d.ts → types-CrKkVzBB.d.ts} +9 -3
- package/package.json +10 -25
package/dist/expo/background.cjs
CHANGED
|
@@ -41,6 +41,7 @@ var TaskManager = __toESM(require("expo-task-manager"), 1);
|
|
|
41
41
|
var BackgroundTask = __toESM(require("expo-background-task"), 1);
|
|
42
42
|
var import_expo = require("@arkade-os/sdk/worker/expo");
|
|
43
43
|
var import_expo2 = require("@arkade-os/sdk/adapters/expo");
|
|
44
|
+
var import_sdk9 = require("@arkade-os/sdk");
|
|
44
45
|
|
|
45
46
|
// src/boltz-swap-provider.ts
|
|
46
47
|
var import_sdk = require("@arkade-os/sdk");
|
|
@@ -54,7 +55,10 @@ var SwapError = class extends Error {
|
|
|
54
55
|
/** The pending swap associated with this error, if available. */
|
|
55
56
|
pendingSwap;
|
|
56
57
|
constructor(options = {}) {
|
|
57
|
-
super(
|
|
58
|
+
super(
|
|
59
|
+
options.message ?? "Error during swap.",
|
|
60
|
+
options.cause !== void 0 ? { cause: options.cause } : void 0
|
|
61
|
+
);
|
|
58
62
|
this.name = "SwapError";
|
|
59
63
|
this.isClaimable = options.isClaimable ?? false;
|
|
60
64
|
this.isRefundable = options.isRefundable ?? false;
|
|
@@ -131,6 +135,96 @@ var TransactionRefundedError = class extends SwapError {
|
|
|
131
135
|
this.name = "TransactionRefundedError";
|
|
132
136
|
}
|
|
133
137
|
};
|
|
138
|
+
var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
|
|
139
|
+
reason;
|
|
140
|
+
quotedAmount;
|
|
141
|
+
floor;
|
|
142
|
+
constructor(options) {
|
|
143
|
+
super({
|
|
144
|
+
message: options.message ?? _QuoteRejectedError.defaultMessage(options),
|
|
145
|
+
...options
|
|
146
|
+
});
|
|
147
|
+
this.name = "QuoteRejectedError";
|
|
148
|
+
this.reason = options.reason;
|
|
149
|
+
this.quotedAmount = "quotedAmount" in options ? options.quotedAmount : void 0;
|
|
150
|
+
this.floor = "floor" in options ? options.floor : void 0;
|
|
151
|
+
}
|
|
152
|
+
static defaultMessage(options) {
|
|
153
|
+
switch (options.reason) {
|
|
154
|
+
case "below_floor":
|
|
155
|
+
return `Boltz quote ${options.quotedAmount} is below acceptable floor ${options.floor}`;
|
|
156
|
+
case "non_positive":
|
|
157
|
+
return `Boltz quote ${options.quotedAmount} is not positive`;
|
|
158
|
+
case "no_baseline":
|
|
159
|
+
return "Cannot accept quote: no minAcceptableAmount and no stored pending swap";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Serialize into a plain `Error` whose `.message` carries the full
|
|
164
|
+
* rejection payload as JSON behind a marker prefix. Structured clone
|
|
165
|
+
* (used by `postMessage` between page and service worker) preserves
|
|
166
|
+
* `Error.message` reliably but strips custom `.name` and own properties,
|
|
167
|
+
* so we move the typed data into the message field for transport.
|
|
168
|
+
*/
|
|
169
|
+
toTransportError() {
|
|
170
|
+
return new Error(
|
|
171
|
+
QUOTE_REJECTION_TRANSPORT_PREFIX + JSON.stringify({
|
|
172
|
+
reason: this.reason,
|
|
173
|
+
message: this.message,
|
|
174
|
+
quotedAmount: this.quotedAmount,
|
|
175
|
+
floor: this.floor
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Inverse of `toTransportError`. Returns a real `QuoteRejectedError` if
|
|
181
|
+
* `error` carries the transport prefix, else `null`.
|
|
182
|
+
*/
|
|
183
|
+
static fromTransportError(error) {
|
|
184
|
+
if (!(error instanceof Error) || !error.message.startsWith(QUOTE_REJECTION_TRANSPORT_PREFIX)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const payload = error.message.slice(QUOTE_REJECTION_TRANSPORT_PREFIX.length);
|
|
188
|
+
let data;
|
|
189
|
+
try {
|
|
190
|
+
data = JSON.parse(payload);
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (typeof data.reason !== "string" || !QUOTE_REJECTION_REASONS.has(data.reason)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const message = typeof data.message === "string" ? data.message : void 0;
|
|
198
|
+
const reason = data.reason;
|
|
199
|
+
const quotedAmount = typeof data.quotedAmount === "number" ? data.quotedAmount : null;
|
|
200
|
+
const floor = typeof data.floor === "number" ? data.floor : null;
|
|
201
|
+
switch (reason) {
|
|
202
|
+
case "below_floor":
|
|
203
|
+
if (quotedAmount === null || floor === null) return null;
|
|
204
|
+
return new _QuoteRejectedError({
|
|
205
|
+
reason,
|
|
206
|
+
quotedAmount,
|
|
207
|
+
floor,
|
|
208
|
+
message
|
|
209
|
+
});
|
|
210
|
+
case "non_positive":
|
|
211
|
+
if (quotedAmount === null) return null;
|
|
212
|
+
return new _QuoteRejectedError({
|
|
213
|
+
reason,
|
|
214
|
+
quotedAmount,
|
|
215
|
+
message
|
|
216
|
+
});
|
|
217
|
+
case "no_baseline":
|
|
218
|
+
return new _QuoteRejectedError({ reason, message });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
var QUOTE_REJECTION_TRANSPORT_PREFIX = "QUOTE_REJECTED::";
|
|
223
|
+
var QUOTE_REJECTION_REASONS = /* @__PURE__ */ new Set([
|
|
224
|
+
"below_floor",
|
|
225
|
+
"non_positive",
|
|
226
|
+
"no_baseline"
|
|
227
|
+
]);
|
|
134
228
|
var BoltzRefundError = class extends Error {
|
|
135
229
|
constructor(message, cause) {
|
|
136
230
|
super(message);
|
|
@@ -142,18 +236,10 @@ var BoltzRefundError = class extends Error {
|
|
|
142
236
|
// src/boltz-swap-provider.ts
|
|
143
237
|
var import_base = require("@scure/base");
|
|
144
238
|
var isSubmarineFinalStatus = (status) => {
|
|
145
|
-
return [
|
|
146
|
-
"invoice.failedToPay",
|
|
147
|
-
"transaction.claimed",
|
|
148
|
-
"swap.expired"
|
|
149
|
-
].includes(status);
|
|
239
|
+
return ["invoice.failedToPay", "transaction.claimed", "swap.expired"].includes(status);
|
|
150
240
|
};
|
|
151
241
|
var isSubmarineRefundableStatus = (status) => {
|
|
152
|
-
return [
|
|
153
|
-
"invoice.failedToPay",
|
|
154
|
-
"transaction.lockupFailed",
|
|
155
|
-
"swap.expired"
|
|
156
|
-
].includes(status);
|
|
242
|
+
return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
|
|
157
243
|
};
|
|
158
244
|
var isSubmarineSuccessStatus = (status) => {
|
|
159
245
|
return status === "transaction.claimed";
|
|
@@ -172,10 +258,7 @@ var isReverseClaimableStatus = (status) => {
|
|
|
172
258
|
return ["transaction.mempool", "transaction.confirmed"].includes(status);
|
|
173
259
|
};
|
|
174
260
|
var isChainClaimableStatus = (status) => {
|
|
175
|
-
return [
|
|
176
|
-
"transaction.server.mempool",
|
|
177
|
-
"transaction.server.confirmed"
|
|
178
|
-
].includes(status);
|
|
261
|
+
return ["transaction.server.mempool", "transaction.server.confirmed"].includes(status);
|
|
179
262
|
};
|
|
180
263
|
var isChainFinalStatus = (status) => {
|
|
181
264
|
return [
|
|
@@ -292,8 +375,7 @@ var BASE_URLS = {
|
|
|
292
375
|
var isSwapNotFoundBody = (error) => {
|
|
293
376
|
const needle = "could not find swap";
|
|
294
377
|
const fromJson = error.errorData?.error;
|
|
295
|
-
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle))
|
|
296
|
-
return true;
|
|
378
|
+
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle)) return true;
|
|
297
379
|
return error.message.toLowerCase().includes(needle);
|
|
298
380
|
};
|
|
299
381
|
var BoltzSwapProvider = class {
|
|
@@ -307,10 +389,7 @@ var BoltzSwapProvider = class {
|
|
|
307
389
|
this.network = config.network;
|
|
308
390
|
this.referralId = config.referralId ?? "arkade-ts-sdk";
|
|
309
391
|
const apiUrl = config.apiUrl || BASE_URLS[config.network];
|
|
310
|
-
if (!apiUrl)
|
|
311
|
-
throw new Error(
|
|
312
|
-
`API URL is required for network: ${config.network}`
|
|
313
|
-
);
|
|
392
|
+
if (!apiUrl) throw new Error(`API URL is required for network: ${config.network}`);
|
|
314
393
|
this.apiUrl = apiUrl;
|
|
315
394
|
this.wsUrl = this.apiUrl.replace(/^http(s)?:\/\//, "ws$1://").replace("9069", "9004") + "/v2/ws";
|
|
316
395
|
}
|
|
@@ -329,10 +408,7 @@ var BoltzSwapProvider = class {
|
|
|
329
408
|
/** Returns current Lightning swap fees (submarine + reverse) from Boltz. */
|
|
330
409
|
async getFees() {
|
|
331
410
|
const [submarine, reverse] = await Promise.all([
|
|
332
|
-
this.request(
|
|
333
|
-
"/v2/swap/submarine",
|
|
334
|
-
"GET"
|
|
335
|
-
),
|
|
411
|
+
this.request("/v2/swap/submarine", "GET"),
|
|
336
412
|
this.request("/v2/swap/reverse", "GET")
|
|
337
413
|
]);
|
|
338
414
|
if (!isGetSubmarinePairsResponse(submarine))
|
|
@@ -352,10 +428,7 @@ var BoltzSwapProvider = class {
|
|
|
352
428
|
}
|
|
353
429
|
/** Returns current Lightning swap min/max limits from Boltz. */
|
|
354
430
|
async getLimits() {
|
|
355
|
-
const response = await this.request(
|
|
356
|
-
"/v2/swap/submarine",
|
|
357
|
-
"GET"
|
|
358
|
-
);
|
|
431
|
+
const response = await this.request("/v2/swap/submarine", "GET");
|
|
359
432
|
if (!isGetSubmarinePairsResponse(response))
|
|
360
433
|
throw new SchemaError({ message: "error fetching limits" });
|
|
361
434
|
return {
|
|
@@ -365,10 +438,7 @@ var BoltzSwapProvider = class {
|
|
|
365
438
|
}
|
|
366
439
|
/** Returns the current BTC chain tip height from Boltz. */
|
|
367
440
|
async getChainHeight() {
|
|
368
|
-
const response = await this.request(
|
|
369
|
-
"/v2/chain/heights",
|
|
370
|
-
"GET"
|
|
371
|
-
);
|
|
441
|
+
const response = await this.request("/v2/chain/heights", "GET");
|
|
372
442
|
if (typeof response?.BTC !== "number")
|
|
373
443
|
throw new SchemaError({
|
|
374
444
|
message: "error fetching chain heights"
|
|
@@ -398,10 +468,7 @@ var BoltzSwapProvider = class {
|
|
|
398
468
|
async getSwapStatus(id) {
|
|
399
469
|
let response;
|
|
400
470
|
try {
|
|
401
|
-
response = await this.request(
|
|
402
|
-
`/v2/swap/${id}`,
|
|
403
|
-
"GET"
|
|
404
|
-
);
|
|
471
|
+
response = await this.request(`/v2/swap/${id}`, "GET");
|
|
405
472
|
} catch (error) {
|
|
406
473
|
if (error instanceof NetworkError && error.statusCode === 404 && isSwapNotFoundBody(error)) {
|
|
407
474
|
throw new SwapNotFoundError(id, error.errorData);
|
|
@@ -497,12 +564,10 @@ var BoltzSwapProvider = class {
|
|
|
497
564
|
throw new SwapError({ message: "Invalid 'to' chain" });
|
|
498
565
|
if (["BTC", "ARK"].indexOf(from) === -1)
|
|
499
566
|
throw new SwapError({ message: "Invalid 'from' chain" });
|
|
500
|
-
if (to === from)
|
|
501
|
-
throw new SwapError({ message: "Invalid swap direction" });
|
|
567
|
+
if (to === from) throw new SwapError({ message: "Invalid swap direction" });
|
|
502
568
|
if (!preimageHash || preimageHash.length != 64)
|
|
503
569
|
throw new SwapError({ message: "Invalid preimageHash" });
|
|
504
|
-
if (feeSatsPerByte <= 0)
|
|
505
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
570
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
506
571
|
if (serverLockAmount !== void 0 && userLockAmount !== void 0 || serverLockAmount === void 0 && userLockAmount === void 0)
|
|
507
572
|
throw new SwapError({
|
|
508
573
|
message: "Either serverLockAmount or userLockAmount must be provided"
|
|
@@ -557,12 +622,8 @@ var BoltzSwapProvider = class {
|
|
|
557
622
|
message: "Error refunding submarine swap"
|
|
558
623
|
});
|
|
559
624
|
return {
|
|
560
|
-
transaction: import_sdk.Transaction.fromPSBT(
|
|
561
|
-
|
|
562
|
-
),
|
|
563
|
-
checkpoint: import_sdk.Transaction.fromPSBT(
|
|
564
|
-
import_base.base64.decode(response.checkpoint)
|
|
565
|
-
)
|
|
625
|
+
transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
|
|
626
|
+
checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
|
|
566
627
|
};
|
|
567
628
|
}
|
|
568
629
|
/** Requests Boltz co-signature for a chain swap refund. Returns signed transaction + checkpoint. */
|
|
@@ -581,53 +642,53 @@ var BoltzSwapProvider = class {
|
|
|
581
642
|
message: "Error refunding chain swap"
|
|
582
643
|
});
|
|
583
644
|
return {
|
|
584
|
-
transaction: import_sdk.Transaction.fromPSBT(
|
|
585
|
-
|
|
586
|
-
),
|
|
587
|
-
checkpoint: import_sdk.Transaction.fromPSBT(
|
|
588
|
-
import_base.base64.decode(response.checkpoint)
|
|
589
|
-
)
|
|
645
|
+
transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
|
|
646
|
+
checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
|
|
590
647
|
};
|
|
591
648
|
}
|
|
592
|
-
/**
|
|
649
|
+
/**
|
|
650
|
+
* Monitors swap status updates and forwards them to the update callback.
|
|
651
|
+
* Prefers a WebSocket subscription; on connection error or premature close
|
|
652
|
+
* it falls back to REST polling so callers don't fail when the WS endpoint
|
|
653
|
+
* is flaky (observed in CI). Resolves when the swap reaches a terminal
|
|
654
|
+
* status.
|
|
655
|
+
*/
|
|
593
656
|
async monitorSwap(swapId, update) {
|
|
594
657
|
return new Promise((resolve, reject) => {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
);
|
|
658
|
+
let settled = false;
|
|
659
|
+
let lastStatus = null;
|
|
660
|
+
let pollTimer = null;
|
|
661
|
+
let webSocket = null;
|
|
662
|
+
let connectionTimeout = null;
|
|
663
|
+
const cleanup = () => {
|
|
664
|
+
if (connectionTimeout) {
|
|
665
|
+
clearTimeout(connectionTimeout);
|
|
666
|
+
connectionTimeout = null;
|
|
667
|
+
}
|
|
668
|
+
if (pollTimer) {
|
|
669
|
+
clearInterval(pollTimer);
|
|
670
|
+
pollTimer = null;
|
|
671
|
+
}
|
|
672
|
+
if (webSocket) {
|
|
673
|
+
try {
|
|
674
|
+
webSocket.close();
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
webSocket = null;
|
|
678
|
+
}
|
|
617
679
|
};
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
680
|
+
const finish = (err) => {
|
|
681
|
+
if (settled) return;
|
|
682
|
+
settled = true;
|
|
683
|
+
cleanup();
|
|
684
|
+
if (err) reject(err);
|
|
685
|
+
else resolve();
|
|
621
686
|
};
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
if (
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
reject(new SwapError({ message: msg.args[0].error }));
|
|
628
|
-
}
|
|
629
|
-
const status = msg.args[0].status;
|
|
630
|
-
const negotiable = status === "transaction.lockupFailed" && msg.args[0].failureDetails?.actual !== void 0 && msg.args[0].failureDetails?.expected !== void 0;
|
|
687
|
+
const handleStatus = (status, data) => {
|
|
688
|
+
if (settled) return;
|
|
689
|
+
if (status === lastStatus) return;
|
|
690
|
+
lastStatus = status;
|
|
691
|
+
const negotiable = status === "transaction.lockupFailed" && data?.failureDetails?.actual !== void 0 && data?.failureDetails?.expected !== void 0;
|
|
631
692
|
switch (status) {
|
|
632
693
|
case "invoice.settled":
|
|
633
694
|
case "transaction.claimed":
|
|
@@ -636,13 +697,13 @@ var BoltzSwapProvider = class {
|
|
|
636
697
|
case "invoice.failedToPay":
|
|
637
698
|
case "transaction.failed":
|
|
638
699
|
case "swap.expired":
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
700
|
+
update(status, data);
|
|
701
|
+
finish();
|
|
702
|
+
return;
|
|
642
703
|
case "transaction.lockupFailed":
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
704
|
+
update(status, data);
|
|
705
|
+
if (!negotiable) finish();
|
|
706
|
+
return;
|
|
646
707
|
case "invoice.paid":
|
|
647
708
|
case "invoice.pending":
|
|
648
709
|
case "invoice.set":
|
|
@@ -652,9 +713,77 @@ var BoltzSwapProvider = class {
|
|
|
652
713
|
case "transaction.claim.pending":
|
|
653
714
|
case "transaction.server.mempool":
|
|
654
715
|
case "transaction.server.confirmed":
|
|
655
|
-
update(status,
|
|
716
|
+
update(status, data);
|
|
717
|
+
return;
|
|
656
718
|
}
|
|
657
719
|
};
|
|
720
|
+
const startPolling = () => {
|
|
721
|
+
if (settled || pollTimer) return;
|
|
722
|
+
const poll = async () => {
|
|
723
|
+
if (settled) return;
|
|
724
|
+
try {
|
|
725
|
+
const result = await this.getSwapStatus(swapId);
|
|
726
|
+
handleStatus(result.status, { ...result, id: swapId });
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
pollTimer = setInterval(poll, 1e3);
|
|
731
|
+
poll();
|
|
732
|
+
};
|
|
733
|
+
try {
|
|
734
|
+
webSocket = new globalThis.WebSocket(this.wsUrl);
|
|
735
|
+
connectionTimeout = setTimeout(() => {
|
|
736
|
+
try {
|
|
737
|
+
webSocket?.close();
|
|
738
|
+
} catch {
|
|
739
|
+
}
|
|
740
|
+
webSocket = null;
|
|
741
|
+
startPolling();
|
|
742
|
+
}, 5e3);
|
|
743
|
+
webSocket.onerror = () => {
|
|
744
|
+
if (connectionTimeout) {
|
|
745
|
+
clearTimeout(connectionTimeout);
|
|
746
|
+
connectionTimeout = null;
|
|
747
|
+
}
|
|
748
|
+
webSocket = null;
|
|
749
|
+
startPolling();
|
|
750
|
+
};
|
|
751
|
+
webSocket.onopen = () => {
|
|
752
|
+
if (connectionTimeout) {
|
|
753
|
+
clearTimeout(connectionTimeout);
|
|
754
|
+
connectionTimeout = null;
|
|
755
|
+
}
|
|
756
|
+
webSocket?.send(
|
|
757
|
+
JSON.stringify({
|
|
758
|
+
op: "subscribe",
|
|
759
|
+
channel: "swap.update",
|
|
760
|
+
args: [swapId]
|
|
761
|
+
})
|
|
762
|
+
);
|
|
763
|
+
};
|
|
764
|
+
webSocket.onclose = () => {
|
|
765
|
+
if (connectionTimeout) {
|
|
766
|
+
clearTimeout(connectionTimeout);
|
|
767
|
+
connectionTimeout = null;
|
|
768
|
+
}
|
|
769
|
+
if (!settled && !pollTimer) startPolling();
|
|
770
|
+
};
|
|
771
|
+
webSocket.onmessage = (rawMsg) => {
|
|
772
|
+
try {
|
|
773
|
+
const msg = JSON.parse(rawMsg.data);
|
|
774
|
+
if (msg.event !== "update" || msg.args?.[0]?.id !== swapId) return;
|
|
775
|
+
if (msg.args[0].error) {
|
|
776
|
+
finish(new SwapError({ message: msg.args[0].error }));
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
handleStatus(msg.args[0].status, msg.args[0]);
|
|
780
|
+
} catch {
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
} catch {
|
|
784
|
+
webSocket = null;
|
|
785
|
+
startPolling();
|
|
786
|
+
}
|
|
658
787
|
});
|
|
659
788
|
}
|
|
660
789
|
/** Returns current chain swap fees for a given pair (e.g. ARK→BTC). */
|
|
@@ -662,10 +791,7 @@ var BoltzSwapProvider = class {
|
|
|
662
791
|
if (from === to) {
|
|
663
792
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
664
793
|
}
|
|
665
|
-
const response = await this.request(
|
|
666
|
-
"/v2/swap/chain",
|
|
667
|
-
"GET"
|
|
668
|
-
);
|
|
794
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
669
795
|
if (!isGetChainPairsResponse(response))
|
|
670
796
|
throw new SchemaError({ message: "error fetching fees" });
|
|
671
797
|
if (!response[from]?.[to]) {
|
|
@@ -680,10 +806,7 @@ var BoltzSwapProvider = class {
|
|
|
680
806
|
if (from === to) {
|
|
681
807
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
682
808
|
}
|
|
683
|
-
const response = await this.request(
|
|
684
|
-
"/v2/swap/chain",
|
|
685
|
-
"GET"
|
|
686
|
-
);
|
|
809
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
687
810
|
if (!isGetChainPairsResponse(response))
|
|
688
811
|
throw new SchemaError({ message: "error fetching limits" });
|
|
689
812
|
if (!response[from]?.[to]) {
|
|
@@ -812,9 +935,7 @@ var BoltzSwapProvider = class {
|
|
|
812
935
|
return await response.json();
|
|
813
936
|
} catch (error) {
|
|
814
937
|
if (error instanceof NetworkError) throw error;
|
|
815
|
-
throw new NetworkError(
|
|
816
|
-
`Request to ${url} failed: ${error.message}`
|
|
817
|
-
);
|
|
938
|
+
throw new NetworkError(`Request to ${url} failed: ${error.message}`);
|
|
818
939
|
}
|
|
819
940
|
}
|
|
820
941
|
};
|
|
@@ -837,8 +958,7 @@ var findKeyIndex = (keys, target) => keys.findIndex((k) => (0, import_utils.equa
|
|
|
837
958
|
var assertPublicKeys = (keys) => {
|
|
838
959
|
const seen = /* @__PURE__ */ new Set();
|
|
839
960
|
for (const key of keys) {
|
|
840
|
-
if (key.length !== 33)
|
|
841
|
-
throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
961
|
+
if (key.length !== 33) throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
842
962
|
const enc = import_base2.hex.encode(key);
|
|
843
963
|
if (seen.has(enc)) throw new Error(`duplicate public key ${enc}`);
|
|
844
964
|
seen.add(enc);
|
|
@@ -846,9 +966,7 @@ var assertPublicKeys = (keys) => {
|
|
|
846
966
|
};
|
|
847
967
|
var aggregateKeys = (publicKeys, tweak) => {
|
|
848
968
|
assertPublicKeys([...publicKeys]);
|
|
849
|
-
return (0, import_musig2.keyAggExport)(
|
|
850
|
-
(0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : [])
|
|
851
|
-
);
|
|
969
|
+
return (0, import_musig2.keyAggExport)((0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : []));
|
|
852
970
|
};
|
|
853
971
|
var MusigKeyAgg = class _MusigKeyAgg {
|
|
854
972
|
constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, internalKey, _tweak) {
|
|
@@ -895,18 +1013,12 @@ var MusigWithMessage = class {
|
|
|
895
1013
|
this.msg = msg;
|
|
896
1014
|
}
|
|
897
1015
|
generateNonce() {
|
|
898
|
-
const nonce = (0, import_musig2.nonceGen)(
|
|
899
|
-
this.myPublicKey,
|
|
900
|
-
this.privateKey,
|
|
901
|
-
this.aggPubkey,
|
|
902
|
-
this.msg
|
|
903
|
-
);
|
|
1016
|
+
const nonce = (0, import_musig2.nonceGen)(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
|
|
904
1017
|
return new MusigWithNonce(
|
|
905
1018
|
this.privateKey,
|
|
906
1019
|
this.myPublicKey,
|
|
907
1020
|
this.publicKeys,
|
|
908
1021
|
this.myIndex,
|
|
909
|
-
this.aggPubkey,
|
|
910
1022
|
this.tweak,
|
|
911
1023
|
this.msg,
|
|
912
1024
|
nonce
|
|
@@ -914,12 +1026,11 @@ var MusigWithMessage = class {
|
|
|
914
1026
|
}
|
|
915
1027
|
};
|
|
916
1028
|
var MusigWithNonce = class {
|
|
917
|
-
constructor(privateKey, myPublicKey, publicKeys, myIndex,
|
|
1029
|
+
constructor(privateKey, myPublicKey, publicKeys, myIndex, tweak, msg, nonce) {
|
|
918
1030
|
this.privateKey = privateKey;
|
|
919
1031
|
this.myPublicKey = myPublicKey;
|
|
920
1032
|
this.publicKeys = publicKeys;
|
|
921
1033
|
this.myIndex = myIndex;
|
|
922
|
-
this.aggPubkey = aggPubkey;
|
|
923
1034
|
this.tweak = tweak;
|
|
924
1035
|
this.msg = msg;
|
|
925
1036
|
this.nonce = nonce;
|
|
@@ -951,10 +1062,8 @@ var MusigWithNonce = class {
|
|
|
951
1062
|
const aggregatedNonce = (0, import_musig2.nonceAggregate)([...ordered]);
|
|
952
1063
|
return new MusigNoncesAggregated(
|
|
953
1064
|
this.privateKey,
|
|
954
|
-
this.myPublicKey,
|
|
955
1065
|
this.publicKeys,
|
|
956
1066
|
this.myIndex,
|
|
957
|
-
this.aggPubkey,
|
|
958
1067
|
this.tweak,
|
|
959
1068
|
this.msg,
|
|
960
1069
|
this.nonce,
|
|
@@ -964,12 +1073,10 @@ var MusigWithNonce = class {
|
|
|
964
1073
|
}
|
|
965
1074
|
};
|
|
966
1075
|
var MusigNoncesAggregated = class {
|
|
967
|
-
constructor(privateKey,
|
|
1076
|
+
constructor(privateKey, publicKeys, myIndex, tweak, msg, nonce, pubNonces, aggregatedNonce) {
|
|
968
1077
|
this.privateKey = privateKey;
|
|
969
|
-
this.myPublicKey = myPublicKey;
|
|
970
1078
|
this.publicKeys = publicKeys;
|
|
971
1079
|
this.myIndex = myIndex;
|
|
972
|
-
this.aggPubkey = aggPubkey;
|
|
973
1080
|
this.tweak = tweak;
|
|
974
1081
|
this.msg = msg;
|
|
975
1082
|
this.nonce = nonce;
|
|
@@ -1015,11 +1122,7 @@ var MusigSession = class {
|
|
|
1015
1122
|
const index = typeof publicKeyOrIndex === "number" ? publicKeyOrIndex : findKeyIndex(this.publicKeys, publicKeyOrIndex);
|
|
1016
1123
|
if (index < 0 || index >= this.publicKeys.length)
|
|
1017
1124
|
throw new Error("public key not found or index out of range");
|
|
1018
|
-
if (!this.session.partialSigVerify(
|
|
1019
|
-
signature,
|
|
1020
|
-
[...this.pubNonces],
|
|
1021
|
-
index
|
|
1022
|
-
)) {
|
|
1125
|
+
if (!this.session.partialSigVerify(signature, [...this.pubNonces], index)) {
|
|
1023
1126
|
throw new Error("invalid partial signature");
|
|
1024
1127
|
}
|
|
1025
1128
|
this.partialSignatures[index] = signature;
|
|
@@ -1028,12 +1131,7 @@ var MusigSession = class {
|
|
|
1028
1131
|
signPartial() {
|
|
1029
1132
|
const sig = this.session.sign(this.nonce.secret, this.privateKey, true);
|
|
1030
1133
|
this.partialSignatures[this.myIndex] = sig;
|
|
1031
|
-
return new MusigSigned(
|
|
1032
|
-
this.session,
|
|
1033
|
-
[...this.partialSignatures],
|
|
1034
|
-
sig,
|
|
1035
|
-
this.nonce.public
|
|
1036
|
-
);
|
|
1134
|
+
return new MusigSigned(this.session, [...this.partialSignatures], sig, this.nonce.public);
|
|
1037
1135
|
}
|
|
1038
1136
|
};
|
|
1039
1137
|
var MusigSigned = class {
|
|
@@ -1047,14 +1145,11 @@ var MusigSigned = class {
|
|
|
1047
1145
|
if (this.partialSignatures.some((s) => s === null)) {
|
|
1048
1146
|
throw new Error("not all partial signatures are set");
|
|
1049
1147
|
}
|
|
1050
|
-
return this.session.partialSigAgg(
|
|
1051
|
-
this.partialSignatures
|
|
1052
|
-
);
|
|
1148
|
+
return this.session.partialSigAgg(this.partialSignatures);
|
|
1053
1149
|
}
|
|
1054
1150
|
};
|
|
1055
1151
|
var create = (privateKey, publicKeys) => {
|
|
1056
|
-
if (publicKeys.length < 2)
|
|
1057
|
-
throw new Error("need at least 2 keys to aggregate");
|
|
1152
|
+
if (publicKeys.length < 2) throw new Error("need at least 2 keys to aggregate");
|
|
1058
1153
|
const keys = [...publicKeys];
|
|
1059
1154
|
assertPublicKeys(keys);
|
|
1060
1155
|
Object.freeze(keys);
|
|
@@ -1062,14 +1157,7 @@ var create = (privateKey, publicKeys) => {
|
|
|
1062
1157
|
const myIndex = findKeyIndex(keys, myPublicKey);
|
|
1063
1158
|
if (myIndex === -1) throw new Error("our key is not in publicKeys");
|
|
1064
1159
|
const aggPubkey = aggregateKeys(keys);
|
|
1065
|
-
return new MusigKeyAgg(
|
|
1066
|
-
privateKey,
|
|
1067
|
-
myPublicKey,
|
|
1068
|
-
keys,
|
|
1069
|
-
myIndex,
|
|
1070
|
-
aggPubkey,
|
|
1071
|
-
aggPubkey
|
|
1072
|
-
);
|
|
1160
|
+
return new MusigKeyAgg(privateKey, myPublicKey, keys, myIndex, aggPubkey, aggPubkey);
|
|
1073
1161
|
};
|
|
1074
1162
|
|
|
1075
1163
|
// src/utils/boltz-swap-tx.ts
|
|
@@ -1145,9 +1233,7 @@ var taprootHashTree = (tree) => {
|
|
|
1145
1233
|
};
|
|
1146
1234
|
var tweakMusig = (musig, tree) => {
|
|
1147
1235
|
const tweak = taprootHashTree(tree).hash;
|
|
1148
|
-
return musig.xonlyTweakAdd(
|
|
1149
|
-
import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak)
|
|
1150
|
-
);
|
|
1236
|
+
return musig.xonlyTweakAdd(import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak));
|
|
1151
1237
|
};
|
|
1152
1238
|
var toXOnly = (pubKey) => {
|
|
1153
1239
|
if (pubKey.length === 32) return pubKey;
|
|
@@ -1159,9 +1245,7 @@ var toXOnly = (pubKey) => {
|
|
|
1159
1245
|
}
|
|
1160
1246
|
return pubKey.subarray(1, 33);
|
|
1161
1247
|
}
|
|
1162
|
-
throw new Error(
|
|
1163
|
-
`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`
|
|
1164
|
-
);
|
|
1248
|
+
throw new Error(`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`);
|
|
1165
1249
|
};
|
|
1166
1250
|
var p2trScript = (publicKey) => import_btc_signer.Script.encode(["OP_1", toXOnly(publicKey)]);
|
|
1167
1251
|
var detectSwapOutput = (tweakedKey, transaction) => {
|
|
@@ -1176,8 +1260,7 @@ var detectSwapOutput = (tweakedKey, transaction) => {
|
|
|
1176
1260
|
};
|
|
1177
1261
|
var DUMMY_TAPROOT_SIGNATURE = new Uint8Array(64);
|
|
1178
1262
|
var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
1179
|
-
if (fee < BigInt(0) || fee >= utxo.amount)
|
|
1180
|
-
throw new Error("fee exceeds utxo amount");
|
|
1263
|
+
if (fee < BigInt(0) || fee >= utxo.amount) throw new Error("fee exceeds utxo amount");
|
|
1181
1264
|
const tx = new import_btc_signer.Transaction({ version: 2 });
|
|
1182
1265
|
tx.addOutput({
|
|
1183
1266
|
amount: utxo.amount - fee,
|
|
@@ -1196,9 +1279,7 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
|
1196
1279
|
};
|
|
1197
1280
|
var targetFee = (satPerVbyte, constructTx) => {
|
|
1198
1281
|
const tx = constructTx(BigInt(1));
|
|
1199
|
-
return constructTx(
|
|
1200
|
-
BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte))
|
|
1201
|
-
);
|
|
1282
|
+
return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
|
|
1202
1283
|
};
|
|
1203
1284
|
|
|
1204
1285
|
// src/utils/decoding.ts
|
|
@@ -1206,9 +1287,7 @@ var import_light_bolt11_decoder = __toESM(require("light-bolt11-decoder"), 1);
|
|
|
1206
1287
|
var import_sdk2 = require("@arkade-os/sdk");
|
|
1207
1288
|
var decodeInvoice = (invoice) => {
|
|
1208
1289
|
const decoded = import_light_bolt11_decoder.default.decode(invoice);
|
|
1209
|
-
const millisats = Number(
|
|
1210
|
-
decoded.sections.find((s) => s.name === "amount")?.value ?? "0"
|
|
1211
|
-
);
|
|
1290
|
+
const millisats = Number(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
|
|
1212
1291
|
return {
|
|
1213
1292
|
expiry: decoded.expiry ?? 3600,
|
|
1214
1293
|
amountSats: Math.floor(millisats / 1e3),
|
|
@@ -1271,10 +1350,7 @@ function extractTimeLockFromLeafOutput(scriptHex) {
|
|
|
1271
1350
|
const data = opcodes[hasCSV - 1];
|
|
1272
1351
|
if (data instanceof Uint8Array) {
|
|
1273
1352
|
const dataBytes = new Uint8Array(data).reverse();
|
|
1274
|
-
const {
|
|
1275
|
-
blocks,
|
|
1276
|
-
seconds
|
|
1277
|
-
} = import_bip68.default.decode(
|
|
1353
|
+
const { blocks, seconds } = import_bip68.default.decode(
|
|
1278
1354
|
parseInt(import_base5.hex.encode(dataBytes), 16)
|
|
1279
1355
|
);
|
|
1280
1356
|
return blocks ?? seconds ?? 0;
|
|
@@ -1378,9 +1454,7 @@ var SwapManager = class _SwapManager {
|
|
|
1378
1454
|
this.wsConnectedListeners.add(config.events.onWebSocketConnected);
|
|
1379
1455
|
}
|
|
1380
1456
|
if (config.events?.onWebSocketDisconnected) {
|
|
1381
|
-
this.wsDisconnectedListeners.add(
|
|
1382
|
-
config.events.onWebSocketDisconnected
|
|
1383
|
-
);
|
|
1457
|
+
this.wsDisconnectedListeners.add(config.events.onWebSocketDisconnected);
|
|
1384
1458
|
}
|
|
1385
1459
|
this.currentReconnectDelay = this.config.reconnectDelayMs;
|
|
1386
1460
|
this.currentPollRetryDelay = this.config.pollRetryDelayMs;
|
|
@@ -1531,9 +1605,7 @@ var SwapManager = class _SwapManager {
|
|
|
1531
1605
|
*/
|
|
1532
1606
|
setPollInterval(ms) {
|
|
1533
1607
|
if (ms <= 0) {
|
|
1534
|
-
throw new RangeError(
|
|
1535
|
-
`setPollInterval: ms must be a positive number, got ${ms}`
|
|
1536
|
-
);
|
|
1608
|
+
throw new RangeError(`setPollInterval: ms must be a positive number, got ${ms}`);
|
|
1537
1609
|
}
|
|
1538
1610
|
const cappedInterval = Math.min(ms, this.config.maxPollIntervalMs);
|
|
1539
1611
|
if (cappedInterval !== ms) {
|
|
@@ -1542,10 +1614,7 @@ var SwapManager = class _SwapManager {
|
|
|
1542
1614
|
);
|
|
1543
1615
|
}
|
|
1544
1616
|
this.config.pollInterval = cappedInterval;
|
|
1545
|
-
this.currentPollRetryDelay = Math.min(
|
|
1546
|
-
cappedInterval,
|
|
1547
|
-
this.config.pollRetryDelayMs
|
|
1548
|
-
);
|
|
1617
|
+
this.currentPollRetryDelay = Math.min(cappedInterval, this.config.pollRetryDelayMs);
|
|
1549
1618
|
if (this.isRunning) {
|
|
1550
1619
|
if (this.usePollingFallback) {
|
|
1551
1620
|
this.startPollingFallback();
|
|
@@ -1630,9 +1699,7 @@ var SwapManager = class _SwapManager {
|
|
|
1630
1699
|
}
|
|
1631
1700
|
if (this.isFinalStatus(swap)) {
|
|
1632
1701
|
if (isPendingReverseSwap(swap)) {
|
|
1633
|
-
const response = await this.swapProvider.getReverseSwapTxId(
|
|
1634
|
-
swap.id
|
|
1635
|
-
);
|
|
1702
|
+
const response = await this.swapProvider.getReverseSwapTxId(swap.id);
|
|
1636
1703
|
return { txid: response.id };
|
|
1637
1704
|
}
|
|
1638
1705
|
if (isPendingSubmarineSwap(swap)) {
|
|
@@ -1649,31 +1716,19 @@ var SwapManager = class _SwapManager {
|
|
|
1649
1716
|
if (updatedSwap.status === "invoice.settled") {
|
|
1650
1717
|
this.swapProvider.getReverseSwapTxId(updatedSwap.id).then((response) => resolve({ txid: response.id })).catch((error) => reject(error));
|
|
1651
1718
|
} else {
|
|
1652
|
-
reject(
|
|
1653
|
-
new Error(
|
|
1654
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1655
|
-
)
|
|
1656
|
-
);
|
|
1719
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1657
1720
|
}
|
|
1658
1721
|
} else if (isPendingSubmarineSwap(updatedSwap)) {
|
|
1659
1722
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1660
1723
|
resolve({ txid: updatedSwap.id });
|
|
1661
1724
|
} else {
|
|
1662
|
-
reject(
|
|
1663
|
-
new Error(
|
|
1664
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1665
|
-
)
|
|
1666
|
-
);
|
|
1725
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1667
1726
|
}
|
|
1668
1727
|
} else if (isPendingChainSwap(updatedSwap)) {
|
|
1669
1728
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1670
1729
|
resolve({ txid: updatedSwap.id });
|
|
1671
1730
|
} else {
|
|
1672
|
-
reject(
|
|
1673
|
-
new Error(
|
|
1674
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1675
|
-
)
|
|
1676
|
-
);
|
|
1731
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1677
1732
|
}
|
|
1678
1733
|
}
|
|
1679
1734
|
};
|
|
@@ -1716,16 +1771,12 @@ var SwapManager = class _SwapManager {
|
|
|
1716
1771
|
const connectionTimeout = setTimeout(() => {
|
|
1717
1772
|
logger.error("WebSocket connection timeout");
|
|
1718
1773
|
this.websocket?.close();
|
|
1719
|
-
this.enterPollingFallback(
|
|
1720
|
-
new NetworkError("WebSocket connection failed")
|
|
1721
|
-
);
|
|
1774
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1722
1775
|
}, 1e4);
|
|
1723
1776
|
this.websocket.onerror = (error) => {
|
|
1724
1777
|
clearTimeout(connectionTimeout);
|
|
1725
1778
|
logger.error("WebSocket error:", error);
|
|
1726
|
-
this.enterPollingFallback(
|
|
1727
|
-
new NetworkError("WebSocket connection failed")
|
|
1728
|
-
);
|
|
1779
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1729
1780
|
};
|
|
1730
1781
|
this.websocket.onopen = () => {
|
|
1731
1782
|
clearTimeout(connectionTimeout);
|
|
@@ -1765,9 +1816,7 @@ var SwapManager = class _SwapManager {
|
|
|
1765
1816
|
};
|
|
1766
1817
|
} catch (error) {
|
|
1767
1818
|
logger.error("Failed to create WebSocket:", error);
|
|
1768
|
-
this.enterPollingFallback(
|
|
1769
|
-
new NetworkError("WebSocket connection failed")
|
|
1770
|
-
);
|
|
1819
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1771
1820
|
}
|
|
1772
1821
|
}
|
|
1773
1822
|
/**
|
|
@@ -1802,11 +1851,8 @@ var SwapManager = class _SwapManager {
|
|
|
1802
1851
|
* Schedule WebSocket reconnection with exponential backoff
|
|
1803
1852
|
*/
|
|
1804
1853
|
scheduleReconnect() {
|
|
1805
|
-
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport())
|
|
1806
|
-
|
|
1807
|
-
logger.log(
|
|
1808
|
-
`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`
|
|
1809
|
-
);
|
|
1854
|
+
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport()) return;
|
|
1855
|
+
logger.log(`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`);
|
|
1810
1856
|
this.reconnectTimer = setTimeout(() => {
|
|
1811
1857
|
this.reconnectTimer = null;
|
|
1812
1858
|
this.isReconnecting = false;
|
|
@@ -1821,8 +1867,7 @@ var SwapManager = class _SwapManager {
|
|
|
1821
1867
|
* Subscribe to a specific swap ID on the WebSocket
|
|
1822
1868
|
*/
|
|
1823
1869
|
subscribeToSwap(swapId) {
|
|
1824
|
-
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN)
|
|
1825
|
-
return;
|
|
1870
|
+
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return;
|
|
1826
1871
|
this.websocket.send(
|
|
1827
1872
|
JSON.stringify({
|
|
1828
1873
|
op: "subscribe",
|
|
@@ -1845,9 +1890,7 @@ var SwapManager = class _SwapManager {
|
|
|
1845
1890
|
if (msg.args[0].error) {
|
|
1846
1891
|
logger.error(`Swap ${swapId} error:`, msg.args[0].error);
|
|
1847
1892
|
const error = new Error(msg.args[0].error);
|
|
1848
|
-
this.swapFailedListeners.forEach(
|
|
1849
|
-
(listener) => listener(swap, error)
|
|
1850
|
-
);
|
|
1893
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1851
1894
|
return;
|
|
1852
1895
|
}
|
|
1853
1896
|
const newStatus = msg.args[0].status;
|
|
@@ -1865,19 +1908,14 @@ var SwapManager = class _SwapManager {
|
|
|
1865
1908
|
const oldStatus = swap.status;
|
|
1866
1909
|
if (oldStatus === newStatus) return;
|
|
1867
1910
|
swap.status = newStatus;
|
|
1868
|
-
this.swapUpdateListeners.forEach(
|
|
1869
|
-
(listener) => listener(swap, oldStatus)
|
|
1870
|
-
);
|
|
1911
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
1871
1912
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
1872
1913
|
if (subscribers) {
|
|
1873
1914
|
subscribers.forEach((callback) => {
|
|
1874
1915
|
try {
|
|
1875
1916
|
callback(swap, oldStatus);
|
|
1876
1917
|
} catch (error) {
|
|
1877
|
-
logger.error(
|
|
1878
|
-
`Error in swap subscription callback for ${swap.id}:`,
|
|
1879
|
-
error
|
|
1880
|
-
);
|
|
1918
|
+
logger.error(`Error in swap subscription callback for ${swap.id}:`, error);
|
|
1881
1919
|
}
|
|
1882
1920
|
});
|
|
1883
1921
|
}
|
|
@@ -1902,9 +1940,7 @@ var SwapManager = class _SwapManager {
|
|
|
1902
1940
|
*/
|
|
1903
1941
|
async executeAutonomousAction(swap) {
|
|
1904
1942
|
if (this.swapsInProgress.has(swap.id)) {
|
|
1905
|
-
logger.log(
|
|
1906
|
-
`Swap ${swap.id} is already being processed, skipping autonomous action`
|
|
1907
|
-
);
|
|
1943
|
+
logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
|
|
1908
1944
|
return;
|
|
1909
1945
|
}
|
|
1910
1946
|
try {
|
|
@@ -1919,9 +1955,7 @@ var SwapManager = class _SwapManager {
|
|
|
1919
1955
|
if (isReverseClaimableStatus(swap.status)) {
|
|
1920
1956
|
logger.log(`Auto-claiming reverse swap ${swap.id}`);
|
|
1921
1957
|
await this.executeClaimAction(swap);
|
|
1922
|
-
this.actionExecutedListeners.forEach(
|
|
1923
|
-
(listener) => listener(swap, "claim")
|
|
1924
|
-
);
|
|
1958
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "claim"));
|
|
1925
1959
|
}
|
|
1926
1960
|
} else if (isPendingSubmarineSwap(swap)) {
|
|
1927
1961
|
if (!swap.request?.invoice || swap.request.invoice.length === 0) {
|
|
@@ -1933,9 +1967,7 @@ var SwapManager = class _SwapManager {
|
|
|
1933
1967
|
if (isSubmarineRefundableStatus(swap.status)) {
|
|
1934
1968
|
logger.log(`Auto-refunding submarine swap ${swap.id}`);
|
|
1935
1969
|
await this.executeRefundAction(swap);
|
|
1936
|
-
this.actionExecutedListeners.forEach(
|
|
1937
|
-
(listener) => listener(swap, "refund")
|
|
1938
|
-
);
|
|
1970
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
|
|
1939
1971
|
}
|
|
1940
1972
|
} else if (isPendingChainSwap(swap)) {
|
|
1941
1973
|
if (isChainClaimableStatus(swap.status)) {
|
|
@@ -1985,13 +2017,8 @@ var SwapManager = class _SwapManager {
|
|
|
1985
2017
|
}
|
|
1986
2018
|
}
|
|
1987
2019
|
} catch (error) {
|
|
1988
|
-
logger.error(
|
|
1989
|
-
|
|
1990
|
-
error
|
|
1991
|
-
);
|
|
1992
|
-
this.swapFailedListeners.forEach(
|
|
1993
|
-
(listener) => listener(swap, error)
|
|
1994
|
-
);
|
|
2020
|
+
logger.error(`Failed to execute autonomous action for swap ${swap.id}:`, error);
|
|
2021
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1995
2022
|
} finally {
|
|
1996
2023
|
this.swapsInProgress.delete(swap.id);
|
|
1997
2024
|
}
|
|
@@ -2092,9 +2119,7 @@ var SwapManager = class _SwapManager {
|
|
|
2092
2119
|
logger.log(`Resuming chain refund for swap ${swap.id}`);
|
|
2093
2120
|
await this.executeAutonomousAction(swap);
|
|
2094
2121
|
} else if (isPendingChainSwap(swap) && swap.request.to === "ARK" && isChainSignableStatus(swap.status)) {
|
|
2095
|
-
logger.log(
|
|
2096
|
-
`Resuming server claim signing for swap ${swap.id}`
|
|
2097
|
-
);
|
|
2122
|
+
logger.log(`Resuming server claim signing for swap ${swap.id}`);
|
|
2098
2123
|
await this.executeAutonomousAction(swap);
|
|
2099
2124
|
}
|
|
2100
2125
|
} catch (error) {
|
|
@@ -2153,9 +2178,7 @@ var SwapManager = class _SwapManager {
|
|
|
2153
2178
|
}
|
|
2154
2179
|
async pollSingleSwap(swap) {
|
|
2155
2180
|
try {
|
|
2156
|
-
const statusResponse = await this.swapProvider.getSwapStatus(
|
|
2157
|
-
swap.id
|
|
2158
|
-
);
|
|
2181
|
+
const statusResponse = await this.swapProvider.getSwapStatus(swap.id);
|
|
2159
2182
|
this.notFoundCounts.delete(swap.id);
|
|
2160
2183
|
if (statusResponse.status !== swap.status) {
|
|
2161
2184
|
await this.handleSwapStatusUpdate(swap, statusResponse.status);
|
|
@@ -2166,9 +2189,7 @@ var SwapManager = class _SwapManager {
|
|
|
2166
2189
|
return;
|
|
2167
2190
|
}
|
|
2168
2191
|
if (error instanceof NetworkError && error.statusCode === 429) {
|
|
2169
|
-
logger.warn(
|
|
2170
|
-
`Rate-limited polling swap ${swap.id}, retrying in 2s`
|
|
2171
|
-
);
|
|
2192
|
+
logger.warn(`Rate-limited polling swap ${swap.id}, retrying in 2s`);
|
|
2172
2193
|
const existing = this.pollRetryTimers.get(swap.id);
|
|
2173
2194
|
if (existing) clearTimeout(existing);
|
|
2174
2195
|
this.pollRetryTimers.set(
|
|
@@ -2176,25 +2197,17 @@ var SwapManager = class _SwapManager {
|
|
|
2176
2197
|
setTimeout(async () => {
|
|
2177
2198
|
this.pollRetryTimers.delete(swap.id);
|
|
2178
2199
|
try {
|
|
2179
|
-
const retry = await this.swapProvider.getSwapStatus(
|
|
2180
|
-
swap.id
|
|
2181
|
-
);
|
|
2200
|
+
const retry = await this.swapProvider.getSwapStatus(swap.id);
|
|
2182
2201
|
this.notFoundCounts.delete(swap.id);
|
|
2183
2202
|
if (retry.status !== swap.status) {
|
|
2184
|
-
await this.handleSwapStatusUpdate(
|
|
2185
|
-
swap,
|
|
2186
|
-
retry.status
|
|
2187
|
-
);
|
|
2203
|
+
await this.handleSwapStatusUpdate(swap, retry.status);
|
|
2188
2204
|
}
|
|
2189
2205
|
} catch (retryError) {
|
|
2190
2206
|
if (retryError instanceof SwapNotFoundError) {
|
|
2191
2207
|
await this.handleSwapNotFound(swap);
|
|
2192
2208
|
return;
|
|
2193
2209
|
}
|
|
2194
|
-
logger.error(
|
|
2195
|
-
`Retry poll for swap ${swap.id} also failed:`,
|
|
2196
|
-
retryError
|
|
2197
|
-
);
|
|
2210
|
+
logger.error(`Retry poll for swap ${swap.id} also failed:`, retryError);
|
|
2198
2211
|
}
|
|
2199
2212
|
}, 2e3)
|
|
2200
2213
|
);
|
|
@@ -2246,9 +2259,7 @@ var SwapManager = class _SwapManager {
|
|
|
2246
2259
|
this.pollRetryTimers.delete(swap.id);
|
|
2247
2260
|
}
|
|
2248
2261
|
this.notFoundCounts.delete(swap.id);
|
|
2249
|
-
this.swapUpdateListeners.forEach(
|
|
2250
|
-
(listener) => listener(swap, oldStatus)
|
|
2251
|
-
);
|
|
2262
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
2252
2263
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
2253
2264
|
if (subscribers) {
|
|
2254
2265
|
subscribers.forEach((callback) => {
|
|
@@ -2313,9 +2324,7 @@ async function saveSwap(swap, saver) {
|
|
|
2313
2324
|
if (saver.saveSubmarineSwap) {
|
|
2314
2325
|
await saver.saveSubmarineSwap(swap);
|
|
2315
2326
|
} else {
|
|
2316
|
-
console.warn(
|
|
2317
|
-
"No saveSubmarineSwap handler provided, swap not saved"
|
|
2318
|
-
);
|
|
2327
|
+
console.warn("No saveSubmarineSwap handler provided, swap not saved");
|
|
2319
2328
|
}
|
|
2320
2329
|
} else if (isPendingChainSwap(swap)) {
|
|
2321
2330
|
if (saver.saveChainSwap) {
|
|
@@ -2372,6 +2381,26 @@ function enrichSubmarineSwapInvoice(swap, invoice) {
|
|
|
2372
2381
|
return swap;
|
|
2373
2382
|
}
|
|
2374
2383
|
|
|
2384
|
+
// src/repositories/swap-repository.ts
|
|
2385
|
+
function hasImpossibleSwapsFilter(filter) {
|
|
2386
|
+
if (!filter) return false;
|
|
2387
|
+
return Array.isArray(filter.id) && filter.id.length === 0 || Array.isArray(filter.status) && filter.status.length === 0 || Array.isArray(filter.type) && filter.type.length === 0;
|
|
2388
|
+
}
|
|
2389
|
+
function matchesCriterion(value, criterion) {
|
|
2390
|
+
if (criterion === void 0) return true;
|
|
2391
|
+
return Array.isArray(criterion) ? criterion.includes(value) : value === criterion;
|
|
2392
|
+
}
|
|
2393
|
+
function applySwapsFilter(swaps, filter) {
|
|
2394
|
+
return swaps.filter(
|
|
2395
|
+
(swap) => !!swap && matchesCriterion(swap.id, filter.id) && matchesCriterion(swap.status, filter.status) && matchesCriterion(swap.type, filter.type)
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
function applyCreatedAtOrder(swaps, filter) {
|
|
2399
|
+
if (filter?.orderBy !== "createdAt") return swaps;
|
|
2400
|
+
const direction = filter.orderDirection === "asc" ? 1 : -1;
|
|
2401
|
+
return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2375
2404
|
// src/repositories/IndexedDb/swap-repository.ts
|
|
2376
2405
|
var import_sdk4 = require("@arkade-os/sdk");
|
|
2377
2406
|
var DEFAULT_DB_NAME = "arkade-boltz-swap";
|
|
@@ -2387,6 +2416,10 @@ function initDatabase(db) {
|
|
|
2387
2416
|
swapStore.createIndex("createdAt", "createdAt", { unique: false });
|
|
2388
2417
|
}
|
|
2389
2418
|
}
|
|
2419
|
+
function asArray(v) {
|
|
2420
|
+
if (v === void 0) return void 0;
|
|
2421
|
+
return Array.isArray(v) ? v : [v];
|
|
2422
|
+
}
|
|
2390
2423
|
var IndexedDbSwapRepository = class {
|
|
2391
2424
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
2392
2425
|
this.dbName = dbName;
|
|
@@ -2401,10 +2434,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2401
2434
|
async saveSwap(swap) {
|
|
2402
2435
|
const db = await this.getDB();
|
|
2403
2436
|
return new Promise((resolve, reject) => {
|
|
2404
|
-
const transaction = db.transaction(
|
|
2405
|
-
[STORE_SWAPS_STATE],
|
|
2406
|
-
"readwrite"
|
|
2407
|
-
);
|
|
2437
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2408
2438
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2409
2439
|
const request = store.put(swap);
|
|
2410
2440
|
request.onsuccess = () => resolve();
|
|
@@ -2414,10 +2444,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2414
2444
|
async deleteSwap(id) {
|
|
2415
2445
|
const db = await this.getDB();
|
|
2416
2446
|
return new Promise((resolve, reject) => {
|
|
2417
|
-
const transaction = db.transaction(
|
|
2418
|
-
[STORE_SWAPS_STATE],
|
|
2419
|
-
"readwrite"
|
|
2420
|
-
);
|
|
2447
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2421
2448
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2422
2449
|
const request = store.delete(id);
|
|
2423
2450
|
request.onsuccess = () => resolve();
|
|
@@ -2430,10 +2457,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2430
2457
|
async clear() {
|
|
2431
2458
|
const db = await this.getDB();
|
|
2432
2459
|
return new Promise((resolve, reject) => {
|
|
2433
|
-
const transaction = db.transaction(
|
|
2434
|
-
[STORE_SWAPS_STATE],
|
|
2435
|
-
"readwrite"
|
|
2436
|
-
);
|
|
2460
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2437
2461
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2438
2462
|
const request = store.clear();
|
|
2439
2463
|
request.onsuccess = () => resolve();
|
|
@@ -2450,11 +2474,10 @@ var IndexedDbSwapRepository = class {
|
|
|
2450
2474
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2451
2475
|
})
|
|
2452
2476
|
);
|
|
2453
|
-
return Promise.all(requests).then(
|
|
2454
|
-
(results) => results.flatMap((result) => result)
|
|
2455
|
-
);
|
|
2477
|
+
return Promise.all(requests).then((results) => results.flatMap((result) => result));
|
|
2456
2478
|
}
|
|
2457
2479
|
async getAllSwapsFromStore(filter) {
|
|
2480
|
+
if (hasImpossibleSwapsFilter(filter)) return [];
|
|
2458
2481
|
const db = await this.getDB();
|
|
2459
2482
|
const store = db.transaction([STORE_SWAPS_STATE], "readonly").objectStore(STORE_SWAPS_STATE);
|
|
2460
2483
|
if (!filter || Object.keys(filter).length === 0) {
|
|
@@ -2464,9 +2487,8 @@ var IndexedDbSwapRepository = class {
|
|
|
2464
2487
|
request.onerror = () => reject(request.error);
|
|
2465
2488
|
});
|
|
2466
2489
|
}
|
|
2467
|
-
const
|
|
2468
|
-
if (
|
|
2469
|
-
const ids = normalizedFilter.get("id");
|
|
2490
|
+
const ids = asArray(filter.id);
|
|
2491
|
+
if (ids) {
|
|
2470
2492
|
const swaps = await Promise.all(
|
|
2471
2493
|
ids.map(
|
|
2472
2494
|
(id) => new Promise((resolve, reject) => {
|
|
@@ -2476,34 +2498,17 @@ var IndexedDbSwapRepository = class {
|
|
|
2476
2498
|
})
|
|
2477
2499
|
)
|
|
2478
2500
|
);
|
|
2479
|
-
return
|
|
2480
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2481
|
-
filter
|
|
2482
|
-
);
|
|
2501
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2483
2502
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2487
|
-
|
|
2488
|
-
"type",
|
|
2489
|
-
types
|
|
2490
|
-
);
|
|
2491
|
-
return this.sortIfNeeded(
|
|
2492
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2493
|
-
filter
|
|
2494
|
-
);
|
|
2503
|
+
const types = asArray(filter.type);
|
|
2504
|
+
if (types) {
|
|
2505
|
+
const swaps = await this.getSwapsByIndexValues(store, "type", types);
|
|
2506
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2495
2507
|
}
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2499
|
-
|
|
2500
|
-
"status",
|
|
2501
|
-
ids
|
|
2502
|
-
);
|
|
2503
|
-
return this.sortIfNeeded(
|
|
2504
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2505
|
-
filter
|
|
2506
|
-
);
|
|
2508
|
+
const statuses = asArray(filter.status);
|
|
2509
|
+
if (statuses) {
|
|
2510
|
+
const swaps = await this.getSwapsByIndexValues(store, "status", statuses);
|
|
2511
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2507
2512
|
}
|
|
2508
2513
|
if (filter.orderBy === "createdAt") {
|
|
2509
2514
|
return this.getAllSwapsByCreatedAt(store, filter.orderDirection);
|
|
@@ -2513,22 +2518,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2513
2518
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2514
2519
|
request.onerror = () => reject(request.error);
|
|
2515
2520
|
});
|
|
2516
|
-
return
|
|
2517
|
-
this.applySwapsFilter(allSwaps, normalizedFilter),
|
|
2518
|
-
filter
|
|
2519
|
-
);
|
|
2520
|
-
}
|
|
2521
|
-
applySwapsFilter(swaps, filter) {
|
|
2522
|
-
return swaps.filter((swap) => {
|
|
2523
|
-
if (swap === void 0) return false;
|
|
2524
|
-
if (filter.has("id") && !filter.get("id")?.includes(swap.id))
|
|
2525
|
-
return false;
|
|
2526
|
-
if (filter.has("status") && !filter.get("status")?.includes(swap.status))
|
|
2527
|
-
return false;
|
|
2528
|
-
if (filter.has("type") && !filter.get("type")?.includes(swap.type))
|
|
2529
|
-
return false;
|
|
2530
|
-
return true;
|
|
2531
|
-
});
|
|
2521
|
+
return applyCreatedAtOrder(applySwapsFilter(allSwaps, filter), filter);
|
|
2532
2522
|
}
|
|
2533
2523
|
async getAllSwapsByCreatedAt(store, orderDirection) {
|
|
2534
2524
|
const index = store.index("createdAt");
|
|
@@ -2548,30 +2538,12 @@ var IndexedDbSwapRepository = class {
|
|
|
2548
2538
|
};
|
|
2549
2539
|
});
|
|
2550
2540
|
}
|
|
2551
|
-
sortIfNeeded(swaps, filter) {
|
|
2552
|
-
if (filter?.orderBy !== "createdAt") return swaps;
|
|
2553
|
-
const direction = filter.orderDirection === "asc" ? 1 : -1;
|
|
2554
|
-
return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
|
|
2555
|
-
}
|
|
2556
2541
|
async [Symbol.asyncDispose]() {
|
|
2557
2542
|
if (!this.db) return;
|
|
2558
2543
|
await (0, import_sdk4.closeDatabase)(this.dbName);
|
|
2559
2544
|
this.db = null;
|
|
2560
2545
|
}
|
|
2561
2546
|
};
|
|
2562
|
-
var FILTER_FIELDS = ["id", "status", "type"];
|
|
2563
|
-
function normalizeFilter(filter) {
|
|
2564
|
-
const res = /* @__PURE__ */ new Map();
|
|
2565
|
-
FILTER_FIELDS.forEach((current) => {
|
|
2566
|
-
if (!filter?.[current]) return;
|
|
2567
|
-
if (Array.isArray(filter[current])) {
|
|
2568
|
-
res.set(current, filter[current]);
|
|
2569
|
-
} else {
|
|
2570
|
-
res.set(current, [filter[current]]);
|
|
2571
|
-
}
|
|
2572
|
-
});
|
|
2573
|
-
return res;
|
|
2574
|
-
}
|
|
2575
2547
|
|
|
2576
2548
|
// src/utils/identity.ts
|
|
2577
2549
|
var import_sdk5 = require("@arkade-os/sdk");
|
|
@@ -2583,13 +2555,8 @@ function claimVHTLCIdentity(identity, preimage) {
|
|
|
2583
2555
|
let signedTx = await identity.sign(cpy, inputIndexes);
|
|
2584
2556
|
signedTx = import_sdk5.Transaction.fromPSBT(signedTx.toPSBT());
|
|
2585
2557
|
if (preimage) {
|
|
2586
|
-
for (const inputIndex of inputIndexes || Array.from(
|
|
2587
|
-
|
|
2588
|
-
(_, i) => i
|
|
2589
|
-
)) {
|
|
2590
|
-
(0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [
|
|
2591
|
-
preimage
|
|
2592
|
-
]);
|
|
2558
|
+
for (const inputIndex of inputIndexes || Array.from({ length: signedTx.inputsLength }, (_, i) => i)) {
|
|
2559
|
+
(0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [preimage]);
|
|
2593
2560
|
}
|
|
2594
2561
|
}
|
|
2595
2562
|
return signedTx;
|
|
@@ -2644,17 +2611,13 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2644
2611
|
if (!sweepTapTreeRoot) {
|
|
2645
2612
|
throw new Error("Sweep tap tree root not set");
|
|
2646
2613
|
}
|
|
2647
|
-
const xOnlyPublicKeys = event.cosignersPublicKeys.map(
|
|
2648
|
-
(k) => k.slice(2)
|
|
2649
|
-
);
|
|
2614
|
+
const xOnlyPublicKeys = event.cosignersPublicKeys.map((k) => k.slice(2));
|
|
2650
2615
|
const signerPublicKey = await session.getPublicKey();
|
|
2651
2616
|
const xonlySignerPublicKey = signerPublicKey.subarray(1);
|
|
2652
2617
|
if (!xOnlyPublicKeys.includes(import_base7.hex.encode(xonlySignerPublicKey))) {
|
|
2653
2618
|
return { skip: true };
|
|
2654
2619
|
}
|
|
2655
|
-
const commitmentTx = import_sdk6.Transaction.fromPSBT(
|
|
2656
|
-
import_base7.base64.decode(event.unsignedCommitmentTx)
|
|
2657
|
-
);
|
|
2620
|
+
const commitmentTx = import_sdk6.Transaction.fromPSBT(import_base7.base64.decode(event.unsignedCommitmentTx));
|
|
2658
2621
|
(0, import_sdk6.validateVtxoTxGraph)(vtxoTree, commitmentTx, sweepTapTreeRoot);
|
|
2659
2622
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
2660
2623
|
if (!sharedOutput?.amount) {
|
|
@@ -2670,18 +2633,11 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2670
2633
|
if (!session) {
|
|
2671
2634
|
return { fullySigned: true };
|
|
2672
2635
|
}
|
|
2673
|
-
const { hasAllNonces } = await session.aggregatedNonces(
|
|
2674
|
-
event.txid,
|
|
2675
|
-
event.nonces
|
|
2676
|
-
);
|
|
2636
|
+
const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
|
|
2677
2637
|
if (!hasAllNonces) return { fullySigned: false };
|
|
2678
2638
|
const signatures = await session.sign();
|
|
2679
2639
|
const pubkey = import_base7.hex.encode(await session.getPublicKey());
|
|
2680
|
-
await arkProvider.submitTreeSignatures(
|
|
2681
|
-
event.id,
|
|
2682
|
-
pubkey,
|
|
2683
|
-
signatures
|
|
2684
|
-
);
|
|
2640
|
+
await arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
|
|
2685
2641
|
return { fullySigned: true };
|
|
2686
2642
|
},
|
|
2687
2643
|
onBatchFinalization: async (event, _, connectorTree) => {
|
|
@@ -2689,9 +2645,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2689
2645
|
return;
|
|
2690
2646
|
}
|
|
2691
2647
|
if (!connectorTree) {
|
|
2692
|
-
throw new Error(
|
|
2693
|
-
"BatchFinalizationEvent: expected connector tree to be defined"
|
|
2694
|
-
);
|
|
2648
|
+
throw new Error("BatchFinalizationEvent: expected connector tree to be defined");
|
|
2695
2649
|
}
|
|
2696
2650
|
(0, import_sdk6.validateConnectorsTxGraph)(event.commitmentTx, connectorTree);
|
|
2697
2651
|
const connectors = connectorTree.leaves();
|
|
@@ -2706,9 +2660,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2706
2660
|
connectors[connectorIndex]
|
|
2707
2661
|
);
|
|
2708
2662
|
const signedForfeitTx = await identity.sign(forfeitTx);
|
|
2709
|
-
await arkProvider.submitSignedForfeitTxs([
|
|
2710
|
-
import_base7.base64.encode(signedForfeitTx.toPSBT())
|
|
2711
|
-
]);
|
|
2663
|
+
await arkProvider.submitSignedForfeitTxs([import_base7.base64.encode(signedForfeitTx.toPSBT())]);
|
|
2712
2664
|
}
|
|
2713
2665
|
};
|
|
2714
2666
|
}
|
|
@@ -2768,36 +2720,22 @@ var createVHTLCScript = (args) => {
|
|
|
2768
2720
|
serverPubkey,
|
|
2769
2721
|
timeoutBlockHeights: vhtlcTimeouts
|
|
2770
2722
|
} = args;
|
|
2771
|
-
const receiverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
);
|
|
2775
|
-
const senderXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2776
|
-
import_base8.hex.decode(senderPubkey),
|
|
2777
|
-
"sender"
|
|
2778
|
-
);
|
|
2779
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2780
|
-
import_base8.hex.decode(serverPubkey),
|
|
2781
|
-
"server"
|
|
2782
|
-
);
|
|
2723
|
+
const receiverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(receiverPubkey), "receiver");
|
|
2724
|
+
const senderXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(senderPubkey), "sender");
|
|
2725
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(serverPubkey), "server");
|
|
2783
2726
|
const vhtlcScript = new import_sdk7.VHTLC.Script({
|
|
2784
2727
|
preimageHash: (0, import_legacy.ripemd160)(preimageHash),
|
|
2785
2728
|
sender: senderXOnlyPublicKey,
|
|
2786
2729
|
receiver: receiverXOnlyPublicKey,
|
|
2787
2730
|
server: serverXOnlyPublicKey,
|
|
2788
2731
|
refundLocktime: BigInt(vhtlcTimeouts.refund),
|
|
2789
|
-
unilateralClaimDelay: toBip68RelativeTimelock(
|
|
2790
|
-
|
|
2791
|
-
),
|
|
2792
|
-
unilateralRefundDelay: toBip68RelativeTimelock(
|
|
2793
|
-
vhtlcTimeouts.unilateralRefund
|
|
2794
|
-
),
|
|
2732
|
+
unilateralClaimDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralClaim),
|
|
2733
|
+
unilateralRefundDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralRefund),
|
|
2795
2734
|
unilateralRefundWithoutReceiverDelay: toBip68RelativeTimelock(
|
|
2796
2735
|
vhtlcTimeouts.unilateralRefundWithoutReceiver
|
|
2797
2736
|
)
|
|
2798
2737
|
});
|
|
2799
|
-
if (!vhtlcScript.claimScript)
|
|
2800
|
-
throw new Error("Failed to create VHTLC script");
|
|
2738
|
+
if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
|
|
2801
2739
|
const hrp = network === "bitcoin" ? "ark" : "tark";
|
|
2802
2740
|
const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
|
|
2803
2741
|
return { vhtlcScript, vhtlcAddress };
|
|
@@ -2831,11 +2769,7 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2831
2769
|
unknown: [import_sdk7.VtxoTaprootTree.encode(input.tapTree)],
|
|
2832
2770
|
sequence: (0, import_sdk7.getSequence)(input.tapLeafScript)
|
|
2833
2771
|
};
|
|
2834
|
-
const registerIntent = import_sdk7.Intent.create(
|
|
2835
|
-
intentMessage,
|
|
2836
|
-
[intentInput],
|
|
2837
|
-
[output]
|
|
2838
|
-
);
|
|
2772
|
+
const registerIntent = import_sdk7.Intent.create(intentMessage, [intentInput], [output]);
|
|
2839
2773
|
const deleteIntent = import_sdk7.Intent.create(deleteMessage, [intentInput]);
|
|
2840
2774
|
const [signedRegisterIntent, signedDeleteIntent] = await Promise.all([
|
|
2841
2775
|
identity.sign(registerIntent),
|
|
@@ -2859,14 +2793,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2859
2793
|
normalizeToXOnlyKey(forfeitPubkey, "forfeit"),
|
|
2860
2794
|
isRecoverable2 ? void 0 : import_btc_signer4.OutScript.encode(decodedAddress)
|
|
2861
2795
|
);
|
|
2862
|
-
const topics = [
|
|
2863
|
-
|
|
2864
|
-
`${input.txid}:${input.vout}`
|
|
2865
|
-
];
|
|
2866
|
-
const eventStream = arkProvider.getEventStream(
|
|
2867
|
-
abortController.signal,
|
|
2868
|
-
topics
|
|
2869
|
-
);
|
|
2796
|
+
const topics = [import_base8.hex.encode(signerPublicKey), `${input.txid}:${input.vout}`];
|
|
2797
|
+
const eventStream = arkProvider.getEventStream(abortController.signal, topics);
|
|
2870
2798
|
const commitmentTxid = await import_sdk7.Batch.join(eventStream, handler, {
|
|
2871
2799
|
abortController
|
|
2872
2800
|
});
|
|
@@ -2887,14 +2815,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2887
2815
|
};
|
|
2888
2816
|
var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKey, input, output, arkInfo, arkProvider) => {
|
|
2889
2817
|
const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
|
|
2890
|
-
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
|
|
2891
|
-
|
|
2892
|
-
);
|
|
2893
|
-
const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)(
|
|
2894
|
-
[input],
|
|
2895
|
-
[output],
|
|
2896
|
-
serverUnrollScript
|
|
2897
|
-
);
|
|
2818
|
+
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
|
|
2819
|
+
const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
|
|
2898
2820
|
const signedArkTx = await identity.sign(arkTx);
|
|
2899
2821
|
const { arkTxid, finalArkTx, signedCheckpointTxs } = await arkProvider.submitTx(
|
|
2900
2822
|
import_base8.base64.encode(signedArkTx.toPSBT()),
|
|
@@ -2902,9 +2824,7 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2902
2824
|
);
|
|
2903
2825
|
const finalTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(finalArkTx));
|
|
2904
2826
|
const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
|
|
2905
|
-
const claimLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2906
|
-
scriptFromTapLeafScript(vhtlcScript.claim())
|
|
2907
|
-
);
|
|
2827
|
+
const claimLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(vhtlcScript.claim()));
|
|
2908
2828
|
for (let i = 0; i < finalTx.inputsLength; i++) {
|
|
2909
2829
|
if (!verifySignatures(finalTx, i, [serverPubkeyHex], claimLeafHash)) {
|
|
2910
2830
|
throw new Error("Invalid final Ark transaction");
|
|
@@ -2914,13 +2834,9 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2914
2834
|
signedCheckpointTxs.map(async (c, idx) => {
|
|
2915
2835
|
const tx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(c));
|
|
2916
2836
|
const checkpointLeaf = checkpoints[idx].getInput(0).tapLeafScript[0];
|
|
2917
|
-
const cpLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2918
|
-
scriptFromTapLeafScript(checkpointLeaf)
|
|
2919
|
-
);
|
|
2837
|
+
const cpLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
|
|
2920
2838
|
if (!verifySignatures(tx, 0, [serverPubkeyHex], cpLeafHash)) {
|
|
2921
|
-
throw new Error(
|
|
2922
|
-
"Invalid server signature in checkpoint transaction"
|
|
2923
|
-
);
|
|
2839
|
+
throw new Error("Invalid server signature in checkpoint transaction");
|
|
2924
2840
|
}
|
|
2925
2841
|
const signedCheckpoint = await identity.sign(tx, [0]);
|
|
2926
2842
|
return import_base8.base64.encode(signedCheckpoint.toPSBT());
|
|
@@ -2930,61 +2846,37 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2930
2846
|
};
|
|
2931
2847
|
var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnlyPublicKey, ourXOnlyPublicKey, serverXOnlyPublicKey, input, output, arkInfo, refundFunc) => {
|
|
2932
2848
|
const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
|
|
2933
|
-
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
|
|
2934
|
-
|
|
2849
|
+
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
|
|
2850
|
+
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)(
|
|
2851
|
+
[input],
|
|
2852
|
+
[output],
|
|
2853
|
+
serverUnrollScript
|
|
2935
2854
|
);
|
|
2936
|
-
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
|
|
2937
2855
|
if (checkpointPtxs.length !== 1)
|
|
2938
|
-
throw new Error(
|
|
2939
|
-
`Expected one checkpoint transaction, got ${checkpointPtxs.length}`
|
|
2940
|
-
);
|
|
2856
|
+
throw new Error(`Expected one checkpoint transaction, got ${checkpointPtxs.length}`);
|
|
2941
2857
|
const unsignedCheckpointTx = checkpointPtxs[0];
|
|
2942
2858
|
let boltzSignedRefundTx;
|
|
2943
2859
|
let boltzSignedCheckpointTx;
|
|
2944
2860
|
try {
|
|
2945
|
-
const result = await refundFunc(
|
|
2946
|
-
swapId,
|
|
2947
|
-
unsignedRefundTx,
|
|
2948
|
-
unsignedCheckpointTx
|
|
2949
|
-
);
|
|
2861
|
+
const result = await refundFunc(swapId, unsignedRefundTx, unsignedCheckpointTx);
|
|
2950
2862
|
boltzSignedRefundTx = result.transaction;
|
|
2951
2863
|
boltzSignedCheckpointTx = result.checkpoint;
|
|
2952
2864
|
} catch (error) {
|
|
2953
|
-
throw new BoltzRefundError(
|
|
2954
|
-
`Boltz rejected refund for swap ${swapId}`,
|
|
2955
|
-
error
|
|
2956
|
-
);
|
|
2865
|
+
throw new BoltzRefundError(`Boltz rejected refund for swap ${swapId}`, error);
|
|
2957
2866
|
}
|
|
2958
2867
|
const boltzXOnlyPublicKeyHex = import_base8.hex.encode(boltzXOnlyPublicKey);
|
|
2959
|
-
const refundLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2960
|
-
|
|
2961
|
-
);
|
|
2962
|
-
if (!verifySignatures(
|
|
2963
|
-
boltzSignedRefundTx,
|
|
2964
|
-
0,
|
|
2965
|
-
[boltzXOnlyPublicKeyHex],
|
|
2966
|
-
refundLeafHash
|
|
2967
|
-
)) {
|
|
2868
|
+
const refundLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(input.tapLeafScript));
|
|
2869
|
+
if (!verifySignatures(boltzSignedRefundTx, 0, [boltzXOnlyPublicKeyHex], refundLeafHash)) {
|
|
2968
2870
|
throw new Error("Invalid Boltz signature in refund transaction");
|
|
2969
2871
|
}
|
|
2970
2872
|
const checkpointLeaf = unsignedCheckpointTx.getInput(0).tapLeafScript[0];
|
|
2971
|
-
const checkpointLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2972
|
-
|
|
2973
|
-
);
|
|
2974
|
-
if (!verifySignatures(
|
|
2975
|
-
boltzSignedCheckpointTx,
|
|
2976
|
-
0,
|
|
2977
|
-
[boltzXOnlyPublicKeyHex],
|
|
2978
|
-
checkpointLeafHash
|
|
2979
|
-
)) {
|
|
2873
|
+
const checkpointLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
|
|
2874
|
+
if (!verifySignatures(boltzSignedCheckpointTx, 0, [boltzXOnlyPublicKeyHex], checkpointLeafHash)) {
|
|
2980
2875
|
throw new Error("Invalid Boltz signature in checkpoint transaction");
|
|
2981
2876
|
}
|
|
2982
2877
|
const signedRefundTx = await identity.sign(unsignedRefundTx);
|
|
2983
2878
|
const signedCheckpointTx = await identity.sign(unsignedCheckpointTx);
|
|
2984
|
-
const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
2985
|
-
boltzSignedRefundTx,
|
|
2986
|
-
signedRefundTx
|
|
2987
|
-
);
|
|
2879
|
+
const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(boltzSignedRefundTx, signedRefundTx);
|
|
2988
2880
|
const combinedSignedCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
2989
2881
|
boltzSignedCheckpointTx,
|
|
2990
2882
|
signedCheckpointTx
|
|
@@ -3008,25 +2900,16 @@ var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnly
|
|
|
3008
2900
|
`Expected one signed checkpoint transaction, got ${signedCheckpointTxs.length}`
|
|
3009
2901
|
);
|
|
3010
2902
|
}
|
|
3011
|
-
const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(
|
|
3012
|
-
import_base8.base64.decode(signedCheckpointTxs[0])
|
|
3013
|
-
);
|
|
2903
|
+
const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(signedCheckpointTxs[0]));
|
|
3014
2904
|
const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
|
|
3015
|
-
if (!verifySignatures(
|
|
3016
|
-
serverSignedCheckpointTx,
|
|
3017
|
-
0,
|
|
3018
|
-
[serverPubkeyHex],
|
|
3019
|
-
checkpointLeafHash
|
|
3020
|
-
)) {
|
|
2905
|
+
if (!verifySignatures(serverSignedCheckpointTx, 0, [serverPubkeyHex], checkpointLeafHash)) {
|
|
3021
2906
|
throw new Error("Invalid server signature in checkpoint transaction");
|
|
3022
2907
|
}
|
|
3023
2908
|
const finalCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
3024
2909
|
combinedSignedCheckpointTx,
|
|
3025
2910
|
serverSignedCheckpointTx
|
|
3026
2911
|
);
|
|
3027
|
-
await arkProvider.finalizeTx(arkTxid, [
|
|
3028
|
-
import_base8.base64.encode(finalCheckpointTx.toPSBT())
|
|
3029
|
-
]);
|
|
2912
|
+
await arkProvider.finalizeTx(arkTxid, [import_base8.base64.encode(finalCheckpointTx.toPSBT())]);
|
|
3030
2913
|
};
|
|
3031
2914
|
function scriptFromTapLeafScript(leaf) {
|
|
3032
2915
|
return leaf[1].subarray(0, leaf[1].length - 1);
|
|
@@ -3034,21 +2917,21 @@ function scriptFromTapLeafScript(leaf) {
|
|
|
3034
2917
|
|
|
3035
2918
|
// src/arkade-swaps.ts
|
|
3036
2919
|
var dedupeVtxos = (vtxos) => [
|
|
3037
|
-
...new Map(
|
|
3038
|
-
vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])
|
|
3039
|
-
).values()
|
|
2920
|
+
...new Map(vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])).values()
|
|
3040
2921
|
];
|
|
3041
2922
|
var hasNonEmptyString = (value) => typeof value === "string" && value.length > 0;
|
|
3042
2923
|
var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
|
|
3043
2924
|
const hasRequiredSwapMetadata = hasNonEmptyString(swap.id) && hasNonEmptyString(swap.request.refundPublicKey) && hasNonEmptyString(swap.response.address) && hasNonEmptyString(swap.response.claimPublicKey) && !!swap.response.timeoutBlockHeights;
|
|
3044
2925
|
if (!hasRequiredSwapMetadata) return false;
|
|
3045
|
-
return refundableVtxos.some(
|
|
3046
|
-
(vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo)
|
|
3047
|
-
);
|
|
2926
|
+
return refundableVtxos.some((vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo));
|
|
3048
2927
|
};
|
|
3049
2928
|
var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
|
|
3050
2929
|
var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
|
|
3051
2930
|
var CLAIM_VTXO_RETRY_DELAY_MS = 500;
|
|
2931
|
+
var quoteOptionsForSwap = (swap) => {
|
|
2932
|
+
const amount = swap.response?.claimDetails?.amount;
|
|
2933
|
+
return typeof amount === "number" ? { minAcceptableAmount: amount } : void 0;
|
|
2934
|
+
};
|
|
3052
2935
|
var ArkadeSwaps = class _ArkadeSwaps {
|
|
3053
2936
|
/** The Arkade wallet instance used for signing and address generation. */
|
|
3054
2937
|
wallet;
|
|
@@ -3084,10 +2967,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3084
2967
|
return new _ArkadeSwaps(config);
|
|
3085
2968
|
}
|
|
3086
2969
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3087
|
-
if (!arkProvider)
|
|
3088
|
-
throw new Error(
|
|
3089
|
-
"Ark provider is required either in wallet or config."
|
|
3090
|
-
);
|
|
2970
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3091
2971
|
const arkInfo = await arkProvider.getInfo();
|
|
3092
2972
|
const network = arkInfo.network;
|
|
3093
2973
|
const swapProvider = new BoltzSwapProvider({ network });
|
|
@@ -3098,16 +2978,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3098
2978
|
if (!config.swapProvider) throw new Error("Swap provider is required.");
|
|
3099
2979
|
this.wallet = config.wallet;
|
|
3100
2980
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3101
|
-
if (!arkProvider)
|
|
3102
|
-
throw new Error(
|
|
3103
|
-
"Ark provider is required either in wallet or config."
|
|
3104
|
-
);
|
|
2981
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3105
2982
|
this.arkProvider = arkProvider;
|
|
3106
2983
|
const indexerProvider = config.indexerProvider ?? config.wallet.indexerProvider;
|
|
3107
2984
|
if (!indexerProvider)
|
|
3108
|
-
throw new Error(
|
|
3109
|
-
"Indexer provider is required either in wallet or config."
|
|
3110
|
-
);
|
|
2985
|
+
throw new Error("Indexer provider is required either in wallet or config.");
|
|
3111
2986
|
this.indexerProvider = indexerProvider;
|
|
3112
2987
|
this.swapProvider = config.swapProvider;
|
|
3113
2988
|
if (config.swapRepository) {
|
|
@@ -3118,10 +2993,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3118
2993
|
if (config.swapManager !== false) {
|
|
3119
2994
|
const swapManagerConfig = !config.swapManager || config.swapManager === true ? {} : config.swapManager;
|
|
3120
2995
|
const shouldAutostart = swapManagerConfig.autoStart ?? true;
|
|
3121
|
-
this.swapManager = new SwapManager(
|
|
3122
|
-
this.swapProvider,
|
|
3123
|
-
swapManagerConfig
|
|
3124
|
-
);
|
|
2996
|
+
this.swapManager = new SwapManager(this.swapProvider, swapManagerConfig);
|
|
3125
2997
|
this.swapManager.setCallbacks({
|
|
3126
2998
|
claim: async (swap) => {
|
|
3127
2999
|
await this.claimVHTLC(swap);
|
|
@@ -3269,19 +3141,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3269
3141
|
* @throws {SwapError} If amount is <= 0 or key retrieval fails.
|
|
3270
3142
|
*/
|
|
3271
3143
|
async createReverseSwap(args) {
|
|
3272
|
-
if (args.amount <= 0)
|
|
3273
|
-
|
|
3274
|
-
const claimPublicKey = import_base9.hex.encode(
|
|
3275
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3276
|
-
);
|
|
3144
|
+
if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
|
|
3145
|
+
const claimPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
3277
3146
|
if (!claimPublicKey)
|
|
3278
3147
|
throw new SwapError({
|
|
3279
3148
|
message: "Failed to get claim public key from wallet"
|
|
3280
3149
|
});
|
|
3281
3150
|
const preimage = (0, import_utils3.randomBytes)(32);
|
|
3282
3151
|
const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
|
|
3283
|
-
if (!preimageHash)
|
|
3284
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3152
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3285
3153
|
const swapRequest = {
|
|
3286
3154
|
invoiceAmount: args.amount,
|
|
3287
3155
|
claimPublicKey,
|
|
@@ -3311,18 +3179,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3311
3179
|
*/
|
|
3312
3180
|
async claimVHTLC(pendingSwap) {
|
|
3313
3181
|
if (!pendingSwap.preimage)
|
|
3314
|
-
throw new Error(
|
|
3315
|
-
`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`
|
|
3316
|
-
);
|
|
3182
|
+
throw new Error(`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`);
|
|
3317
3183
|
const {
|
|
3318
3184
|
refundPublicKey,
|
|
3319
3185
|
lockupAddress,
|
|
3320
3186
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3321
3187
|
} = pendingSwap.response;
|
|
3322
3188
|
if (!refundPublicKey || !lockupAddress || !vhtlcTimeouts)
|
|
3323
|
-
throw new Error(
|
|
3324
|
-
`Swap ${pendingSwap.id}: incomplete reverse swap response`
|
|
3325
|
-
);
|
|
3189
|
+
throw new Error(`Swap ${pendingSwap.id}: incomplete reverse swap response`);
|
|
3326
3190
|
const preimage = import_base9.hex.decode(pendingSwap.preimage);
|
|
3327
3191
|
const arkInfo = await this.arkProvider.getInfo();
|
|
3328
3192
|
const address = await this.wallet.getAddress();
|
|
@@ -3367,15 +3231,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3367
3231
|
break;
|
|
3368
3232
|
}
|
|
3369
3233
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
3370
|
-
await new Promise(
|
|
3371
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
3372
|
-
);
|
|
3234
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
3373
3235
|
}
|
|
3374
3236
|
}
|
|
3375
3237
|
if (!vtxo) {
|
|
3376
|
-
throw new Error(
|
|
3377
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
3378
|
-
);
|
|
3238
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
3379
3239
|
}
|
|
3380
3240
|
if (vtxo.isSpent) {
|
|
3381
3241
|
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
@@ -3389,10 +3249,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3389
3249
|
amount: BigInt(vtxo.value),
|
|
3390
3250
|
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
3391
3251
|
};
|
|
3392
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
3393
|
-
this.wallet.identity,
|
|
3394
|
-
preimage
|
|
3395
|
-
);
|
|
3252
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
3396
3253
|
let finalStatus;
|
|
3397
3254
|
if ((0, import_sdk8.isRecoverable)(vtxo)) {
|
|
3398
3255
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
@@ -3511,9 +3368,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3511
3368
|
async sendLightningPayment(args) {
|
|
3512
3369
|
const pendingSwap = await this.createSubmarineSwap(args);
|
|
3513
3370
|
if (!pendingSwap.response.address)
|
|
3514
|
-
throw new Error(
|
|
3515
|
-
`Swap ${pendingSwap.id}: missing address in submarine swap response`
|
|
3516
|
-
);
|
|
3371
|
+
throw new Error(`Swap ${pendingSwap.id}: missing address in submarine swap response`);
|
|
3517
3372
|
await this.savePendingSubmarineSwap(pendingSwap);
|
|
3518
3373
|
const txid = await this.wallet.send({
|
|
3519
3374
|
address: pendingSwap.response.address,
|
|
@@ -3546,9 +3401,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3546
3401
|
* @throws {SwapError} If invoice is missing or key retrieval fails.
|
|
3547
3402
|
*/
|
|
3548
3403
|
async createSubmarineSwap(args) {
|
|
3549
|
-
const refundPublicKey = import_base9.hex.encode(
|
|
3550
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3551
|
-
);
|
|
3404
|
+
const refundPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
3552
3405
|
if (!refundPublicKey)
|
|
3553
3406
|
throw new SwapError({
|
|
3554
3407
|
message: "Failed to get refund public key from wallet"
|
|
@@ -3586,9 +3439,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3586
3439
|
async buildSubmarineVHTLCContext(swap, arkInfo) {
|
|
3587
3440
|
const preimageHash = swap.request.invoice ? getInvoicePaymentHash(swap.request.invoice) : swap.preimageHash;
|
|
3588
3441
|
if (!preimageHash)
|
|
3589
|
-
throw new Error(
|
|
3590
|
-
`Swap ${swap.id}: preimage hash is required to refund VHTLC`
|
|
3591
|
-
);
|
|
3442
|
+
throw new Error(`Swap ${swap.id}: preimage hash is required to refund VHTLC`);
|
|
3592
3443
|
const resolvedArkInfo = arkInfo ?? await this.arkProvider.getInfo();
|
|
3593
3444
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3594
3445
|
await this.wallet.identity.xOnlyPublicKey(),
|
|
@@ -3602,9 +3453,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3602
3453
|
);
|
|
3603
3454
|
const { claimPublicKey, timeoutBlockHeights: vhtlcTimeouts } = swap.response;
|
|
3604
3455
|
if (!claimPublicKey || !vhtlcTimeouts)
|
|
3605
|
-
throw new Error(
|
|
3606
|
-
`Swap ${swap.id}: incomplete submarine swap response`
|
|
3607
|
-
);
|
|
3456
|
+
throw new Error(`Swap ${swap.id}: incomplete submarine swap response`);
|
|
3608
3457
|
const boltzXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3609
3458
|
import_base9.hex.decode(claimPublicKey),
|
|
3610
3459
|
"boltz",
|
|
@@ -3619,9 +3468,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3619
3468
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3620
3469
|
});
|
|
3621
3470
|
if (!vhtlcScript.claimScript)
|
|
3622
|
-
throw new Error(
|
|
3623
|
-
`Swap ${swap.id}: failed to create VHTLC script for submarine swap`
|
|
3624
|
-
);
|
|
3471
|
+
throw new Error(`Swap ${swap.id}: failed to create VHTLC script for submarine swap`);
|
|
3625
3472
|
if (vhtlcAddress !== swap.response.address)
|
|
3626
3473
|
throw new Error(
|
|
3627
3474
|
`VHTLC address mismatch for swap ${swap.id}: expected ${swap.response.address}, got ${vhtlcAddress}`
|
|
@@ -3660,10 +3507,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3660
3507
|
recoverableOnly: true
|
|
3661
3508
|
})
|
|
3662
3509
|
]);
|
|
3663
|
-
const refundableVtxos = dedupeVtxos([
|
|
3664
|
-
...spendableResult.vtxos,
|
|
3665
|
-
...recoverableResult.vtxos
|
|
3666
|
-
]);
|
|
3510
|
+
const refundableVtxos = dedupeVtxos([...spendableResult.vtxos, ...recoverableResult.vtxos]);
|
|
3667
3511
|
let diagnostic;
|
|
3668
3512
|
if (refundableVtxos.length === 0) {
|
|
3669
3513
|
const { vtxos: allVtxos } = await this.indexerProvider.getVtxos({
|
|
@@ -3683,13 +3527,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3683
3527
|
submarineRecoveryInfoFromLookup(swap, lookup) {
|
|
3684
3528
|
const { refundableVtxos, diagnostic, vhtlcTimeouts } = lookup;
|
|
3685
3529
|
if (refundableVtxos.length > 0) {
|
|
3686
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3687
|
-
|
|
3688
|
-
);
|
|
3689
|
-
const amountSats = refundableVtxos.reduce(
|
|
3690
|
-
(sum, vtxo) => sum + Number(vtxo.value),
|
|
3691
|
-
0
|
|
3692
|
-
);
|
|
3530
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3531
|
+
const amountSats = refundableVtxos.reduce((sum, vtxo) => sum + Number(vtxo.value), 0);
|
|
3693
3532
|
const isRecoverable2 = cltvSatisfied || canRecoverViaBoltz3of3(refundableVtxos, swap);
|
|
3694
3533
|
return {
|
|
3695
3534
|
swap,
|
|
@@ -3753,19 +3592,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3753
3592
|
);
|
|
3754
3593
|
}
|
|
3755
3594
|
if (diagnostic.allSpent) {
|
|
3756
|
-
throw new Error(
|
|
3757
|
-
`Swap ${pendingSwap.id}: VHTLC is already spent`
|
|
3758
|
-
);
|
|
3595
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
3759
3596
|
}
|
|
3760
|
-
throw new Error(
|
|
3761
|
-
`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`
|
|
3762
|
-
);
|
|
3597
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`);
|
|
3763
3598
|
}
|
|
3764
3599
|
const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
|
|
3765
3600
|
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
3766
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3767
|
-
vhtlcTimeouts.refund
|
|
3768
|
-
);
|
|
3601
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3769
3602
|
let boltzCallCount = 0;
|
|
3770
3603
|
let sweptCount = 0;
|
|
3771
3604
|
let skippedCount = 0;
|
|
@@ -3817,9 +3650,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3817
3650
|
input,
|
|
3818
3651
|
output,
|
|
3819
3652
|
arkInfo,
|
|
3820
|
-
this.swapProvider.refundSubmarineSwap.bind(
|
|
3821
|
-
this.swapProvider
|
|
3822
|
-
)
|
|
3653
|
+
this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3823
3654
|
);
|
|
3824
3655
|
boltzCallCount++;
|
|
3825
3656
|
sweptCount++;
|
|
@@ -3842,13 +3673,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3842
3673
|
tapLeafScript: refundWithoutReceiverLeaf,
|
|
3843
3674
|
tapTree: vhtlcScript.encode()
|
|
3844
3675
|
};
|
|
3845
|
-
await this.joinBatch(
|
|
3846
|
-
this.wallet.identity,
|
|
3847
|
-
fallbackInput,
|
|
3848
|
-
output,
|
|
3849
|
-
arkInfo,
|
|
3850
|
-
false
|
|
3851
|
-
);
|
|
3676
|
+
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
3852
3677
|
sweptCount++;
|
|
3853
3678
|
}
|
|
3854
3679
|
}
|
|
@@ -3944,10 +3769,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3944
3769
|
try {
|
|
3945
3770
|
return {
|
|
3946
3771
|
swap,
|
|
3947
|
-
context: await this.buildSubmarineVHTLCContext(
|
|
3948
|
-
swap,
|
|
3949
|
-
arkInfo
|
|
3950
|
-
)
|
|
3772
|
+
context: await this.buildSubmarineVHTLCContext(swap, arkInfo)
|
|
3951
3773
|
};
|
|
3952
3774
|
} catch (err) {
|
|
3953
3775
|
return {
|
|
@@ -3960,9 +3782,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3960
3782
|
const valid = prepared.filter(
|
|
3961
3783
|
(item) => "context" in item
|
|
3962
3784
|
);
|
|
3963
|
-
const scripts = [
|
|
3964
|
-
...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))
|
|
3965
|
-
];
|
|
3785
|
+
const scripts = [...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))];
|
|
3966
3786
|
const refundableByScript = /* @__PURE__ */ new Map();
|
|
3967
3787
|
if (scripts.length > 0) {
|
|
3968
3788
|
const [spendableResult, recoverableResult] = await Promise.all([
|
|
@@ -3997,9 +3817,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3997
3817
|
error: item.error
|
|
3998
3818
|
};
|
|
3999
3819
|
}
|
|
4000
|
-
const refundableVtxos = refundableByScript.get(
|
|
4001
|
-
item.context.vhtlcPkScriptHex.toLowerCase()
|
|
4002
|
-
) ?? [];
|
|
3820
|
+
const refundableVtxos = refundableByScript.get(item.context.vhtlcPkScriptHex.toLowerCase()) ?? [];
|
|
4003
3821
|
return this.submarineRecoveryInfoFromLookup(item.swap, {
|
|
4004
3822
|
...item.context,
|
|
4005
3823
|
refundableVtxos
|
|
@@ -4178,9 +3996,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4178
3996
|
*/
|
|
4179
3997
|
async waitAndClaimBtc(pendingSwap) {
|
|
4180
3998
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4181
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4182
|
-
pendingSwap.id
|
|
4183
|
-
);
|
|
3999
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4184
4000
|
return { txid };
|
|
4185
4001
|
}
|
|
4186
4002
|
return new Promise((resolve, reject) => {
|
|
@@ -4207,24 +4023,25 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4207
4023
|
}
|
|
4208
4024
|
case "transaction.claimed":
|
|
4209
4025
|
await updateSwapStatus();
|
|
4210
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4211
|
-
pendingSwap.id
|
|
4212
|
-
);
|
|
4026
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4213
4027
|
resolve({
|
|
4214
4028
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4215
4029
|
});
|
|
4216
4030
|
break;
|
|
4217
4031
|
case "transaction.lockupFailed":
|
|
4218
4032
|
await updateSwapStatus();
|
|
4219
|
-
await this.quoteSwap(swap.response.id).catch(
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4033
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4034
|
+
(err) => {
|
|
4035
|
+
reject(
|
|
4036
|
+
new SwapError({
|
|
4037
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4038
|
+
isRefundable: true,
|
|
4039
|
+
pendingSwap: swap,
|
|
4040
|
+
cause: err
|
|
4041
|
+
})
|
|
4042
|
+
);
|
|
4043
|
+
}
|
|
4044
|
+
);
|
|
4228
4045
|
break;
|
|
4229
4046
|
case "swap.expired":
|
|
4230
4047
|
await updateSwapStatus();
|
|
@@ -4262,30 +4079,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4262
4079
|
*/
|
|
4263
4080
|
async claimBtc(pendingSwap) {
|
|
4264
4081
|
if (!pendingSwap.toAddress)
|
|
4265
|
-
throw new Error(
|
|
4266
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4267
|
-
);
|
|
4082
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4268
4083
|
if (!pendingSwap.response.claimDetails.swapTree)
|
|
4269
|
-
throw new Error(
|
|
4270
|
-
`Swap ${pendingSwap.id}: missing swap tree in claim details`
|
|
4271
|
-
);
|
|
4084
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in claim details`);
|
|
4272
4085
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4273
|
-
throw new Error(
|
|
4274
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4275
|
-
);
|
|
4086
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4276
4087
|
const swapStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4277
4088
|
if (!swapStatus.transaction?.hex)
|
|
4278
|
-
throw new Error(
|
|
4279
|
-
|
|
4280
|
-
);
|
|
4281
|
-
const lockupTx = import_btc_signer5.Transaction.fromRaw(
|
|
4282
|
-
import_base9.hex.decode(swapStatus.transaction.hex)
|
|
4283
|
-
);
|
|
4089
|
+
throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
|
|
4090
|
+
const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
|
|
4284
4091
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4285
4092
|
const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
|
|
4286
|
-
const swapTree = deserializeSwapTree(
|
|
4287
|
-
pendingSwap.response.claimDetails.swapTree
|
|
4288
|
-
);
|
|
4093
|
+
const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
|
|
4289
4094
|
const musig = tweakMusig(
|
|
4290
4095
|
create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
|
|
4291
4096
|
import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
@@ -4306,19 +4111,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4306
4111
|
vout: swapOutput.vout,
|
|
4307
4112
|
transactionId: lockupTx.id
|
|
4308
4113
|
},
|
|
4309
|
-
import_btc_signer5.OutScript.encode(
|
|
4310
|
-
(0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)
|
|
4311
|
-
),
|
|
4114
|
+
import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)),
|
|
4312
4115
|
feeToDeliverExactAmount > fee ? feeToDeliverExactAmount : fee
|
|
4313
4116
|
)
|
|
4314
4117
|
);
|
|
4315
4118
|
const musigMessage = musig.message(
|
|
4316
|
-
claimTx.preimageWitnessV1(
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
import_btc_signer5.SigHash.DEFAULT,
|
|
4320
|
-
[swapOutput.amount]
|
|
4321
|
-
)
|
|
4119
|
+
claimTx.preimageWitnessV1(0, [swapOutput.script], import_btc_signer5.SigHash.DEFAULT, [
|
|
4120
|
+
swapOutput.amount
|
|
4121
|
+
])
|
|
4322
4122
|
).generateNonce();
|
|
4323
4123
|
const signedTxData = await this.swapProvider.postChainClaimDetails(
|
|
4324
4124
|
pendingSwap.response.id,
|
|
@@ -4332,14 +4132,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4332
4132
|
}
|
|
4333
4133
|
);
|
|
4334
4134
|
if (!signedTxData.pubNonce || !signedTxData.partialSignature)
|
|
4335
|
-
throw new Error(
|
|
4336
|
-
`Swap ${pendingSwap.id}: invalid signature data from server`
|
|
4337
|
-
);
|
|
4135
|
+
throw new Error(`Swap ${pendingSwap.id}: invalid signature data from server`);
|
|
4338
4136
|
const musigSession = musigMessage.aggregateNonces([
|
|
4339
4137
|
[
|
|
4340
|
-
import_base9.hex.decode(
|
|
4341
|
-
pendingSwap.response.claimDetails.serverPublicKey
|
|
4342
|
-
),
|
|
4138
|
+
import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
4343
4139
|
import_base9.hex.decode(signedTxData.pubNonce)
|
|
4344
4140
|
]
|
|
4345
4141
|
]).initializeSession();
|
|
@@ -4359,13 +4155,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4359
4155
|
*/
|
|
4360
4156
|
async refundArk(pendingSwap) {
|
|
4361
4157
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4362
|
-
throw new Error(
|
|
4363
|
-
`Swap ${pendingSwap.id}: missing server public key in lockup details`
|
|
4364
|
-
);
|
|
4158
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4365
4159
|
if (!pendingSwap.response.lockupDetails.timeouts)
|
|
4366
|
-
throw new Error(
|
|
4367
|
-
`Swap ${pendingSwap.id}: missing timeouts in lockup details`
|
|
4368
|
-
);
|
|
4160
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in lockup details`);
|
|
4369
4161
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4370
4162
|
const address = await this.wallet.getAddress();
|
|
4371
4163
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
@@ -4407,9 +4199,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4407
4199
|
timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts
|
|
4408
4200
|
});
|
|
4409
4201
|
if (!vhtlcScript.refundScript)
|
|
4410
|
-
throw new Error(
|
|
4411
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4412
|
-
);
|
|
4202
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4413
4203
|
if (pendingSwap.response.lockupDetails.lockupAddress !== vhtlcAddress) {
|
|
4414
4204
|
throw new SwapError({
|
|
4415
4205
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4490,9 +4280,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4490
4280
|
*/
|
|
4491
4281
|
async waitAndClaimArk(pendingSwap) {
|
|
4492
4282
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4493
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4494
|
-
pendingSwap.id
|
|
4495
|
-
);
|
|
4283
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4496
4284
|
return { txid };
|
|
4497
4285
|
}
|
|
4498
4286
|
return new Promise((resolve, reject) => {
|
|
@@ -4513,37 +4301,33 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4513
4301
|
break;
|
|
4514
4302
|
case "transaction.claimed":
|
|
4515
4303
|
await updateSwapStatus();
|
|
4516
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4517
|
-
pendingSwap.id
|
|
4518
|
-
);
|
|
4304
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4519
4305
|
resolve({
|
|
4520
4306
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4521
4307
|
});
|
|
4522
4308
|
break;
|
|
4523
4309
|
case "transaction.claim.pending":
|
|
4524
4310
|
await updateSwapStatus();
|
|
4525
|
-
await this.signCooperativeClaimForServer(swap).catch(
|
|
4311
|
+
await this.signCooperativeClaimForServer(swap).catch((err) => {
|
|
4312
|
+
logger.error(`Failed to sign cooperative claim for ${swap.id}:`, err);
|
|
4313
|
+
});
|
|
4314
|
+
break;
|
|
4315
|
+
case "transaction.lockupFailed":
|
|
4316
|
+
await updateSwapStatus();
|
|
4317
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4526
4318
|
(err) => {
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4319
|
+
reject(
|
|
4320
|
+
new SwapError({
|
|
4321
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4322
|
+
isRefundable: false,
|
|
4323
|
+
// TODO btc refund not implemented yet
|
|
4324
|
+
pendingSwap: swap,
|
|
4325
|
+
cause: err
|
|
4326
|
+
})
|
|
4530
4327
|
);
|
|
4531
4328
|
}
|
|
4532
4329
|
);
|
|
4533
4330
|
break;
|
|
4534
|
-
case "transaction.lockupFailed":
|
|
4535
|
-
await updateSwapStatus();
|
|
4536
|
-
await this.quoteSwap(swap.response.id).catch((err) => {
|
|
4537
|
-
reject(
|
|
4538
|
-
new SwapError({
|
|
4539
|
-
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4540
|
-
isRefundable: false,
|
|
4541
|
-
// TODO btc refund not implemented yet
|
|
4542
|
-
pendingSwap: swap
|
|
4543
|
-
})
|
|
4544
|
-
);
|
|
4545
|
-
});
|
|
4546
|
-
break;
|
|
4547
4331
|
case "swap.expired":
|
|
4548
4332
|
await updateSwapStatus();
|
|
4549
4333
|
reject(
|
|
@@ -4583,17 +4367,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4583
4367
|
*/
|
|
4584
4368
|
async claimArk(pendingSwap) {
|
|
4585
4369
|
if (!pendingSwap.toAddress)
|
|
4586
|
-
throw new Error(
|
|
4587
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4588
|
-
);
|
|
4370
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4589
4371
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4590
|
-
throw new Error(
|
|
4591
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4592
|
-
);
|
|
4372
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4593
4373
|
if (!pendingSwap.response.claimDetails.timeouts)
|
|
4594
|
-
throw new Error(
|
|
4595
|
-
`Swap ${pendingSwap.id}: missing timeouts in claim details`
|
|
4596
|
-
);
|
|
4374
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in claim details`);
|
|
4597
4375
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4598
4376
|
const preimage = import_base9.hex.decode(pendingSwap.preimage);
|
|
4599
4377
|
const address = await this.wallet.getAddress();
|
|
@@ -4605,10 +4383,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4605
4383
|
pendingSwap.response.claimDetails.serverPublicKey,
|
|
4606
4384
|
"sender"
|
|
4607
4385
|
);
|
|
4608
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
4609
|
-
arkInfo.signerPubkey,
|
|
4610
|
-
"server"
|
|
4611
|
-
);
|
|
4386
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(arkInfo.signerPubkey, "server");
|
|
4612
4387
|
const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
|
|
4613
4388
|
network: arkInfo.network,
|
|
4614
4389
|
preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
|
|
@@ -4618,9 +4393,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4618
4393
|
timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts
|
|
4619
4394
|
});
|
|
4620
4395
|
if (!vhtlcScript.claimScript)
|
|
4621
|
-
throw new Error(
|
|
4622
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4623
|
-
);
|
|
4396
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4624
4397
|
if (pendingSwap.response.claimDetails.lockupAddress !== vhtlcAddress) {
|
|
4625
4398
|
throw new SwapError({
|
|
4626
4399
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4637,15 +4410,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4637
4410
|
break;
|
|
4638
4411
|
}
|
|
4639
4412
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
4640
|
-
await new Promise(
|
|
4641
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
4642
|
-
);
|
|
4413
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
4643
4414
|
}
|
|
4644
4415
|
}
|
|
4645
4416
|
if (!vtxo) {
|
|
4646
|
-
throw new Error(
|
|
4647
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
4648
|
-
);
|
|
4417
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
4649
4418
|
}
|
|
4650
4419
|
const input = {
|
|
4651
4420
|
...vtxo,
|
|
@@ -4656,10 +4425,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4656
4425
|
amount: BigInt(vtxo.value),
|
|
4657
4426
|
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
4658
4427
|
};
|
|
4659
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
4660
|
-
this.wallet.identity,
|
|
4661
|
-
preimage
|
|
4662
|
-
);
|
|
4428
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
4663
4429
|
if ((0, import_sdk8.isRecoverable)(vtxo)) {
|
|
4664
4430
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
4665
4431
|
} else {
|
|
@@ -4685,16 +4451,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4685
4451
|
*/
|
|
4686
4452
|
async signCooperativeClaimForServer(pendingSwap) {
|
|
4687
4453
|
if (!pendingSwap.response.lockupDetails.swapTree)
|
|
4688
|
-
throw new Error(
|
|
4689
|
-
`Swap ${pendingSwap.id}: missing swap tree in lockup details`
|
|
4690
|
-
);
|
|
4454
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in lockup details`);
|
|
4691
4455
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4692
|
-
throw new Error(
|
|
4693
|
-
|
|
4694
|
-
);
|
|
4695
|
-
const claimDetails = await this.swapProvider.getChainClaimDetails(
|
|
4696
|
-
pendingSwap.id
|
|
4697
|
-
);
|
|
4456
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4457
|
+
const claimDetails = await this.swapProvider.getChainClaimDetails(pendingSwap.id);
|
|
4698
4458
|
const serverPubKey = pendingSwap.response.lockupDetails.serverPublicKey;
|
|
4699
4459
|
if (claimDetails.publicKey !== serverPubKey) {
|
|
4700
4460
|
throw new Error(
|
|
@@ -4710,9 +4470,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4710
4470
|
);
|
|
4711
4471
|
const musigNonces = musig.message(import_base9.hex.decode(claimDetails.transactionHash)).generateNonce().aggregateNonces([
|
|
4712
4472
|
[
|
|
4713
|
-
import_base9.hex.decode(
|
|
4714
|
-
pendingSwap.response.lockupDetails.serverPublicKey
|
|
4715
|
-
),
|
|
4473
|
+
import_base9.hex.decode(pendingSwap.response.lockupDetails.serverPublicKey),
|
|
4716
4474
|
import_base9.hex.decode(claimDetails.pubNonce)
|
|
4717
4475
|
]
|
|
4718
4476
|
]).initializeSession();
|
|
@@ -4731,10 +4489,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4731
4489
|
* @returns The transaction ID of the claim.
|
|
4732
4490
|
*/
|
|
4733
4491
|
async waitAndClaimChain(pendingSwap) {
|
|
4734
|
-
if (pendingSwap.request.to === "ARK")
|
|
4735
|
-
|
|
4736
|
-
if (pendingSwap.request.to === "BTC")
|
|
4737
|
-
return this.waitAndClaimBtc(pendingSwap);
|
|
4492
|
+
if (pendingSwap.request.to === "ARK") return this.waitAndClaimArk(pendingSwap);
|
|
4493
|
+
if (pendingSwap.request.to === "BTC") return this.waitAndClaimBtc(pendingSwap);
|
|
4738
4494
|
throw new SwapError({
|
|
4739
4495
|
message: `Unsupported swap destination: ${pendingSwap.request.to}`
|
|
4740
4496
|
});
|
|
@@ -4749,11 +4505,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4749
4505
|
*/
|
|
4750
4506
|
async createChainSwap(args) {
|
|
4751
4507
|
const { to, from, receiverLockAmount, senderLockAmount, toAddress } = args;
|
|
4752
|
-
if (!toAddress)
|
|
4753
|
-
throw new SwapError({ message: "Destination address is required" });
|
|
4508
|
+
if (!toAddress) throw new SwapError({ message: "Destination address is required" });
|
|
4754
4509
|
const feeSatsPerByte = args.feeSatsPerByte ?? 1;
|
|
4755
|
-
if (feeSatsPerByte <= 0)
|
|
4756
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4510
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4757
4511
|
let amount, serverLockAmount, userLockAmount;
|
|
4758
4512
|
if (receiverLockAmount) {
|
|
4759
4513
|
amount = receiverLockAmount;
|
|
@@ -4768,8 +4522,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4768
4522
|
}
|
|
4769
4523
|
const preimage = (0, import_utils3.randomBytes)(32);
|
|
4770
4524
|
const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
|
|
4771
|
-
if (!preimageHash)
|
|
4772
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4525
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4773
4526
|
const ephemeralKey = import_secp256k13.secp256k1.utils.randomSecretKey();
|
|
4774
4527
|
const refundPublicKey = to === "ARK" ? import_base9.hex.encode(import_secp256k13.secp256k1.getPublicKey(ephemeralKey)) : import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
4775
4528
|
if (!refundPublicKey)
|
|
@@ -4818,30 +4571,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4818
4571
|
const { to, from, swap, arkInfo } = args;
|
|
4819
4572
|
if (from === "ARK") {
|
|
4820
4573
|
if (!swap.response.lockupDetails.serverPublicKey)
|
|
4821
|
-
throw new Error(
|
|
4822
|
-
`Swap ${swap.id}: missing serverPublicKey in lockup details`
|
|
4823
|
-
);
|
|
4574
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in lockup details`);
|
|
4824
4575
|
if (!swap.response.lockupDetails.timeouts)
|
|
4825
|
-
throw new Error(
|
|
4826
|
-
`Swap ${swap.id}: missing timeouts in lockup details`
|
|
4827
|
-
);
|
|
4576
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in lockup details`);
|
|
4828
4577
|
}
|
|
4829
4578
|
if (to === "ARK") {
|
|
4830
4579
|
if (!swap.response.claimDetails.serverPublicKey)
|
|
4831
|
-
throw new Error(
|
|
4832
|
-
`Swap ${swap.id}: missing serverPublicKey in claim details`
|
|
4833
|
-
);
|
|
4580
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in claim details`);
|
|
4834
4581
|
if (!swap.response.claimDetails.timeouts)
|
|
4835
|
-
throw new Error(
|
|
4836
|
-
`Swap ${swap.id}: missing timeouts in claim details`
|
|
4837
|
-
);
|
|
4582
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in claim details`);
|
|
4838
4583
|
}
|
|
4839
4584
|
const lockupAddress = to === "ARK" ? swap.response.claimDetails.lockupAddress : swap.response.lockupDetails.lockupAddress;
|
|
4840
4585
|
const receiverPubkey = to === "ARK" ? swap.request.claimPublicKey : swap.response.lockupDetails.serverPublicKey;
|
|
4841
4586
|
const senderPubkey = to === "ARK" ? swap.response.claimDetails.serverPublicKey : swap.request.refundPublicKey;
|
|
4842
|
-
const serverPubkey = import_base9.hex.encode(
|
|
4843
|
-
normalizeToXOnlyKey(arkInfo.signerPubkey, "server")
|
|
4844
|
-
);
|
|
4587
|
+
const serverPubkey = import_base9.hex.encode(normalizeToXOnlyKey(arkInfo.signerPubkey, "server"));
|
|
4845
4588
|
const vhtlcTimeouts = to === "ARK" ? swap.response.claimDetails.timeouts : swap.response.lockupDetails.timeouts;
|
|
4846
4589
|
const { vhtlcAddress } = this.createVHTLCScript({
|
|
4847
4590
|
network: arkInfo.network,
|
|
@@ -4859,15 +4602,110 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4859
4602
|
return true;
|
|
4860
4603
|
}
|
|
4861
4604
|
/**
|
|
4862
|
-
* Renegotiates the quote for an existing swap.
|
|
4605
|
+
* Renegotiates the quote for an existing chain swap. Convenience wrapper
|
|
4606
|
+
* over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
|
|
4607
|
+
*
|
|
4608
|
+
* The floor is resolved in order:
|
|
4609
|
+
* 1. `options.minAcceptableAmount` if provided.
|
|
4610
|
+
* 2. The original `response.claimDetails.amount` of the stored
|
|
4611
|
+
* pending swap (Boltz-confirmed server-lock amount at creation).
|
|
4612
|
+
* 3. Otherwise throws `QuoteRejectedError({ reason: "no_baseline" })`.
|
|
4613
|
+
*
|
|
4614
|
+
* `options.maxSlippageBps` (default 0) relaxes the floor by basis points.
|
|
4615
|
+
* Quotes ≤ 0 are always rejected. On rejection the acceptance is NOT
|
|
4616
|
+
* posted to Boltz.
|
|
4617
|
+
*
|
|
4618
|
+
* Prefer `getSwapQuote` / `acceptSwapQuote` for callers that want to
|
|
4619
|
+
* inspect the quote before committing.
|
|
4620
|
+
*
|
|
4863
4621
|
* @param swapId - The ID of the swap.
|
|
4622
|
+
* @param options - Optional floor and slippage configuration.
|
|
4864
4623
|
* @returns The accepted quote amount.
|
|
4624
|
+
* @throws QuoteRejectedError if the quote is non-positive, below the
|
|
4625
|
+
* effective floor, or no baseline is available.
|
|
4865
4626
|
*/
|
|
4866
|
-
async quoteSwap(swapId) {
|
|
4627
|
+
async quoteSwap(swapId, options) {
|
|
4628
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4629
|
+
const amount = await this.getSwapQuote(swapId);
|
|
4630
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4631
|
+
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4632
|
+
return amount;
|
|
4633
|
+
}
|
|
4634
|
+
/**
|
|
4635
|
+
* Fetches a renegotiated quote from Boltz without accepting it.
|
|
4636
|
+
* Pair with `acceptSwapQuote` to commit a specific value.
|
|
4637
|
+
*/
|
|
4638
|
+
async getSwapQuote(swapId) {
|
|
4867
4639
|
const { amount } = await this.swapProvider.getChainQuote(swapId);
|
|
4640
|
+
return amount;
|
|
4641
|
+
}
|
|
4642
|
+
/**
|
|
4643
|
+
* Accepts a quote amount for an existing chain swap, after validating it
|
|
4644
|
+
* against the configured floor. See `quoteSwap` for floor-resolution rules.
|
|
4645
|
+
*
|
|
4646
|
+
* @throws QuoteRejectedError if `amount` ≤ 0, below the effective floor,
|
|
4647
|
+
* or no baseline is available.
|
|
4648
|
+
*/
|
|
4649
|
+
async acceptSwapQuote(swapId, amount, options) {
|
|
4650
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4651
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4868
4652
|
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4869
4653
|
return amount;
|
|
4870
4654
|
}
|
|
4655
|
+
async resolveEffectiveFloor(swapId, options) {
|
|
4656
|
+
this.validateQuoteOptions(options);
|
|
4657
|
+
const floor = await this.resolveQuoteFloor(swapId, options);
|
|
4658
|
+
const slippageBps = options?.maxSlippageBps ?? 0;
|
|
4659
|
+
return Math.floor(floor - floor * slippageBps / 1e4);
|
|
4660
|
+
}
|
|
4661
|
+
async resolveQuoteFloor(swapId, options) {
|
|
4662
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4663
|
+
return options.minAcceptableAmount;
|
|
4664
|
+
}
|
|
4665
|
+
const swaps = await this.swapRepository.getAllSwaps({
|
|
4666
|
+
id: swapId,
|
|
4667
|
+
type: "chain"
|
|
4668
|
+
});
|
|
4669
|
+
const stored = swaps[0];
|
|
4670
|
+
const amount = stored?.response?.claimDetails?.amount;
|
|
4671
|
+
if (typeof amount !== "number") {
|
|
4672
|
+
throw new QuoteRejectedError({ reason: "no_baseline" });
|
|
4673
|
+
}
|
|
4674
|
+
return amount;
|
|
4675
|
+
}
|
|
4676
|
+
validateQuoteOptions(options) {
|
|
4677
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4678
|
+
const v = options.minAcceptableAmount;
|
|
4679
|
+
if (!Number.isInteger(v) || v <= 0) {
|
|
4680
|
+
throw new TypeError(
|
|
4681
|
+
`Invalid minAcceptableAmount: ${v} \u2014 must be a positive integer`
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
if (options?.maxSlippageBps !== void 0) {
|
|
4686
|
+
const v = options.maxSlippageBps;
|
|
4687
|
+
if (!Number.isInteger(v) || v < 0 || v > 1e4) {
|
|
4688
|
+
throw new TypeError(
|
|
4689
|
+
`Invalid maxSlippageBps: ${v} \u2014 must be an integer in [0, 10000]`
|
|
4690
|
+
);
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
validateQuote(amount, effectiveFloor) {
|
|
4695
|
+
if (!(amount > 0)) {
|
|
4696
|
+
throw new QuoteRejectedError({
|
|
4697
|
+
reason: "non_positive",
|
|
4698
|
+
quotedAmount: amount
|
|
4699
|
+
});
|
|
4700
|
+
}
|
|
4701
|
+
if (amount < effectiveFloor) {
|
|
4702
|
+
throw new QuoteRejectedError({
|
|
4703
|
+
reason: "below_floor",
|
|
4704
|
+
quotedAmount: amount,
|
|
4705
|
+
floor: effectiveFloor
|
|
4706
|
+
});
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4871
4709
|
// =========================================================================
|
|
4872
4710
|
// Shared utilities
|
|
4873
4711
|
// =========================================================================
|
|
@@ -4881,14 +4719,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4881
4719
|
* @returns The commitment transaction ID.
|
|
4882
4720
|
*/
|
|
4883
4721
|
async joinBatch(identity, input, output, arkInfo, isRecoverable2 = true) {
|
|
4884
|
-
return joinBatch(
|
|
4885
|
-
this.arkProvider,
|
|
4886
|
-
identity,
|
|
4887
|
-
input,
|
|
4888
|
-
output,
|
|
4889
|
-
arkInfo,
|
|
4890
|
-
isRecoverable2
|
|
4891
|
-
);
|
|
4722
|
+
return joinBatch(this.arkProvider, identity, input, output, arkInfo, isRecoverable2);
|
|
4892
4723
|
}
|
|
4893
4724
|
/**
|
|
4894
4725
|
* Creates a VHTLC script for the swap.
|
|
@@ -4926,9 +4757,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4926
4757
|
async getPendingSubmarineSwaps() {
|
|
4927
4758
|
const swaps = await this.getPendingSubmarineSwapsFromStorage();
|
|
4928
4759
|
if (!swaps) return [];
|
|
4929
|
-
return swaps.filter(
|
|
4930
|
-
(swap) => swap.status === "invoice.set"
|
|
4931
|
-
);
|
|
4760
|
+
return swaps.filter((swap) => swap.status === "invoice.set");
|
|
4932
4761
|
}
|
|
4933
4762
|
/**
|
|
4934
4763
|
* Returns pending reverse swaps (those with status `swap.created`).
|
|
@@ -4936,9 +4765,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4936
4765
|
async getPendingReverseSwaps() {
|
|
4937
4766
|
const swaps = await this.getPendingReverseSwapsFromStorage();
|
|
4938
4767
|
if (!swaps) return [];
|
|
4939
|
-
return swaps.filter(
|
|
4940
|
-
(swap) => swap.status === "swap.created"
|
|
4941
|
-
);
|
|
4768
|
+
return swaps.filter((swap) => swap.status === "swap.created");
|
|
4942
4769
|
}
|
|
4943
4770
|
/**
|
|
4944
4771
|
* Returns pending chain swaps (those with status `swap.created`).
|
|
@@ -4977,10 +4804,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4977
4804
|
this.savePendingReverseSwap.bind(this)
|
|
4978
4805
|
)
|
|
4979
4806
|
).catch((error) => {
|
|
4980
|
-
logger.error(
|
|
4981
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
4982
|
-
error
|
|
4983
|
-
);
|
|
4807
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
4984
4808
|
})
|
|
4985
4809
|
);
|
|
4986
4810
|
}
|
|
@@ -4994,23 +4818,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4994
4818
|
this.savePendingSubmarineSwap.bind(this)
|
|
4995
4819
|
)
|
|
4996
4820
|
).catch((error) => {
|
|
4997
|
-
logger.error(
|
|
4998
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
4999
|
-
error
|
|
5000
|
-
);
|
|
4821
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5001
4822
|
})
|
|
5002
4823
|
);
|
|
5003
4824
|
}
|
|
5004
4825
|
for (const swap of await this.getPendingChainSwapsFromStorage()) {
|
|
5005
4826
|
if (isChainFinalStatus(swap.status)) continue;
|
|
5006
4827
|
promises.push(
|
|
5007
|
-
this.getSwapStatus(swap.id).then(
|
|
5008
|
-
(
|
|
5009
|
-
).catch((error) => {
|
|
5010
|
-
logger.error(
|
|
5011
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
5012
|
-
error
|
|
5013
|
-
);
|
|
4828
|
+
this.getSwapStatus(swap.id).then(({ status }) => this.savePendingChainSwap({ ...swap, status })).catch((error) => {
|
|
4829
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5014
4830
|
})
|
|
5015
4831
|
);
|
|
5016
4832
|
}
|
|
@@ -5027,9 +4843,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5027
4843
|
* display/monitoring and are not automatically wired into the SwapManager.
|
|
5028
4844
|
*/
|
|
5029
4845
|
async restoreSwaps(boltzFees) {
|
|
5030
|
-
const publicKey = import_base9.hex.encode(
|
|
5031
|
-
await this.wallet.identity.compressedPublicKey()
|
|
5032
|
-
);
|
|
4846
|
+
const publicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
5033
4847
|
if (!publicKey) throw new Error("Failed to get public key from wallet");
|
|
5034
4848
|
const fees = boltzFees ?? await this.swapProvider.getFees();
|
|
5035
4849
|
const chainSwaps = [];
|
|
@@ -5081,25 +4895,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5081
4895
|
preimage: ""
|
|
5082
4896
|
});
|
|
5083
4897
|
} else if (isRestoredSubmarineSwap(swap)) {
|
|
5084
|
-
const {
|
|
5085
|
-
amount,
|
|
5086
|
-
lockupAddress,
|
|
5087
|
-
serverPublicKey,
|
|
5088
|
-
tree,
|
|
5089
|
-
timeoutBlockHeights
|
|
5090
|
-
} = swap.refundDetails;
|
|
4898
|
+
const { amount, lockupAddress, serverPublicKey, tree, timeoutBlockHeights } = swap.refundDetails;
|
|
5091
4899
|
let preimage = "";
|
|
5092
4900
|
if (!isSubmarineFinalStatus(status)) {
|
|
5093
4901
|
try {
|
|
5094
|
-
const data = await this.swapProvider.getSwapPreimage(
|
|
5095
|
-
swap.id
|
|
5096
|
-
);
|
|
4902
|
+
const data = await this.swapProvider.getSwapPreimage(swap.id);
|
|
5097
4903
|
preimage = data.preimage;
|
|
5098
4904
|
} catch (error) {
|
|
5099
|
-
logger.warn(
|
|
5100
|
-
`Failed to restore preimage for submarine swap ${id}`,
|
|
5101
|
-
error
|
|
5102
|
-
);
|
|
4905
|
+
logger.warn(`Failed to restore preimage for submarine swap ${id}`, error);
|
|
5103
4906
|
}
|
|
5104
4907
|
}
|
|
5105
4908
|
submarineSwaps.push({
|
|
@@ -5137,12 +4940,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5137
4940
|
} else if (isRestoredChainSwap(swap)) {
|
|
5138
4941
|
const refundDetails = swap.refundDetails;
|
|
5139
4942
|
if (!refundDetails) continue;
|
|
5140
|
-
const {
|
|
5141
|
-
amount,
|
|
5142
|
-
lockupAddress,
|
|
5143
|
-
serverPublicKey,
|
|
5144
|
-
timeoutBlockHeight
|
|
5145
|
-
} = refundDetails;
|
|
4943
|
+
const { amount, lockupAddress, serverPublicKey, timeoutBlockHeight } = refundDetails;
|
|
5146
4944
|
chainSwaps.push({
|
|
5147
4945
|
id,
|
|
5148
4946
|
type: "chain",
|
|
@@ -5195,19 +4993,11 @@ var SWAP_POLL_TASK_TYPE = "swap-poll";
|
|
|
5195
4993
|
var swapsPollProcessor = {
|
|
5196
4994
|
taskType: SWAP_POLL_TASK_TYPE,
|
|
5197
4995
|
async execute(item, deps) {
|
|
5198
|
-
const {
|
|
5199
|
-
swapRepository,
|
|
5200
|
-
swapProvider,
|
|
5201
|
-
wallet,
|
|
5202
|
-
arkProvider,
|
|
5203
|
-
indexerProvider
|
|
5204
|
-
} = deps;
|
|
4996
|
+
const { swapRepository, swapProvider, wallet, arkProvider, indexerProvider } = deps;
|
|
5205
4997
|
const allSwaps = await swapRepository.getAllSwaps();
|
|
5206
4998
|
const pendingSwaps = allSwaps.filter((swap) => {
|
|
5207
|
-
if (isPendingReverseSwap(swap))
|
|
5208
|
-
|
|
5209
|
-
if (isPendingSubmarineSwap(swap))
|
|
5210
|
-
return !isSubmarineFinalStatus(swap.status);
|
|
4999
|
+
if (isPendingReverseSwap(swap)) return !isReverseFinalStatus(swap.status);
|
|
5000
|
+
if (isPendingSubmarineSwap(swap)) return !isSubmarineFinalStatus(swap.status);
|
|
5211
5001
|
return false;
|
|
5212
5002
|
});
|
|
5213
5003
|
let polled = 0;
|
|
@@ -5237,19 +5027,14 @@ var swapsPollProcessor = {
|
|
|
5237
5027
|
}
|
|
5238
5028
|
if (isPendingReverseSwap(swap) && isReverseClaimableStatus(currentStatus)) {
|
|
5239
5029
|
if (!swap.preimage) {
|
|
5240
|
-
logger.warn(
|
|
5241
|
-
`[swap-poll] Skipping claim for ${swap.id}: no preimage`
|
|
5242
|
-
);
|
|
5030
|
+
logger.warn(`[swap-poll] Skipping claim for ${swap.id}: no preimage`);
|
|
5243
5031
|
continue;
|
|
5244
5032
|
}
|
|
5245
5033
|
try {
|
|
5246
5034
|
await tempSwaps.claimVHTLC(swap);
|
|
5247
5035
|
claimed++;
|
|
5248
5036
|
} catch (claimError) {
|
|
5249
|
-
logger.error(
|
|
5250
|
-
`[swap-poll] Claim failed for ${swap.id}:`,
|
|
5251
|
-
claimError
|
|
5252
|
-
);
|
|
5037
|
+
logger.error(`[swap-poll] Claim failed for ${swap.id}:`, claimError);
|
|
5253
5038
|
errors++;
|
|
5254
5039
|
}
|
|
5255
5040
|
}
|
|
@@ -5265,18 +5050,12 @@ var swapsPollProcessor = {
|
|
|
5265
5050
|
await tempSwaps.refundVHTLC(swapWithStatus);
|
|
5266
5051
|
refunded++;
|
|
5267
5052
|
} catch (refundError) {
|
|
5268
|
-
logger.error(
|
|
5269
|
-
`[swap-poll] Refund failed for ${swap.id}:`,
|
|
5270
|
-
refundError
|
|
5271
|
-
);
|
|
5053
|
+
logger.error(`[swap-poll] Refund failed for ${swap.id}:`, refundError);
|
|
5272
5054
|
errors++;
|
|
5273
5055
|
}
|
|
5274
5056
|
}
|
|
5275
5057
|
} catch (swapError) {
|
|
5276
|
-
logger.error(
|
|
5277
|
-
`[swap-poll] Error processing swap ${swap.id}:`,
|
|
5278
|
-
swapError
|
|
5279
|
-
);
|
|
5058
|
+
logger.error(`[swap-poll] Error processing swap ${swap.id}:`, swapError);
|
|
5280
5059
|
errors++;
|
|
5281
5060
|
}
|
|
5282
5061
|
}
|
|
@@ -5293,9 +5072,6 @@ var swapsPollProcessor = {
|
|
|
5293
5072
|
};
|
|
5294
5073
|
|
|
5295
5074
|
// src/expo/background.ts
|
|
5296
|
-
function getRandomId() {
|
|
5297
|
-
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
5298
|
-
}
|
|
5299
5075
|
function createBackgroundWalletShim(args) {
|
|
5300
5076
|
const notImplemented = (method) => {
|
|
5301
5077
|
throw new Error(
|
|
@@ -5330,9 +5106,7 @@ function defineExpoSwapBackgroundTask(taskName, options) {
|
|
|
5330
5106
|
}
|
|
5331
5107
|
const identity = await identityFactory();
|
|
5332
5108
|
const arkProvider = new import_expo2.ExpoArkProvider(config.arkServerUrl);
|
|
5333
|
-
const indexerProvider = new import_expo2.ExpoIndexerProvider(
|
|
5334
|
-
config.arkServerUrl
|
|
5335
|
-
);
|
|
5109
|
+
const indexerProvider = new import_expo2.ExpoIndexerProvider(config.arkServerUrl);
|
|
5336
5110
|
const swapProvider = new BoltzSwapProvider({
|
|
5337
5111
|
network: config.network,
|
|
5338
5112
|
apiUrl: config.boltzApiUrl
|
|
@@ -5347,11 +5121,7 @@ function defineExpoSwapBackgroundTask(taskName, options) {
|
|
|
5347
5121
|
const serverPubKey = hex9.decode(info.signerPubkey);
|
|
5348
5122
|
const xOnlyServerPubKey = serverPubKey.length === 33 ? serverPubKey.slice(1) : serverPubKey;
|
|
5349
5123
|
const hrp = info.network === "bitcoin" ? "ark" : "tark";
|
|
5350
|
-
return new ArkAddress3(
|
|
5351
|
-
xOnlyServerPubKey,
|
|
5352
|
-
pubkey,
|
|
5353
|
-
hrp
|
|
5354
|
-
).encode();
|
|
5124
|
+
return new ArkAddress3(xOnlyServerPubKey, pubkey, hrp).encode();
|
|
5355
5125
|
}
|
|
5356
5126
|
});
|
|
5357
5127
|
const deps = {
|
|
@@ -5365,14 +5135,12 @@ function defineExpoSwapBackgroundTask(taskName, options) {
|
|
|
5365
5135
|
await (0, import_expo.runTasks)(taskQueue, [swapsPollProcessor], deps);
|
|
5366
5136
|
const results = await taskQueue.getResults();
|
|
5367
5137
|
if (results.length > 0) {
|
|
5368
|
-
await taskQueue.acknowledgeResults(
|
|
5369
|
-
results.map((r) => r.id)
|
|
5370
|
-
);
|
|
5138
|
+
await taskQueue.acknowledgeResults(results.map((r) => r.id));
|
|
5371
5139
|
}
|
|
5372
5140
|
const existing = await taskQueue.getTasks(SWAP_POLL_TASK_TYPE);
|
|
5373
5141
|
if (existing.length === 0) {
|
|
5374
5142
|
const task = {
|
|
5375
|
-
id: getRandomId(),
|
|
5143
|
+
id: (0, import_sdk9.getRandomId)(),
|
|
5376
5144
|
type: SWAP_POLL_TASK_TYPE,
|
|
5377
5145
|
data: {},
|
|
5378
5146
|
createdAt: Date.now()
|