@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/index.cjs
CHANGED
|
@@ -45,7 +45,10 @@ var SwapError = class extends Error {
|
|
|
45
45
|
/** The pending swap associated with this error, if available. */
|
|
46
46
|
pendingSwap;
|
|
47
47
|
constructor(options = {}) {
|
|
48
|
-
super(
|
|
48
|
+
super(
|
|
49
|
+
options.message ?? "Error during swap.",
|
|
50
|
+
options.cause !== void 0 ? { cause: options.cause } : void 0
|
|
51
|
+
);
|
|
49
52
|
this.name = "SwapError";
|
|
50
53
|
this.isClaimable = options.isClaimable ?? false;
|
|
51
54
|
this.isRefundable = options.isRefundable ?? false;
|
|
@@ -122,6 +125,96 @@ var TransactionRefundedError = class extends SwapError {
|
|
|
122
125
|
this.name = "TransactionRefundedError";
|
|
123
126
|
}
|
|
124
127
|
};
|
|
128
|
+
var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
|
|
129
|
+
reason;
|
|
130
|
+
quotedAmount;
|
|
131
|
+
floor;
|
|
132
|
+
constructor(options) {
|
|
133
|
+
super({
|
|
134
|
+
message: options.message ?? _QuoteRejectedError.defaultMessage(options),
|
|
135
|
+
...options
|
|
136
|
+
});
|
|
137
|
+
this.name = "QuoteRejectedError";
|
|
138
|
+
this.reason = options.reason;
|
|
139
|
+
this.quotedAmount = "quotedAmount" in options ? options.quotedAmount : void 0;
|
|
140
|
+
this.floor = "floor" in options ? options.floor : void 0;
|
|
141
|
+
}
|
|
142
|
+
static defaultMessage(options) {
|
|
143
|
+
switch (options.reason) {
|
|
144
|
+
case "below_floor":
|
|
145
|
+
return `Boltz quote ${options.quotedAmount} is below acceptable floor ${options.floor}`;
|
|
146
|
+
case "non_positive":
|
|
147
|
+
return `Boltz quote ${options.quotedAmount} is not positive`;
|
|
148
|
+
case "no_baseline":
|
|
149
|
+
return "Cannot accept quote: no minAcceptableAmount and no stored pending swap";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Serialize into a plain `Error` whose `.message` carries the full
|
|
154
|
+
* rejection payload as JSON behind a marker prefix. Structured clone
|
|
155
|
+
* (used by `postMessage` between page and service worker) preserves
|
|
156
|
+
* `Error.message` reliably but strips custom `.name` and own properties,
|
|
157
|
+
* so we move the typed data into the message field for transport.
|
|
158
|
+
*/
|
|
159
|
+
toTransportError() {
|
|
160
|
+
return new Error(
|
|
161
|
+
QUOTE_REJECTION_TRANSPORT_PREFIX + JSON.stringify({
|
|
162
|
+
reason: this.reason,
|
|
163
|
+
message: this.message,
|
|
164
|
+
quotedAmount: this.quotedAmount,
|
|
165
|
+
floor: this.floor
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Inverse of `toTransportError`. Returns a real `QuoteRejectedError` if
|
|
171
|
+
* `error` carries the transport prefix, else `null`.
|
|
172
|
+
*/
|
|
173
|
+
static fromTransportError(error) {
|
|
174
|
+
if (!(error instanceof Error) || !error.message.startsWith(QUOTE_REJECTION_TRANSPORT_PREFIX)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const payload = error.message.slice(QUOTE_REJECTION_TRANSPORT_PREFIX.length);
|
|
178
|
+
let data;
|
|
179
|
+
try {
|
|
180
|
+
data = JSON.parse(payload);
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
if (typeof data.reason !== "string" || !QUOTE_REJECTION_REASONS.has(data.reason)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const message = typeof data.message === "string" ? data.message : void 0;
|
|
188
|
+
const reason = data.reason;
|
|
189
|
+
const quotedAmount = typeof data.quotedAmount === "number" ? data.quotedAmount : null;
|
|
190
|
+
const floor = typeof data.floor === "number" ? data.floor : null;
|
|
191
|
+
switch (reason) {
|
|
192
|
+
case "below_floor":
|
|
193
|
+
if (quotedAmount === null || floor === null) return null;
|
|
194
|
+
return new _QuoteRejectedError({
|
|
195
|
+
reason,
|
|
196
|
+
quotedAmount,
|
|
197
|
+
floor,
|
|
198
|
+
message
|
|
199
|
+
});
|
|
200
|
+
case "non_positive":
|
|
201
|
+
if (quotedAmount === null) return null;
|
|
202
|
+
return new _QuoteRejectedError({
|
|
203
|
+
reason,
|
|
204
|
+
quotedAmount,
|
|
205
|
+
message
|
|
206
|
+
});
|
|
207
|
+
case "no_baseline":
|
|
208
|
+
return new _QuoteRejectedError({ reason, message });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var QUOTE_REJECTION_TRANSPORT_PREFIX = "QUOTE_REJECTED::";
|
|
213
|
+
var QUOTE_REJECTION_REASONS = /* @__PURE__ */ new Set([
|
|
214
|
+
"below_floor",
|
|
215
|
+
"non_positive",
|
|
216
|
+
"no_baseline"
|
|
217
|
+
]);
|
|
125
218
|
var BoltzRefundError = class extends Error {
|
|
126
219
|
constructor(message, cause) {
|
|
127
220
|
super(message);
|
|
@@ -137,18 +230,10 @@ var import_sdk8 = require("@arkade-os/sdk");
|
|
|
137
230
|
var import_sdk = require("@arkade-os/sdk");
|
|
138
231
|
var import_base = require("@scure/base");
|
|
139
232
|
var isSubmarineFinalStatus = (status) => {
|
|
140
|
-
return [
|
|
141
|
-
"invoice.failedToPay",
|
|
142
|
-
"transaction.claimed",
|
|
143
|
-
"swap.expired"
|
|
144
|
-
].includes(status);
|
|
233
|
+
return ["invoice.failedToPay", "transaction.claimed", "swap.expired"].includes(status);
|
|
145
234
|
};
|
|
146
235
|
var isSubmarineRefundableStatus = (status) => {
|
|
147
|
-
return [
|
|
148
|
-
"invoice.failedToPay",
|
|
149
|
-
"transaction.lockupFailed",
|
|
150
|
-
"swap.expired"
|
|
151
|
-
].includes(status);
|
|
236
|
+
return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
|
|
152
237
|
};
|
|
153
238
|
var isSubmarineSuccessStatus = (status) => {
|
|
154
239
|
return status === "transaction.claimed";
|
|
@@ -167,10 +252,7 @@ var isReverseClaimableStatus = (status) => {
|
|
|
167
252
|
return ["transaction.mempool", "transaction.confirmed"].includes(status);
|
|
168
253
|
};
|
|
169
254
|
var isChainClaimableStatus = (status) => {
|
|
170
|
-
return [
|
|
171
|
-
"transaction.server.mempool",
|
|
172
|
-
"transaction.server.confirmed"
|
|
173
|
-
].includes(status);
|
|
255
|
+
return ["transaction.server.mempool", "transaction.server.confirmed"].includes(status);
|
|
174
256
|
};
|
|
175
257
|
var isChainFinalStatus = (status) => {
|
|
176
258
|
return [
|
|
@@ -284,8 +366,7 @@ var BASE_URLS = {
|
|
|
284
366
|
var isSwapNotFoundBody = (error) => {
|
|
285
367
|
const needle = "could not find swap";
|
|
286
368
|
const fromJson = error.errorData?.error;
|
|
287
|
-
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle))
|
|
288
|
-
return true;
|
|
369
|
+
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle)) return true;
|
|
289
370
|
return error.message.toLowerCase().includes(needle);
|
|
290
371
|
};
|
|
291
372
|
var BoltzSwapProvider = class {
|
|
@@ -299,10 +380,7 @@ var BoltzSwapProvider = class {
|
|
|
299
380
|
this.network = config.network;
|
|
300
381
|
this.referralId = config.referralId ?? "arkade-ts-sdk";
|
|
301
382
|
const apiUrl = config.apiUrl || BASE_URLS[config.network];
|
|
302
|
-
if (!apiUrl)
|
|
303
|
-
throw new Error(
|
|
304
|
-
`API URL is required for network: ${config.network}`
|
|
305
|
-
);
|
|
383
|
+
if (!apiUrl) throw new Error(`API URL is required for network: ${config.network}`);
|
|
306
384
|
this.apiUrl = apiUrl;
|
|
307
385
|
this.wsUrl = this.apiUrl.replace(/^http(s)?:\/\//, "ws$1://").replace("9069", "9004") + "/v2/ws";
|
|
308
386
|
}
|
|
@@ -321,10 +399,7 @@ var BoltzSwapProvider = class {
|
|
|
321
399
|
/** Returns current Lightning swap fees (submarine + reverse) from Boltz. */
|
|
322
400
|
async getFees() {
|
|
323
401
|
const [submarine, reverse] = await Promise.all([
|
|
324
|
-
this.request(
|
|
325
|
-
"/v2/swap/submarine",
|
|
326
|
-
"GET"
|
|
327
|
-
),
|
|
402
|
+
this.request("/v2/swap/submarine", "GET"),
|
|
328
403
|
this.request("/v2/swap/reverse", "GET")
|
|
329
404
|
]);
|
|
330
405
|
if (!isGetSubmarinePairsResponse(submarine))
|
|
@@ -344,10 +419,7 @@ var BoltzSwapProvider = class {
|
|
|
344
419
|
}
|
|
345
420
|
/** Returns current Lightning swap min/max limits from Boltz. */
|
|
346
421
|
async getLimits() {
|
|
347
|
-
const response = await this.request(
|
|
348
|
-
"/v2/swap/submarine",
|
|
349
|
-
"GET"
|
|
350
|
-
);
|
|
422
|
+
const response = await this.request("/v2/swap/submarine", "GET");
|
|
351
423
|
if (!isGetSubmarinePairsResponse(response))
|
|
352
424
|
throw new SchemaError({ message: "error fetching limits" });
|
|
353
425
|
return {
|
|
@@ -357,10 +429,7 @@ var BoltzSwapProvider = class {
|
|
|
357
429
|
}
|
|
358
430
|
/** Returns the current BTC chain tip height from Boltz. */
|
|
359
431
|
async getChainHeight() {
|
|
360
|
-
const response = await this.request(
|
|
361
|
-
"/v2/chain/heights",
|
|
362
|
-
"GET"
|
|
363
|
-
);
|
|
432
|
+
const response = await this.request("/v2/chain/heights", "GET");
|
|
364
433
|
if (typeof response?.BTC !== "number")
|
|
365
434
|
throw new SchemaError({
|
|
366
435
|
message: "error fetching chain heights"
|
|
@@ -390,10 +459,7 @@ var BoltzSwapProvider = class {
|
|
|
390
459
|
async getSwapStatus(id) {
|
|
391
460
|
let response;
|
|
392
461
|
try {
|
|
393
|
-
response = await this.request(
|
|
394
|
-
`/v2/swap/${id}`,
|
|
395
|
-
"GET"
|
|
396
|
-
);
|
|
462
|
+
response = await this.request(`/v2/swap/${id}`, "GET");
|
|
397
463
|
} catch (error) {
|
|
398
464
|
if (error instanceof NetworkError && error.statusCode === 404 && isSwapNotFoundBody(error)) {
|
|
399
465
|
throw new SwapNotFoundError(id, error.errorData);
|
|
@@ -489,12 +555,10 @@ var BoltzSwapProvider = class {
|
|
|
489
555
|
throw new SwapError({ message: "Invalid 'to' chain" });
|
|
490
556
|
if (["BTC", "ARK"].indexOf(from) === -1)
|
|
491
557
|
throw new SwapError({ message: "Invalid 'from' chain" });
|
|
492
|
-
if (to === from)
|
|
493
|
-
throw new SwapError({ message: "Invalid swap direction" });
|
|
558
|
+
if (to === from) throw new SwapError({ message: "Invalid swap direction" });
|
|
494
559
|
if (!preimageHash || preimageHash.length != 64)
|
|
495
560
|
throw new SwapError({ message: "Invalid preimageHash" });
|
|
496
|
-
if (feeSatsPerByte <= 0)
|
|
497
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
561
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
498
562
|
if (serverLockAmount !== void 0 && userLockAmount !== void 0 || serverLockAmount === void 0 && userLockAmount === void 0)
|
|
499
563
|
throw new SwapError({
|
|
500
564
|
message: "Either serverLockAmount or userLockAmount must be provided"
|
|
@@ -549,12 +613,8 @@ var BoltzSwapProvider = class {
|
|
|
549
613
|
message: "Error refunding submarine swap"
|
|
550
614
|
});
|
|
551
615
|
return {
|
|
552
|
-
transaction: import_sdk.Transaction.fromPSBT(
|
|
553
|
-
|
|
554
|
-
),
|
|
555
|
-
checkpoint: import_sdk.Transaction.fromPSBT(
|
|
556
|
-
import_base.base64.decode(response.checkpoint)
|
|
557
|
-
)
|
|
616
|
+
transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
|
|
617
|
+
checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
|
|
558
618
|
};
|
|
559
619
|
}
|
|
560
620
|
/** Requests Boltz co-signature for a chain swap refund. Returns signed transaction + checkpoint. */
|
|
@@ -573,53 +633,53 @@ var BoltzSwapProvider = class {
|
|
|
573
633
|
message: "Error refunding chain swap"
|
|
574
634
|
});
|
|
575
635
|
return {
|
|
576
|
-
transaction: import_sdk.Transaction.fromPSBT(
|
|
577
|
-
|
|
578
|
-
),
|
|
579
|
-
checkpoint: import_sdk.Transaction.fromPSBT(
|
|
580
|
-
import_base.base64.decode(response.checkpoint)
|
|
581
|
-
)
|
|
636
|
+
transaction: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.transaction)),
|
|
637
|
+
checkpoint: import_sdk.Transaction.fromPSBT(import_base.base64.decode(response.checkpoint))
|
|
582
638
|
};
|
|
583
639
|
}
|
|
584
|
-
/**
|
|
640
|
+
/**
|
|
641
|
+
* Monitors swap status updates and forwards them to the update callback.
|
|
642
|
+
* Prefers a WebSocket subscription; on connection error or premature close
|
|
643
|
+
* it falls back to REST polling so callers don't fail when the WS endpoint
|
|
644
|
+
* is flaky (observed in CI). Resolves when the swap reaches a terminal
|
|
645
|
+
* status.
|
|
646
|
+
*/
|
|
585
647
|
async monitorSwap(swapId, update) {
|
|
586
648
|
return new Promise((resolve, reject) => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
);
|
|
649
|
+
let settled = false;
|
|
650
|
+
let lastStatus = null;
|
|
651
|
+
let pollTimer = null;
|
|
652
|
+
let webSocket = null;
|
|
653
|
+
let connectionTimeout = null;
|
|
654
|
+
const cleanup = () => {
|
|
655
|
+
if (connectionTimeout) {
|
|
656
|
+
clearTimeout(connectionTimeout);
|
|
657
|
+
connectionTimeout = null;
|
|
658
|
+
}
|
|
659
|
+
if (pollTimer) {
|
|
660
|
+
clearInterval(pollTimer);
|
|
661
|
+
pollTimer = null;
|
|
662
|
+
}
|
|
663
|
+
if (webSocket) {
|
|
664
|
+
try {
|
|
665
|
+
webSocket.close();
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
webSocket = null;
|
|
669
|
+
}
|
|
609
670
|
};
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
671
|
+
const finish = (err) => {
|
|
672
|
+
if (settled) return;
|
|
673
|
+
settled = true;
|
|
674
|
+
cleanup();
|
|
675
|
+
if (err) reject(err);
|
|
676
|
+
else resolve();
|
|
613
677
|
};
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
reject(new SwapError({ message: msg.args[0].error }));
|
|
620
|
-
}
|
|
621
|
-
const status = msg.args[0].status;
|
|
622
|
-
const negotiable = status === "transaction.lockupFailed" && msg.args[0].failureDetails?.actual !== void 0 && msg.args[0].failureDetails?.expected !== void 0;
|
|
678
|
+
const handleStatus = (status, data) => {
|
|
679
|
+
if (settled) return;
|
|
680
|
+
if (status === lastStatus) return;
|
|
681
|
+
lastStatus = status;
|
|
682
|
+
const negotiable = status === "transaction.lockupFailed" && data?.failureDetails?.actual !== void 0 && data?.failureDetails?.expected !== void 0;
|
|
623
683
|
switch (status) {
|
|
624
684
|
case "invoice.settled":
|
|
625
685
|
case "transaction.claimed":
|
|
@@ -628,13 +688,13 @@ var BoltzSwapProvider = class {
|
|
|
628
688
|
case "invoice.failedToPay":
|
|
629
689
|
case "transaction.failed":
|
|
630
690
|
case "swap.expired":
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
691
|
+
update(status, data);
|
|
692
|
+
finish();
|
|
693
|
+
return;
|
|
634
694
|
case "transaction.lockupFailed":
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
695
|
+
update(status, data);
|
|
696
|
+
if (!negotiable) finish();
|
|
697
|
+
return;
|
|
638
698
|
case "invoice.paid":
|
|
639
699
|
case "invoice.pending":
|
|
640
700
|
case "invoice.set":
|
|
@@ -644,9 +704,77 @@ var BoltzSwapProvider = class {
|
|
|
644
704
|
case "transaction.claim.pending":
|
|
645
705
|
case "transaction.server.mempool":
|
|
646
706
|
case "transaction.server.confirmed":
|
|
647
|
-
update(status,
|
|
707
|
+
update(status, data);
|
|
708
|
+
return;
|
|
648
709
|
}
|
|
649
710
|
};
|
|
711
|
+
const startPolling = () => {
|
|
712
|
+
if (settled || pollTimer) return;
|
|
713
|
+
const poll = async () => {
|
|
714
|
+
if (settled) return;
|
|
715
|
+
try {
|
|
716
|
+
const result = await this.getSwapStatus(swapId);
|
|
717
|
+
handleStatus(result.status, { ...result, id: swapId });
|
|
718
|
+
} catch {
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
pollTimer = setInterval(poll, 1e3);
|
|
722
|
+
poll();
|
|
723
|
+
};
|
|
724
|
+
try {
|
|
725
|
+
webSocket = new globalThis.WebSocket(this.wsUrl);
|
|
726
|
+
connectionTimeout = setTimeout(() => {
|
|
727
|
+
try {
|
|
728
|
+
webSocket?.close();
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
webSocket = null;
|
|
732
|
+
startPolling();
|
|
733
|
+
}, 5e3);
|
|
734
|
+
webSocket.onerror = () => {
|
|
735
|
+
if (connectionTimeout) {
|
|
736
|
+
clearTimeout(connectionTimeout);
|
|
737
|
+
connectionTimeout = null;
|
|
738
|
+
}
|
|
739
|
+
webSocket = null;
|
|
740
|
+
startPolling();
|
|
741
|
+
};
|
|
742
|
+
webSocket.onopen = () => {
|
|
743
|
+
if (connectionTimeout) {
|
|
744
|
+
clearTimeout(connectionTimeout);
|
|
745
|
+
connectionTimeout = null;
|
|
746
|
+
}
|
|
747
|
+
webSocket?.send(
|
|
748
|
+
JSON.stringify({
|
|
749
|
+
op: "subscribe",
|
|
750
|
+
channel: "swap.update",
|
|
751
|
+
args: [swapId]
|
|
752
|
+
})
|
|
753
|
+
);
|
|
754
|
+
};
|
|
755
|
+
webSocket.onclose = () => {
|
|
756
|
+
if (connectionTimeout) {
|
|
757
|
+
clearTimeout(connectionTimeout);
|
|
758
|
+
connectionTimeout = null;
|
|
759
|
+
}
|
|
760
|
+
if (!settled && !pollTimer) startPolling();
|
|
761
|
+
};
|
|
762
|
+
webSocket.onmessage = (rawMsg) => {
|
|
763
|
+
try {
|
|
764
|
+
const msg = JSON.parse(rawMsg.data);
|
|
765
|
+
if (msg.event !== "update" || msg.args?.[0]?.id !== swapId) return;
|
|
766
|
+
if (msg.args[0].error) {
|
|
767
|
+
finish(new SwapError({ message: msg.args[0].error }));
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
handleStatus(msg.args[0].status, msg.args[0]);
|
|
771
|
+
} catch {
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
} catch {
|
|
775
|
+
webSocket = null;
|
|
776
|
+
startPolling();
|
|
777
|
+
}
|
|
650
778
|
});
|
|
651
779
|
}
|
|
652
780
|
/** Returns current chain swap fees for a given pair (e.g. ARK→BTC). */
|
|
@@ -654,10 +782,7 @@ var BoltzSwapProvider = class {
|
|
|
654
782
|
if (from === to) {
|
|
655
783
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
656
784
|
}
|
|
657
|
-
const response = await this.request(
|
|
658
|
-
"/v2/swap/chain",
|
|
659
|
-
"GET"
|
|
660
|
-
);
|
|
785
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
661
786
|
if (!isGetChainPairsResponse(response))
|
|
662
787
|
throw new SchemaError({ message: "error fetching fees" });
|
|
663
788
|
if (!response[from]?.[to]) {
|
|
@@ -672,10 +797,7 @@ var BoltzSwapProvider = class {
|
|
|
672
797
|
if (from === to) {
|
|
673
798
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
674
799
|
}
|
|
675
|
-
const response = await this.request(
|
|
676
|
-
"/v2/swap/chain",
|
|
677
|
-
"GET"
|
|
678
|
-
);
|
|
800
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
679
801
|
if (!isGetChainPairsResponse(response))
|
|
680
802
|
throw new SchemaError({ message: "error fetching limits" });
|
|
681
803
|
if (!response[from]?.[to]) {
|
|
@@ -804,9 +926,7 @@ var BoltzSwapProvider = class {
|
|
|
804
926
|
return await response.json();
|
|
805
927
|
} catch (error) {
|
|
806
928
|
if (error instanceof NetworkError) throw error;
|
|
807
|
-
throw new NetworkError(
|
|
808
|
-
`Request to ${url} failed: ${error.message}`
|
|
809
|
-
);
|
|
929
|
+
throw new NetworkError(`Request to ${url} failed: ${error.message}`);
|
|
810
930
|
}
|
|
811
931
|
}
|
|
812
932
|
};
|
|
@@ -828,8 +948,7 @@ var findKeyIndex = (keys, target) => keys.findIndex((k) => (0, import_utils.equa
|
|
|
828
948
|
var assertPublicKeys = (keys) => {
|
|
829
949
|
const seen = /* @__PURE__ */ new Set();
|
|
830
950
|
for (const key of keys) {
|
|
831
|
-
if (key.length !== 33)
|
|
832
|
-
throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
951
|
+
if (key.length !== 33) throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
833
952
|
const enc = import_base2.hex.encode(key);
|
|
834
953
|
if (seen.has(enc)) throw new Error(`duplicate public key ${enc}`);
|
|
835
954
|
seen.add(enc);
|
|
@@ -837,9 +956,7 @@ var assertPublicKeys = (keys) => {
|
|
|
837
956
|
};
|
|
838
957
|
var aggregateKeys = (publicKeys, tweak) => {
|
|
839
958
|
assertPublicKeys([...publicKeys]);
|
|
840
|
-
return (0, import_musig2.keyAggExport)(
|
|
841
|
-
(0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : [])
|
|
842
|
-
);
|
|
959
|
+
return (0, import_musig2.keyAggExport)((0, import_musig2.keyAggregate)([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : []));
|
|
843
960
|
};
|
|
844
961
|
var MusigKeyAgg = class _MusigKeyAgg {
|
|
845
962
|
constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, internalKey, _tweak) {
|
|
@@ -886,18 +1003,12 @@ var MusigWithMessage = class {
|
|
|
886
1003
|
this.msg = msg;
|
|
887
1004
|
}
|
|
888
1005
|
generateNonce() {
|
|
889
|
-
const nonce = (0, import_musig2.nonceGen)(
|
|
890
|
-
this.myPublicKey,
|
|
891
|
-
this.privateKey,
|
|
892
|
-
this.aggPubkey,
|
|
893
|
-
this.msg
|
|
894
|
-
);
|
|
1006
|
+
const nonce = (0, import_musig2.nonceGen)(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
|
|
895
1007
|
return new MusigWithNonce(
|
|
896
1008
|
this.privateKey,
|
|
897
1009
|
this.myPublicKey,
|
|
898
1010
|
this.publicKeys,
|
|
899
1011
|
this.myIndex,
|
|
900
|
-
this.aggPubkey,
|
|
901
1012
|
this.tweak,
|
|
902
1013
|
this.msg,
|
|
903
1014
|
nonce
|
|
@@ -905,12 +1016,11 @@ var MusigWithMessage = class {
|
|
|
905
1016
|
}
|
|
906
1017
|
};
|
|
907
1018
|
var MusigWithNonce = class {
|
|
908
|
-
constructor(privateKey, myPublicKey, publicKeys, myIndex,
|
|
1019
|
+
constructor(privateKey, myPublicKey, publicKeys, myIndex, tweak, msg, nonce) {
|
|
909
1020
|
this.privateKey = privateKey;
|
|
910
1021
|
this.myPublicKey = myPublicKey;
|
|
911
1022
|
this.publicKeys = publicKeys;
|
|
912
1023
|
this.myIndex = myIndex;
|
|
913
|
-
this.aggPubkey = aggPubkey;
|
|
914
1024
|
this.tweak = tweak;
|
|
915
1025
|
this.msg = msg;
|
|
916
1026
|
this.nonce = nonce;
|
|
@@ -942,10 +1052,8 @@ var MusigWithNonce = class {
|
|
|
942
1052
|
const aggregatedNonce = (0, import_musig2.nonceAggregate)([...ordered]);
|
|
943
1053
|
return new MusigNoncesAggregated(
|
|
944
1054
|
this.privateKey,
|
|
945
|
-
this.myPublicKey,
|
|
946
1055
|
this.publicKeys,
|
|
947
1056
|
this.myIndex,
|
|
948
|
-
this.aggPubkey,
|
|
949
1057
|
this.tweak,
|
|
950
1058
|
this.msg,
|
|
951
1059
|
this.nonce,
|
|
@@ -955,12 +1063,10 @@ var MusigWithNonce = class {
|
|
|
955
1063
|
}
|
|
956
1064
|
};
|
|
957
1065
|
var MusigNoncesAggregated = class {
|
|
958
|
-
constructor(privateKey,
|
|
1066
|
+
constructor(privateKey, publicKeys, myIndex, tweak, msg, nonce, pubNonces, aggregatedNonce) {
|
|
959
1067
|
this.privateKey = privateKey;
|
|
960
|
-
this.myPublicKey = myPublicKey;
|
|
961
1068
|
this.publicKeys = publicKeys;
|
|
962
1069
|
this.myIndex = myIndex;
|
|
963
|
-
this.aggPubkey = aggPubkey;
|
|
964
1070
|
this.tweak = tweak;
|
|
965
1071
|
this.msg = msg;
|
|
966
1072
|
this.nonce = nonce;
|
|
@@ -1006,11 +1112,7 @@ var MusigSession = class {
|
|
|
1006
1112
|
const index = typeof publicKeyOrIndex === "number" ? publicKeyOrIndex : findKeyIndex(this.publicKeys, publicKeyOrIndex);
|
|
1007
1113
|
if (index < 0 || index >= this.publicKeys.length)
|
|
1008
1114
|
throw new Error("public key not found or index out of range");
|
|
1009
|
-
if (!this.session.partialSigVerify(
|
|
1010
|
-
signature,
|
|
1011
|
-
[...this.pubNonces],
|
|
1012
|
-
index
|
|
1013
|
-
)) {
|
|
1115
|
+
if (!this.session.partialSigVerify(signature, [...this.pubNonces], index)) {
|
|
1014
1116
|
throw new Error("invalid partial signature");
|
|
1015
1117
|
}
|
|
1016
1118
|
this.partialSignatures[index] = signature;
|
|
@@ -1019,12 +1121,7 @@ var MusigSession = class {
|
|
|
1019
1121
|
signPartial() {
|
|
1020
1122
|
const sig = this.session.sign(this.nonce.secret, this.privateKey, true);
|
|
1021
1123
|
this.partialSignatures[this.myIndex] = sig;
|
|
1022
|
-
return new MusigSigned(
|
|
1023
|
-
this.session,
|
|
1024
|
-
[...this.partialSignatures],
|
|
1025
|
-
sig,
|
|
1026
|
-
this.nonce.public
|
|
1027
|
-
);
|
|
1124
|
+
return new MusigSigned(this.session, [...this.partialSignatures], sig, this.nonce.public);
|
|
1028
1125
|
}
|
|
1029
1126
|
};
|
|
1030
1127
|
var MusigSigned = class {
|
|
@@ -1038,14 +1135,11 @@ var MusigSigned = class {
|
|
|
1038
1135
|
if (this.partialSignatures.some((s) => s === null)) {
|
|
1039
1136
|
throw new Error("not all partial signatures are set");
|
|
1040
1137
|
}
|
|
1041
|
-
return this.session.partialSigAgg(
|
|
1042
|
-
this.partialSignatures
|
|
1043
|
-
);
|
|
1138
|
+
return this.session.partialSigAgg(this.partialSignatures);
|
|
1044
1139
|
}
|
|
1045
1140
|
};
|
|
1046
1141
|
var create = (privateKey, publicKeys) => {
|
|
1047
|
-
if (publicKeys.length < 2)
|
|
1048
|
-
throw new Error("need at least 2 keys to aggregate");
|
|
1142
|
+
if (publicKeys.length < 2) throw new Error("need at least 2 keys to aggregate");
|
|
1049
1143
|
const keys = [...publicKeys];
|
|
1050
1144
|
assertPublicKeys(keys);
|
|
1051
1145
|
Object.freeze(keys);
|
|
@@ -1053,14 +1147,7 @@ var create = (privateKey, publicKeys) => {
|
|
|
1053
1147
|
const myIndex = findKeyIndex(keys, myPublicKey);
|
|
1054
1148
|
if (myIndex === -1) throw new Error("our key is not in publicKeys");
|
|
1055
1149
|
const aggPubkey = aggregateKeys(keys);
|
|
1056
|
-
return new MusigKeyAgg(
|
|
1057
|
-
privateKey,
|
|
1058
|
-
myPublicKey,
|
|
1059
|
-
keys,
|
|
1060
|
-
myIndex,
|
|
1061
|
-
aggPubkey,
|
|
1062
|
-
aggPubkey
|
|
1063
|
-
);
|
|
1150
|
+
return new MusigKeyAgg(privateKey, myPublicKey, keys, myIndex, aggPubkey, aggPubkey);
|
|
1064
1151
|
};
|
|
1065
1152
|
|
|
1066
1153
|
// src/utils/boltz-swap-tx.ts
|
|
@@ -1136,9 +1223,7 @@ var taprootHashTree = (tree) => {
|
|
|
1136
1223
|
};
|
|
1137
1224
|
var tweakMusig = (musig, tree) => {
|
|
1138
1225
|
const tweak = taprootHashTree(tree).hash;
|
|
1139
|
-
return musig.xonlyTweakAdd(
|
|
1140
|
-
import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak)
|
|
1141
|
-
);
|
|
1226
|
+
return musig.xonlyTweakAdd(import_secp256k12.schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak));
|
|
1142
1227
|
};
|
|
1143
1228
|
var toXOnly = (pubKey) => {
|
|
1144
1229
|
if (pubKey.length === 32) return pubKey;
|
|
@@ -1150,9 +1235,7 @@ var toXOnly = (pubKey) => {
|
|
|
1150
1235
|
}
|
|
1151
1236
|
return pubKey.subarray(1, 33);
|
|
1152
1237
|
}
|
|
1153
|
-
throw new Error(
|
|
1154
|
-
`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`
|
|
1155
|
-
);
|
|
1238
|
+
throw new Error(`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`);
|
|
1156
1239
|
};
|
|
1157
1240
|
var p2trScript = (publicKey) => import_btc_signer.Script.encode(["OP_1", toXOnly(publicKey)]);
|
|
1158
1241
|
var detectSwapOutput = (tweakedKey, transaction) => {
|
|
@@ -1167,8 +1250,7 @@ var detectSwapOutput = (tweakedKey, transaction) => {
|
|
|
1167
1250
|
};
|
|
1168
1251
|
var DUMMY_TAPROOT_SIGNATURE = new Uint8Array(64);
|
|
1169
1252
|
var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
1170
|
-
if (fee < BigInt(0) || fee >= utxo.amount)
|
|
1171
|
-
throw new Error("fee exceeds utxo amount");
|
|
1253
|
+
if (fee < BigInt(0) || fee >= utxo.amount) throw new Error("fee exceeds utxo amount");
|
|
1172
1254
|
const tx = new import_btc_signer.Transaction({ version: 2 });
|
|
1173
1255
|
tx.addOutput({
|
|
1174
1256
|
amount: utxo.amount - fee,
|
|
@@ -1187,9 +1269,7 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
|
1187
1269
|
};
|
|
1188
1270
|
var targetFee = (satPerVbyte, constructTx) => {
|
|
1189
1271
|
const tx = constructTx(BigInt(1));
|
|
1190
|
-
return constructTx(
|
|
1191
|
-
BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte))
|
|
1192
|
-
);
|
|
1272
|
+
return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
|
|
1193
1273
|
};
|
|
1194
1274
|
|
|
1195
1275
|
// src/utils/decoding.ts
|
|
@@ -1197,9 +1277,7 @@ var import_light_bolt11_decoder = __toESM(require("light-bolt11-decoder"), 1);
|
|
|
1197
1277
|
var import_sdk2 = require("@arkade-os/sdk");
|
|
1198
1278
|
var decodeInvoice = (invoice) => {
|
|
1199
1279
|
const decoded = import_light_bolt11_decoder.default.decode(invoice);
|
|
1200
|
-
const millisats = Number(
|
|
1201
|
-
decoded.sections.find((s) => s.name === "amount")?.value ?? "0"
|
|
1202
|
-
);
|
|
1280
|
+
const millisats = Number(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
|
|
1203
1281
|
return {
|
|
1204
1282
|
expiry: decoded.expiry ?? 3600,
|
|
1205
1283
|
amountSats: Math.floor(millisats / 1e3),
|
|
@@ -1262,10 +1340,7 @@ function extractTimeLockFromLeafOutput(scriptHex) {
|
|
|
1262
1340
|
const data = opcodes[hasCSV - 1];
|
|
1263
1341
|
if (data instanceof Uint8Array) {
|
|
1264
1342
|
const dataBytes = new Uint8Array(data).reverse();
|
|
1265
|
-
const {
|
|
1266
|
-
blocks,
|
|
1267
|
-
seconds
|
|
1268
|
-
} = import_bip68.default.decode(
|
|
1343
|
+
const { blocks, seconds } = import_bip68.default.decode(
|
|
1269
1344
|
parseInt(import_base5.hex.encode(dataBytes), 16)
|
|
1270
1345
|
);
|
|
1271
1346
|
return blocks ?? seconds ?? 0;
|
|
@@ -1369,9 +1444,7 @@ var SwapManager = class _SwapManager {
|
|
|
1369
1444
|
this.wsConnectedListeners.add(config.events.onWebSocketConnected);
|
|
1370
1445
|
}
|
|
1371
1446
|
if (config.events?.onWebSocketDisconnected) {
|
|
1372
|
-
this.wsDisconnectedListeners.add(
|
|
1373
|
-
config.events.onWebSocketDisconnected
|
|
1374
|
-
);
|
|
1447
|
+
this.wsDisconnectedListeners.add(config.events.onWebSocketDisconnected);
|
|
1375
1448
|
}
|
|
1376
1449
|
this.currentReconnectDelay = this.config.reconnectDelayMs;
|
|
1377
1450
|
this.currentPollRetryDelay = this.config.pollRetryDelayMs;
|
|
@@ -1522,9 +1595,7 @@ var SwapManager = class _SwapManager {
|
|
|
1522
1595
|
*/
|
|
1523
1596
|
setPollInterval(ms) {
|
|
1524
1597
|
if (ms <= 0) {
|
|
1525
|
-
throw new RangeError(
|
|
1526
|
-
`setPollInterval: ms must be a positive number, got ${ms}`
|
|
1527
|
-
);
|
|
1598
|
+
throw new RangeError(`setPollInterval: ms must be a positive number, got ${ms}`);
|
|
1528
1599
|
}
|
|
1529
1600
|
const cappedInterval = Math.min(ms, this.config.maxPollIntervalMs);
|
|
1530
1601
|
if (cappedInterval !== ms) {
|
|
@@ -1533,10 +1604,7 @@ var SwapManager = class _SwapManager {
|
|
|
1533
1604
|
);
|
|
1534
1605
|
}
|
|
1535
1606
|
this.config.pollInterval = cappedInterval;
|
|
1536
|
-
this.currentPollRetryDelay = Math.min(
|
|
1537
|
-
cappedInterval,
|
|
1538
|
-
this.config.pollRetryDelayMs
|
|
1539
|
-
);
|
|
1607
|
+
this.currentPollRetryDelay = Math.min(cappedInterval, this.config.pollRetryDelayMs);
|
|
1540
1608
|
if (this.isRunning) {
|
|
1541
1609
|
if (this.usePollingFallback) {
|
|
1542
1610
|
this.startPollingFallback();
|
|
@@ -1621,9 +1689,7 @@ var SwapManager = class _SwapManager {
|
|
|
1621
1689
|
}
|
|
1622
1690
|
if (this.isFinalStatus(swap)) {
|
|
1623
1691
|
if (isPendingReverseSwap(swap)) {
|
|
1624
|
-
const response = await this.swapProvider.getReverseSwapTxId(
|
|
1625
|
-
swap.id
|
|
1626
|
-
);
|
|
1692
|
+
const response = await this.swapProvider.getReverseSwapTxId(swap.id);
|
|
1627
1693
|
return { txid: response.id };
|
|
1628
1694
|
}
|
|
1629
1695
|
if (isPendingSubmarineSwap(swap)) {
|
|
@@ -1640,31 +1706,19 @@ var SwapManager = class _SwapManager {
|
|
|
1640
1706
|
if (updatedSwap.status === "invoice.settled") {
|
|
1641
1707
|
this.swapProvider.getReverseSwapTxId(updatedSwap.id).then((response) => resolve({ txid: response.id })).catch((error) => reject(error));
|
|
1642
1708
|
} else {
|
|
1643
|
-
reject(
|
|
1644
|
-
new Error(
|
|
1645
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1646
|
-
)
|
|
1647
|
-
);
|
|
1709
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1648
1710
|
}
|
|
1649
1711
|
} else if (isPendingSubmarineSwap(updatedSwap)) {
|
|
1650
1712
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1651
1713
|
resolve({ txid: updatedSwap.id });
|
|
1652
1714
|
} else {
|
|
1653
|
-
reject(
|
|
1654
|
-
new Error(
|
|
1655
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1656
|
-
)
|
|
1657
|
-
);
|
|
1715
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1658
1716
|
}
|
|
1659
1717
|
} else if (isPendingChainSwap(updatedSwap)) {
|
|
1660
1718
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1661
1719
|
resolve({ txid: updatedSwap.id });
|
|
1662
1720
|
} else {
|
|
1663
|
-
reject(
|
|
1664
|
-
new Error(
|
|
1665
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1666
|
-
)
|
|
1667
|
-
);
|
|
1721
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1668
1722
|
}
|
|
1669
1723
|
}
|
|
1670
1724
|
};
|
|
@@ -1707,16 +1761,12 @@ var SwapManager = class _SwapManager {
|
|
|
1707
1761
|
const connectionTimeout = setTimeout(() => {
|
|
1708
1762
|
logger.error("WebSocket connection timeout");
|
|
1709
1763
|
this.websocket?.close();
|
|
1710
|
-
this.enterPollingFallback(
|
|
1711
|
-
new NetworkError("WebSocket connection failed")
|
|
1712
|
-
);
|
|
1764
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1713
1765
|
}, 1e4);
|
|
1714
1766
|
this.websocket.onerror = (error) => {
|
|
1715
1767
|
clearTimeout(connectionTimeout);
|
|
1716
1768
|
logger.error("WebSocket error:", error);
|
|
1717
|
-
this.enterPollingFallback(
|
|
1718
|
-
new NetworkError("WebSocket connection failed")
|
|
1719
|
-
);
|
|
1769
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1720
1770
|
};
|
|
1721
1771
|
this.websocket.onopen = () => {
|
|
1722
1772
|
clearTimeout(connectionTimeout);
|
|
@@ -1756,9 +1806,7 @@ var SwapManager = class _SwapManager {
|
|
|
1756
1806
|
};
|
|
1757
1807
|
} catch (error) {
|
|
1758
1808
|
logger.error("Failed to create WebSocket:", error);
|
|
1759
|
-
this.enterPollingFallback(
|
|
1760
|
-
new NetworkError("WebSocket connection failed")
|
|
1761
|
-
);
|
|
1809
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1762
1810
|
}
|
|
1763
1811
|
}
|
|
1764
1812
|
/**
|
|
@@ -1793,11 +1841,8 @@ var SwapManager = class _SwapManager {
|
|
|
1793
1841
|
* Schedule WebSocket reconnection with exponential backoff
|
|
1794
1842
|
*/
|
|
1795
1843
|
scheduleReconnect() {
|
|
1796
|
-
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport())
|
|
1797
|
-
|
|
1798
|
-
logger.log(
|
|
1799
|
-
`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`
|
|
1800
|
-
);
|
|
1844
|
+
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport()) return;
|
|
1845
|
+
logger.log(`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`);
|
|
1801
1846
|
this.reconnectTimer = setTimeout(() => {
|
|
1802
1847
|
this.reconnectTimer = null;
|
|
1803
1848
|
this.isReconnecting = false;
|
|
@@ -1812,8 +1857,7 @@ var SwapManager = class _SwapManager {
|
|
|
1812
1857
|
* Subscribe to a specific swap ID on the WebSocket
|
|
1813
1858
|
*/
|
|
1814
1859
|
subscribeToSwap(swapId) {
|
|
1815
|
-
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN)
|
|
1816
|
-
return;
|
|
1860
|
+
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return;
|
|
1817
1861
|
this.websocket.send(
|
|
1818
1862
|
JSON.stringify({
|
|
1819
1863
|
op: "subscribe",
|
|
@@ -1836,9 +1880,7 @@ var SwapManager = class _SwapManager {
|
|
|
1836
1880
|
if (msg.args[0].error) {
|
|
1837
1881
|
logger.error(`Swap ${swapId} error:`, msg.args[0].error);
|
|
1838
1882
|
const error = new Error(msg.args[0].error);
|
|
1839
|
-
this.swapFailedListeners.forEach(
|
|
1840
|
-
(listener) => listener(swap, error)
|
|
1841
|
-
);
|
|
1883
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1842
1884
|
return;
|
|
1843
1885
|
}
|
|
1844
1886
|
const newStatus = msg.args[0].status;
|
|
@@ -1856,19 +1898,14 @@ var SwapManager = class _SwapManager {
|
|
|
1856
1898
|
const oldStatus = swap.status;
|
|
1857
1899
|
if (oldStatus === newStatus) return;
|
|
1858
1900
|
swap.status = newStatus;
|
|
1859
|
-
this.swapUpdateListeners.forEach(
|
|
1860
|
-
(listener) => listener(swap, oldStatus)
|
|
1861
|
-
);
|
|
1901
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
1862
1902
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
1863
1903
|
if (subscribers) {
|
|
1864
1904
|
subscribers.forEach((callback) => {
|
|
1865
1905
|
try {
|
|
1866
1906
|
callback(swap, oldStatus);
|
|
1867
1907
|
} catch (error) {
|
|
1868
|
-
logger.error(
|
|
1869
|
-
`Error in swap subscription callback for ${swap.id}:`,
|
|
1870
|
-
error
|
|
1871
|
-
);
|
|
1908
|
+
logger.error(`Error in swap subscription callback for ${swap.id}:`, error);
|
|
1872
1909
|
}
|
|
1873
1910
|
});
|
|
1874
1911
|
}
|
|
@@ -1893,9 +1930,7 @@ var SwapManager = class _SwapManager {
|
|
|
1893
1930
|
*/
|
|
1894
1931
|
async executeAutonomousAction(swap) {
|
|
1895
1932
|
if (this.swapsInProgress.has(swap.id)) {
|
|
1896
|
-
logger.log(
|
|
1897
|
-
`Swap ${swap.id} is already being processed, skipping autonomous action`
|
|
1898
|
-
);
|
|
1933
|
+
logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
|
|
1899
1934
|
return;
|
|
1900
1935
|
}
|
|
1901
1936
|
try {
|
|
@@ -1910,9 +1945,7 @@ var SwapManager = class _SwapManager {
|
|
|
1910
1945
|
if (isReverseClaimableStatus(swap.status)) {
|
|
1911
1946
|
logger.log(`Auto-claiming reverse swap ${swap.id}`);
|
|
1912
1947
|
await this.executeClaimAction(swap);
|
|
1913
|
-
this.actionExecutedListeners.forEach(
|
|
1914
|
-
(listener) => listener(swap, "claim")
|
|
1915
|
-
);
|
|
1948
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "claim"));
|
|
1916
1949
|
}
|
|
1917
1950
|
} else if (isPendingSubmarineSwap(swap)) {
|
|
1918
1951
|
if (!swap.request?.invoice || swap.request.invoice.length === 0) {
|
|
@@ -1924,9 +1957,7 @@ var SwapManager = class _SwapManager {
|
|
|
1924
1957
|
if (isSubmarineRefundableStatus(swap.status)) {
|
|
1925
1958
|
logger.log(`Auto-refunding submarine swap ${swap.id}`);
|
|
1926
1959
|
await this.executeRefundAction(swap);
|
|
1927
|
-
this.actionExecutedListeners.forEach(
|
|
1928
|
-
(listener) => listener(swap, "refund")
|
|
1929
|
-
);
|
|
1960
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
|
|
1930
1961
|
}
|
|
1931
1962
|
} else if (isPendingChainSwap(swap)) {
|
|
1932
1963
|
if (isChainClaimableStatus(swap.status)) {
|
|
@@ -1976,13 +2007,8 @@ var SwapManager = class _SwapManager {
|
|
|
1976
2007
|
}
|
|
1977
2008
|
}
|
|
1978
2009
|
} catch (error) {
|
|
1979
|
-
logger.error(
|
|
1980
|
-
|
|
1981
|
-
error
|
|
1982
|
-
);
|
|
1983
|
-
this.swapFailedListeners.forEach(
|
|
1984
|
-
(listener) => listener(swap, error)
|
|
1985
|
-
);
|
|
2010
|
+
logger.error(`Failed to execute autonomous action for swap ${swap.id}:`, error);
|
|
2011
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1986
2012
|
} finally {
|
|
1987
2013
|
this.swapsInProgress.delete(swap.id);
|
|
1988
2014
|
}
|
|
@@ -2083,9 +2109,7 @@ var SwapManager = class _SwapManager {
|
|
|
2083
2109
|
logger.log(`Resuming chain refund for swap ${swap.id}`);
|
|
2084
2110
|
await this.executeAutonomousAction(swap);
|
|
2085
2111
|
} else if (isPendingChainSwap(swap) && swap.request.to === "ARK" && isChainSignableStatus(swap.status)) {
|
|
2086
|
-
logger.log(
|
|
2087
|
-
`Resuming server claim signing for swap ${swap.id}`
|
|
2088
|
-
);
|
|
2112
|
+
logger.log(`Resuming server claim signing for swap ${swap.id}`);
|
|
2089
2113
|
await this.executeAutonomousAction(swap);
|
|
2090
2114
|
}
|
|
2091
2115
|
} catch (error) {
|
|
@@ -2144,9 +2168,7 @@ var SwapManager = class _SwapManager {
|
|
|
2144
2168
|
}
|
|
2145
2169
|
async pollSingleSwap(swap) {
|
|
2146
2170
|
try {
|
|
2147
|
-
const statusResponse = await this.swapProvider.getSwapStatus(
|
|
2148
|
-
swap.id
|
|
2149
|
-
);
|
|
2171
|
+
const statusResponse = await this.swapProvider.getSwapStatus(swap.id);
|
|
2150
2172
|
this.notFoundCounts.delete(swap.id);
|
|
2151
2173
|
if (statusResponse.status !== swap.status) {
|
|
2152
2174
|
await this.handleSwapStatusUpdate(swap, statusResponse.status);
|
|
@@ -2157,9 +2179,7 @@ var SwapManager = class _SwapManager {
|
|
|
2157
2179
|
return;
|
|
2158
2180
|
}
|
|
2159
2181
|
if (error instanceof NetworkError && error.statusCode === 429) {
|
|
2160
|
-
logger.warn(
|
|
2161
|
-
`Rate-limited polling swap ${swap.id}, retrying in 2s`
|
|
2162
|
-
);
|
|
2182
|
+
logger.warn(`Rate-limited polling swap ${swap.id}, retrying in 2s`);
|
|
2163
2183
|
const existing = this.pollRetryTimers.get(swap.id);
|
|
2164
2184
|
if (existing) clearTimeout(existing);
|
|
2165
2185
|
this.pollRetryTimers.set(
|
|
@@ -2167,25 +2187,17 @@ var SwapManager = class _SwapManager {
|
|
|
2167
2187
|
setTimeout(async () => {
|
|
2168
2188
|
this.pollRetryTimers.delete(swap.id);
|
|
2169
2189
|
try {
|
|
2170
|
-
const retry = await this.swapProvider.getSwapStatus(
|
|
2171
|
-
swap.id
|
|
2172
|
-
);
|
|
2190
|
+
const retry = await this.swapProvider.getSwapStatus(swap.id);
|
|
2173
2191
|
this.notFoundCounts.delete(swap.id);
|
|
2174
2192
|
if (retry.status !== swap.status) {
|
|
2175
|
-
await this.handleSwapStatusUpdate(
|
|
2176
|
-
swap,
|
|
2177
|
-
retry.status
|
|
2178
|
-
);
|
|
2193
|
+
await this.handleSwapStatusUpdate(swap, retry.status);
|
|
2179
2194
|
}
|
|
2180
2195
|
} catch (retryError) {
|
|
2181
2196
|
if (retryError instanceof SwapNotFoundError) {
|
|
2182
2197
|
await this.handleSwapNotFound(swap);
|
|
2183
2198
|
return;
|
|
2184
2199
|
}
|
|
2185
|
-
logger.error(
|
|
2186
|
-
`Retry poll for swap ${swap.id} also failed:`,
|
|
2187
|
-
retryError
|
|
2188
|
-
);
|
|
2200
|
+
logger.error(`Retry poll for swap ${swap.id} also failed:`, retryError);
|
|
2189
2201
|
}
|
|
2190
2202
|
}, 2e3)
|
|
2191
2203
|
);
|
|
@@ -2237,9 +2249,7 @@ var SwapManager = class _SwapManager {
|
|
|
2237
2249
|
this.pollRetryTimers.delete(swap.id);
|
|
2238
2250
|
}
|
|
2239
2251
|
this.notFoundCounts.delete(swap.id);
|
|
2240
|
-
this.swapUpdateListeners.forEach(
|
|
2241
|
-
(listener) => listener(swap, oldStatus)
|
|
2242
|
-
);
|
|
2252
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
2243
2253
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
2244
2254
|
if (subscribers) {
|
|
2245
2255
|
subscribers.forEach((callback) => {
|
|
@@ -2304,9 +2314,7 @@ async function saveSwap(swap, saver) {
|
|
|
2304
2314
|
if (saver.saveSubmarineSwap) {
|
|
2305
2315
|
await saver.saveSubmarineSwap(swap);
|
|
2306
2316
|
} else {
|
|
2307
|
-
console.warn(
|
|
2308
|
-
"No saveSubmarineSwap handler provided, swap not saved"
|
|
2309
|
-
);
|
|
2317
|
+
console.warn("No saveSubmarineSwap handler provided, swap not saved");
|
|
2310
2318
|
}
|
|
2311
2319
|
} else if (isPendingChainSwap(swap)) {
|
|
2312
2320
|
if (saver.saveChainSwap) {
|
|
@@ -2363,6 +2371,26 @@ function enrichSubmarineSwapInvoice(swap, invoice) {
|
|
|
2363
2371
|
return swap;
|
|
2364
2372
|
}
|
|
2365
2373
|
|
|
2374
|
+
// src/repositories/swap-repository.ts
|
|
2375
|
+
function hasImpossibleSwapsFilter(filter) {
|
|
2376
|
+
if (!filter) return false;
|
|
2377
|
+
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;
|
|
2378
|
+
}
|
|
2379
|
+
function matchesCriterion(value, criterion) {
|
|
2380
|
+
if (criterion === void 0) return true;
|
|
2381
|
+
return Array.isArray(criterion) ? criterion.includes(value) : value === criterion;
|
|
2382
|
+
}
|
|
2383
|
+
function applySwapsFilter(swaps, filter) {
|
|
2384
|
+
return swaps.filter(
|
|
2385
|
+
(swap) => !!swap && matchesCriterion(swap.id, filter.id) && matchesCriterion(swap.status, filter.status) && matchesCriterion(swap.type, filter.type)
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
function applyCreatedAtOrder(swaps, filter) {
|
|
2389
|
+
if (filter?.orderBy !== "createdAt") return swaps;
|
|
2390
|
+
const direction = filter.orderDirection === "asc" ? 1 : -1;
|
|
2391
|
+
return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2366
2394
|
// src/repositories/IndexedDb/swap-repository.ts
|
|
2367
2395
|
var import_sdk4 = require("@arkade-os/sdk");
|
|
2368
2396
|
var DEFAULT_DB_NAME = "arkade-boltz-swap";
|
|
@@ -2378,6 +2406,10 @@ function initDatabase(db) {
|
|
|
2378
2406
|
swapStore.createIndex("createdAt", "createdAt", { unique: false });
|
|
2379
2407
|
}
|
|
2380
2408
|
}
|
|
2409
|
+
function asArray(v) {
|
|
2410
|
+
if (v === void 0) return void 0;
|
|
2411
|
+
return Array.isArray(v) ? v : [v];
|
|
2412
|
+
}
|
|
2381
2413
|
var IndexedDbSwapRepository = class {
|
|
2382
2414
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
2383
2415
|
this.dbName = dbName;
|
|
@@ -2392,10 +2424,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2392
2424
|
async saveSwap(swap) {
|
|
2393
2425
|
const db = await this.getDB();
|
|
2394
2426
|
return new Promise((resolve, reject) => {
|
|
2395
|
-
const transaction = db.transaction(
|
|
2396
|
-
[STORE_SWAPS_STATE],
|
|
2397
|
-
"readwrite"
|
|
2398
|
-
);
|
|
2427
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2399
2428
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2400
2429
|
const request = store.put(swap);
|
|
2401
2430
|
request.onsuccess = () => resolve();
|
|
@@ -2405,10 +2434,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2405
2434
|
async deleteSwap(id) {
|
|
2406
2435
|
const db = await this.getDB();
|
|
2407
2436
|
return new Promise((resolve, reject) => {
|
|
2408
|
-
const transaction = db.transaction(
|
|
2409
|
-
[STORE_SWAPS_STATE],
|
|
2410
|
-
"readwrite"
|
|
2411
|
-
);
|
|
2437
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2412
2438
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2413
2439
|
const request = store.delete(id);
|
|
2414
2440
|
request.onsuccess = () => resolve();
|
|
@@ -2421,10 +2447,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2421
2447
|
async clear() {
|
|
2422
2448
|
const db = await this.getDB();
|
|
2423
2449
|
return new Promise((resolve, reject) => {
|
|
2424
|
-
const transaction = db.transaction(
|
|
2425
|
-
[STORE_SWAPS_STATE],
|
|
2426
|
-
"readwrite"
|
|
2427
|
-
);
|
|
2450
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2428
2451
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2429
2452
|
const request = store.clear();
|
|
2430
2453
|
request.onsuccess = () => resolve();
|
|
@@ -2441,11 +2464,10 @@ var IndexedDbSwapRepository = class {
|
|
|
2441
2464
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2442
2465
|
})
|
|
2443
2466
|
);
|
|
2444
|
-
return Promise.all(requests).then(
|
|
2445
|
-
(results) => results.flatMap((result) => result)
|
|
2446
|
-
);
|
|
2467
|
+
return Promise.all(requests).then((results) => results.flatMap((result) => result));
|
|
2447
2468
|
}
|
|
2448
2469
|
async getAllSwapsFromStore(filter) {
|
|
2470
|
+
if (hasImpossibleSwapsFilter(filter)) return [];
|
|
2449
2471
|
const db = await this.getDB();
|
|
2450
2472
|
const store = db.transaction([STORE_SWAPS_STATE], "readonly").objectStore(STORE_SWAPS_STATE);
|
|
2451
2473
|
if (!filter || Object.keys(filter).length === 0) {
|
|
@@ -2455,9 +2477,8 @@ var IndexedDbSwapRepository = class {
|
|
|
2455
2477
|
request.onerror = () => reject(request.error);
|
|
2456
2478
|
});
|
|
2457
2479
|
}
|
|
2458
|
-
const
|
|
2459
|
-
if (
|
|
2460
|
-
const ids = normalizedFilter.get("id");
|
|
2480
|
+
const ids = asArray(filter.id);
|
|
2481
|
+
if (ids) {
|
|
2461
2482
|
const swaps = await Promise.all(
|
|
2462
2483
|
ids.map(
|
|
2463
2484
|
(id) => new Promise((resolve, reject) => {
|
|
@@ -2467,34 +2488,17 @@ var IndexedDbSwapRepository = class {
|
|
|
2467
2488
|
})
|
|
2468
2489
|
)
|
|
2469
2490
|
);
|
|
2470
|
-
return
|
|
2471
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2472
|
-
filter
|
|
2473
|
-
);
|
|
2491
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2474
2492
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2478
|
-
|
|
2479
|
-
"type",
|
|
2480
|
-
types
|
|
2481
|
-
);
|
|
2482
|
-
return this.sortIfNeeded(
|
|
2483
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2484
|
-
filter
|
|
2485
|
-
);
|
|
2493
|
+
const types = asArray(filter.type);
|
|
2494
|
+
if (types) {
|
|
2495
|
+
const swaps = await this.getSwapsByIndexValues(store, "type", types);
|
|
2496
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2486
2497
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2490
|
-
|
|
2491
|
-
"status",
|
|
2492
|
-
ids
|
|
2493
|
-
);
|
|
2494
|
-
return this.sortIfNeeded(
|
|
2495
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2496
|
-
filter
|
|
2497
|
-
);
|
|
2498
|
+
const statuses = asArray(filter.status);
|
|
2499
|
+
if (statuses) {
|
|
2500
|
+
const swaps = await this.getSwapsByIndexValues(store, "status", statuses);
|
|
2501
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2498
2502
|
}
|
|
2499
2503
|
if (filter.orderBy === "createdAt") {
|
|
2500
2504
|
return this.getAllSwapsByCreatedAt(store, filter.orderDirection);
|
|
@@ -2504,22 +2508,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2504
2508
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2505
2509
|
request.onerror = () => reject(request.error);
|
|
2506
2510
|
});
|
|
2507
|
-
return
|
|
2508
|
-
this.applySwapsFilter(allSwaps, normalizedFilter),
|
|
2509
|
-
filter
|
|
2510
|
-
);
|
|
2511
|
-
}
|
|
2512
|
-
applySwapsFilter(swaps, filter) {
|
|
2513
|
-
return swaps.filter((swap) => {
|
|
2514
|
-
if (swap === void 0) return false;
|
|
2515
|
-
if (filter.has("id") && !filter.get("id")?.includes(swap.id))
|
|
2516
|
-
return false;
|
|
2517
|
-
if (filter.has("status") && !filter.get("status")?.includes(swap.status))
|
|
2518
|
-
return false;
|
|
2519
|
-
if (filter.has("type") && !filter.get("type")?.includes(swap.type))
|
|
2520
|
-
return false;
|
|
2521
|
-
return true;
|
|
2522
|
-
});
|
|
2511
|
+
return applyCreatedAtOrder(applySwapsFilter(allSwaps, filter), filter);
|
|
2523
2512
|
}
|
|
2524
2513
|
async getAllSwapsByCreatedAt(store, orderDirection) {
|
|
2525
2514
|
const index = store.index("createdAt");
|
|
@@ -2539,30 +2528,12 @@ var IndexedDbSwapRepository = class {
|
|
|
2539
2528
|
};
|
|
2540
2529
|
});
|
|
2541
2530
|
}
|
|
2542
|
-
sortIfNeeded(swaps, filter) {
|
|
2543
|
-
if (filter?.orderBy !== "createdAt") return swaps;
|
|
2544
|
-
const direction = filter.orderDirection === "asc" ? 1 : -1;
|
|
2545
|
-
return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
|
|
2546
|
-
}
|
|
2547
2531
|
async [Symbol.asyncDispose]() {
|
|
2548
2532
|
if (!this.db) return;
|
|
2549
2533
|
await (0, import_sdk4.closeDatabase)(this.dbName);
|
|
2550
2534
|
this.db = null;
|
|
2551
2535
|
}
|
|
2552
2536
|
};
|
|
2553
|
-
var FILTER_FIELDS = ["id", "status", "type"];
|
|
2554
|
-
function normalizeFilter(filter) {
|
|
2555
|
-
const res = /* @__PURE__ */ new Map();
|
|
2556
|
-
FILTER_FIELDS.forEach((current) => {
|
|
2557
|
-
if (!filter?.[current]) return;
|
|
2558
|
-
if (Array.isArray(filter[current])) {
|
|
2559
|
-
res.set(current, filter[current]);
|
|
2560
|
-
} else {
|
|
2561
|
-
res.set(current, [filter[current]]);
|
|
2562
|
-
}
|
|
2563
|
-
});
|
|
2564
|
-
return res;
|
|
2565
|
-
}
|
|
2566
2537
|
|
|
2567
2538
|
// src/utils/identity.ts
|
|
2568
2539
|
var import_sdk5 = require("@arkade-os/sdk");
|
|
@@ -2574,13 +2545,8 @@ function claimVHTLCIdentity(identity, preimage) {
|
|
|
2574
2545
|
let signedTx = await identity.sign(cpy, inputIndexes);
|
|
2575
2546
|
signedTx = import_sdk5.Transaction.fromPSBT(signedTx.toPSBT());
|
|
2576
2547
|
if (preimage) {
|
|
2577
|
-
for (const inputIndex of inputIndexes || Array.from(
|
|
2578
|
-
|
|
2579
|
-
(_, i) => i
|
|
2580
|
-
)) {
|
|
2581
|
-
(0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [
|
|
2582
|
-
preimage
|
|
2583
|
-
]);
|
|
2548
|
+
for (const inputIndex of inputIndexes || Array.from({ length: signedTx.inputsLength }, (_, i) => i)) {
|
|
2549
|
+
(0, import_sdk5.setArkPsbtField)(signedTx, inputIndex, import_sdk5.ConditionWitness, [preimage]);
|
|
2584
2550
|
}
|
|
2585
2551
|
}
|
|
2586
2552
|
return signedTx;
|
|
@@ -2635,17 +2601,13 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2635
2601
|
if (!sweepTapTreeRoot) {
|
|
2636
2602
|
throw new Error("Sweep tap tree root not set");
|
|
2637
2603
|
}
|
|
2638
|
-
const xOnlyPublicKeys = event.cosignersPublicKeys.map(
|
|
2639
|
-
(k) => k.slice(2)
|
|
2640
|
-
);
|
|
2604
|
+
const xOnlyPublicKeys = event.cosignersPublicKeys.map((k) => k.slice(2));
|
|
2641
2605
|
const signerPublicKey = await session.getPublicKey();
|
|
2642
2606
|
const xonlySignerPublicKey = signerPublicKey.subarray(1);
|
|
2643
2607
|
if (!xOnlyPublicKeys.includes(import_base7.hex.encode(xonlySignerPublicKey))) {
|
|
2644
2608
|
return { skip: true };
|
|
2645
2609
|
}
|
|
2646
|
-
const commitmentTx = import_sdk6.Transaction.fromPSBT(
|
|
2647
|
-
import_base7.base64.decode(event.unsignedCommitmentTx)
|
|
2648
|
-
);
|
|
2610
|
+
const commitmentTx = import_sdk6.Transaction.fromPSBT(import_base7.base64.decode(event.unsignedCommitmentTx));
|
|
2649
2611
|
(0, import_sdk6.validateVtxoTxGraph)(vtxoTree, commitmentTx, sweepTapTreeRoot);
|
|
2650
2612
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
2651
2613
|
if (!sharedOutput?.amount) {
|
|
@@ -2661,18 +2623,11 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2661
2623
|
if (!session) {
|
|
2662
2624
|
return { fullySigned: true };
|
|
2663
2625
|
}
|
|
2664
|
-
const { hasAllNonces } = await session.aggregatedNonces(
|
|
2665
|
-
event.txid,
|
|
2666
|
-
event.nonces
|
|
2667
|
-
);
|
|
2626
|
+
const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
|
|
2668
2627
|
if (!hasAllNonces) return { fullySigned: false };
|
|
2669
2628
|
const signatures = await session.sign();
|
|
2670
2629
|
const pubkey = import_base7.hex.encode(await session.getPublicKey());
|
|
2671
|
-
await arkProvider.submitTreeSignatures(
|
|
2672
|
-
event.id,
|
|
2673
|
-
pubkey,
|
|
2674
|
-
signatures
|
|
2675
|
-
);
|
|
2630
|
+
await arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
|
|
2676
2631
|
return { fullySigned: true };
|
|
2677
2632
|
},
|
|
2678
2633
|
onBatchFinalization: async (event, _, connectorTree) => {
|
|
@@ -2680,9 +2635,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2680
2635
|
return;
|
|
2681
2636
|
}
|
|
2682
2637
|
if (!connectorTree) {
|
|
2683
|
-
throw new Error(
|
|
2684
|
-
"BatchFinalizationEvent: expected connector tree to be defined"
|
|
2685
|
-
);
|
|
2638
|
+
throw new Error("BatchFinalizationEvent: expected connector tree to be defined");
|
|
2686
2639
|
}
|
|
2687
2640
|
(0, import_sdk6.validateConnectorsTxGraph)(event.commitmentTx, connectorTree);
|
|
2688
2641
|
const connectors = connectorTree.leaves();
|
|
@@ -2697,9 +2650,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2697
2650
|
connectors[connectorIndex]
|
|
2698
2651
|
);
|
|
2699
2652
|
const signedForfeitTx = await identity.sign(forfeitTx);
|
|
2700
|
-
await arkProvider.submitSignedForfeitTxs([
|
|
2701
|
-
import_base7.base64.encode(signedForfeitTx.toPSBT())
|
|
2702
|
-
]);
|
|
2653
|
+
await arkProvider.submitSignedForfeitTxs([import_base7.base64.encode(signedForfeitTx.toPSBT())]);
|
|
2703
2654
|
}
|
|
2704
2655
|
};
|
|
2705
2656
|
}
|
|
@@ -2759,36 +2710,22 @@ var createVHTLCScript = (args) => {
|
|
|
2759
2710
|
serverPubkey,
|
|
2760
2711
|
timeoutBlockHeights: vhtlcTimeouts
|
|
2761
2712
|
} = args;
|
|
2762
|
-
const receiverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
);
|
|
2766
|
-
const senderXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2767
|
-
import_base8.hex.decode(senderPubkey),
|
|
2768
|
-
"sender"
|
|
2769
|
-
);
|
|
2770
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2771
|
-
import_base8.hex.decode(serverPubkey),
|
|
2772
|
-
"server"
|
|
2773
|
-
);
|
|
2713
|
+
const receiverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(receiverPubkey), "receiver");
|
|
2714
|
+
const senderXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(senderPubkey), "sender");
|
|
2715
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(import_base8.hex.decode(serverPubkey), "server");
|
|
2774
2716
|
const vhtlcScript = new import_sdk7.VHTLC.Script({
|
|
2775
2717
|
preimageHash: (0, import_legacy.ripemd160)(preimageHash),
|
|
2776
2718
|
sender: senderXOnlyPublicKey,
|
|
2777
2719
|
receiver: receiverXOnlyPublicKey,
|
|
2778
2720
|
server: serverXOnlyPublicKey,
|
|
2779
2721
|
refundLocktime: BigInt(vhtlcTimeouts.refund),
|
|
2780
|
-
unilateralClaimDelay: toBip68RelativeTimelock(
|
|
2781
|
-
|
|
2782
|
-
),
|
|
2783
|
-
unilateralRefundDelay: toBip68RelativeTimelock(
|
|
2784
|
-
vhtlcTimeouts.unilateralRefund
|
|
2785
|
-
),
|
|
2722
|
+
unilateralClaimDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralClaim),
|
|
2723
|
+
unilateralRefundDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralRefund),
|
|
2786
2724
|
unilateralRefundWithoutReceiverDelay: toBip68RelativeTimelock(
|
|
2787
2725
|
vhtlcTimeouts.unilateralRefundWithoutReceiver
|
|
2788
2726
|
)
|
|
2789
2727
|
});
|
|
2790
|
-
if (!vhtlcScript.claimScript)
|
|
2791
|
-
throw new Error("Failed to create VHTLC script");
|
|
2728
|
+
if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
|
|
2792
2729
|
const hrp = network === "bitcoin" ? "ark" : "tark";
|
|
2793
2730
|
const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
|
|
2794
2731
|
return { vhtlcScript, vhtlcAddress };
|
|
@@ -2822,11 +2759,7 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2822
2759
|
unknown: [import_sdk7.VtxoTaprootTree.encode(input.tapTree)],
|
|
2823
2760
|
sequence: (0, import_sdk7.getSequence)(input.tapLeafScript)
|
|
2824
2761
|
};
|
|
2825
|
-
const registerIntent = import_sdk7.Intent.create(
|
|
2826
|
-
intentMessage,
|
|
2827
|
-
[intentInput],
|
|
2828
|
-
[output]
|
|
2829
|
-
);
|
|
2762
|
+
const registerIntent = import_sdk7.Intent.create(intentMessage, [intentInput], [output]);
|
|
2830
2763
|
const deleteIntent = import_sdk7.Intent.create(deleteMessage, [intentInput]);
|
|
2831
2764
|
const [signedRegisterIntent, signedDeleteIntent] = await Promise.all([
|
|
2832
2765
|
identity.sign(registerIntent),
|
|
@@ -2850,14 +2783,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2850
2783
|
normalizeToXOnlyKey(forfeitPubkey, "forfeit"),
|
|
2851
2784
|
isRecoverable2 ? void 0 : import_btc_signer4.OutScript.encode(decodedAddress)
|
|
2852
2785
|
);
|
|
2853
|
-
const topics = [
|
|
2854
|
-
|
|
2855
|
-
`${input.txid}:${input.vout}`
|
|
2856
|
-
];
|
|
2857
|
-
const eventStream = arkProvider.getEventStream(
|
|
2858
|
-
abortController.signal,
|
|
2859
|
-
topics
|
|
2860
|
-
);
|
|
2786
|
+
const topics = [import_base8.hex.encode(signerPublicKey), `${input.txid}:${input.vout}`];
|
|
2787
|
+
const eventStream = arkProvider.getEventStream(abortController.signal, topics);
|
|
2861
2788
|
const commitmentTxid = await import_sdk7.Batch.join(eventStream, handler, {
|
|
2862
2789
|
abortController
|
|
2863
2790
|
});
|
|
@@ -2878,14 +2805,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2878
2805
|
};
|
|
2879
2806
|
var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKey, input, output, arkInfo, arkProvider) => {
|
|
2880
2807
|
const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
|
|
2881
|
-
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
|
|
2882
|
-
|
|
2883
|
-
);
|
|
2884
|
-
const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)(
|
|
2885
|
-
[input],
|
|
2886
|
-
[output],
|
|
2887
|
-
serverUnrollScript
|
|
2888
|
-
);
|
|
2808
|
+
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
|
|
2809
|
+
const { arkTx, checkpoints } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
|
|
2889
2810
|
const signedArkTx = await identity.sign(arkTx);
|
|
2890
2811
|
const { arkTxid, finalArkTx, signedCheckpointTxs } = await arkProvider.submitTx(
|
|
2891
2812
|
import_base8.base64.encode(signedArkTx.toPSBT()),
|
|
@@ -2893,9 +2814,7 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2893
2814
|
);
|
|
2894
2815
|
const finalTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(finalArkTx));
|
|
2895
2816
|
const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
|
|
2896
|
-
const claimLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2897
|
-
scriptFromTapLeafScript(vhtlcScript.claim())
|
|
2898
|
-
);
|
|
2817
|
+
const claimLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(vhtlcScript.claim()));
|
|
2899
2818
|
for (let i = 0; i < finalTx.inputsLength; i++) {
|
|
2900
2819
|
if (!verifySignatures(finalTx, i, [serverPubkeyHex], claimLeafHash)) {
|
|
2901
2820
|
throw new Error("Invalid final Ark transaction");
|
|
@@ -2905,13 +2824,9 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2905
2824
|
signedCheckpointTxs.map(async (c, idx) => {
|
|
2906
2825
|
const tx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(c));
|
|
2907
2826
|
const checkpointLeaf = checkpoints[idx].getInput(0).tapLeafScript[0];
|
|
2908
|
-
const cpLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2909
|
-
scriptFromTapLeafScript(checkpointLeaf)
|
|
2910
|
-
);
|
|
2827
|
+
const cpLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
|
|
2911
2828
|
if (!verifySignatures(tx, 0, [serverPubkeyHex], cpLeafHash)) {
|
|
2912
|
-
throw new Error(
|
|
2913
|
-
"Invalid server signature in checkpoint transaction"
|
|
2914
|
-
);
|
|
2829
|
+
throw new Error("Invalid server signature in checkpoint transaction");
|
|
2915
2830
|
}
|
|
2916
2831
|
const signedCheckpoint = await identity.sign(tx, [0]);
|
|
2917
2832
|
return import_base8.base64.encode(signedCheckpoint.toPSBT());
|
|
@@ -2921,61 +2836,37 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2921
2836
|
};
|
|
2922
2837
|
var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnlyPublicKey, ourXOnlyPublicKey, serverXOnlyPublicKey, input, output, arkInfo, refundFunc) => {
|
|
2923
2838
|
const rawCheckpointTapscript = import_base8.hex.decode(arkInfo.checkpointTapscript);
|
|
2924
|
-
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(
|
|
2925
|
-
|
|
2839
|
+
const serverUnrollScript = import_sdk7.CSVMultisigTapscript.decode(rawCheckpointTapscript);
|
|
2840
|
+
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)(
|
|
2841
|
+
[input],
|
|
2842
|
+
[output],
|
|
2843
|
+
serverUnrollScript
|
|
2926
2844
|
);
|
|
2927
|
-
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = (0, import_sdk7.buildOffchainTx)([input], [output], serverUnrollScript);
|
|
2928
2845
|
if (checkpointPtxs.length !== 1)
|
|
2929
|
-
throw new Error(
|
|
2930
|
-
`Expected one checkpoint transaction, got ${checkpointPtxs.length}`
|
|
2931
|
-
);
|
|
2846
|
+
throw new Error(`Expected one checkpoint transaction, got ${checkpointPtxs.length}`);
|
|
2932
2847
|
const unsignedCheckpointTx = checkpointPtxs[0];
|
|
2933
2848
|
let boltzSignedRefundTx;
|
|
2934
2849
|
let boltzSignedCheckpointTx;
|
|
2935
2850
|
try {
|
|
2936
|
-
const result = await refundFunc(
|
|
2937
|
-
swapId,
|
|
2938
|
-
unsignedRefundTx,
|
|
2939
|
-
unsignedCheckpointTx
|
|
2940
|
-
);
|
|
2851
|
+
const result = await refundFunc(swapId, unsignedRefundTx, unsignedCheckpointTx);
|
|
2941
2852
|
boltzSignedRefundTx = result.transaction;
|
|
2942
2853
|
boltzSignedCheckpointTx = result.checkpoint;
|
|
2943
2854
|
} catch (error) {
|
|
2944
|
-
throw new BoltzRefundError(
|
|
2945
|
-
`Boltz rejected refund for swap ${swapId}`,
|
|
2946
|
-
error
|
|
2947
|
-
);
|
|
2855
|
+
throw new BoltzRefundError(`Boltz rejected refund for swap ${swapId}`, error);
|
|
2948
2856
|
}
|
|
2949
2857
|
const boltzXOnlyPublicKeyHex = import_base8.hex.encode(boltzXOnlyPublicKey);
|
|
2950
|
-
const refundLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2951
|
-
|
|
2952
|
-
);
|
|
2953
|
-
if (!verifySignatures(
|
|
2954
|
-
boltzSignedRefundTx,
|
|
2955
|
-
0,
|
|
2956
|
-
[boltzXOnlyPublicKeyHex],
|
|
2957
|
-
refundLeafHash
|
|
2958
|
-
)) {
|
|
2858
|
+
const refundLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(input.tapLeafScript));
|
|
2859
|
+
if (!verifySignatures(boltzSignedRefundTx, 0, [boltzXOnlyPublicKeyHex], refundLeafHash)) {
|
|
2959
2860
|
throw new Error("Invalid Boltz signature in refund transaction");
|
|
2960
2861
|
}
|
|
2961
2862
|
const checkpointLeaf = unsignedCheckpointTx.getInput(0).tapLeafScript[0];
|
|
2962
|
-
const checkpointLeafHash = (0, import_payment3.tapLeafHash)(
|
|
2963
|
-
|
|
2964
|
-
);
|
|
2965
|
-
if (!verifySignatures(
|
|
2966
|
-
boltzSignedCheckpointTx,
|
|
2967
|
-
0,
|
|
2968
|
-
[boltzXOnlyPublicKeyHex],
|
|
2969
|
-
checkpointLeafHash
|
|
2970
|
-
)) {
|
|
2863
|
+
const checkpointLeafHash = (0, import_payment3.tapLeafHash)(scriptFromTapLeafScript(checkpointLeaf));
|
|
2864
|
+
if (!verifySignatures(boltzSignedCheckpointTx, 0, [boltzXOnlyPublicKeyHex], checkpointLeafHash)) {
|
|
2971
2865
|
throw new Error("Invalid Boltz signature in checkpoint transaction");
|
|
2972
2866
|
}
|
|
2973
2867
|
const signedRefundTx = await identity.sign(unsignedRefundTx);
|
|
2974
2868
|
const signedCheckpointTx = await identity.sign(unsignedCheckpointTx);
|
|
2975
|
-
const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
2976
|
-
boltzSignedRefundTx,
|
|
2977
|
-
signedRefundTx
|
|
2978
|
-
);
|
|
2869
|
+
const combinedSignedRefundTx = (0, import_sdk7.combineTapscriptSigs)(boltzSignedRefundTx, signedRefundTx);
|
|
2979
2870
|
const combinedSignedCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
2980
2871
|
boltzSignedCheckpointTx,
|
|
2981
2872
|
signedCheckpointTx
|
|
@@ -2999,25 +2890,16 @@ var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnly
|
|
|
2999
2890
|
`Expected one signed checkpoint transaction, got ${signedCheckpointTxs.length}`
|
|
3000
2891
|
);
|
|
3001
2892
|
}
|
|
3002
|
-
const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(
|
|
3003
|
-
import_base8.base64.decode(signedCheckpointTxs[0])
|
|
3004
|
-
);
|
|
2893
|
+
const serverSignedCheckpointTx = import_sdk7.Transaction.fromPSBT(import_base8.base64.decode(signedCheckpointTxs[0]));
|
|
3005
2894
|
const serverPubkeyHex = import_base8.hex.encode(serverXOnlyPublicKey);
|
|
3006
|
-
if (!verifySignatures(
|
|
3007
|
-
serverSignedCheckpointTx,
|
|
3008
|
-
0,
|
|
3009
|
-
[serverPubkeyHex],
|
|
3010
|
-
checkpointLeafHash
|
|
3011
|
-
)) {
|
|
2895
|
+
if (!verifySignatures(serverSignedCheckpointTx, 0, [serverPubkeyHex], checkpointLeafHash)) {
|
|
3012
2896
|
throw new Error("Invalid server signature in checkpoint transaction");
|
|
3013
2897
|
}
|
|
3014
2898
|
const finalCheckpointTx = (0, import_sdk7.combineTapscriptSigs)(
|
|
3015
2899
|
combinedSignedCheckpointTx,
|
|
3016
2900
|
serverSignedCheckpointTx
|
|
3017
2901
|
);
|
|
3018
|
-
await arkProvider.finalizeTx(arkTxid, [
|
|
3019
|
-
import_base8.base64.encode(finalCheckpointTx.toPSBT())
|
|
3020
|
-
]);
|
|
2902
|
+
await arkProvider.finalizeTx(arkTxid, [import_base8.base64.encode(finalCheckpointTx.toPSBT())]);
|
|
3021
2903
|
};
|
|
3022
2904
|
function scriptFromTapLeafScript(leaf) {
|
|
3023
2905
|
return leaf[1].subarray(0, leaf[1].length - 1);
|
|
@@ -3025,21 +2907,21 @@ function scriptFromTapLeafScript(leaf) {
|
|
|
3025
2907
|
|
|
3026
2908
|
// src/arkade-swaps.ts
|
|
3027
2909
|
var dedupeVtxos = (vtxos) => [
|
|
3028
|
-
...new Map(
|
|
3029
|
-
vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])
|
|
3030
|
-
).values()
|
|
2910
|
+
...new Map(vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])).values()
|
|
3031
2911
|
];
|
|
3032
2912
|
var hasNonEmptyString = (value) => typeof value === "string" && value.length > 0;
|
|
3033
2913
|
var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
|
|
3034
2914
|
const hasRequiredSwapMetadata = hasNonEmptyString(swap.id) && hasNonEmptyString(swap.request.refundPublicKey) && hasNonEmptyString(swap.response.address) && hasNonEmptyString(swap.response.claimPublicKey) && !!swap.response.timeoutBlockHeights;
|
|
3035
2915
|
if (!hasRequiredSwapMetadata) return false;
|
|
3036
|
-
return refundableVtxos.some(
|
|
3037
|
-
(vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo)
|
|
3038
|
-
);
|
|
2916
|
+
return refundableVtxos.some((vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo));
|
|
3039
2917
|
};
|
|
3040
2918
|
var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
|
|
3041
2919
|
var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
|
|
3042
2920
|
var CLAIM_VTXO_RETRY_DELAY_MS = 500;
|
|
2921
|
+
var quoteOptionsForSwap = (swap) => {
|
|
2922
|
+
const amount = swap.response?.claimDetails?.amount;
|
|
2923
|
+
return typeof amount === "number" ? { minAcceptableAmount: amount } : void 0;
|
|
2924
|
+
};
|
|
3043
2925
|
var ArkadeSwaps = class _ArkadeSwaps {
|
|
3044
2926
|
/** The Arkade wallet instance used for signing and address generation. */
|
|
3045
2927
|
wallet;
|
|
@@ -3075,10 +2957,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3075
2957
|
return new _ArkadeSwaps(config);
|
|
3076
2958
|
}
|
|
3077
2959
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3078
|
-
if (!arkProvider)
|
|
3079
|
-
throw new Error(
|
|
3080
|
-
"Ark provider is required either in wallet or config."
|
|
3081
|
-
);
|
|
2960
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3082
2961
|
const arkInfo = await arkProvider.getInfo();
|
|
3083
2962
|
const network = arkInfo.network;
|
|
3084
2963
|
const swapProvider = new BoltzSwapProvider({ network });
|
|
@@ -3089,16 +2968,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3089
2968
|
if (!config.swapProvider) throw new Error("Swap provider is required.");
|
|
3090
2969
|
this.wallet = config.wallet;
|
|
3091
2970
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3092
|
-
if (!arkProvider)
|
|
3093
|
-
throw new Error(
|
|
3094
|
-
"Ark provider is required either in wallet or config."
|
|
3095
|
-
);
|
|
2971
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3096
2972
|
this.arkProvider = arkProvider;
|
|
3097
2973
|
const indexerProvider = config.indexerProvider ?? config.wallet.indexerProvider;
|
|
3098
2974
|
if (!indexerProvider)
|
|
3099
|
-
throw new Error(
|
|
3100
|
-
"Indexer provider is required either in wallet or config."
|
|
3101
|
-
);
|
|
2975
|
+
throw new Error("Indexer provider is required either in wallet or config.");
|
|
3102
2976
|
this.indexerProvider = indexerProvider;
|
|
3103
2977
|
this.swapProvider = config.swapProvider;
|
|
3104
2978
|
if (config.swapRepository) {
|
|
@@ -3109,10 +2983,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3109
2983
|
if (config.swapManager !== false) {
|
|
3110
2984
|
const swapManagerConfig = !config.swapManager || config.swapManager === true ? {} : config.swapManager;
|
|
3111
2985
|
const shouldAutostart = swapManagerConfig.autoStart ?? true;
|
|
3112
|
-
this.swapManager = new SwapManager(
|
|
3113
|
-
this.swapProvider,
|
|
3114
|
-
swapManagerConfig
|
|
3115
|
-
);
|
|
2986
|
+
this.swapManager = new SwapManager(this.swapProvider, swapManagerConfig);
|
|
3116
2987
|
this.swapManager.setCallbacks({
|
|
3117
2988
|
claim: async (swap) => {
|
|
3118
2989
|
await this.claimVHTLC(swap);
|
|
@@ -3260,19 +3131,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3260
3131
|
* @throws {SwapError} If amount is <= 0 or key retrieval fails.
|
|
3261
3132
|
*/
|
|
3262
3133
|
async createReverseSwap(args) {
|
|
3263
|
-
if (args.amount <= 0)
|
|
3264
|
-
|
|
3265
|
-
const claimPublicKey = import_base9.hex.encode(
|
|
3266
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3267
|
-
);
|
|
3134
|
+
if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
|
|
3135
|
+
const claimPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
3268
3136
|
if (!claimPublicKey)
|
|
3269
3137
|
throw new SwapError({
|
|
3270
3138
|
message: "Failed to get claim public key from wallet"
|
|
3271
3139
|
});
|
|
3272
3140
|
const preimage = (0, import_utils3.randomBytes)(32);
|
|
3273
3141
|
const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
|
|
3274
|
-
if (!preimageHash)
|
|
3275
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3142
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3276
3143
|
const swapRequest = {
|
|
3277
3144
|
invoiceAmount: args.amount,
|
|
3278
3145
|
claimPublicKey,
|
|
@@ -3302,18 +3169,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3302
3169
|
*/
|
|
3303
3170
|
async claimVHTLC(pendingSwap) {
|
|
3304
3171
|
if (!pendingSwap.preimage)
|
|
3305
|
-
throw new Error(
|
|
3306
|
-
`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`
|
|
3307
|
-
);
|
|
3172
|
+
throw new Error(`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`);
|
|
3308
3173
|
const {
|
|
3309
3174
|
refundPublicKey,
|
|
3310
3175
|
lockupAddress,
|
|
3311
3176
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3312
3177
|
} = pendingSwap.response;
|
|
3313
3178
|
if (!refundPublicKey || !lockupAddress || !vhtlcTimeouts)
|
|
3314
|
-
throw new Error(
|
|
3315
|
-
`Swap ${pendingSwap.id}: incomplete reverse swap response`
|
|
3316
|
-
);
|
|
3179
|
+
throw new Error(`Swap ${pendingSwap.id}: incomplete reverse swap response`);
|
|
3317
3180
|
const preimage = import_base9.hex.decode(pendingSwap.preimage);
|
|
3318
3181
|
const arkInfo = await this.arkProvider.getInfo();
|
|
3319
3182
|
const address = await this.wallet.getAddress();
|
|
@@ -3358,15 +3221,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3358
3221
|
break;
|
|
3359
3222
|
}
|
|
3360
3223
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
3361
|
-
await new Promise(
|
|
3362
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
3363
|
-
);
|
|
3224
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
3364
3225
|
}
|
|
3365
3226
|
}
|
|
3366
3227
|
if (!vtxo) {
|
|
3367
|
-
throw new Error(
|
|
3368
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
3369
|
-
);
|
|
3228
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
3370
3229
|
}
|
|
3371
3230
|
if (vtxo.isSpent) {
|
|
3372
3231
|
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
@@ -3380,10 +3239,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3380
3239
|
amount: BigInt(vtxo.value),
|
|
3381
3240
|
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
3382
3241
|
};
|
|
3383
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
3384
|
-
this.wallet.identity,
|
|
3385
|
-
preimage
|
|
3386
|
-
);
|
|
3242
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
3387
3243
|
let finalStatus;
|
|
3388
3244
|
if ((0, import_sdk8.isRecoverable)(vtxo)) {
|
|
3389
3245
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
@@ -3502,9 +3358,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3502
3358
|
async sendLightningPayment(args) {
|
|
3503
3359
|
const pendingSwap = await this.createSubmarineSwap(args);
|
|
3504
3360
|
if (!pendingSwap.response.address)
|
|
3505
|
-
throw new Error(
|
|
3506
|
-
`Swap ${pendingSwap.id}: missing address in submarine swap response`
|
|
3507
|
-
);
|
|
3361
|
+
throw new Error(`Swap ${pendingSwap.id}: missing address in submarine swap response`);
|
|
3508
3362
|
await this.savePendingSubmarineSwap(pendingSwap);
|
|
3509
3363
|
const txid = await this.wallet.send({
|
|
3510
3364
|
address: pendingSwap.response.address,
|
|
@@ -3537,9 +3391,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3537
3391
|
* @throws {SwapError} If invoice is missing or key retrieval fails.
|
|
3538
3392
|
*/
|
|
3539
3393
|
async createSubmarineSwap(args) {
|
|
3540
|
-
const refundPublicKey = import_base9.hex.encode(
|
|
3541
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3542
|
-
);
|
|
3394
|
+
const refundPublicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
3543
3395
|
if (!refundPublicKey)
|
|
3544
3396
|
throw new SwapError({
|
|
3545
3397
|
message: "Failed to get refund public key from wallet"
|
|
@@ -3577,9 +3429,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3577
3429
|
async buildSubmarineVHTLCContext(swap, arkInfo) {
|
|
3578
3430
|
const preimageHash = swap.request.invoice ? getInvoicePaymentHash(swap.request.invoice) : swap.preimageHash;
|
|
3579
3431
|
if (!preimageHash)
|
|
3580
|
-
throw new Error(
|
|
3581
|
-
`Swap ${swap.id}: preimage hash is required to refund VHTLC`
|
|
3582
|
-
);
|
|
3432
|
+
throw new Error(`Swap ${swap.id}: preimage hash is required to refund VHTLC`);
|
|
3583
3433
|
const resolvedArkInfo = arkInfo ?? await this.arkProvider.getInfo();
|
|
3584
3434
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3585
3435
|
await this.wallet.identity.xOnlyPublicKey(),
|
|
@@ -3593,9 +3443,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3593
3443
|
);
|
|
3594
3444
|
const { claimPublicKey, timeoutBlockHeights: vhtlcTimeouts } = swap.response;
|
|
3595
3445
|
if (!claimPublicKey || !vhtlcTimeouts)
|
|
3596
|
-
throw new Error(
|
|
3597
|
-
`Swap ${swap.id}: incomplete submarine swap response`
|
|
3598
|
-
);
|
|
3446
|
+
throw new Error(`Swap ${swap.id}: incomplete submarine swap response`);
|
|
3599
3447
|
const boltzXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3600
3448
|
import_base9.hex.decode(claimPublicKey),
|
|
3601
3449
|
"boltz",
|
|
@@ -3610,9 +3458,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3610
3458
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3611
3459
|
});
|
|
3612
3460
|
if (!vhtlcScript.claimScript)
|
|
3613
|
-
throw new Error(
|
|
3614
|
-
`Swap ${swap.id}: failed to create VHTLC script for submarine swap`
|
|
3615
|
-
);
|
|
3461
|
+
throw new Error(`Swap ${swap.id}: failed to create VHTLC script for submarine swap`);
|
|
3616
3462
|
if (vhtlcAddress !== swap.response.address)
|
|
3617
3463
|
throw new Error(
|
|
3618
3464
|
`VHTLC address mismatch for swap ${swap.id}: expected ${swap.response.address}, got ${vhtlcAddress}`
|
|
@@ -3651,10 +3497,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3651
3497
|
recoverableOnly: true
|
|
3652
3498
|
})
|
|
3653
3499
|
]);
|
|
3654
|
-
const refundableVtxos = dedupeVtxos([
|
|
3655
|
-
...spendableResult.vtxos,
|
|
3656
|
-
...recoverableResult.vtxos
|
|
3657
|
-
]);
|
|
3500
|
+
const refundableVtxos = dedupeVtxos([...spendableResult.vtxos, ...recoverableResult.vtxos]);
|
|
3658
3501
|
let diagnostic;
|
|
3659
3502
|
if (refundableVtxos.length === 0) {
|
|
3660
3503
|
const { vtxos: allVtxos } = await this.indexerProvider.getVtxos({
|
|
@@ -3674,13 +3517,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3674
3517
|
submarineRecoveryInfoFromLookup(swap, lookup) {
|
|
3675
3518
|
const { refundableVtxos, diagnostic, vhtlcTimeouts } = lookup;
|
|
3676
3519
|
if (refundableVtxos.length > 0) {
|
|
3677
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3678
|
-
|
|
3679
|
-
);
|
|
3680
|
-
const amountSats = refundableVtxos.reduce(
|
|
3681
|
-
(sum, vtxo) => sum + Number(vtxo.value),
|
|
3682
|
-
0
|
|
3683
|
-
);
|
|
3520
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3521
|
+
const amountSats = refundableVtxos.reduce((sum, vtxo) => sum + Number(vtxo.value), 0);
|
|
3684
3522
|
const isRecoverable2 = cltvSatisfied || canRecoverViaBoltz3of3(refundableVtxos, swap);
|
|
3685
3523
|
return {
|
|
3686
3524
|
swap,
|
|
@@ -3744,19 +3582,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3744
3582
|
);
|
|
3745
3583
|
}
|
|
3746
3584
|
if (diagnostic.allSpent) {
|
|
3747
|
-
throw new Error(
|
|
3748
|
-
`Swap ${pendingSwap.id}: VHTLC is already spent`
|
|
3749
|
-
);
|
|
3585
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
3750
3586
|
}
|
|
3751
|
-
throw new Error(
|
|
3752
|
-
`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`
|
|
3753
|
-
);
|
|
3587
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`);
|
|
3754
3588
|
}
|
|
3755
3589
|
const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
|
|
3756
3590
|
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
3757
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3758
|
-
vhtlcTimeouts.refund
|
|
3759
|
-
);
|
|
3591
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3760
3592
|
let boltzCallCount = 0;
|
|
3761
3593
|
let sweptCount = 0;
|
|
3762
3594
|
let skippedCount = 0;
|
|
@@ -3808,9 +3640,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3808
3640
|
input,
|
|
3809
3641
|
output,
|
|
3810
3642
|
arkInfo,
|
|
3811
|
-
this.swapProvider.refundSubmarineSwap.bind(
|
|
3812
|
-
this.swapProvider
|
|
3813
|
-
)
|
|
3643
|
+
this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3814
3644
|
);
|
|
3815
3645
|
boltzCallCount++;
|
|
3816
3646
|
sweptCount++;
|
|
@@ -3833,13 +3663,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3833
3663
|
tapLeafScript: refundWithoutReceiverLeaf,
|
|
3834
3664
|
tapTree: vhtlcScript.encode()
|
|
3835
3665
|
};
|
|
3836
|
-
await this.joinBatch(
|
|
3837
|
-
this.wallet.identity,
|
|
3838
|
-
fallbackInput,
|
|
3839
|
-
output,
|
|
3840
|
-
arkInfo,
|
|
3841
|
-
false
|
|
3842
|
-
);
|
|
3666
|
+
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
3843
3667
|
sweptCount++;
|
|
3844
3668
|
}
|
|
3845
3669
|
}
|
|
@@ -3935,10 +3759,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3935
3759
|
try {
|
|
3936
3760
|
return {
|
|
3937
3761
|
swap,
|
|
3938
|
-
context: await this.buildSubmarineVHTLCContext(
|
|
3939
|
-
swap,
|
|
3940
|
-
arkInfo
|
|
3941
|
-
)
|
|
3762
|
+
context: await this.buildSubmarineVHTLCContext(swap, arkInfo)
|
|
3942
3763
|
};
|
|
3943
3764
|
} catch (err) {
|
|
3944
3765
|
return {
|
|
@@ -3951,9 +3772,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3951
3772
|
const valid = prepared.filter(
|
|
3952
3773
|
(item) => "context" in item
|
|
3953
3774
|
);
|
|
3954
|
-
const scripts = [
|
|
3955
|
-
...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))
|
|
3956
|
-
];
|
|
3775
|
+
const scripts = [...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))];
|
|
3957
3776
|
const refundableByScript = /* @__PURE__ */ new Map();
|
|
3958
3777
|
if (scripts.length > 0) {
|
|
3959
3778
|
const [spendableResult, recoverableResult] = await Promise.all([
|
|
@@ -3988,9 +3807,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3988
3807
|
error: item.error
|
|
3989
3808
|
};
|
|
3990
3809
|
}
|
|
3991
|
-
const refundableVtxos = refundableByScript.get(
|
|
3992
|
-
item.context.vhtlcPkScriptHex.toLowerCase()
|
|
3993
|
-
) ?? [];
|
|
3810
|
+
const refundableVtxos = refundableByScript.get(item.context.vhtlcPkScriptHex.toLowerCase()) ?? [];
|
|
3994
3811
|
return this.submarineRecoveryInfoFromLookup(item.swap, {
|
|
3995
3812
|
...item.context,
|
|
3996
3813
|
refundableVtxos
|
|
@@ -4169,9 +3986,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4169
3986
|
*/
|
|
4170
3987
|
async waitAndClaimBtc(pendingSwap) {
|
|
4171
3988
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4172
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4173
|
-
pendingSwap.id
|
|
4174
|
-
);
|
|
3989
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4175
3990
|
return { txid };
|
|
4176
3991
|
}
|
|
4177
3992
|
return new Promise((resolve, reject) => {
|
|
@@ -4198,24 +4013,25 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4198
4013
|
}
|
|
4199
4014
|
case "transaction.claimed":
|
|
4200
4015
|
await updateSwapStatus();
|
|
4201
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4202
|
-
pendingSwap.id
|
|
4203
|
-
);
|
|
4016
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4204
4017
|
resolve({
|
|
4205
4018
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4206
4019
|
});
|
|
4207
4020
|
break;
|
|
4208
4021
|
case "transaction.lockupFailed":
|
|
4209
4022
|
await updateSwapStatus();
|
|
4210
|
-
await this.quoteSwap(swap.response.id).catch(
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4023
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4024
|
+
(err) => {
|
|
4025
|
+
reject(
|
|
4026
|
+
new SwapError({
|
|
4027
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4028
|
+
isRefundable: true,
|
|
4029
|
+
pendingSwap: swap,
|
|
4030
|
+
cause: err
|
|
4031
|
+
})
|
|
4032
|
+
);
|
|
4033
|
+
}
|
|
4034
|
+
);
|
|
4219
4035
|
break;
|
|
4220
4036
|
case "swap.expired":
|
|
4221
4037
|
await updateSwapStatus();
|
|
@@ -4253,30 +4069,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4253
4069
|
*/
|
|
4254
4070
|
async claimBtc(pendingSwap) {
|
|
4255
4071
|
if (!pendingSwap.toAddress)
|
|
4256
|
-
throw new Error(
|
|
4257
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4258
|
-
);
|
|
4072
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4259
4073
|
if (!pendingSwap.response.claimDetails.swapTree)
|
|
4260
|
-
throw new Error(
|
|
4261
|
-
`Swap ${pendingSwap.id}: missing swap tree in claim details`
|
|
4262
|
-
);
|
|
4074
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in claim details`);
|
|
4263
4075
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4264
|
-
throw new Error(
|
|
4265
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4266
|
-
);
|
|
4076
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4267
4077
|
const swapStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4268
4078
|
if (!swapStatus.transaction?.hex)
|
|
4269
|
-
throw new Error(
|
|
4270
|
-
|
|
4271
|
-
);
|
|
4272
|
-
const lockupTx = import_btc_signer5.Transaction.fromRaw(
|
|
4273
|
-
import_base9.hex.decode(swapStatus.transaction.hex)
|
|
4274
|
-
);
|
|
4079
|
+
throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
|
|
4080
|
+
const lockupTx = import_btc_signer5.Transaction.fromRaw(import_base9.hex.decode(swapStatus.transaction.hex));
|
|
4275
4081
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4276
4082
|
const network = arkInfo.network === "bitcoin" ? import_utils4.NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
|
|
4277
|
-
const swapTree = deserializeSwapTree(
|
|
4278
|
-
pendingSwap.response.claimDetails.swapTree
|
|
4279
|
-
);
|
|
4083
|
+
const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
|
|
4280
4084
|
const musig = tweakMusig(
|
|
4281
4085
|
create(import_base9.hex.decode(pendingSwap.ephemeralKey), [
|
|
4282
4086
|
import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
@@ -4297,19 +4101,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4297
4101
|
vout: swapOutput.vout,
|
|
4298
4102
|
transactionId: lockupTx.id
|
|
4299
4103
|
},
|
|
4300
|
-
import_btc_signer5.OutScript.encode(
|
|
4301
|
-
(0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)
|
|
4302
|
-
),
|
|
4104
|
+
import_btc_signer5.OutScript.encode((0, import_btc_signer5.Address)(network).decode(pendingSwap.toAddress)),
|
|
4303
4105
|
feeToDeliverExactAmount > fee ? feeToDeliverExactAmount : fee
|
|
4304
4106
|
)
|
|
4305
4107
|
);
|
|
4306
4108
|
const musigMessage = musig.message(
|
|
4307
|
-
claimTx.preimageWitnessV1(
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
import_btc_signer5.SigHash.DEFAULT,
|
|
4311
|
-
[swapOutput.amount]
|
|
4312
|
-
)
|
|
4109
|
+
claimTx.preimageWitnessV1(0, [swapOutput.script], import_btc_signer5.SigHash.DEFAULT, [
|
|
4110
|
+
swapOutput.amount
|
|
4111
|
+
])
|
|
4313
4112
|
).generateNonce();
|
|
4314
4113
|
const signedTxData = await this.swapProvider.postChainClaimDetails(
|
|
4315
4114
|
pendingSwap.response.id,
|
|
@@ -4323,14 +4122,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4323
4122
|
}
|
|
4324
4123
|
);
|
|
4325
4124
|
if (!signedTxData.pubNonce || !signedTxData.partialSignature)
|
|
4326
|
-
throw new Error(
|
|
4327
|
-
`Swap ${pendingSwap.id}: invalid signature data from server`
|
|
4328
|
-
);
|
|
4125
|
+
throw new Error(`Swap ${pendingSwap.id}: invalid signature data from server`);
|
|
4329
4126
|
const musigSession = musigMessage.aggregateNonces([
|
|
4330
4127
|
[
|
|
4331
|
-
import_base9.hex.decode(
|
|
4332
|
-
pendingSwap.response.claimDetails.serverPublicKey
|
|
4333
|
-
),
|
|
4128
|
+
import_base9.hex.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
4334
4129
|
import_base9.hex.decode(signedTxData.pubNonce)
|
|
4335
4130
|
]
|
|
4336
4131
|
]).initializeSession();
|
|
@@ -4350,13 +4145,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4350
4145
|
*/
|
|
4351
4146
|
async refundArk(pendingSwap) {
|
|
4352
4147
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4353
|
-
throw new Error(
|
|
4354
|
-
`Swap ${pendingSwap.id}: missing server public key in lockup details`
|
|
4355
|
-
);
|
|
4148
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4356
4149
|
if (!pendingSwap.response.lockupDetails.timeouts)
|
|
4357
|
-
throw new Error(
|
|
4358
|
-
`Swap ${pendingSwap.id}: missing timeouts in lockup details`
|
|
4359
|
-
);
|
|
4150
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in lockup details`);
|
|
4360
4151
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4361
4152
|
const address = await this.wallet.getAddress();
|
|
4362
4153
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
@@ -4398,9 +4189,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4398
4189
|
timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts
|
|
4399
4190
|
});
|
|
4400
4191
|
if (!vhtlcScript.refundScript)
|
|
4401
|
-
throw new Error(
|
|
4402
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4403
|
-
);
|
|
4192
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4404
4193
|
if (pendingSwap.response.lockupDetails.lockupAddress !== vhtlcAddress) {
|
|
4405
4194
|
throw new SwapError({
|
|
4406
4195
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4481,9 +4270,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4481
4270
|
*/
|
|
4482
4271
|
async waitAndClaimArk(pendingSwap) {
|
|
4483
4272
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4484
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4485
|
-
pendingSwap.id
|
|
4486
|
-
);
|
|
4273
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4487
4274
|
return { txid };
|
|
4488
4275
|
}
|
|
4489
4276
|
return new Promise((resolve, reject) => {
|
|
@@ -4504,37 +4291,33 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4504
4291
|
break;
|
|
4505
4292
|
case "transaction.claimed":
|
|
4506
4293
|
await updateSwapStatus();
|
|
4507
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4508
|
-
pendingSwap.id
|
|
4509
|
-
);
|
|
4294
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4510
4295
|
resolve({
|
|
4511
4296
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4512
4297
|
});
|
|
4513
4298
|
break;
|
|
4514
4299
|
case "transaction.claim.pending":
|
|
4515
4300
|
await updateSwapStatus();
|
|
4516
|
-
await this.signCooperativeClaimForServer(swap).catch(
|
|
4301
|
+
await this.signCooperativeClaimForServer(swap).catch((err) => {
|
|
4302
|
+
logger.error(`Failed to sign cooperative claim for ${swap.id}:`, err);
|
|
4303
|
+
});
|
|
4304
|
+
break;
|
|
4305
|
+
case "transaction.lockupFailed":
|
|
4306
|
+
await updateSwapStatus();
|
|
4307
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4517
4308
|
(err) => {
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4309
|
+
reject(
|
|
4310
|
+
new SwapError({
|
|
4311
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4312
|
+
isRefundable: false,
|
|
4313
|
+
// TODO btc refund not implemented yet
|
|
4314
|
+
pendingSwap: swap,
|
|
4315
|
+
cause: err
|
|
4316
|
+
})
|
|
4521
4317
|
);
|
|
4522
4318
|
}
|
|
4523
4319
|
);
|
|
4524
4320
|
break;
|
|
4525
|
-
case "transaction.lockupFailed":
|
|
4526
|
-
await updateSwapStatus();
|
|
4527
|
-
await this.quoteSwap(swap.response.id).catch((err) => {
|
|
4528
|
-
reject(
|
|
4529
|
-
new SwapError({
|
|
4530
|
-
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4531
|
-
isRefundable: false,
|
|
4532
|
-
// TODO btc refund not implemented yet
|
|
4533
|
-
pendingSwap: swap
|
|
4534
|
-
})
|
|
4535
|
-
);
|
|
4536
|
-
});
|
|
4537
|
-
break;
|
|
4538
4321
|
case "swap.expired":
|
|
4539
4322
|
await updateSwapStatus();
|
|
4540
4323
|
reject(
|
|
@@ -4574,17 +4357,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4574
4357
|
*/
|
|
4575
4358
|
async claimArk(pendingSwap) {
|
|
4576
4359
|
if (!pendingSwap.toAddress)
|
|
4577
|
-
throw new Error(
|
|
4578
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4579
|
-
);
|
|
4360
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4580
4361
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4581
|
-
throw new Error(
|
|
4582
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4583
|
-
);
|
|
4362
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4584
4363
|
if (!pendingSwap.response.claimDetails.timeouts)
|
|
4585
|
-
throw new Error(
|
|
4586
|
-
`Swap ${pendingSwap.id}: missing timeouts in claim details`
|
|
4587
|
-
);
|
|
4364
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in claim details`);
|
|
4588
4365
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4589
4366
|
const preimage = import_base9.hex.decode(pendingSwap.preimage);
|
|
4590
4367
|
const address = await this.wallet.getAddress();
|
|
@@ -4596,10 +4373,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4596
4373
|
pendingSwap.response.claimDetails.serverPublicKey,
|
|
4597
4374
|
"sender"
|
|
4598
4375
|
);
|
|
4599
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
4600
|
-
arkInfo.signerPubkey,
|
|
4601
|
-
"server"
|
|
4602
|
-
);
|
|
4376
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(arkInfo.signerPubkey, "server");
|
|
4603
4377
|
const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
|
|
4604
4378
|
network: arkInfo.network,
|
|
4605
4379
|
preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
|
|
@@ -4609,9 +4383,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4609
4383
|
timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts
|
|
4610
4384
|
});
|
|
4611
4385
|
if (!vhtlcScript.claimScript)
|
|
4612
|
-
throw new Error(
|
|
4613
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4614
|
-
);
|
|
4386
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4615
4387
|
if (pendingSwap.response.claimDetails.lockupAddress !== vhtlcAddress) {
|
|
4616
4388
|
throw new SwapError({
|
|
4617
4389
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4628,15 +4400,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4628
4400
|
break;
|
|
4629
4401
|
}
|
|
4630
4402
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
4631
|
-
await new Promise(
|
|
4632
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
4633
|
-
);
|
|
4403
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
4634
4404
|
}
|
|
4635
4405
|
}
|
|
4636
4406
|
if (!vtxo) {
|
|
4637
|
-
throw new Error(
|
|
4638
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
4639
|
-
);
|
|
4407
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
4640
4408
|
}
|
|
4641
4409
|
const input = {
|
|
4642
4410
|
...vtxo,
|
|
@@ -4647,10 +4415,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4647
4415
|
amount: BigInt(vtxo.value),
|
|
4648
4416
|
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
4649
4417
|
};
|
|
4650
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
4651
|
-
this.wallet.identity,
|
|
4652
|
-
preimage
|
|
4653
|
-
);
|
|
4418
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
4654
4419
|
if ((0, import_sdk8.isRecoverable)(vtxo)) {
|
|
4655
4420
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
4656
4421
|
} else {
|
|
@@ -4676,16 +4441,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4676
4441
|
*/
|
|
4677
4442
|
async signCooperativeClaimForServer(pendingSwap) {
|
|
4678
4443
|
if (!pendingSwap.response.lockupDetails.swapTree)
|
|
4679
|
-
throw new Error(
|
|
4680
|
-
`Swap ${pendingSwap.id}: missing swap tree in lockup details`
|
|
4681
|
-
);
|
|
4444
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in lockup details`);
|
|
4682
4445
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4683
|
-
throw new Error(
|
|
4684
|
-
|
|
4685
|
-
);
|
|
4686
|
-
const claimDetails = await this.swapProvider.getChainClaimDetails(
|
|
4687
|
-
pendingSwap.id
|
|
4688
|
-
);
|
|
4446
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4447
|
+
const claimDetails = await this.swapProvider.getChainClaimDetails(pendingSwap.id);
|
|
4689
4448
|
const serverPubKey = pendingSwap.response.lockupDetails.serverPublicKey;
|
|
4690
4449
|
if (claimDetails.publicKey !== serverPubKey) {
|
|
4691
4450
|
throw new Error(
|
|
@@ -4701,9 +4460,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4701
4460
|
);
|
|
4702
4461
|
const musigNonces = musig.message(import_base9.hex.decode(claimDetails.transactionHash)).generateNonce().aggregateNonces([
|
|
4703
4462
|
[
|
|
4704
|
-
import_base9.hex.decode(
|
|
4705
|
-
pendingSwap.response.lockupDetails.serverPublicKey
|
|
4706
|
-
),
|
|
4463
|
+
import_base9.hex.decode(pendingSwap.response.lockupDetails.serverPublicKey),
|
|
4707
4464
|
import_base9.hex.decode(claimDetails.pubNonce)
|
|
4708
4465
|
]
|
|
4709
4466
|
]).initializeSession();
|
|
@@ -4722,10 +4479,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4722
4479
|
* @returns The transaction ID of the claim.
|
|
4723
4480
|
*/
|
|
4724
4481
|
async waitAndClaimChain(pendingSwap) {
|
|
4725
|
-
if (pendingSwap.request.to === "ARK")
|
|
4726
|
-
|
|
4727
|
-
if (pendingSwap.request.to === "BTC")
|
|
4728
|
-
return this.waitAndClaimBtc(pendingSwap);
|
|
4482
|
+
if (pendingSwap.request.to === "ARK") return this.waitAndClaimArk(pendingSwap);
|
|
4483
|
+
if (pendingSwap.request.to === "BTC") return this.waitAndClaimBtc(pendingSwap);
|
|
4729
4484
|
throw new SwapError({
|
|
4730
4485
|
message: `Unsupported swap destination: ${pendingSwap.request.to}`
|
|
4731
4486
|
});
|
|
@@ -4740,11 +4495,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4740
4495
|
*/
|
|
4741
4496
|
async createChainSwap(args) {
|
|
4742
4497
|
const { to, from, receiverLockAmount, senderLockAmount, toAddress } = args;
|
|
4743
|
-
if (!toAddress)
|
|
4744
|
-
throw new SwapError({ message: "Destination address is required" });
|
|
4498
|
+
if (!toAddress) throw new SwapError({ message: "Destination address is required" });
|
|
4745
4499
|
const feeSatsPerByte = args.feeSatsPerByte ?? 1;
|
|
4746
|
-
if (feeSatsPerByte <= 0)
|
|
4747
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4500
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4748
4501
|
let amount, serverLockAmount, userLockAmount;
|
|
4749
4502
|
if (receiverLockAmount) {
|
|
4750
4503
|
amount = receiverLockAmount;
|
|
@@ -4759,8 +4512,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4759
4512
|
}
|
|
4760
4513
|
const preimage = (0, import_utils3.randomBytes)(32);
|
|
4761
4514
|
const preimageHash = import_base9.hex.encode((0, import_sha23.sha256)(preimage));
|
|
4762
|
-
if (!preimageHash)
|
|
4763
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4515
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4764
4516
|
const ephemeralKey = import_secp256k13.secp256k1.utils.randomSecretKey();
|
|
4765
4517
|
const refundPublicKey = to === "ARK" ? import_base9.hex.encode(import_secp256k13.secp256k1.getPublicKey(ephemeralKey)) : import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
4766
4518
|
if (!refundPublicKey)
|
|
@@ -4809,30 +4561,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4809
4561
|
const { to, from, swap, arkInfo } = args;
|
|
4810
4562
|
if (from === "ARK") {
|
|
4811
4563
|
if (!swap.response.lockupDetails.serverPublicKey)
|
|
4812
|
-
throw new Error(
|
|
4813
|
-
`Swap ${swap.id}: missing serverPublicKey in lockup details`
|
|
4814
|
-
);
|
|
4564
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in lockup details`);
|
|
4815
4565
|
if (!swap.response.lockupDetails.timeouts)
|
|
4816
|
-
throw new Error(
|
|
4817
|
-
`Swap ${swap.id}: missing timeouts in lockup details`
|
|
4818
|
-
);
|
|
4566
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in lockup details`);
|
|
4819
4567
|
}
|
|
4820
4568
|
if (to === "ARK") {
|
|
4821
4569
|
if (!swap.response.claimDetails.serverPublicKey)
|
|
4822
|
-
throw new Error(
|
|
4823
|
-
`Swap ${swap.id}: missing serverPublicKey in claim details`
|
|
4824
|
-
);
|
|
4570
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in claim details`);
|
|
4825
4571
|
if (!swap.response.claimDetails.timeouts)
|
|
4826
|
-
throw new Error(
|
|
4827
|
-
`Swap ${swap.id}: missing timeouts in claim details`
|
|
4828
|
-
);
|
|
4572
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in claim details`);
|
|
4829
4573
|
}
|
|
4830
4574
|
const lockupAddress = to === "ARK" ? swap.response.claimDetails.lockupAddress : swap.response.lockupDetails.lockupAddress;
|
|
4831
4575
|
const receiverPubkey = to === "ARK" ? swap.request.claimPublicKey : swap.response.lockupDetails.serverPublicKey;
|
|
4832
4576
|
const senderPubkey = to === "ARK" ? swap.response.claimDetails.serverPublicKey : swap.request.refundPublicKey;
|
|
4833
|
-
const serverPubkey = import_base9.hex.encode(
|
|
4834
|
-
normalizeToXOnlyKey(arkInfo.signerPubkey, "server")
|
|
4835
|
-
);
|
|
4577
|
+
const serverPubkey = import_base9.hex.encode(normalizeToXOnlyKey(arkInfo.signerPubkey, "server"));
|
|
4836
4578
|
const vhtlcTimeouts = to === "ARK" ? swap.response.claimDetails.timeouts : swap.response.lockupDetails.timeouts;
|
|
4837
4579
|
const { vhtlcAddress } = this.createVHTLCScript({
|
|
4838
4580
|
network: arkInfo.network,
|
|
@@ -4850,15 +4592,110 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4850
4592
|
return true;
|
|
4851
4593
|
}
|
|
4852
4594
|
/**
|
|
4853
|
-
* Renegotiates the quote for an existing swap.
|
|
4595
|
+
* Renegotiates the quote for an existing chain swap. Convenience wrapper
|
|
4596
|
+
* over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
|
|
4597
|
+
*
|
|
4598
|
+
* The floor is resolved in order:
|
|
4599
|
+
* 1. `options.minAcceptableAmount` if provided.
|
|
4600
|
+
* 2. The original `response.claimDetails.amount` of the stored
|
|
4601
|
+
* pending swap (Boltz-confirmed server-lock amount at creation).
|
|
4602
|
+
* 3. Otherwise throws `QuoteRejectedError({ reason: "no_baseline" })`.
|
|
4603
|
+
*
|
|
4604
|
+
* `options.maxSlippageBps` (default 0) relaxes the floor by basis points.
|
|
4605
|
+
* Quotes ≤ 0 are always rejected. On rejection the acceptance is NOT
|
|
4606
|
+
* posted to Boltz.
|
|
4607
|
+
*
|
|
4608
|
+
* Prefer `getSwapQuote` / `acceptSwapQuote` for callers that want to
|
|
4609
|
+
* inspect the quote before committing.
|
|
4610
|
+
*
|
|
4854
4611
|
* @param swapId - The ID of the swap.
|
|
4612
|
+
* @param options - Optional floor and slippage configuration.
|
|
4855
4613
|
* @returns The accepted quote amount.
|
|
4614
|
+
* @throws QuoteRejectedError if the quote is non-positive, below the
|
|
4615
|
+
* effective floor, or no baseline is available.
|
|
4856
4616
|
*/
|
|
4857
|
-
async quoteSwap(swapId) {
|
|
4617
|
+
async quoteSwap(swapId, options) {
|
|
4618
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4619
|
+
const amount = await this.getSwapQuote(swapId);
|
|
4620
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4621
|
+
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4622
|
+
return amount;
|
|
4623
|
+
}
|
|
4624
|
+
/**
|
|
4625
|
+
* Fetches a renegotiated quote from Boltz without accepting it.
|
|
4626
|
+
* Pair with `acceptSwapQuote` to commit a specific value.
|
|
4627
|
+
*/
|
|
4628
|
+
async getSwapQuote(swapId) {
|
|
4858
4629
|
const { amount } = await this.swapProvider.getChainQuote(swapId);
|
|
4630
|
+
return amount;
|
|
4631
|
+
}
|
|
4632
|
+
/**
|
|
4633
|
+
* Accepts a quote amount for an existing chain swap, after validating it
|
|
4634
|
+
* against the configured floor. See `quoteSwap` for floor-resolution rules.
|
|
4635
|
+
*
|
|
4636
|
+
* @throws QuoteRejectedError if `amount` ≤ 0, below the effective floor,
|
|
4637
|
+
* or no baseline is available.
|
|
4638
|
+
*/
|
|
4639
|
+
async acceptSwapQuote(swapId, amount, options) {
|
|
4640
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4641
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4859
4642
|
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4860
4643
|
return amount;
|
|
4861
4644
|
}
|
|
4645
|
+
async resolveEffectiveFloor(swapId, options) {
|
|
4646
|
+
this.validateQuoteOptions(options);
|
|
4647
|
+
const floor = await this.resolveQuoteFloor(swapId, options);
|
|
4648
|
+
const slippageBps = options?.maxSlippageBps ?? 0;
|
|
4649
|
+
return Math.floor(floor - floor * slippageBps / 1e4);
|
|
4650
|
+
}
|
|
4651
|
+
async resolveQuoteFloor(swapId, options) {
|
|
4652
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4653
|
+
return options.minAcceptableAmount;
|
|
4654
|
+
}
|
|
4655
|
+
const swaps = await this.swapRepository.getAllSwaps({
|
|
4656
|
+
id: swapId,
|
|
4657
|
+
type: "chain"
|
|
4658
|
+
});
|
|
4659
|
+
const stored = swaps[0];
|
|
4660
|
+
const amount = stored?.response?.claimDetails?.amount;
|
|
4661
|
+
if (typeof amount !== "number") {
|
|
4662
|
+
throw new QuoteRejectedError({ reason: "no_baseline" });
|
|
4663
|
+
}
|
|
4664
|
+
return amount;
|
|
4665
|
+
}
|
|
4666
|
+
validateQuoteOptions(options) {
|
|
4667
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4668
|
+
const v = options.minAcceptableAmount;
|
|
4669
|
+
if (!Number.isInteger(v) || v <= 0) {
|
|
4670
|
+
throw new TypeError(
|
|
4671
|
+
`Invalid minAcceptableAmount: ${v} \u2014 must be a positive integer`
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
if (options?.maxSlippageBps !== void 0) {
|
|
4676
|
+
const v = options.maxSlippageBps;
|
|
4677
|
+
if (!Number.isInteger(v) || v < 0 || v > 1e4) {
|
|
4678
|
+
throw new TypeError(
|
|
4679
|
+
`Invalid maxSlippageBps: ${v} \u2014 must be an integer in [0, 10000]`
|
|
4680
|
+
);
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
validateQuote(amount, effectiveFloor) {
|
|
4685
|
+
if (!(amount > 0)) {
|
|
4686
|
+
throw new QuoteRejectedError({
|
|
4687
|
+
reason: "non_positive",
|
|
4688
|
+
quotedAmount: amount
|
|
4689
|
+
});
|
|
4690
|
+
}
|
|
4691
|
+
if (amount < effectiveFloor) {
|
|
4692
|
+
throw new QuoteRejectedError({
|
|
4693
|
+
reason: "below_floor",
|
|
4694
|
+
quotedAmount: amount,
|
|
4695
|
+
floor: effectiveFloor
|
|
4696
|
+
});
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4862
4699
|
// =========================================================================
|
|
4863
4700
|
// Shared utilities
|
|
4864
4701
|
// =========================================================================
|
|
@@ -4872,14 +4709,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4872
4709
|
* @returns The commitment transaction ID.
|
|
4873
4710
|
*/
|
|
4874
4711
|
async joinBatch(identity, input, output, arkInfo, isRecoverable2 = true) {
|
|
4875
|
-
return joinBatch(
|
|
4876
|
-
this.arkProvider,
|
|
4877
|
-
identity,
|
|
4878
|
-
input,
|
|
4879
|
-
output,
|
|
4880
|
-
arkInfo,
|
|
4881
|
-
isRecoverable2
|
|
4882
|
-
);
|
|
4712
|
+
return joinBatch(this.arkProvider, identity, input, output, arkInfo, isRecoverable2);
|
|
4883
4713
|
}
|
|
4884
4714
|
/**
|
|
4885
4715
|
* Creates a VHTLC script for the swap.
|
|
@@ -4917,9 +4747,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4917
4747
|
async getPendingSubmarineSwaps() {
|
|
4918
4748
|
const swaps = await this.getPendingSubmarineSwapsFromStorage();
|
|
4919
4749
|
if (!swaps) return [];
|
|
4920
|
-
return swaps.filter(
|
|
4921
|
-
(swap) => swap.status === "invoice.set"
|
|
4922
|
-
);
|
|
4750
|
+
return swaps.filter((swap) => swap.status === "invoice.set");
|
|
4923
4751
|
}
|
|
4924
4752
|
/**
|
|
4925
4753
|
* Returns pending reverse swaps (those with status `swap.created`).
|
|
@@ -4927,9 +4755,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4927
4755
|
async getPendingReverseSwaps() {
|
|
4928
4756
|
const swaps = await this.getPendingReverseSwapsFromStorage();
|
|
4929
4757
|
if (!swaps) return [];
|
|
4930
|
-
return swaps.filter(
|
|
4931
|
-
(swap) => swap.status === "swap.created"
|
|
4932
|
-
);
|
|
4758
|
+
return swaps.filter((swap) => swap.status === "swap.created");
|
|
4933
4759
|
}
|
|
4934
4760
|
/**
|
|
4935
4761
|
* Returns pending chain swaps (those with status `swap.created`).
|
|
@@ -4968,10 +4794,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4968
4794
|
this.savePendingReverseSwap.bind(this)
|
|
4969
4795
|
)
|
|
4970
4796
|
).catch((error) => {
|
|
4971
|
-
logger.error(
|
|
4972
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
4973
|
-
error
|
|
4974
|
-
);
|
|
4797
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
4975
4798
|
})
|
|
4976
4799
|
);
|
|
4977
4800
|
}
|
|
@@ -4985,23 +4808,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4985
4808
|
this.savePendingSubmarineSwap.bind(this)
|
|
4986
4809
|
)
|
|
4987
4810
|
).catch((error) => {
|
|
4988
|
-
logger.error(
|
|
4989
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
4990
|
-
error
|
|
4991
|
-
);
|
|
4811
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
4992
4812
|
})
|
|
4993
4813
|
);
|
|
4994
4814
|
}
|
|
4995
4815
|
for (const swap of await this.getPendingChainSwapsFromStorage()) {
|
|
4996
4816
|
if (isChainFinalStatus(swap.status)) continue;
|
|
4997
4817
|
promises.push(
|
|
4998
|
-
this.getSwapStatus(swap.id).then(
|
|
4999
|
-
(
|
|
5000
|
-
).catch((error) => {
|
|
5001
|
-
logger.error(
|
|
5002
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
5003
|
-
error
|
|
5004
|
-
);
|
|
4818
|
+
this.getSwapStatus(swap.id).then(({ status }) => this.savePendingChainSwap({ ...swap, status })).catch((error) => {
|
|
4819
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5005
4820
|
})
|
|
5006
4821
|
);
|
|
5007
4822
|
}
|
|
@@ -5018,9 +4833,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5018
4833
|
* display/monitoring and are not automatically wired into the SwapManager.
|
|
5019
4834
|
*/
|
|
5020
4835
|
async restoreSwaps(boltzFees) {
|
|
5021
|
-
const publicKey = import_base9.hex.encode(
|
|
5022
|
-
await this.wallet.identity.compressedPublicKey()
|
|
5023
|
-
);
|
|
4836
|
+
const publicKey = import_base9.hex.encode(await this.wallet.identity.compressedPublicKey());
|
|
5024
4837
|
if (!publicKey) throw new Error("Failed to get public key from wallet");
|
|
5025
4838
|
const fees = boltzFees ?? await this.swapProvider.getFees();
|
|
5026
4839
|
const chainSwaps = [];
|
|
@@ -5072,25 +4885,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5072
4885
|
preimage: ""
|
|
5073
4886
|
});
|
|
5074
4887
|
} else if (isRestoredSubmarineSwap(swap)) {
|
|
5075
|
-
const {
|
|
5076
|
-
amount,
|
|
5077
|
-
lockupAddress,
|
|
5078
|
-
serverPublicKey,
|
|
5079
|
-
tree,
|
|
5080
|
-
timeoutBlockHeights
|
|
5081
|
-
} = swap.refundDetails;
|
|
4888
|
+
const { amount, lockupAddress, serverPublicKey, tree, timeoutBlockHeights } = swap.refundDetails;
|
|
5082
4889
|
let preimage = "";
|
|
5083
4890
|
if (!isSubmarineFinalStatus(status)) {
|
|
5084
4891
|
try {
|
|
5085
|
-
const data = await this.swapProvider.getSwapPreimage(
|
|
5086
|
-
swap.id
|
|
5087
|
-
);
|
|
4892
|
+
const data = await this.swapProvider.getSwapPreimage(swap.id);
|
|
5088
4893
|
preimage = data.preimage;
|
|
5089
4894
|
} catch (error) {
|
|
5090
|
-
logger.warn(
|
|
5091
|
-
`Failed to restore preimage for submarine swap ${id}`,
|
|
5092
|
-
error
|
|
5093
|
-
);
|
|
4895
|
+
logger.warn(`Failed to restore preimage for submarine swap ${id}`, error);
|
|
5094
4896
|
}
|
|
5095
4897
|
}
|
|
5096
4898
|
submarineSwaps.push({
|
|
@@ -5128,12 +4930,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5128
4930
|
} else if (isRestoredChainSwap(swap)) {
|
|
5129
4931
|
const refundDetails = swap.refundDetails;
|
|
5130
4932
|
if (!refundDetails) continue;
|
|
5131
|
-
const {
|
|
5132
|
-
amount,
|
|
5133
|
-
lockupAddress,
|
|
5134
|
-
serverPublicKey,
|
|
5135
|
-
timeoutBlockHeight
|
|
5136
|
-
} = refundDetails;
|
|
4933
|
+
const { amount, lockupAddress, serverPublicKey, timeoutBlockHeight } = refundDetails;
|
|
5137
4934
|
chainSwaps.push({
|
|
5138
4935
|
id,
|
|
5139
4936
|
type: "chain",
|
|
@@ -5185,9 +4982,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5185
4982
|
var SWAP_POLL_TASK_TYPE = "swap-poll";
|
|
5186
4983
|
|
|
5187
4984
|
// src/expo/arkade-lightning.ts
|
|
5188
|
-
|
|
5189
|
-
return Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
5190
|
-
}
|
|
4985
|
+
var import_sdk9 = require("@arkade-os/sdk");
|
|
5191
4986
|
function warnOnRemovedBackgroundFields(bg) {
|
|
5192
4987
|
if (!bg || typeof bg !== "object") return;
|
|
5193
4988
|
const removed = [];
|
|
@@ -5243,9 +5038,7 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
5243
5038
|
const instance = new _ExpoArkadeSwaps(inner, config);
|
|
5244
5039
|
await instance.seedSwapPollTask();
|
|
5245
5040
|
if (config.background.foregroundIntervalMs && config.background.foregroundIntervalMs > 0) {
|
|
5246
|
-
instance.startForegroundPolling(
|
|
5247
|
-
config.background.foregroundIntervalMs
|
|
5248
|
-
);
|
|
5041
|
+
instance.startForegroundPolling(config.background.foregroundIntervalMs);
|
|
5249
5042
|
}
|
|
5250
5043
|
return instance;
|
|
5251
5044
|
}
|
|
@@ -5259,9 +5052,7 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
5259
5052
|
const { taskQueue } = this.config.background;
|
|
5260
5053
|
const results = await taskQueue.getResults();
|
|
5261
5054
|
if (results.length > 0) {
|
|
5262
|
-
await taskQueue.acknowledgeResults(
|
|
5263
|
-
results.map((r) => r.id)
|
|
5264
|
-
);
|
|
5055
|
+
await taskQueue.acknowledgeResults(results.map((r) => r.id));
|
|
5265
5056
|
}
|
|
5266
5057
|
await this.seedSwapPollTask();
|
|
5267
5058
|
}
|
|
@@ -5270,7 +5061,7 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
5270
5061
|
const existing = await taskQueue.getTasks(SWAP_POLL_TASK_TYPE);
|
|
5271
5062
|
if (existing.length > 0) return;
|
|
5272
5063
|
const task = {
|
|
5273
|
-
id: getRandomId(),
|
|
5064
|
+
id: (0, import_sdk9.getRandomId)(),
|
|
5274
5065
|
type: SWAP_POLL_TASK_TYPE,
|
|
5275
5066
|
data: {},
|
|
5276
5067
|
createdAt: Date.now()
|
|
@@ -5387,17 +5178,17 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
5387
5178
|
verifyChainSwap(args) {
|
|
5388
5179
|
return this.inner.verifyChainSwap(args);
|
|
5389
5180
|
}
|
|
5390
|
-
quoteSwap(swapId) {
|
|
5391
|
-
return this.inner.quoteSwap(swapId);
|
|
5181
|
+
quoteSwap(swapId, options) {
|
|
5182
|
+
return this.inner.quoteSwap(swapId, options);
|
|
5183
|
+
}
|
|
5184
|
+
getSwapQuote(swapId) {
|
|
5185
|
+
return this.inner.getSwapQuote(swapId);
|
|
5186
|
+
}
|
|
5187
|
+
acceptSwapQuote(swapId, amount, options) {
|
|
5188
|
+
return this.inner.acceptSwapQuote(swapId, amount, options);
|
|
5392
5189
|
}
|
|
5393
5190
|
joinBatch(identity, input, output, arkInfo, isRecoverable2) {
|
|
5394
|
-
return this.inner.joinBatch(
|
|
5395
|
-
identity,
|
|
5396
|
-
input,
|
|
5397
|
-
output,
|
|
5398
|
-
arkInfo,
|
|
5399
|
-
isRecoverable2
|
|
5400
|
-
);
|
|
5191
|
+
return this.inner.joinBatch(identity, input, output, arkInfo, isRecoverable2);
|
|
5401
5192
|
}
|
|
5402
5193
|
createVHTLCScript(params) {
|
|
5403
5194
|
return this.inner.createVHTLCScript(params);
|