@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
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyCreatedAtOrder,
|
|
3
|
+
applySwapsFilter,
|
|
4
|
+
hasImpossibleSwapsFilter
|
|
5
|
+
} from "./chunk-SJQJQO7P.js";
|
|
6
|
+
|
|
1
7
|
// src/errors.ts
|
|
2
8
|
var SwapError = class extends Error {
|
|
3
9
|
/** Whether the swap can still be claimed (default: false). */
|
|
@@ -7,7 +13,10 @@ var SwapError = class extends Error {
|
|
|
7
13
|
/** The pending swap associated with this error, if available. */
|
|
8
14
|
pendingSwap;
|
|
9
15
|
constructor(options = {}) {
|
|
10
|
-
super(
|
|
16
|
+
super(
|
|
17
|
+
options.message ?? "Error during swap.",
|
|
18
|
+
options.cause !== void 0 ? { cause: options.cause } : void 0
|
|
19
|
+
);
|
|
11
20
|
this.name = "SwapError";
|
|
12
21
|
this.isClaimable = options.isClaimable ?? false;
|
|
13
22
|
this.isRefundable = options.isRefundable ?? false;
|
|
@@ -99,6 +108,96 @@ var TransactionRefundedError = class extends SwapError {
|
|
|
99
108
|
this.name = "TransactionRefundedError";
|
|
100
109
|
}
|
|
101
110
|
};
|
|
111
|
+
var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
|
|
112
|
+
reason;
|
|
113
|
+
quotedAmount;
|
|
114
|
+
floor;
|
|
115
|
+
constructor(options) {
|
|
116
|
+
super({
|
|
117
|
+
message: options.message ?? _QuoteRejectedError.defaultMessage(options),
|
|
118
|
+
...options
|
|
119
|
+
});
|
|
120
|
+
this.name = "QuoteRejectedError";
|
|
121
|
+
this.reason = options.reason;
|
|
122
|
+
this.quotedAmount = "quotedAmount" in options ? options.quotedAmount : void 0;
|
|
123
|
+
this.floor = "floor" in options ? options.floor : void 0;
|
|
124
|
+
}
|
|
125
|
+
static defaultMessage(options) {
|
|
126
|
+
switch (options.reason) {
|
|
127
|
+
case "below_floor":
|
|
128
|
+
return `Boltz quote ${options.quotedAmount} is below acceptable floor ${options.floor}`;
|
|
129
|
+
case "non_positive":
|
|
130
|
+
return `Boltz quote ${options.quotedAmount} is not positive`;
|
|
131
|
+
case "no_baseline":
|
|
132
|
+
return "Cannot accept quote: no minAcceptableAmount and no stored pending swap";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Serialize into a plain `Error` whose `.message` carries the full
|
|
137
|
+
* rejection payload as JSON behind a marker prefix. Structured clone
|
|
138
|
+
* (used by `postMessage` between page and service worker) preserves
|
|
139
|
+
* `Error.message` reliably but strips custom `.name` and own properties,
|
|
140
|
+
* so we move the typed data into the message field for transport.
|
|
141
|
+
*/
|
|
142
|
+
toTransportError() {
|
|
143
|
+
return new Error(
|
|
144
|
+
QUOTE_REJECTION_TRANSPORT_PREFIX + JSON.stringify({
|
|
145
|
+
reason: this.reason,
|
|
146
|
+
message: this.message,
|
|
147
|
+
quotedAmount: this.quotedAmount,
|
|
148
|
+
floor: this.floor
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Inverse of `toTransportError`. Returns a real `QuoteRejectedError` if
|
|
154
|
+
* `error` carries the transport prefix, else `null`.
|
|
155
|
+
*/
|
|
156
|
+
static fromTransportError(error) {
|
|
157
|
+
if (!(error instanceof Error) || !error.message.startsWith(QUOTE_REJECTION_TRANSPORT_PREFIX)) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
const payload = error.message.slice(QUOTE_REJECTION_TRANSPORT_PREFIX.length);
|
|
161
|
+
let data;
|
|
162
|
+
try {
|
|
163
|
+
data = JSON.parse(payload);
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (typeof data.reason !== "string" || !QUOTE_REJECTION_REASONS.has(data.reason)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
const message = typeof data.message === "string" ? data.message : void 0;
|
|
171
|
+
const reason = data.reason;
|
|
172
|
+
const quotedAmount = typeof data.quotedAmount === "number" ? data.quotedAmount : null;
|
|
173
|
+
const floor = typeof data.floor === "number" ? data.floor : null;
|
|
174
|
+
switch (reason) {
|
|
175
|
+
case "below_floor":
|
|
176
|
+
if (quotedAmount === null || floor === null) return null;
|
|
177
|
+
return new _QuoteRejectedError({
|
|
178
|
+
reason,
|
|
179
|
+
quotedAmount,
|
|
180
|
+
floor,
|
|
181
|
+
message
|
|
182
|
+
});
|
|
183
|
+
case "non_positive":
|
|
184
|
+
if (quotedAmount === null) return null;
|
|
185
|
+
return new _QuoteRejectedError({
|
|
186
|
+
reason,
|
|
187
|
+
quotedAmount,
|
|
188
|
+
message
|
|
189
|
+
});
|
|
190
|
+
case "no_baseline":
|
|
191
|
+
return new _QuoteRejectedError({ reason, message });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
var QUOTE_REJECTION_TRANSPORT_PREFIX = "QUOTE_REJECTED::";
|
|
196
|
+
var QUOTE_REJECTION_REASONS = /* @__PURE__ */ new Set([
|
|
197
|
+
"below_floor",
|
|
198
|
+
"non_positive",
|
|
199
|
+
"no_baseline"
|
|
200
|
+
]);
|
|
102
201
|
var BoltzRefundError = class extends Error {
|
|
103
202
|
constructor(message, cause) {
|
|
104
203
|
super(message);
|
|
@@ -111,18 +210,10 @@ var BoltzRefundError = class extends Error {
|
|
|
111
210
|
import { Transaction } from "@arkade-os/sdk";
|
|
112
211
|
import { base64 } from "@scure/base";
|
|
113
212
|
var isSubmarineFailedStatus = (status) => {
|
|
114
|
-
return [
|
|
115
|
-
"invoice.failedToPay",
|
|
116
|
-
"transaction.lockupFailed",
|
|
117
|
-
"swap.expired"
|
|
118
|
-
].includes(status);
|
|
213
|
+
return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
|
|
119
214
|
};
|
|
120
215
|
var isSubmarineFinalStatus = (status) => {
|
|
121
|
-
return [
|
|
122
|
-
"invoice.failedToPay",
|
|
123
|
-
"transaction.claimed",
|
|
124
|
-
"swap.expired"
|
|
125
|
-
].includes(status);
|
|
216
|
+
return ["invoice.failedToPay", "transaction.claimed", "swap.expired"].includes(status);
|
|
126
217
|
};
|
|
127
218
|
var isSubmarinePendingStatus = (status) => {
|
|
128
219
|
return [
|
|
@@ -136,11 +227,7 @@ var isSubmarinePendingStatus = (status) => {
|
|
|
136
227
|
].includes(status);
|
|
137
228
|
};
|
|
138
229
|
var isSubmarineRefundableStatus = (status) => {
|
|
139
|
-
return [
|
|
140
|
-
"invoice.failedToPay",
|
|
141
|
-
"transaction.lockupFailed",
|
|
142
|
-
"swap.expired"
|
|
143
|
-
].includes(status);
|
|
230
|
+
return ["invoice.failedToPay", "transaction.lockupFailed", "swap.expired"].includes(status);
|
|
144
231
|
};
|
|
145
232
|
var isSubmarineSuccessStatus = (status) => {
|
|
146
233
|
return status === "transaction.claimed";
|
|
@@ -164,11 +251,7 @@ var isReverseFinalStatus = (status) => {
|
|
|
164
251
|
].includes(status);
|
|
165
252
|
};
|
|
166
253
|
var isReversePendingStatus = (status) => {
|
|
167
|
-
return [
|
|
168
|
-
"swap.created",
|
|
169
|
-
"transaction.mempool",
|
|
170
|
-
"transaction.confirmed"
|
|
171
|
-
].includes(status);
|
|
254
|
+
return ["swap.created", "transaction.mempool", "transaction.confirmed"].includes(status);
|
|
172
255
|
};
|
|
173
256
|
var isReverseClaimableStatus = (status) => {
|
|
174
257
|
return ["transaction.mempool", "transaction.confirmed"].includes(status);
|
|
@@ -180,10 +263,7 @@ var isChainFailedStatus = (status) => {
|
|
|
180
263
|
return ["transaction.failed", "swap.expired"].includes(status);
|
|
181
264
|
};
|
|
182
265
|
var isChainClaimableStatus = (status) => {
|
|
183
|
-
return [
|
|
184
|
-
"transaction.server.mempool",
|
|
185
|
-
"transaction.server.confirmed"
|
|
186
|
-
].includes(status);
|
|
266
|
+
return ["transaction.server.mempool", "transaction.server.confirmed"].includes(status);
|
|
187
267
|
};
|
|
188
268
|
var isChainFinalStatus = (status) => {
|
|
189
269
|
return [
|
|
@@ -322,8 +402,7 @@ var BASE_URLS = {
|
|
|
322
402
|
var isSwapNotFoundBody = (error) => {
|
|
323
403
|
const needle = "could not find swap";
|
|
324
404
|
const fromJson = error.errorData?.error;
|
|
325
|
-
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle))
|
|
326
|
-
return true;
|
|
405
|
+
if (typeof fromJson === "string" && fromJson.toLowerCase().includes(needle)) return true;
|
|
327
406
|
return error.message.toLowerCase().includes(needle);
|
|
328
407
|
};
|
|
329
408
|
var BoltzSwapProvider = class {
|
|
@@ -337,10 +416,7 @@ var BoltzSwapProvider = class {
|
|
|
337
416
|
this.network = config.network;
|
|
338
417
|
this.referralId = config.referralId ?? "arkade-ts-sdk";
|
|
339
418
|
const apiUrl = config.apiUrl || BASE_URLS[config.network];
|
|
340
|
-
if (!apiUrl)
|
|
341
|
-
throw new Error(
|
|
342
|
-
`API URL is required for network: ${config.network}`
|
|
343
|
-
);
|
|
419
|
+
if (!apiUrl) throw new Error(`API URL is required for network: ${config.network}`);
|
|
344
420
|
this.apiUrl = apiUrl;
|
|
345
421
|
this.wsUrl = this.apiUrl.replace(/^http(s)?:\/\//, "ws$1://").replace("9069", "9004") + "/v2/ws";
|
|
346
422
|
}
|
|
@@ -359,10 +435,7 @@ var BoltzSwapProvider = class {
|
|
|
359
435
|
/** Returns current Lightning swap fees (submarine + reverse) from Boltz. */
|
|
360
436
|
async getFees() {
|
|
361
437
|
const [submarine, reverse] = await Promise.all([
|
|
362
|
-
this.request(
|
|
363
|
-
"/v2/swap/submarine",
|
|
364
|
-
"GET"
|
|
365
|
-
),
|
|
438
|
+
this.request("/v2/swap/submarine", "GET"),
|
|
366
439
|
this.request("/v2/swap/reverse", "GET")
|
|
367
440
|
]);
|
|
368
441
|
if (!isGetSubmarinePairsResponse(submarine))
|
|
@@ -382,10 +455,7 @@ var BoltzSwapProvider = class {
|
|
|
382
455
|
}
|
|
383
456
|
/** Returns current Lightning swap min/max limits from Boltz. */
|
|
384
457
|
async getLimits() {
|
|
385
|
-
const response = await this.request(
|
|
386
|
-
"/v2/swap/submarine",
|
|
387
|
-
"GET"
|
|
388
|
-
);
|
|
458
|
+
const response = await this.request("/v2/swap/submarine", "GET");
|
|
389
459
|
if (!isGetSubmarinePairsResponse(response))
|
|
390
460
|
throw new SchemaError({ message: "error fetching limits" });
|
|
391
461
|
return {
|
|
@@ -395,10 +465,7 @@ var BoltzSwapProvider = class {
|
|
|
395
465
|
}
|
|
396
466
|
/** Returns the current BTC chain tip height from Boltz. */
|
|
397
467
|
async getChainHeight() {
|
|
398
|
-
const response = await this.request(
|
|
399
|
-
"/v2/chain/heights",
|
|
400
|
-
"GET"
|
|
401
|
-
);
|
|
468
|
+
const response = await this.request("/v2/chain/heights", "GET");
|
|
402
469
|
if (typeof response?.BTC !== "number")
|
|
403
470
|
throw new SchemaError({
|
|
404
471
|
message: "error fetching chain heights"
|
|
@@ -428,10 +495,7 @@ var BoltzSwapProvider = class {
|
|
|
428
495
|
async getSwapStatus(id) {
|
|
429
496
|
let response;
|
|
430
497
|
try {
|
|
431
|
-
response = await this.request(
|
|
432
|
-
`/v2/swap/${id}`,
|
|
433
|
-
"GET"
|
|
434
|
-
);
|
|
498
|
+
response = await this.request(`/v2/swap/${id}`, "GET");
|
|
435
499
|
} catch (error) {
|
|
436
500
|
if (error instanceof NetworkError && error.statusCode === 404 && isSwapNotFoundBody(error)) {
|
|
437
501
|
throw new SwapNotFoundError(id, error.errorData);
|
|
@@ -527,12 +591,10 @@ var BoltzSwapProvider = class {
|
|
|
527
591
|
throw new SwapError({ message: "Invalid 'to' chain" });
|
|
528
592
|
if (["BTC", "ARK"].indexOf(from) === -1)
|
|
529
593
|
throw new SwapError({ message: "Invalid 'from' chain" });
|
|
530
|
-
if (to === from)
|
|
531
|
-
throw new SwapError({ message: "Invalid swap direction" });
|
|
594
|
+
if (to === from) throw new SwapError({ message: "Invalid swap direction" });
|
|
532
595
|
if (!preimageHash || preimageHash.length != 64)
|
|
533
596
|
throw new SwapError({ message: "Invalid preimageHash" });
|
|
534
|
-
if (feeSatsPerByte <= 0)
|
|
535
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
597
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
536
598
|
if (serverLockAmount !== void 0 && userLockAmount !== void 0 || serverLockAmount === void 0 && userLockAmount === void 0)
|
|
537
599
|
throw new SwapError({
|
|
538
600
|
message: "Either serverLockAmount or userLockAmount must be provided"
|
|
@@ -587,12 +649,8 @@ var BoltzSwapProvider = class {
|
|
|
587
649
|
message: "Error refunding submarine swap"
|
|
588
650
|
});
|
|
589
651
|
return {
|
|
590
|
-
transaction: Transaction.fromPSBT(
|
|
591
|
-
|
|
592
|
-
),
|
|
593
|
-
checkpoint: Transaction.fromPSBT(
|
|
594
|
-
base64.decode(response.checkpoint)
|
|
595
|
-
)
|
|
652
|
+
transaction: Transaction.fromPSBT(base64.decode(response.transaction)),
|
|
653
|
+
checkpoint: Transaction.fromPSBT(base64.decode(response.checkpoint))
|
|
596
654
|
};
|
|
597
655
|
}
|
|
598
656
|
/** Requests Boltz co-signature for a chain swap refund. Returns signed transaction + checkpoint. */
|
|
@@ -611,53 +669,53 @@ var BoltzSwapProvider = class {
|
|
|
611
669
|
message: "Error refunding chain swap"
|
|
612
670
|
});
|
|
613
671
|
return {
|
|
614
|
-
transaction: Transaction.fromPSBT(
|
|
615
|
-
|
|
616
|
-
),
|
|
617
|
-
checkpoint: Transaction.fromPSBT(
|
|
618
|
-
base64.decode(response.checkpoint)
|
|
619
|
-
)
|
|
672
|
+
transaction: Transaction.fromPSBT(base64.decode(response.transaction)),
|
|
673
|
+
checkpoint: Transaction.fromPSBT(base64.decode(response.checkpoint))
|
|
620
674
|
};
|
|
621
675
|
}
|
|
622
|
-
/**
|
|
676
|
+
/**
|
|
677
|
+
* Monitors swap status updates and forwards them to the update callback.
|
|
678
|
+
* Prefers a WebSocket subscription; on connection error or premature close
|
|
679
|
+
* it falls back to REST polling so callers don't fail when the WS endpoint
|
|
680
|
+
* is flaky (observed in CI). Resolves when the swap reaches a terminal
|
|
681
|
+
* status.
|
|
682
|
+
*/
|
|
623
683
|
async monitorSwap(swapId, update) {
|
|
624
684
|
return new Promise((resolve, reject) => {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
);
|
|
685
|
+
let settled = false;
|
|
686
|
+
let lastStatus = null;
|
|
687
|
+
let pollTimer = null;
|
|
688
|
+
let webSocket = null;
|
|
689
|
+
let connectionTimeout = null;
|
|
690
|
+
const cleanup = () => {
|
|
691
|
+
if (connectionTimeout) {
|
|
692
|
+
clearTimeout(connectionTimeout);
|
|
693
|
+
connectionTimeout = null;
|
|
694
|
+
}
|
|
695
|
+
if (pollTimer) {
|
|
696
|
+
clearInterval(pollTimer);
|
|
697
|
+
pollTimer = null;
|
|
698
|
+
}
|
|
699
|
+
if (webSocket) {
|
|
700
|
+
try {
|
|
701
|
+
webSocket.close();
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
webSocket = null;
|
|
705
|
+
}
|
|
647
706
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
707
|
+
const finish = (err) => {
|
|
708
|
+
if (settled) return;
|
|
709
|
+
settled = true;
|
|
710
|
+
cleanup();
|
|
711
|
+
if (err) reject(err);
|
|
712
|
+
else resolve();
|
|
651
713
|
};
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
reject(new SwapError({ message: msg.args[0].error }));
|
|
658
|
-
}
|
|
659
|
-
const status = msg.args[0].status;
|
|
660
|
-
const negotiable = status === "transaction.lockupFailed" && msg.args[0].failureDetails?.actual !== void 0 && msg.args[0].failureDetails?.expected !== void 0;
|
|
714
|
+
const handleStatus = (status, data) => {
|
|
715
|
+
if (settled) return;
|
|
716
|
+
if (status === lastStatus) return;
|
|
717
|
+
lastStatus = status;
|
|
718
|
+
const negotiable = status === "transaction.lockupFailed" && data?.failureDetails?.actual !== void 0 && data?.failureDetails?.expected !== void 0;
|
|
661
719
|
switch (status) {
|
|
662
720
|
case "invoice.settled":
|
|
663
721
|
case "transaction.claimed":
|
|
@@ -666,13 +724,13 @@ var BoltzSwapProvider = class {
|
|
|
666
724
|
case "invoice.failedToPay":
|
|
667
725
|
case "transaction.failed":
|
|
668
726
|
case "swap.expired":
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
727
|
+
update(status, data);
|
|
728
|
+
finish();
|
|
729
|
+
return;
|
|
672
730
|
case "transaction.lockupFailed":
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
731
|
+
update(status, data);
|
|
732
|
+
if (!negotiable) finish();
|
|
733
|
+
return;
|
|
676
734
|
case "invoice.paid":
|
|
677
735
|
case "invoice.pending":
|
|
678
736
|
case "invoice.set":
|
|
@@ -682,9 +740,77 @@ var BoltzSwapProvider = class {
|
|
|
682
740
|
case "transaction.claim.pending":
|
|
683
741
|
case "transaction.server.mempool":
|
|
684
742
|
case "transaction.server.confirmed":
|
|
685
|
-
update(status,
|
|
743
|
+
update(status, data);
|
|
744
|
+
return;
|
|
686
745
|
}
|
|
687
746
|
};
|
|
747
|
+
const startPolling = () => {
|
|
748
|
+
if (settled || pollTimer) return;
|
|
749
|
+
const poll = async () => {
|
|
750
|
+
if (settled) return;
|
|
751
|
+
try {
|
|
752
|
+
const result = await this.getSwapStatus(swapId);
|
|
753
|
+
handleStatus(result.status, { ...result, id: swapId });
|
|
754
|
+
} catch {
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
pollTimer = setInterval(poll, 1e3);
|
|
758
|
+
poll();
|
|
759
|
+
};
|
|
760
|
+
try {
|
|
761
|
+
webSocket = new globalThis.WebSocket(this.wsUrl);
|
|
762
|
+
connectionTimeout = setTimeout(() => {
|
|
763
|
+
try {
|
|
764
|
+
webSocket?.close();
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
webSocket = null;
|
|
768
|
+
startPolling();
|
|
769
|
+
}, 5e3);
|
|
770
|
+
webSocket.onerror = () => {
|
|
771
|
+
if (connectionTimeout) {
|
|
772
|
+
clearTimeout(connectionTimeout);
|
|
773
|
+
connectionTimeout = null;
|
|
774
|
+
}
|
|
775
|
+
webSocket = null;
|
|
776
|
+
startPolling();
|
|
777
|
+
};
|
|
778
|
+
webSocket.onopen = () => {
|
|
779
|
+
if (connectionTimeout) {
|
|
780
|
+
clearTimeout(connectionTimeout);
|
|
781
|
+
connectionTimeout = null;
|
|
782
|
+
}
|
|
783
|
+
webSocket?.send(
|
|
784
|
+
JSON.stringify({
|
|
785
|
+
op: "subscribe",
|
|
786
|
+
channel: "swap.update",
|
|
787
|
+
args: [swapId]
|
|
788
|
+
})
|
|
789
|
+
);
|
|
790
|
+
};
|
|
791
|
+
webSocket.onclose = () => {
|
|
792
|
+
if (connectionTimeout) {
|
|
793
|
+
clearTimeout(connectionTimeout);
|
|
794
|
+
connectionTimeout = null;
|
|
795
|
+
}
|
|
796
|
+
if (!settled && !pollTimer) startPolling();
|
|
797
|
+
};
|
|
798
|
+
webSocket.onmessage = (rawMsg) => {
|
|
799
|
+
try {
|
|
800
|
+
const msg = JSON.parse(rawMsg.data);
|
|
801
|
+
if (msg.event !== "update" || msg.args?.[0]?.id !== swapId) return;
|
|
802
|
+
if (msg.args[0].error) {
|
|
803
|
+
finish(new SwapError({ message: msg.args[0].error }));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
handleStatus(msg.args[0].status, msg.args[0]);
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
} catch {
|
|
811
|
+
webSocket = null;
|
|
812
|
+
startPolling();
|
|
813
|
+
}
|
|
688
814
|
});
|
|
689
815
|
}
|
|
690
816
|
/** Returns current chain swap fees for a given pair (e.g. ARK→BTC). */
|
|
@@ -692,10 +818,7 @@ var BoltzSwapProvider = class {
|
|
|
692
818
|
if (from === to) {
|
|
693
819
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
694
820
|
}
|
|
695
|
-
const response = await this.request(
|
|
696
|
-
"/v2/swap/chain",
|
|
697
|
-
"GET"
|
|
698
|
-
);
|
|
821
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
699
822
|
if (!isGetChainPairsResponse(response))
|
|
700
823
|
throw new SchemaError({ message: "error fetching fees" });
|
|
701
824
|
if (!response[from]?.[to]) {
|
|
@@ -710,10 +833,7 @@ var BoltzSwapProvider = class {
|
|
|
710
833
|
if (from === to) {
|
|
711
834
|
throw new SwapError({ message: "Invalid chain pair" });
|
|
712
835
|
}
|
|
713
|
-
const response = await this.request(
|
|
714
|
-
"/v2/swap/chain",
|
|
715
|
-
"GET"
|
|
716
|
-
);
|
|
836
|
+
const response = await this.request("/v2/swap/chain", "GET");
|
|
717
837
|
if (!isGetChainPairsResponse(response))
|
|
718
838
|
throw new SchemaError({ message: "error fetching limits" });
|
|
719
839
|
if (!response[from]?.[to]) {
|
|
@@ -842,9 +962,7 @@ var BoltzSwapProvider = class {
|
|
|
842
962
|
return await response.json();
|
|
843
963
|
} catch (error) {
|
|
844
964
|
if (error instanceof NetworkError) throw error;
|
|
845
|
-
throw new NetworkError(
|
|
846
|
-
`Request to ${url} failed: ${error.message}`
|
|
847
|
-
);
|
|
965
|
+
throw new NetworkError(`Request to ${url} failed: ${error.message}`);
|
|
848
966
|
}
|
|
849
967
|
}
|
|
850
968
|
};
|
|
@@ -854,9 +972,7 @@ import bolt11 from "light-bolt11-decoder";
|
|
|
854
972
|
import { ArkAddress } from "@arkade-os/sdk";
|
|
855
973
|
var decodeInvoice = (invoice) => {
|
|
856
974
|
const decoded = bolt11.decode(invoice);
|
|
857
|
-
const millisats = Number(
|
|
858
|
-
decoded.sections.find((s) => s.name === "amount")?.value ?? "0"
|
|
859
|
-
);
|
|
975
|
+
const millisats = Number(decoded.sections.find((s) => s.name === "amount")?.value ?? "0");
|
|
860
976
|
return {
|
|
861
977
|
expiry: decoded.expiry ?? 3600,
|
|
862
978
|
amountSats: Math.floor(millisats / 1e3),
|
|
@@ -996,9 +1112,7 @@ var SwapManager = class _SwapManager {
|
|
|
996
1112
|
this.wsConnectedListeners.add(config.events.onWebSocketConnected);
|
|
997
1113
|
}
|
|
998
1114
|
if (config.events?.onWebSocketDisconnected) {
|
|
999
|
-
this.wsDisconnectedListeners.add(
|
|
1000
|
-
config.events.onWebSocketDisconnected
|
|
1001
|
-
);
|
|
1115
|
+
this.wsDisconnectedListeners.add(config.events.onWebSocketDisconnected);
|
|
1002
1116
|
}
|
|
1003
1117
|
this.currentReconnectDelay = this.config.reconnectDelayMs;
|
|
1004
1118
|
this.currentPollRetryDelay = this.config.pollRetryDelayMs;
|
|
@@ -1149,9 +1263,7 @@ var SwapManager = class _SwapManager {
|
|
|
1149
1263
|
*/
|
|
1150
1264
|
setPollInterval(ms) {
|
|
1151
1265
|
if (ms <= 0) {
|
|
1152
|
-
throw new RangeError(
|
|
1153
|
-
`setPollInterval: ms must be a positive number, got ${ms}`
|
|
1154
|
-
);
|
|
1266
|
+
throw new RangeError(`setPollInterval: ms must be a positive number, got ${ms}`);
|
|
1155
1267
|
}
|
|
1156
1268
|
const cappedInterval = Math.min(ms, this.config.maxPollIntervalMs);
|
|
1157
1269
|
if (cappedInterval !== ms) {
|
|
@@ -1160,10 +1272,7 @@ var SwapManager = class _SwapManager {
|
|
|
1160
1272
|
);
|
|
1161
1273
|
}
|
|
1162
1274
|
this.config.pollInterval = cappedInterval;
|
|
1163
|
-
this.currentPollRetryDelay = Math.min(
|
|
1164
|
-
cappedInterval,
|
|
1165
|
-
this.config.pollRetryDelayMs
|
|
1166
|
-
);
|
|
1275
|
+
this.currentPollRetryDelay = Math.min(cappedInterval, this.config.pollRetryDelayMs);
|
|
1167
1276
|
if (this.isRunning) {
|
|
1168
1277
|
if (this.usePollingFallback) {
|
|
1169
1278
|
this.startPollingFallback();
|
|
@@ -1248,9 +1357,7 @@ var SwapManager = class _SwapManager {
|
|
|
1248
1357
|
}
|
|
1249
1358
|
if (this.isFinalStatus(swap)) {
|
|
1250
1359
|
if (isPendingReverseSwap(swap)) {
|
|
1251
|
-
const response = await this.swapProvider.getReverseSwapTxId(
|
|
1252
|
-
swap.id
|
|
1253
|
-
);
|
|
1360
|
+
const response = await this.swapProvider.getReverseSwapTxId(swap.id);
|
|
1254
1361
|
return { txid: response.id };
|
|
1255
1362
|
}
|
|
1256
1363
|
if (isPendingSubmarineSwap(swap)) {
|
|
@@ -1267,31 +1374,19 @@ var SwapManager = class _SwapManager {
|
|
|
1267
1374
|
if (updatedSwap.status === "invoice.settled") {
|
|
1268
1375
|
this.swapProvider.getReverseSwapTxId(updatedSwap.id).then((response) => resolve({ txid: response.id })).catch((error) => reject(error));
|
|
1269
1376
|
} else {
|
|
1270
|
-
reject(
|
|
1271
|
-
new Error(
|
|
1272
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1273
|
-
)
|
|
1274
|
-
);
|
|
1377
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1275
1378
|
}
|
|
1276
1379
|
} else if (isPendingSubmarineSwap(updatedSwap)) {
|
|
1277
1380
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1278
1381
|
resolve({ txid: updatedSwap.id });
|
|
1279
1382
|
} else {
|
|
1280
|
-
reject(
|
|
1281
|
-
new Error(
|
|
1282
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1283
|
-
)
|
|
1284
|
-
);
|
|
1383
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1285
1384
|
}
|
|
1286
1385
|
} else if (isPendingChainSwap(updatedSwap)) {
|
|
1287
1386
|
if (updatedSwap.status === "transaction.claimed") {
|
|
1288
1387
|
resolve({ txid: updatedSwap.id });
|
|
1289
1388
|
} else {
|
|
1290
|
-
reject(
|
|
1291
|
-
new Error(
|
|
1292
|
-
`Swap failed with status: ${updatedSwap.status}`
|
|
1293
|
-
)
|
|
1294
|
-
);
|
|
1389
|
+
reject(new Error(`Swap failed with status: ${updatedSwap.status}`));
|
|
1295
1390
|
}
|
|
1296
1391
|
}
|
|
1297
1392
|
};
|
|
@@ -1334,16 +1429,12 @@ var SwapManager = class _SwapManager {
|
|
|
1334
1429
|
const connectionTimeout = setTimeout(() => {
|
|
1335
1430
|
logger.error("WebSocket connection timeout");
|
|
1336
1431
|
this.websocket?.close();
|
|
1337
|
-
this.enterPollingFallback(
|
|
1338
|
-
new NetworkError("WebSocket connection failed")
|
|
1339
|
-
);
|
|
1432
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1340
1433
|
}, 1e4);
|
|
1341
1434
|
this.websocket.onerror = (error) => {
|
|
1342
1435
|
clearTimeout(connectionTimeout);
|
|
1343
1436
|
logger.error("WebSocket error:", error);
|
|
1344
|
-
this.enterPollingFallback(
|
|
1345
|
-
new NetworkError("WebSocket connection failed")
|
|
1346
|
-
);
|
|
1437
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1347
1438
|
};
|
|
1348
1439
|
this.websocket.onopen = () => {
|
|
1349
1440
|
clearTimeout(connectionTimeout);
|
|
@@ -1383,9 +1474,7 @@ var SwapManager = class _SwapManager {
|
|
|
1383
1474
|
};
|
|
1384
1475
|
} catch (error) {
|
|
1385
1476
|
logger.error("Failed to create WebSocket:", error);
|
|
1386
|
-
this.enterPollingFallback(
|
|
1387
|
-
new NetworkError("WebSocket connection failed")
|
|
1388
|
-
);
|
|
1477
|
+
this.enterPollingFallback(new NetworkError("WebSocket connection failed"));
|
|
1389
1478
|
}
|
|
1390
1479
|
}
|
|
1391
1480
|
/**
|
|
@@ -1420,11 +1509,8 @@ var SwapManager = class _SwapManager {
|
|
|
1420
1509
|
* Schedule WebSocket reconnection with exponential backoff
|
|
1421
1510
|
*/
|
|
1422
1511
|
scheduleReconnect() {
|
|
1423
|
-
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport())
|
|
1424
|
-
|
|
1425
|
-
logger.log(
|
|
1426
|
-
`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`
|
|
1427
|
-
);
|
|
1512
|
+
if (this.reconnectTimer || this.webSocketUnavailable || !this.hasWebSocketSupport()) return;
|
|
1513
|
+
logger.log(`Scheduling WebSocket reconnect in ${this.currentReconnectDelay}ms`);
|
|
1428
1514
|
this.reconnectTimer = setTimeout(() => {
|
|
1429
1515
|
this.reconnectTimer = null;
|
|
1430
1516
|
this.isReconnecting = false;
|
|
@@ -1439,8 +1525,7 @@ var SwapManager = class _SwapManager {
|
|
|
1439
1525
|
* Subscribe to a specific swap ID on the WebSocket
|
|
1440
1526
|
*/
|
|
1441
1527
|
subscribeToSwap(swapId) {
|
|
1442
|
-
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN)
|
|
1443
|
-
return;
|
|
1528
|
+
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) return;
|
|
1444
1529
|
this.websocket.send(
|
|
1445
1530
|
JSON.stringify({
|
|
1446
1531
|
op: "subscribe",
|
|
@@ -1463,9 +1548,7 @@ var SwapManager = class _SwapManager {
|
|
|
1463
1548
|
if (msg.args[0].error) {
|
|
1464
1549
|
logger.error(`Swap ${swapId} error:`, msg.args[0].error);
|
|
1465
1550
|
const error = new Error(msg.args[0].error);
|
|
1466
|
-
this.swapFailedListeners.forEach(
|
|
1467
|
-
(listener) => listener(swap, error)
|
|
1468
|
-
);
|
|
1551
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1469
1552
|
return;
|
|
1470
1553
|
}
|
|
1471
1554
|
const newStatus = msg.args[0].status;
|
|
@@ -1483,19 +1566,14 @@ var SwapManager = class _SwapManager {
|
|
|
1483
1566
|
const oldStatus = swap.status;
|
|
1484
1567
|
if (oldStatus === newStatus) return;
|
|
1485
1568
|
swap.status = newStatus;
|
|
1486
|
-
this.swapUpdateListeners.forEach(
|
|
1487
|
-
(listener) => listener(swap, oldStatus)
|
|
1488
|
-
);
|
|
1569
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
1489
1570
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
1490
1571
|
if (subscribers) {
|
|
1491
1572
|
subscribers.forEach((callback) => {
|
|
1492
1573
|
try {
|
|
1493
1574
|
callback(swap, oldStatus);
|
|
1494
1575
|
} catch (error) {
|
|
1495
|
-
logger.error(
|
|
1496
|
-
`Error in swap subscription callback for ${swap.id}:`,
|
|
1497
|
-
error
|
|
1498
|
-
);
|
|
1576
|
+
logger.error(`Error in swap subscription callback for ${swap.id}:`, error);
|
|
1499
1577
|
}
|
|
1500
1578
|
});
|
|
1501
1579
|
}
|
|
@@ -1520,9 +1598,7 @@ var SwapManager = class _SwapManager {
|
|
|
1520
1598
|
*/
|
|
1521
1599
|
async executeAutonomousAction(swap) {
|
|
1522
1600
|
if (this.swapsInProgress.has(swap.id)) {
|
|
1523
|
-
logger.log(
|
|
1524
|
-
`Swap ${swap.id} is already being processed, skipping autonomous action`
|
|
1525
|
-
);
|
|
1601
|
+
logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
|
|
1526
1602
|
return;
|
|
1527
1603
|
}
|
|
1528
1604
|
try {
|
|
@@ -1537,9 +1613,7 @@ var SwapManager = class _SwapManager {
|
|
|
1537
1613
|
if (isReverseClaimableStatus(swap.status)) {
|
|
1538
1614
|
logger.log(`Auto-claiming reverse swap ${swap.id}`);
|
|
1539
1615
|
await this.executeClaimAction(swap);
|
|
1540
|
-
this.actionExecutedListeners.forEach(
|
|
1541
|
-
(listener) => listener(swap, "claim")
|
|
1542
|
-
);
|
|
1616
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "claim"));
|
|
1543
1617
|
}
|
|
1544
1618
|
} else if (isPendingSubmarineSwap(swap)) {
|
|
1545
1619
|
if (!swap.request?.invoice || swap.request.invoice.length === 0) {
|
|
@@ -1551,9 +1625,7 @@ var SwapManager = class _SwapManager {
|
|
|
1551
1625
|
if (isSubmarineRefundableStatus(swap.status)) {
|
|
1552
1626
|
logger.log(`Auto-refunding submarine swap ${swap.id}`);
|
|
1553
1627
|
await this.executeRefundAction(swap);
|
|
1554
|
-
this.actionExecutedListeners.forEach(
|
|
1555
|
-
(listener) => listener(swap, "refund")
|
|
1556
|
-
);
|
|
1628
|
+
this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
|
|
1557
1629
|
}
|
|
1558
1630
|
} else if (isPendingChainSwap(swap)) {
|
|
1559
1631
|
if (isChainClaimableStatus(swap.status)) {
|
|
@@ -1603,13 +1675,8 @@ var SwapManager = class _SwapManager {
|
|
|
1603
1675
|
}
|
|
1604
1676
|
}
|
|
1605
1677
|
} catch (error) {
|
|
1606
|
-
logger.error(
|
|
1607
|
-
|
|
1608
|
-
error
|
|
1609
|
-
);
|
|
1610
|
-
this.swapFailedListeners.forEach(
|
|
1611
|
-
(listener) => listener(swap, error)
|
|
1612
|
-
);
|
|
1678
|
+
logger.error(`Failed to execute autonomous action for swap ${swap.id}:`, error);
|
|
1679
|
+
this.swapFailedListeners.forEach((listener) => listener(swap, error));
|
|
1613
1680
|
} finally {
|
|
1614
1681
|
this.swapsInProgress.delete(swap.id);
|
|
1615
1682
|
}
|
|
@@ -1710,9 +1777,7 @@ var SwapManager = class _SwapManager {
|
|
|
1710
1777
|
logger.log(`Resuming chain refund for swap ${swap.id}`);
|
|
1711
1778
|
await this.executeAutonomousAction(swap);
|
|
1712
1779
|
} else if (isPendingChainSwap(swap) && swap.request.to === "ARK" && isChainSignableStatus(swap.status)) {
|
|
1713
|
-
logger.log(
|
|
1714
|
-
`Resuming server claim signing for swap ${swap.id}`
|
|
1715
|
-
);
|
|
1780
|
+
logger.log(`Resuming server claim signing for swap ${swap.id}`);
|
|
1716
1781
|
await this.executeAutonomousAction(swap);
|
|
1717
1782
|
}
|
|
1718
1783
|
} catch (error) {
|
|
@@ -1771,9 +1836,7 @@ var SwapManager = class _SwapManager {
|
|
|
1771
1836
|
}
|
|
1772
1837
|
async pollSingleSwap(swap) {
|
|
1773
1838
|
try {
|
|
1774
|
-
const statusResponse = await this.swapProvider.getSwapStatus(
|
|
1775
|
-
swap.id
|
|
1776
|
-
);
|
|
1839
|
+
const statusResponse = await this.swapProvider.getSwapStatus(swap.id);
|
|
1777
1840
|
this.notFoundCounts.delete(swap.id);
|
|
1778
1841
|
if (statusResponse.status !== swap.status) {
|
|
1779
1842
|
await this.handleSwapStatusUpdate(swap, statusResponse.status);
|
|
@@ -1784,9 +1847,7 @@ var SwapManager = class _SwapManager {
|
|
|
1784
1847
|
return;
|
|
1785
1848
|
}
|
|
1786
1849
|
if (error instanceof NetworkError && error.statusCode === 429) {
|
|
1787
|
-
logger.warn(
|
|
1788
|
-
`Rate-limited polling swap ${swap.id}, retrying in 2s`
|
|
1789
|
-
);
|
|
1850
|
+
logger.warn(`Rate-limited polling swap ${swap.id}, retrying in 2s`);
|
|
1790
1851
|
const existing = this.pollRetryTimers.get(swap.id);
|
|
1791
1852
|
if (existing) clearTimeout(existing);
|
|
1792
1853
|
this.pollRetryTimers.set(
|
|
@@ -1794,25 +1855,17 @@ var SwapManager = class _SwapManager {
|
|
|
1794
1855
|
setTimeout(async () => {
|
|
1795
1856
|
this.pollRetryTimers.delete(swap.id);
|
|
1796
1857
|
try {
|
|
1797
|
-
const retry = await this.swapProvider.getSwapStatus(
|
|
1798
|
-
swap.id
|
|
1799
|
-
);
|
|
1858
|
+
const retry = await this.swapProvider.getSwapStatus(swap.id);
|
|
1800
1859
|
this.notFoundCounts.delete(swap.id);
|
|
1801
1860
|
if (retry.status !== swap.status) {
|
|
1802
|
-
await this.handleSwapStatusUpdate(
|
|
1803
|
-
swap,
|
|
1804
|
-
retry.status
|
|
1805
|
-
);
|
|
1861
|
+
await this.handleSwapStatusUpdate(swap, retry.status);
|
|
1806
1862
|
}
|
|
1807
1863
|
} catch (retryError) {
|
|
1808
1864
|
if (retryError instanceof SwapNotFoundError) {
|
|
1809
1865
|
await this.handleSwapNotFound(swap);
|
|
1810
1866
|
return;
|
|
1811
1867
|
}
|
|
1812
|
-
logger.error(
|
|
1813
|
-
`Retry poll for swap ${swap.id} also failed:`,
|
|
1814
|
-
retryError
|
|
1815
|
-
);
|
|
1868
|
+
logger.error(`Retry poll for swap ${swap.id} also failed:`, retryError);
|
|
1816
1869
|
}
|
|
1817
1870
|
}, 2e3)
|
|
1818
1871
|
);
|
|
@@ -1864,9 +1917,7 @@ var SwapManager = class _SwapManager {
|
|
|
1864
1917
|
this.pollRetryTimers.delete(swap.id);
|
|
1865
1918
|
}
|
|
1866
1919
|
this.notFoundCounts.delete(swap.id);
|
|
1867
|
-
this.swapUpdateListeners.forEach(
|
|
1868
|
-
(listener) => listener(swap, oldStatus)
|
|
1869
|
-
);
|
|
1920
|
+
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
1870
1921
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
1871
1922
|
if (subscribers) {
|
|
1872
1923
|
subscribers.forEach((callback) => {
|
|
@@ -1931,9 +1982,7 @@ async function saveSwap(swap, saver) {
|
|
|
1931
1982
|
if (saver.saveSubmarineSwap) {
|
|
1932
1983
|
await saver.saveSubmarineSwap(swap);
|
|
1933
1984
|
} else {
|
|
1934
|
-
console.warn(
|
|
1935
|
-
"No saveSubmarineSwap handler provided, swap not saved"
|
|
1936
|
-
);
|
|
1985
|
+
console.warn("No saveSubmarineSwap handler provided, swap not saved");
|
|
1937
1986
|
}
|
|
1938
1987
|
} else if (isPendingChainSwap(swap)) {
|
|
1939
1988
|
if (saver.saveChainSwap) {
|
|
@@ -2012,6 +2061,10 @@ function initDatabase(db) {
|
|
|
2012
2061
|
swapStore.createIndex("createdAt", "createdAt", { unique: false });
|
|
2013
2062
|
}
|
|
2014
2063
|
}
|
|
2064
|
+
function asArray(v) {
|
|
2065
|
+
if (v === void 0) return void 0;
|
|
2066
|
+
return Array.isArray(v) ? v : [v];
|
|
2067
|
+
}
|
|
2015
2068
|
var IndexedDbSwapRepository = class {
|
|
2016
2069
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
2017
2070
|
this.dbName = dbName;
|
|
@@ -2026,10 +2079,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2026
2079
|
async saveSwap(swap) {
|
|
2027
2080
|
const db = await this.getDB();
|
|
2028
2081
|
return new Promise((resolve, reject) => {
|
|
2029
|
-
const transaction = db.transaction(
|
|
2030
|
-
[STORE_SWAPS_STATE],
|
|
2031
|
-
"readwrite"
|
|
2032
|
-
);
|
|
2082
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2033
2083
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2034
2084
|
const request = store.put(swap);
|
|
2035
2085
|
request.onsuccess = () => resolve();
|
|
@@ -2039,10 +2089,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2039
2089
|
async deleteSwap(id) {
|
|
2040
2090
|
const db = await this.getDB();
|
|
2041
2091
|
return new Promise((resolve, reject) => {
|
|
2042
|
-
const transaction = db.transaction(
|
|
2043
|
-
[STORE_SWAPS_STATE],
|
|
2044
|
-
"readwrite"
|
|
2045
|
-
);
|
|
2092
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2046
2093
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2047
2094
|
const request = store.delete(id);
|
|
2048
2095
|
request.onsuccess = () => resolve();
|
|
@@ -2055,10 +2102,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2055
2102
|
async clear() {
|
|
2056
2103
|
const db = await this.getDB();
|
|
2057
2104
|
return new Promise((resolve, reject) => {
|
|
2058
|
-
const transaction = db.transaction(
|
|
2059
|
-
[STORE_SWAPS_STATE],
|
|
2060
|
-
"readwrite"
|
|
2061
|
-
);
|
|
2105
|
+
const transaction = db.transaction([STORE_SWAPS_STATE], "readwrite");
|
|
2062
2106
|
const store = transaction.objectStore(STORE_SWAPS_STATE);
|
|
2063
2107
|
const request = store.clear();
|
|
2064
2108
|
request.onsuccess = () => resolve();
|
|
@@ -2075,11 +2119,10 @@ var IndexedDbSwapRepository = class {
|
|
|
2075
2119
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2076
2120
|
})
|
|
2077
2121
|
);
|
|
2078
|
-
return Promise.all(requests).then(
|
|
2079
|
-
(results) => results.flatMap((result) => result)
|
|
2080
|
-
);
|
|
2122
|
+
return Promise.all(requests).then((results) => results.flatMap((result) => result));
|
|
2081
2123
|
}
|
|
2082
2124
|
async getAllSwapsFromStore(filter) {
|
|
2125
|
+
if (hasImpossibleSwapsFilter(filter)) return [];
|
|
2083
2126
|
const db = await this.getDB();
|
|
2084
2127
|
const store = db.transaction([STORE_SWAPS_STATE], "readonly").objectStore(STORE_SWAPS_STATE);
|
|
2085
2128
|
if (!filter || Object.keys(filter).length === 0) {
|
|
@@ -2089,9 +2132,8 @@ var IndexedDbSwapRepository = class {
|
|
|
2089
2132
|
request.onerror = () => reject(request.error);
|
|
2090
2133
|
});
|
|
2091
2134
|
}
|
|
2092
|
-
const
|
|
2093
|
-
if (
|
|
2094
|
-
const ids = normalizedFilter.get("id");
|
|
2135
|
+
const ids = asArray(filter.id);
|
|
2136
|
+
if (ids) {
|
|
2095
2137
|
const swaps = await Promise.all(
|
|
2096
2138
|
ids.map(
|
|
2097
2139
|
(id) => new Promise((resolve, reject) => {
|
|
@@ -2101,34 +2143,17 @@ var IndexedDbSwapRepository = class {
|
|
|
2101
2143
|
})
|
|
2102
2144
|
)
|
|
2103
2145
|
);
|
|
2104
|
-
return
|
|
2105
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2106
|
-
filter
|
|
2107
|
-
);
|
|
2146
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2108
2147
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2112
|
-
|
|
2113
|
-
"type",
|
|
2114
|
-
types
|
|
2115
|
-
);
|
|
2116
|
-
return this.sortIfNeeded(
|
|
2117
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2118
|
-
filter
|
|
2119
|
-
);
|
|
2148
|
+
const types = asArray(filter.type);
|
|
2149
|
+
if (types) {
|
|
2150
|
+
const swaps = await this.getSwapsByIndexValues(store, "type", types);
|
|
2151
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2120
2152
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
const swaps = await this.getSwapsByIndexValues(
|
|
2124
|
-
|
|
2125
|
-
"status",
|
|
2126
|
-
ids
|
|
2127
|
-
);
|
|
2128
|
-
return this.sortIfNeeded(
|
|
2129
|
-
this.applySwapsFilter(swaps, normalizedFilter),
|
|
2130
|
-
filter
|
|
2131
|
-
);
|
|
2153
|
+
const statuses = asArray(filter.status);
|
|
2154
|
+
if (statuses) {
|
|
2155
|
+
const swaps = await this.getSwapsByIndexValues(store, "status", statuses);
|
|
2156
|
+
return applyCreatedAtOrder(applySwapsFilter(swaps, filter), filter);
|
|
2132
2157
|
}
|
|
2133
2158
|
if (filter.orderBy === "createdAt") {
|
|
2134
2159
|
return this.getAllSwapsByCreatedAt(store, filter.orderDirection);
|
|
@@ -2138,22 +2163,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2138
2163
|
request.onsuccess = () => resolve(request.result ?? []);
|
|
2139
2164
|
request.onerror = () => reject(request.error);
|
|
2140
2165
|
});
|
|
2141
|
-
return
|
|
2142
|
-
this.applySwapsFilter(allSwaps, normalizedFilter),
|
|
2143
|
-
filter
|
|
2144
|
-
);
|
|
2145
|
-
}
|
|
2146
|
-
applySwapsFilter(swaps, filter) {
|
|
2147
|
-
return swaps.filter((swap) => {
|
|
2148
|
-
if (swap === void 0) return false;
|
|
2149
|
-
if (filter.has("id") && !filter.get("id")?.includes(swap.id))
|
|
2150
|
-
return false;
|
|
2151
|
-
if (filter.has("status") && !filter.get("status")?.includes(swap.status))
|
|
2152
|
-
return false;
|
|
2153
|
-
if (filter.has("type") && !filter.get("type")?.includes(swap.type))
|
|
2154
|
-
return false;
|
|
2155
|
-
return true;
|
|
2156
|
-
});
|
|
2166
|
+
return applyCreatedAtOrder(applySwapsFilter(allSwaps, filter), filter);
|
|
2157
2167
|
}
|
|
2158
2168
|
async getAllSwapsByCreatedAt(store, orderDirection) {
|
|
2159
2169
|
const index = store.index("createdAt");
|
|
@@ -2173,30 +2183,12 @@ var IndexedDbSwapRepository = class {
|
|
|
2173
2183
|
};
|
|
2174
2184
|
});
|
|
2175
2185
|
}
|
|
2176
|
-
sortIfNeeded(swaps, filter) {
|
|
2177
|
-
if (filter?.orderBy !== "createdAt") return swaps;
|
|
2178
|
-
const direction = filter.orderDirection === "asc" ? 1 : -1;
|
|
2179
|
-
return swaps.slice().sort((a, b) => (a.createdAt - b.createdAt) * direction);
|
|
2180
|
-
}
|
|
2181
2186
|
async [Symbol.asyncDispose]() {
|
|
2182
2187
|
if (!this.db) return;
|
|
2183
2188
|
await closeDatabase(this.dbName);
|
|
2184
2189
|
this.db = null;
|
|
2185
2190
|
}
|
|
2186
2191
|
};
|
|
2187
|
-
var FILTER_FIELDS = ["id", "status", "type"];
|
|
2188
|
-
function normalizeFilter(filter) {
|
|
2189
|
-
const res = /* @__PURE__ */ new Map();
|
|
2190
|
-
FILTER_FIELDS.forEach((current) => {
|
|
2191
|
-
if (!filter?.[current]) return;
|
|
2192
|
-
if (Array.isArray(filter[current])) {
|
|
2193
|
-
res.set(current, filter[current]);
|
|
2194
|
-
} else {
|
|
2195
|
-
res.set(current, [filter[current]]);
|
|
2196
|
-
}
|
|
2197
|
-
});
|
|
2198
|
-
return res;
|
|
2199
|
-
}
|
|
2200
2192
|
|
|
2201
2193
|
// src/arkade-swaps.ts
|
|
2202
2194
|
import {
|
|
@@ -2225,8 +2217,7 @@ var findKeyIndex = (keys, target) => keys.findIndex((k) => equalBytes(target, k)
|
|
|
2225
2217
|
var assertPublicKeys = (keys) => {
|
|
2226
2218
|
const seen = /* @__PURE__ */ new Set();
|
|
2227
2219
|
for (const key of keys) {
|
|
2228
|
-
if (key.length !== 33)
|
|
2229
|
-
throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
2220
|
+
if (key.length !== 33) throw new Error(`public key must be 33 bytes, got ${key.length}`);
|
|
2230
2221
|
const enc = hex3.encode(key);
|
|
2231
2222
|
if (seen.has(enc)) throw new Error(`duplicate public key ${enc}`);
|
|
2232
2223
|
seen.add(enc);
|
|
@@ -2234,9 +2225,7 @@ var assertPublicKeys = (keys) => {
|
|
|
2234
2225
|
};
|
|
2235
2226
|
var aggregateKeys = (publicKeys, tweak) => {
|
|
2236
2227
|
assertPublicKeys([...publicKeys]);
|
|
2237
|
-
return keyAggExport(
|
|
2238
|
-
keyAggregate([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : [])
|
|
2239
|
-
);
|
|
2228
|
+
return keyAggExport(keyAggregate([...publicKeys], tweak ? [tweak] : [], tweak ? [true] : []));
|
|
2240
2229
|
};
|
|
2241
2230
|
var MusigKeyAgg = class _MusigKeyAgg {
|
|
2242
2231
|
constructor(privateKey, myPublicKey, publicKeys, myIndex, aggPubkey, internalKey, _tweak) {
|
|
@@ -2283,18 +2272,12 @@ var MusigWithMessage = class {
|
|
|
2283
2272
|
this.msg = msg;
|
|
2284
2273
|
}
|
|
2285
2274
|
generateNonce() {
|
|
2286
|
-
const nonce = nonceGen(
|
|
2287
|
-
this.myPublicKey,
|
|
2288
|
-
this.privateKey,
|
|
2289
|
-
this.aggPubkey,
|
|
2290
|
-
this.msg
|
|
2291
|
-
);
|
|
2275
|
+
const nonce = nonceGen(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
|
|
2292
2276
|
return new MusigWithNonce(
|
|
2293
2277
|
this.privateKey,
|
|
2294
2278
|
this.myPublicKey,
|
|
2295
2279
|
this.publicKeys,
|
|
2296
2280
|
this.myIndex,
|
|
2297
|
-
this.aggPubkey,
|
|
2298
2281
|
this.tweak,
|
|
2299
2282
|
this.msg,
|
|
2300
2283
|
nonce
|
|
@@ -2302,12 +2285,11 @@ var MusigWithMessage = class {
|
|
|
2302
2285
|
}
|
|
2303
2286
|
};
|
|
2304
2287
|
var MusigWithNonce = class {
|
|
2305
|
-
constructor(privateKey, myPublicKey, publicKeys, myIndex,
|
|
2288
|
+
constructor(privateKey, myPublicKey, publicKeys, myIndex, tweak, msg, nonce) {
|
|
2306
2289
|
this.privateKey = privateKey;
|
|
2307
2290
|
this.myPublicKey = myPublicKey;
|
|
2308
2291
|
this.publicKeys = publicKeys;
|
|
2309
2292
|
this.myIndex = myIndex;
|
|
2310
|
-
this.aggPubkey = aggPubkey;
|
|
2311
2293
|
this.tweak = tweak;
|
|
2312
2294
|
this.msg = msg;
|
|
2313
2295
|
this.nonce = nonce;
|
|
@@ -2339,10 +2321,8 @@ var MusigWithNonce = class {
|
|
|
2339
2321
|
const aggregatedNonce = nonceAggregate([...ordered]);
|
|
2340
2322
|
return new MusigNoncesAggregated(
|
|
2341
2323
|
this.privateKey,
|
|
2342
|
-
this.myPublicKey,
|
|
2343
2324
|
this.publicKeys,
|
|
2344
2325
|
this.myIndex,
|
|
2345
|
-
this.aggPubkey,
|
|
2346
2326
|
this.tweak,
|
|
2347
2327
|
this.msg,
|
|
2348
2328
|
this.nonce,
|
|
@@ -2352,12 +2332,10 @@ var MusigWithNonce = class {
|
|
|
2352
2332
|
}
|
|
2353
2333
|
};
|
|
2354
2334
|
var MusigNoncesAggregated = class {
|
|
2355
|
-
constructor(privateKey,
|
|
2335
|
+
constructor(privateKey, publicKeys, myIndex, tweak, msg, nonce, pubNonces, aggregatedNonce) {
|
|
2356
2336
|
this.privateKey = privateKey;
|
|
2357
|
-
this.myPublicKey = myPublicKey;
|
|
2358
2337
|
this.publicKeys = publicKeys;
|
|
2359
2338
|
this.myIndex = myIndex;
|
|
2360
|
-
this.aggPubkey = aggPubkey;
|
|
2361
2339
|
this.tweak = tweak;
|
|
2362
2340
|
this.msg = msg;
|
|
2363
2341
|
this.nonce = nonce;
|
|
@@ -2403,11 +2381,7 @@ var MusigSession = class {
|
|
|
2403
2381
|
const index = typeof publicKeyOrIndex === "number" ? publicKeyOrIndex : findKeyIndex(this.publicKeys, publicKeyOrIndex);
|
|
2404
2382
|
if (index < 0 || index >= this.publicKeys.length)
|
|
2405
2383
|
throw new Error("public key not found or index out of range");
|
|
2406
|
-
if (!this.session.partialSigVerify(
|
|
2407
|
-
signature,
|
|
2408
|
-
[...this.pubNonces],
|
|
2409
|
-
index
|
|
2410
|
-
)) {
|
|
2384
|
+
if (!this.session.partialSigVerify(signature, [...this.pubNonces], index)) {
|
|
2411
2385
|
throw new Error("invalid partial signature");
|
|
2412
2386
|
}
|
|
2413
2387
|
this.partialSignatures[index] = signature;
|
|
@@ -2416,12 +2390,7 @@ var MusigSession = class {
|
|
|
2416
2390
|
signPartial() {
|
|
2417
2391
|
const sig = this.session.sign(this.nonce.secret, this.privateKey, true);
|
|
2418
2392
|
this.partialSignatures[this.myIndex] = sig;
|
|
2419
|
-
return new MusigSigned(
|
|
2420
|
-
this.session,
|
|
2421
|
-
[...this.partialSignatures],
|
|
2422
|
-
sig,
|
|
2423
|
-
this.nonce.public
|
|
2424
|
-
);
|
|
2393
|
+
return new MusigSigned(this.session, [...this.partialSignatures], sig, this.nonce.public);
|
|
2425
2394
|
}
|
|
2426
2395
|
};
|
|
2427
2396
|
var MusigSigned = class {
|
|
@@ -2435,14 +2404,11 @@ var MusigSigned = class {
|
|
|
2435
2404
|
if (this.partialSignatures.some((s) => s === null)) {
|
|
2436
2405
|
throw new Error("not all partial signatures are set");
|
|
2437
2406
|
}
|
|
2438
|
-
return this.session.partialSigAgg(
|
|
2439
|
-
this.partialSignatures
|
|
2440
|
-
);
|
|
2407
|
+
return this.session.partialSigAgg(this.partialSignatures);
|
|
2441
2408
|
}
|
|
2442
2409
|
};
|
|
2443
2410
|
var create = (privateKey, publicKeys) => {
|
|
2444
|
-
if (publicKeys.length < 2)
|
|
2445
|
-
throw new Error("need at least 2 keys to aggregate");
|
|
2411
|
+
if (publicKeys.length < 2) throw new Error("need at least 2 keys to aggregate");
|
|
2446
2412
|
const keys = [...publicKeys];
|
|
2447
2413
|
assertPublicKeys(keys);
|
|
2448
2414
|
Object.freeze(keys);
|
|
@@ -2450,14 +2416,7 @@ var create = (privateKey, publicKeys) => {
|
|
|
2450
2416
|
const myIndex = findKeyIndex(keys, myPublicKey);
|
|
2451
2417
|
if (myIndex === -1) throw new Error("our key is not in publicKeys");
|
|
2452
2418
|
const aggPubkey = aggregateKeys(keys);
|
|
2453
|
-
return new MusigKeyAgg(
|
|
2454
|
-
privateKey,
|
|
2455
|
-
myPublicKey,
|
|
2456
|
-
keys,
|
|
2457
|
-
myIndex,
|
|
2458
|
-
aggPubkey,
|
|
2459
|
-
aggPubkey
|
|
2460
|
-
);
|
|
2419
|
+
return new MusigKeyAgg(privateKey, myPublicKey, keys, myIndex, aggPubkey, aggPubkey);
|
|
2461
2420
|
};
|
|
2462
2421
|
|
|
2463
2422
|
// src/utils/boltz-swap-tx.ts
|
|
@@ -2533,9 +2492,7 @@ var taprootHashTree = (tree) => {
|
|
|
2533
2492
|
};
|
|
2534
2493
|
var tweakMusig = (musig, tree) => {
|
|
2535
2494
|
const tweak = taprootHashTree(tree).hash;
|
|
2536
|
-
return musig.xonlyTweakAdd(
|
|
2537
|
-
schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak)
|
|
2538
|
-
);
|
|
2495
|
+
return musig.xonlyTweakAdd(schnorr.utils.taggedHash("TapTweak", musig.aggPubkey, tweak));
|
|
2539
2496
|
};
|
|
2540
2497
|
var toXOnly = (pubKey) => {
|
|
2541
2498
|
if (pubKey.length === 32) return pubKey;
|
|
@@ -2547,9 +2504,7 @@ var toXOnly = (pubKey) => {
|
|
|
2547
2504
|
}
|
|
2548
2505
|
return pubKey.subarray(1, 33);
|
|
2549
2506
|
}
|
|
2550
|
-
throw new Error(
|
|
2551
|
-
`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`
|
|
2552
|
-
);
|
|
2507
|
+
throw new Error(`Invalid public key length: expected 32 or 33 bytes, got ${pubKey.length}`);
|
|
2553
2508
|
};
|
|
2554
2509
|
var p2trScript = (publicKey) => Script.encode(["OP_1", toXOnly(publicKey)]);
|
|
2555
2510
|
var detectSwapOutput = (tweakedKey, transaction) => {
|
|
@@ -2564,8 +2519,7 @@ var detectSwapOutput = (tweakedKey, transaction) => {
|
|
|
2564
2519
|
};
|
|
2565
2520
|
var DUMMY_TAPROOT_SIGNATURE = new Uint8Array(64);
|
|
2566
2521
|
var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
2567
|
-
if (fee < BigInt(0) || fee >= utxo.amount)
|
|
2568
|
-
throw new Error("fee exceeds utxo amount");
|
|
2522
|
+
if (fee < BigInt(0) || fee >= utxo.amount) throw new Error("fee exceeds utxo amount");
|
|
2569
2523
|
const tx = new Transaction2({ version: 2 });
|
|
2570
2524
|
tx.addOutput({
|
|
2571
2525
|
amount: utxo.amount - fee,
|
|
@@ -2584,9 +2538,7 @@ var constructClaimTransaction = (utxo, destinationScript, fee) => {
|
|
|
2584
2538
|
};
|
|
2585
2539
|
var targetFee = (satPerVbyte, constructTx) => {
|
|
2586
2540
|
const tx = constructTx(BigInt(1));
|
|
2587
|
-
return constructTx(
|
|
2588
|
-
BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte))
|
|
2589
|
-
);
|
|
2541
|
+
return constructTx(BigInt(Math.ceil((tx.vsize + tx.inputsLength) * satPerVbyte)));
|
|
2590
2542
|
};
|
|
2591
2543
|
|
|
2592
2544
|
// src/utils/restoration.ts
|
|
@@ -2610,10 +2562,7 @@ function extractTimeLockFromLeafOutput(scriptHex) {
|
|
|
2610
2562
|
const data = opcodes[hasCSV - 1];
|
|
2611
2563
|
if (data instanceof Uint8Array) {
|
|
2612
2564
|
const dataBytes = new Uint8Array(data).reverse();
|
|
2613
|
-
const {
|
|
2614
|
-
blocks,
|
|
2615
|
-
seconds
|
|
2616
|
-
} = bip68.decode(
|
|
2565
|
+
const { blocks, seconds } = bip68.decode(
|
|
2617
2566
|
parseInt(hex5.encode(dataBytes), 16)
|
|
2618
2567
|
);
|
|
2619
2568
|
return blocks ?? seconds ?? 0;
|
|
@@ -2634,11 +2583,7 @@ function extractInvoiceAmount(amountSats, fees) {
|
|
|
2634
2583
|
}
|
|
2635
2584
|
|
|
2636
2585
|
// src/utils/identity.ts
|
|
2637
|
-
import {
|
|
2638
|
-
ConditionWitness,
|
|
2639
|
-
setArkPsbtField,
|
|
2640
|
-
Transaction as Transaction3
|
|
2641
|
-
} from "@arkade-os/sdk";
|
|
2586
|
+
import { ConditionWitness, setArkPsbtField, Transaction as Transaction3 } from "@arkade-os/sdk";
|
|
2642
2587
|
function claimVHTLCIdentity(identity, preimage) {
|
|
2643
2588
|
return {
|
|
2644
2589
|
...identity,
|
|
@@ -2647,13 +2592,8 @@ function claimVHTLCIdentity(identity, preimage) {
|
|
|
2647
2592
|
let signedTx = await identity.sign(cpy, inputIndexes);
|
|
2648
2593
|
signedTx = Transaction3.fromPSBT(signedTx.toPSBT());
|
|
2649
2594
|
if (preimage) {
|
|
2650
|
-
for (const inputIndex of inputIndexes || Array.from(
|
|
2651
|
-
|
|
2652
|
-
(_, i) => i
|
|
2653
|
-
)) {
|
|
2654
|
-
setArkPsbtField(signedTx, inputIndex, ConditionWitness, [
|
|
2655
|
-
preimage
|
|
2656
|
-
]);
|
|
2595
|
+
for (const inputIndex of inputIndexes || Array.from({ length: signedTx.inputsLength }, (_, i) => i)) {
|
|
2596
|
+
setArkPsbtField(signedTx, inputIndex, ConditionWitness, [preimage]);
|
|
2657
2597
|
}
|
|
2658
2598
|
}
|
|
2659
2599
|
return signedTx;
|
|
@@ -2728,17 +2668,13 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2728
2668
|
if (!sweepTapTreeRoot) {
|
|
2729
2669
|
throw new Error("Sweep tap tree root not set");
|
|
2730
2670
|
}
|
|
2731
|
-
const xOnlyPublicKeys = event.cosignersPublicKeys.map(
|
|
2732
|
-
(k) => k.slice(2)
|
|
2733
|
-
);
|
|
2671
|
+
const xOnlyPublicKeys = event.cosignersPublicKeys.map((k) => k.slice(2));
|
|
2734
2672
|
const signerPublicKey = await session.getPublicKey();
|
|
2735
2673
|
const xonlySignerPublicKey = signerPublicKey.subarray(1);
|
|
2736
2674
|
if (!xOnlyPublicKeys.includes(hex6.encode(xonlySignerPublicKey))) {
|
|
2737
2675
|
return { skip: true };
|
|
2738
2676
|
}
|
|
2739
|
-
const commitmentTx = Transaction4.fromPSBT(
|
|
2740
|
-
base642.decode(event.unsignedCommitmentTx)
|
|
2741
|
-
);
|
|
2677
|
+
const commitmentTx = Transaction4.fromPSBT(base642.decode(event.unsignedCommitmentTx));
|
|
2742
2678
|
validateVtxoTxGraph(vtxoTree, commitmentTx, sweepTapTreeRoot);
|
|
2743
2679
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
2744
2680
|
if (!sharedOutput?.amount) {
|
|
@@ -2754,18 +2690,11 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2754
2690
|
if (!session) {
|
|
2755
2691
|
return { fullySigned: true };
|
|
2756
2692
|
}
|
|
2757
|
-
const { hasAllNonces } = await session.aggregatedNonces(
|
|
2758
|
-
event.txid,
|
|
2759
|
-
event.nonces
|
|
2760
|
-
);
|
|
2693
|
+
const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
|
|
2761
2694
|
if (!hasAllNonces) return { fullySigned: false };
|
|
2762
2695
|
const signatures = await session.sign();
|
|
2763
2696
|
const pubkey = hex6.encode(await session.getPublicKey());
|
|
2764
|
-
await arkProvider.submitTreeSignatures(
|
|
2765
|
-
event.id,
|
|
2766
|
-
pubkey,
|
|
2767
|
-
signatures
|
|
2768
|
-
);
|
|
2697
|
+
await arkProvider.submitTreeSignatures(event.id, pubkey, signatures);
|
|
2769
2698
|
return { fullySigned: true };
|
|
2770
2699
|
},
|
|
2771
2700
|
onBatchFinalization: async (event, _, connectorTree) => {
|
|
@@ -2773,9 +2702,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2773
2702
|
return;
|
|
2774
2703
|
}
|
|
2775
2704
|
if (!connectorTree) {
|
|
2776
|
-
throw new Error(
|
|
2777
|
-
"BatchFinalizationEvent: expected connector tree to be defined"
|
|
2778
|
-
);
|
|
2705
|
+
throw new Error("BatchFinalizationEvent: expected connector tree to be defined");
|
|
2779
2706
|
}
|
|
2780
2707
|
validateConnectorsTxGraph(event.commitmentTx, connectorTree);
|
|
2781
2708
|
const connectors = connectorTree.leaves();
|
|
@@ -2790,9 +2717,7 @@ function createVHTLCBatchHandler(intentId, vhtlc, arkProvider, identity, session
|
|
|
2790
2717
|
connectors[connectorIndex]
|
|
2791
2718
|
);
|
|
2792
2719
|
const signedForfeitTx = await identity.sign(forfeitTx);
|
|
2793
|
-
await arkProvider.submitSignedForfeitTxs([
|
|
2794
|
-
base642.encode(signedForfeitTx.toPSBT())
|
|
2795
|
-
]);
|
|
2720
|
+
await arkProvider.submitSignedForfeitTxs([base642.encode(signedForfeitTx.toPSBT())]);
|
|
2796
2721
|
}
|
|
2797
2722
|
};
|
|
2798
2723
|
}
|
|
@@ -2852,36 +2777,22 @@ var createVHTLCScript = (args) => {
|
|
|
2852
2777
|
serverPubkey,
|
|
2853
2778
|
timeoutBlockHeights: vhtlcTimeouts
|
|
2854
2779
|
} = args;
|
|
2855
|
-
const receiverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
);
|
|
2859
|
-
const senderXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2860
|
-
hex7.decode(senderPubkey),
|
|
2861
|
-
"sender"
|
|
2862
|
-
);
|
|
2863
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
2864
|
-
hex7.decode(serverPubkey),
|
|
2865
|
-
"server"
|
|
2866
|
-
);
|
|
2780
|
+
const receiverXOnlyPublicKey = normalizeToXOnlyKey(hex7.decode(receiverPubkey), "receiver");
|
|
2781
|
+
const senderXOnlyPublicKey = normalizeToXOnlyKey(hex7.decode(senderPubkey), "sender");
|
|
2782
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(hex7.decode(serverPubkey), "server");
|
|
2867
2783
|
const vhtlcScript = new VHTLC.Script({
|
|
2868
2784
|
preimageHash: ripemd160(preimageHash),
|
|
2869
2785
|
sender: senderXOnlyPublicKey,
|
|
2870
2786
|
receiver: receiverXOnlyPublicKey,
|
|
2871
2787
|
server: serverXOnlyPublicKey,
|
|
2872
2788
|
refundLocktime: BigInt(vhtlcTimeouts.refund),
|
|
2873
|
-
unilateralClaimDelay: toBip68RelativeTimelock(
|
|
2874
|
-
|
|
2875
|
-
),
|
|
2876
|
-
unilateralRefundDelay: toBip68RelativeTimelock(
|
|
2877
|
-
vhtlcTimeouts.unilateralRefund
|
|
2878
|
-
),
|
|
2789
|
+
unilateralClaimDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralClaim),
|
|
2790
|
+
unilateralRefundDelay: toBip68RelativeTimelock(vhtlcTimeouts.unilateralRefund),
|
|
2879
2791
|
unilateralRefundWithoutReceiverDelay: toBip68RelativeTimelock(
|
|
2880
2792
|
vhtlcTimeouts.unilateralRefundWithoutReceiver
|
|
2881
2793
|
)
|
|
2882
2794
|
});
|
|
2883
|
-
if (!vhtlcScript.claimScript)
|
|
2884
|
-
throw new Error("Failed to create VHTLC script");
|
|
2795
|
+
if (!vhtlcScript.claimScript) throw new Error("Failed to create VHTLC script");
|
|
2885
2796
|
const hrp = network === "bitcoin" ? "ark" : "tark";
|
|
2886
2797
|
const vhtlcAddress = vhtlcScript.address(hrp, serverXOnlyPublicKey).encode();
|
|
2887
2798
|
return { vhtlcScript, vhtlcAddress };
|
|
@@ -2915,11 +2826,7 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2915
2826
|
unknown: [VtxoTaprootTree.encode(input.tapTree)],
|
|
2916
2827
|
sequence: getSequence2(input.tapLeafScript)
|
|
2917
2828
|
};
|
|
2918
|
-
const registerIntent = Intent.create(
|
|
2919
|
-
intentMessage,
|
|
2920
|
-
[intentInput],
|
|
2921
|
-
[output]
|
|
2922
|
-
);
|
|
2829
|
+
const registerIntent = Intent.create(intentMessage, [intentInput], [output]);
|
|
2923
2830
|
const deleteIntent = Intent.create(deleteMessage, [intentInput]);
|
|
2924
2831
|
const [signedRegisterIntent, signedDeleteIntent] = await Promise.all([
|
|
2925
2832
|
identity.sign(registerIntent),
|
|
@@ -2943,14 +2850,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2943
2850
|
normalizeToXOnlyKey(forfeitPubkey, "forfeit"),
|
|
2944
2851
|
isRecoverable2 ? void 0 : OutScript.encode(decodedAddress)
|
|
2945
2852
|
);
|
|
2946
|
-
const topics = [
|
|
2947
|
-
|
|
2948
|
-
`${input.txid}:${input.vout}`
|
|
2949
|
-
];
|
|
2950
|
-
const eventStream = arkProvider.getEventStream(
|
|
2951
|
-
abortController.signal,
|
|
2952
|
-
topics
|
|
2953
|
-
);
|
|
2853
|
+
const topics = [hex7.encode(signerPublicKey), `${input.txid}:${input.vout}`];
|
|
2854
|
+
const eventStream = arkProvider.getEventStream(abortController.signal, topics);
|
|
2954
2855
|
const commitmentTxid = await Batch.join(eventStream, handler, {
|
|
2955
2856
|
abortController
|
|
2956
2857
|
});
|
|
@@ -2971,14 +2872,8 @@ var joinBatch = async (arkProvider, identity, input, output, {
|
|
|
2971
2872
|
};
|
|
2972
2873
|
var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKey, input, output, arkInfo, arkProvider) => {
|
|
2973
2874
|
const rawCheckpointTapscript = hex7.decode(arkInfo.checkpointTapscript);
|
|
2974
|
-
const serverUnrollScript = CSVMultisigTapscript2.decode(
|
|
2975
|
-
|
|
2976
|
-
);
|
|
2977
|
-
const { arkTx, checkpoints } = buildOffchainTx(
|
|
2978
|
-
[input],
|
|
2979
|
-
[output],
|
|
2980
|
-
serverUnrollScript
|
|
2981
|
-
);
|
|
2875
|
+
const serverUnrollScript = CSVMultisigTapscript2.decode(rawCheckpointTapscript);
|
|
2876
|
+
const { arkTx, checkpoints } = buildOffchainTx([input], [output], serverUnrollScript);
|
|
2982
2877
|
const signedArkTx = await identity.sign(arkTx);
|
|
2983
2878
|
const { arkTxid, finalArkTx, signedCheckpointTxs } = await arkProvider.submitTx(
|
|
2984
2879
|
base643.encode(signedArkTx.toPSBT()),
|
|
@@ -2986,9 +2881,7 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2986
2881
|
);
|
|
2987
2882
|
const finalTx = Transaction5.fromPSBT(base643.decode(finalArkTx));
|
|
2988
2883
|
const serverPubkeyHex = hex7.encode(serverXOnlyPublicKey);
|
|
2989
|
-
const claimLeafHash = tapLeafHash3(
|
|
2990
|
-
scriptFromTapLeafScript(vhtlcScript.claim())
|
|
2991
|
-
);
|
|
2884
|
+
const claimLeafHash = tapLeafHash3(scriptFromTapLeafScript(vhtlcScript.claim()));
|
|
2992
2885
|
for (let i = 0; i < finalTx.inputsLength; i++) {
|
|
2993
2886
|
if (!verifySignatures(finalTx, i, [serverPubkeyHex], claimLeafHash)) {
|
|
2994
2887
|
throw new Error("Invalid final Ark transaction");
|
|
@@ -2998,13 +2891,9 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
2998
2891
|
signedCheckpointTxs.map(async (c, idx) => {
|
|
2999
2892
|
const tx = Transaction5.fromPSBT(base643.decode(c));
|
|
3000
2893
|
const checkpointLeaf = checkpoints[idx].getInput(0).tapLeafScript[0];
|
|
3001
|
-
const cpLeafHash = tapLeafHash3(
|
|
3002
|
-
scriptFromTapLeafScript(checkpointLeaf)
|
|
3003
|
-
);
|
|
2894
|
+
const cpLeafHash = tapLeafHash3(scriptFromTapLeafScript(checkpointLeaf));
|
|
3004
2895
|
if (!verifySignatures(tx, 0, [serverPubkeyHex], cpLeafHash)) {
|
|
3005
|
-
throw new Error(
|
|
3006
|
-
"Invalid server signature in checkpoint transaction"
|
|
3007
|
-
);
|
|
2896
|
+
throw new Error("Invalid server signature in checkpoint transaction");
|
|
3008
2897
|
}
|
|
3009
2898
|
const signedCheckpoint = await identity.sign(tx, [0]);
|
|
3010
2899
|
return base643.encode(signedCheckpoint.toPSBT());
|
|
@@ -3014,61 +2903,37 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
3014
2903
|
};
|
|
3015
2904
|
var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnlyPublicKey, ourXOnlyPublicKey, serverXOnlyPublicKey, input, output, arkInfo, refundFunc) => {
|
|
3016
2905
|
const rawCheckpointTapscript = hex7.decode(arkInfo.checkpointTapscript);
|
|
3017
|
-
const serverUnrollScript = CSVMultisigTapscript2.decode(
|
|
3018
|
-
|
|
2906
|
+
const serverUnrollScript = CSVMultisigTapscript2.decode(rawCheckpointTapscript);
|
|
2907
|
+
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = buildOffchainTx(
|
|
2908
|
+
[input],
|
|
2909
|
+
[output],
|
|
2910
|
+
serverUnrollScript
|
|
3019
2911
|
);
|
|
3020
|
-
const { arkTx: unsignedRefundTx, checkpoints: checkpointPtxs } = buildOffchainTx([input], [output], serverUnrollScript);
|
|
3021
2912
|
if (checkpointPtxs.length !== 1)
|
|
3022
|
-
throw new Error(
|
|
3023
|
-
`Expected one checkpoint transaction, got ${checkpointPtxs.length}`
|
|
3024
|
-
);
|
|
2913
|
+
throw new Error(`Expected one checkpoint transaction, got ${checkpointPtxs.length}`);
|
|
3025
2914
|
const unsignedCheckpointTx = checkpointPtxs[0];
|
|
3026
2915
|
let boltzSignedRefundTx;
|
|
3027
2916
|
let boltzSignedCheckpointTx;
|
|
3028
2917
|
try {
|
|
3029
|
-
const result = await refundFunc(
|
|
3030
|
-
swapId,
|
|
3031
|
-
unsignedRefundTx,
|
|
3032
|
-
unsignedCheckpointTx
|
|
3033
|
-
);
|
|
2918
|
+
const result = await refundFunc(swapId, unsignedRefundTx, unsignedCheckpointTx);
|
|
3034
2919
|
boltzSignedRefundTx = result.transaction;
|
|
3035
2920
|
boltzSignedCheckpointTx = result.checkpoint;
|
|
3036
2921
|
} catch (error) {
|
|
3037
|
-
throw new BoltzRefundError(
|
|
3038
|
-
`Boltz rejected refund for swap ${swapId}`,
|
|
3039
|
-
error
|
|
3040
|
-
);
|
|
2922
|
+
throw new BoltzRefundError(`Boltz rejected refund for swap ${swapId}`, error);
|
|
3041
2923
|
}
|
|
3042
2924
|
const boltzXOnlyPublicKeyHex = hex7.encode(boltzXOnlyPublicKey);
|
|
3043
|
-
const refundLeafHash = tapLeafHash3(
|
|
3044
|
-
|
|
3045
|
-
);
|
|
3046
|
-
if (!verifySignatures(
|
|
3047
|
-
boltzSignedRefundTx,
|
|
3048
|
-
0,
|
|
3049
|
-
[boltzXOnlyPublicKeyHex],
|
|
3050
|
-
refundLeafHash
|
|
3051
|
-
)) {
|
|
2925
|
+
const refundLeafHash = tapLeafHash3(scriptFromTapLeafScript(input.tapLeafScript));
|
|
2926
|
+
if (!verifySignatures(boltzSignedRefundTx, 0, [boltzXOnlyPublicKeyHex], refundLeafHash)) {
|
|
3052
2927
|
throw new Error("Invalid Boltz signature in refund transaction");
|
|
3053
2928
|
}
|
|
3054
2929
|
const checkpointLeaf = unsignedCheckpointTx.getInput(0).tapLeafScript[0];
|
|
3055
|
-
const checkpointLeafHash = tapLeafHash3(
|
|
3056
|
-
|
|
3057
|
-
);
|
|
3058
|
-
if (!verifySignatures(
|
|
3059
|
-
boltzSignedCheckpointTx,
|
|
3060
|
-
0,
|
|
3061
|
-
[boltzXOnlyPublicKeyHex],
|
|
3062
|
-
checkpointLeafHash
|
|
3063
|
-
)) {
|
|
2930
|
+
const checkpointLeafHash = tapLeafHash3(scriptFromTapLeafScript(checkpointLeaf));
|
|
2931
|
+
if (!verifySignatures(boltzSignedCheckpointTx, 0, [boltzXOnlyPublicKeyHex], checkpointLeafHash)) {
|
|
3064
2932
|
throw new Error("Invalid Boltz signature in checkpoint transaction");
|
|
3065
2933
|
}
|
|
3066
2934
|
const signedRefundTx = await identity.sign(unsignedRefundTx);
|
|
3067
2935
|
const signedCheckpointTx = await identity.sign(unsignedCheckpointTx);
|
|
3068
|
-
const combinedSignedRefundTx = combineTapscriptSigs(
|
|
3069
|
-
boltzSignedRefundTx,
|
|
3070
|
-
signedRefundTx
|
|
3071
|
-
);
|
|
2936
|
+
const combinedSignedRefundTx = combineTapscriptSigs(boltzSignedRefundTx, signedRefundTx);
|
|
3072
2937
|
const combinedSignedCheckpointTx = combineTapscriptSigs(
|
|
3073
2938
|
boltzSignedCheckpointTx,
|
|
3074
2939
|
signedCheckpointTx
|
|
@@ -3092,25 +2957,16 @@ var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnly
|
|
|
3092
2957
|
`Expected one signed checkpoint transaction, got ${signedCheckpointTxs.length}`
|
|
3093
2958
|
);
|
|
3094
2959
|
}
|
|
3095
|
-
const serverSignedCheckpointTx = Transaction5.fromPSBT(
|
|
3096
|
-
base643.decode(signedCheckpointTxs[0])
|
|
3097
|
-
);
|
|
2960
|
+
const serverSignedCheckpointTx = Transaction5.fromPSBT(base643.decode(signedCheckpointTxs[0]));
|
|
3098
2961
|
const serverPubkeyHex = hex7.encode(serverXOnlyPublicKey);
|
|
3099
|
-
if (!verifySignatures(
|
|
3100
|
-
serverSignedCheckpointTx,
|
|
3101
|
-
0,
|
|
3102
|
-
[serverPubkeyHex],
|
|
3103
|
-
checkpointLeafHash
|
|
3104
|
-
)) {
|
|
2962
|
+
if (!verifySignatures(serverSignedCheckpointTx, 0, [serverPubkeyHex], checkpointLeafHash)) {
|
|
3105
2963
|
throw new Error("Invalid server signature in checkpoint transaction");
|
|
3106
2964
|
}
|
|
3107
2965
|
const finalCheckpointTx = combineTapscriptSigs(
|
|
3108
2966
|
combinedSignedCheckpointTx,
|
|
3109
2967
|
serverSignedCheckpointTx
|
|
3110
2968
|
);
|
|
3111
|
-
await arkProvider.finalizeTx(arkTxid, [
|
|
3112
|
-
base643.encode(finalCheckpointTx.toPSBT())
|
|
3113
|
-
]);
|
|
2969
|
+
await arkProvider.finalizeTx(arkTxid, [base643.encode(finalCheckpointTx.toPSBT())]);
|
|
3114
2970
|
};
|
|
3115
2971
|
function scriptFromTapLeafScript(leaf) {
|
|
3116
2972
|
return leaf[1].subarray(0, leaf[1].length - 1);
|
|
@@ -3118,21 +2974,21 @@ function scriptFromTapLeafScript(leaf) {
|
|
|
3118
2974
|
|
|
3119
2975
|
// src/arkade-swaps.ts
|
|
3120
2976
|
var dedupeVtxos = (vtxos) => [
|
|
3121
|
-
...new Map(
|
|
3122
|
-
vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])
|
|
3123
|
-
).values()
|
|
2977
|
+
...new Map(vtxos.map((vtxo) => [`${vtxo.txid}:${vtxo.vout}`, vtxo])).values()
|
|
3124
2978
|
];
|
|
3125
2979
|
var hasNonEmptyString = (value) => typeof value === "string" && value.length > 0;
|
|
3126
2980
|
var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
|
|
3127
2981
|
const hasRequiredSwapMetadata = hasNonEmptyString(swap.id) && hasNonEmptyString(swap.request.refundPublicKey) && hasNonEmptyString(swap.response.address) && hasNonEmptyString(swap.response.claimPublicKey) && !!swap.response.timeoutBlockHeights;
|
|
3128
2982
|
if (!hasRequiredSwapMetadata) return false;
|
|
3129
|
-
return refundableVtxos.some(
|
|
3130
|
-
(vtxo) => !vtxo.isSpent && !isRecoverable(vtxo)
|
|
3131
|
-
);
|
|
2983
|
+
return refundableVtxos.some((vtxo) => !vtxo.isSpent && !isRecoverable(vtxo));
|
|
3132
2984
|
};
|
|
3133
2985
|
var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
|
|
3134
2986
|
var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
|
|
3135
2987
|
var CLAIM_VTXO_RETRY_DELAY_MS = 500;
|
|
2988
|
+
var quoteOptionsForSwap = (swap) => {
|
|
2989
|
+
const amount = swap.response?.claimDetails?.amount;
|
|
2990
|
+
return typeof amount === "number" ? { minAcceptableAmount: amount } : void 0;
|
|
2991
|
+
};
|
|
3136
2992
|
var ArkadeSwaps = class _ArkadeSwaps {
|
|
3137
2993
|
/** The Arkade wallet instance used for signing and address generation. */
|
|
3138
2994
|
wallet;
|
|
@@ -3168,10 +3024,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3168
3024
|
return new _ArkadeSwaps(config);
|
|
3169
3025
|
}
|
|
3170
3026
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3171
|
-
if (!arkProvider)
|
|
3172
|
-
throw new Error(
|
|
3173
|
-
"Ark provider is required either in wallet or config."
|
|
3174
|
-
);
|
|
3027
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3175
3028
|
const arkInfo = await arkProvider.getInfo();
|
|
3176
3029
|
const network = arkInfo.network;
|
|
3177
3030
|
const swapProvider = new BoltzSwapProvider({ network });
|
|
@@ -3182,16 +3035,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3182
3035
|
if (!config.swapProvider) throw new Error("Swap provider is required.");
|
|
3183
3036
|
this.wallet = config.wallet;
|
|
3184
3037
|
const arkProvider = config.arkProvider ?? config.wallet.arkProvider;
|
|
3185
|
-
if (!arkProvider)
|
|
3186
|
-
throw new Error(
|
|
3187
|
-
"Ark provider is required either in wallet or config."
|
|
3188
|
-
);
|
|
3038
|
+
if (!arkProvider) throw new Error("Ark provider is required either in wallet or config.");
|
|
3189
3039
|
this.arkProvider = arkProvider;
|
|
3190
3040
|
const indexerProvider = config.indexerProvider ?? config.wallet.indexerProvider;
|
|
3191
3041
|
if (!indexerProvider)
|
|
3192
|
-
throw new Error(
|
|
3193
|
-
"Indexer provider is required either in wallet or config."
|
|
3194
|
-
);
|
|
3042
|
+
throw new Error("Indexer provider is required either in wallet or config.");
|
|
3195
3043
|
this.indexerProvider = indexerProvider;
|
|
3196
3044
|
this.swapProvider = config.swapProvider;
|
|
3197
3045
|
if (config.swapRepository) {
|
|
@@ -3202,10 +3050,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3202
3050
|
if (config.swapManager !== false) {
|
|
3203
3051
|
const swapManagerConfig = !config.swapManager || config.swapManager === true ? {} : config.swapManager;
|
|
3204
3052
|
const shouldAutostart = swapManagerConfig.autoStart ?? true;
|
|
3205
|
-
this.swapManager = new SwapManager(
|
|
3206
|
-
this.swapProvider,
|
|
3207
|
-
swapManagerConfig
|
|
3208
|
-
);
|
|
3053
|
+
this.swapManager = new SwapManager(this.swapProvider, swapManagerConfig);
|
|
3209
3054
|
this.swapManager.setCallbacks({
|
|
3210
3055
|
claim: async (swap) => {
|
|
3211
3056
|
await this.claimVHTLC(swap);
|
|
@@ -3353,19 +3198,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3353
3198
|
* @throws {SwapError} If amount is <= 0 or key retrieval fails.
|
|
3354
3199
|
*/
|
|
3355
3200
|
async createReverseSwap(args) {
|
|
3356
|
-
if (args.amount <= 0)
|
|
3357
|
-
|
|
3358
|
-
const claimPublicKey = hex8.encode(
|
|
3359
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3360
|
-
);
|
|
3201
|
+
if (args.amount <= 0) throw new SwapError({ message: "Amount must be greater than 0" });
|
|
3202
|
+
const claimPublicKey = hex8.encode(await this.wallet.identity.compressedPublicKey());
|
|
3361
3203
|
if (!claimPublicKey)
|
|
3362
3204
|
throw new SwapError({
|
|
3363
3205
|
message: "Failed to get claim public key from wallet"
|
|
3364
3206
|
});
|
|
3365
3207
|
const preimage = randomBytes(32);
|
|
3366
3208
|
const preimageHash = hex8.encode(sha2563(preimage));
|
|
3367
|
-
if (!preimageHash)
|
|
3368
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3209
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
3369
3210
|
const swapRequest = {
|
|
3370
3211
|
invoiceAmount: args.amount,
|
|
3371
3212
|
claimPublicKey,
|
|
@@ -3395,18 +3236,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3395
3236
|
*/
|
|
3396
3237
|
async claimVHTLC(pendingSwap) {
|
|
3397
3238
|
if (!pendingSwap.preimage)
|
|
3398
|
-
throw new Error(
|
|
3399
|
-
`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`
|
|
3400
|
-
);
|
|
3239
|
+
throw new Error(`Swap ${pendingSwap.id}: preimage is required to claim VHTLC`);
|
|
3401
3240
|
const {
|
|
3402
3241
|
refundPublicKey,
|
|
3403
3242
|
lockupAddress,
|
|
3404
3243
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3405
3244
|
} = pendingSwap.response;
|
|
3406
3245
|
if (!refundPublicKey || !lockupAddress || !vhtlcTimeouts)
|
|
3407
|
-
throw new Error(
|
|
3408
|
-
`Swap ${pendingSwap.id}: incomplete reverse swap response`
|
|
3409
|
-
);
|
|
3246
|
+
throw new Error(`Swap ${pendingSwap.id}: incomplete reverse swap response`);
|
|
3410
3247
|
const preimage = hex8.decode(pendingSwap.preimage);
|
|
3411
3248
|
const arkInfo = await this.arkProvider.getInfo();
|
|
3412
3249
|
const address = await this.wallet.getAddress();
|
|
@@ -3451,15 +3288,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3451
3288
|
break;
|
|
3452
3289
|
}
|
|
3453
3290
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
3454
|
-
await new Promise(
|
|
3455
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
3456
|
-
);
|
|
3291
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
3457
3292
|
}
|
|
3458
3293
|
}
|
|
3459
3294
|
if (!vtxo) {
|
|
3460
|
-
throw new Error(
|
|
3461
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
3462
|
-
);
|
|
3295
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
3463
3296
|
}
|
|
3464
3297
|
if (vtxo.isSpent) {
|
|
3465
3298
|
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
@@ -3473,10 +3306,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3473
3306
|
amount: BigInt(vtxo.value),
|
|
3474
3307
|
script: ArkAddress2.decode(address).pkScript
|
|
3475
3308
|
};
|
|
3476
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
3477
|
-
this.wallet.identity,
|
|
3478
|
-
preimage
|
|
3479
|
-
);
|
|
3309
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
3480
3310
|
let finalStatus;
|
|
3481
3311
|
if (isRecoverable(vtxo)) {
|
|
3482
3312
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
@@ -3595,9 +3425,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3595
3425
|
async sendLightningPayment(args) {
|
|
3596
3426
|
const pendingSwap = await this.createSubmarineSwap(args);
|
|
3597
3427
|
if (!pendingSwap.response.address)
|
|
3598
|
-
throw new Error(
|
|
3599
|
-
`Swap ${pendingSwap.id}: missing address in submarine swap response`
|
|
3600
|
-
);
|
|
3428
|
+
throw new Error(`Swap ${pendingSwap.id}: missing address in submarine swap response`);
|
|
3601
3429
|
await this.savePendingSubmarineSwap(pendingSwap);
|
|
3602
3430
|
const txid = await this.wallet.send({
|
|
3603
3431
|
address: pendingSwap.response.address,
|
|
@@ -3630,9 +3458,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3630
3458
|
* @throws {SwapError} If invoice is missing or key retrieval fails.
|
|
3631
3459
|
*/
|
|
3632
3460
|
async createSubmarineSwap(args) {
|
|
3633
|
-
const refundPublicKey = hex8.encode(
|
|
3634
|
-
await this.wallet.identity.compressedPublicKey()
|
|
3635
|
-
);
|
|
3461
|
+
const refundPublicKey = hex8.encode(await this.wallet.identity.compressedPublicKey());
|
|
3636
3462
|
if (!refundPublicKey)
|
|
3637
3463
|
throw new SwapError({
|
|
3638
3464
|
message: "Failed to get refund public key from wallet"
|
|
@@ -3670,9 +3496,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3670
3496
|
async buildSubmarineVHTLCContext(swap, arkInfo) {
|
|
3671
3497
|
const preimageHash = swap.request.invoice ? getInvoicePaymentHash(swap.request.invoice) : swap.preimageHash;
|
|
3672
3498
|
if (!preimageHash)
|
|
3673
|
-
throw new Error(
|
|
3674
|
-
`Swap ${swap.id}: preimage hash is required to refund VHTLC`
|
|
3675
|
-
);
|
|
3499
|
+
throw new Error(`Swap ${swap.id}: preimage hash is required to refund VHTLC`);
|
|
3676
3500
|
const resolvedArkInfo = arkInfo ?? await this.arkProvider.getInfo();
|
|
3677
3501
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3678
3502
|
await this.wallet.identity.xOnlyPublicKey(),
|
|
@@ -3686,9 +3510,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3686
3510
|
);
|
|
3687
3511
|
const { claimPublicKey, timeoutBlockHeights: vhtlcTimeouts } = swap.response;
|
|
3688
3512
|
if (!claimPublicKey || !vhtlcTimeouts)
|
|
3689
|
-
throw new Error(
|
|
3690
|
-
`Swap ${swap.id}: incomplete submarine swap response`
|
|
3691
|
-
);
|
|
3513
|
+
throw new Error(`Swap ${swap.id}: incomplete submarine swap response`);
|
|
3692
3514
|
const boltzXOnlyPublicKey = normalizeToXOnlyKey(
|
|
3693
3515
|
hex8.decode(claimPublicKey),
|
|
3694
3516
|
"boltz",
|
|
@@ -3703,9 +3525,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3703
3525
|
timeoutBlockHeights: vhtlcTimeouts
|
|
3704
3526
|
});
|
|
3705
3527
|
if (!vhtlcScript.claimScript)
|
|
3706
|
-
throw new Error(
|
|
3707
|
-
`Swap ${swap.id}: failed to create VHTLC script for submarine swap`
|
|
3708
|
-
);
|
|
3528
|
+
throw new Error(`Swap ${swap.id}: failed to create VHTLC script for submarine swap`);
|
|
3709
3529
|
if (vhtlcAddress !== swap.response.address)
|
|
3710
3530
|
throw new Error(
|
|
3711
3531
|
`VHTLC address mismatch for swap ${swap.id}: expected ${swap.response.address}, got ${vhtlcAddress}`
|
|
@@ -3744,10 +3564,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3744
3564
|
recoverableOnly: true
|
|
3745
3565
|
})
|
|
3746
3566
|
]);
|
|
3747
|
-
const refundableVtxos = dedupeVtxos([
|
|
3748
|
-
...spendableResult.vtxos,
|
|
3749
|
-
...recoverableResult.vtxos
|
|
3750
|
-
]);
|
|
3567
|
+
const refundableVtxos = dedupeVtxos([...spendableResult.vtxos, ...recoverableResult.vtxos]);
|
|
3751
3568
|
let diagnostic;
|
|
3752
3569
|
if (refundableVtxos.length === 0) {
|
|
3753
3570
|
const { vtxos: allVtxos } = await this.indexerProvider.getVtxos({
|
|
@@ -3767,13 +3584,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3767
3584
|
submarineRecoveryInfoFromLookup(swap, lookup) {
|
|
3768
3585
|
const { refundableVtxos, diagnostic, vhtlcTimeouts } = lookup;
|
|
3769
3586
|
if (refundableVtxos.length > 0) {
|
|
3770
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3771
|
-
|
|
3772
|
-
);
|
|
3773
|
-
const amountSats = refundableVtxos.reduce(
|
|
3774
|
-
(sum, vtxo) => sum + Number(vtxo.value),
|
|
3775
|
-
0
|
|
3776
|
-
);
|
|
3587
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3588
|
+
const amountSats = refundableVtxos.reduce((sum, vtxo) => sum + Number(vtxo.value), 0);
|
|
3777
3589
|
const isRecoverable2 = cltvSatisfied || canRecoverViaBoltz3of3(refundableVtxos, swap);
|
|
3778
3590
|
return {
|
|
3779
3591
|
swap,
|
|
@@ -3837,19 +3649,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3837
3649
|
);
|
|
3838
3650
|
}
|
|
3839
3651
|
if (diagnostic.allSpent) {
|
|
3840
|
-
throw new Error(
|
|
3841
|
-
`Swap ${pendingSwap.id}: VHTLC is already spent`
|
|
3842
|
-
);
|
|
3652
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
3843
3653
|
}
|
|
3844
|
-
throw new Error(
|
|
3845
|
-
`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`
|
|
3846
|
-
);
|
|
3654
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC has no refundable VTXOs yet`);
|
|
3847
3655
|
}
|
|
3848
3656
|
const outputScript = ArkAddress2.decode(address).pkScript;
|
|
3849
3657
|
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
3850
|
-
const cltvSatisfied = isSubmarineRefundLocktimeReached(
|
|
3851
|
-
vhtlcTimeouts.refund
|
|
3852
|
-
);
|
|
3658
|
+
const cltvSatisfied = isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund);
|
|
3853
3659
|
let boltzCallCount = 0;
|
|
3854
3660
|
let sweptCount = 0;
|
|
3855
3661
|
let skippedCount = 0;
|
|
@@ -3901,9 +3707,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3901
3707
|
input,
|
|
3902
3708
|
output,
|
|
3903
3709
|
arkInfo,
|
|
3904
|
-
this.swapProvider.refundSubmarineSwap.bind(
|
|
3905
|
-
this.swapProvider
|
|
3906
|
-
)
|
|
3710
|
+
this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3907
3711
|
);
|
|
3908
3712
|
boltzCallCount++;
|
|
3909
3713
|
sweptCount++;
|
|
@@ -3926,13 +3730,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3926
3730
|
tapLeafScript: refundWithoutReceiverLeaf,
|
|
3927
3731
|
tapTree: vhtlcScript.encode()
|
|
3928
3732
|
};
|
|
3929
|
-
await this.joinBatch(
|
|
3930
|
-
this.wallet.identity,
|
|
3931
|
-
fallbackInput,
|
|
3932
|
-
output,
|
|
3933
|
-
arkInfo,
|
|
3934
|
-
false
|
|
3935
|
-
);
|
|
3733
|
+
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
3936
3734
|
sweptCount++;
|
|
3937
3735
|
}
|
|
3938
3736
|
}
|
|
@@ -4028,10 +3826,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4028
3826
|
try {
|
|
4029
3827
|
return {
|
|
4030
3828
|
swap,
|
|
4031
|
-
context: await this.buildSubmarineVHTLCContext(
|
|
4032
|
-
swap,
|
|
4033
|
-
arkInfo
|
|
4034
|
-
)
|
|
3829
|
+
context: await this.buildSubmarineVHTLCContext(swap, arkInfo)
|
|
4035
3830
|
};
|
|
4036
3831
|
} catch (err) {
|
|
4037
3832
|
return {
|
|
@@ -4044,9 +3839,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4044
3839
|
const valid = prepared.filter(
|
|
4045
3840
|
(item) => "context" in item
|
|
4046
3841
|
);
|
|
4047
|
-
const scripts = [
|
|
4048
|
-
...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))
|
|
4049
|
-
];
|
|
3842
|
+
const scripts = [...new Set(valid.map(({ context }) => context.vhtlcPkScriptHex))];
|
|
4050
3843
|
const refundableByScript = /* @__PURE__ */ new Map();
|
|
4051
3844
|
if (scripts.length > 0) {
|
|
4052
3845
|
const [spendableResult, recoverableResult] = await Promise.all([
|
|
@@ -4081,9 +3874,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4081
3874
|
error: item.error
|
|
4082
3875
|
};
|
|
4083
3876
|
}
|
|
4084
|
-
const refundableVtxos = refundableByScript.get(
|
|
4085
|
-
item.context.vhtlcPkScriptHex.toLowerCase()
|
|
4086
|
-
) ?? [];
|
|
3877
|
+
const refundableVtxos = refundableByScript.get(item.context.vhtlcPkScriptHex.toLowerCase()) ?? [];
|
|
4087
3878
|
return this.submarineRecoveryInfoFromLookup(item.swap, {
|
|
4088
3879
|
...item.context,
|
|
4089
3880
|
refundableVtxos
|
|
@@ -4262,9 +4053,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4262
4053
|
*/
|
|
4263
4054
|
async waitAndClaimBtc(pendingSwap) {
|
|
4264
4055
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4265
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4266
|
-
pendingSwap.id
|
|
4267
|
-
);
|
|
4056
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4268
4057
|
return { txid };
|
|
4269
4058
|
}
|
|
4270
4059
|
return new Promise((resolve, reject) => {
|
|
@@ -4291,24 +4080,25 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4291
4080
|
}
|
|
4292
4081
|
case "transaction.claimed":
|
|
4293
4082
|
await updateSwapStatus();
|
|
4294
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4295
|
-
pendingSwap.id
|
|
4296
|
-
);
|
|
4083
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4297
4084
|
resolve({
|
|
4298
4085
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4299
4086
|
});
|
|
4300
4087
|
break;
|
|
4301
4088
|
case "transaction.lockupFailed":
|
|
4302
4089
|
await updateSwapStatus();
|
|
4303
|
-
await this.quoteSwap(swap.response.id).catch(
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4090
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4091
|
+
(err) => {
|
|
4092
|
+
reject(
|
|
4093
|
+
new SwapError({
|
|
4094
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4095
|
+
isRefundable: true,
|
|
4096
|
+
pendingSwap: swap,
|
|
4097
|
+
cause: err
|
|
4098
|
+
})
|
|
4099
|
+
);
|
|
4100
|
+
}
|
|
4101
|
+
);
|
|
4312
4102
|
break;
|
|
4313
4103
|
case "swap.expired":
|
|
4314
4104
|
await updateSwapStatus();
|
|
@@ -4346,30 +4136,18 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4346
4136
|
*/
|
|
4347
4137
|
async claimBtc(pendingSwap) {
|
|
4348
4138
|
if (!pendingSwap.toAddress)
|
|
4349
|
-
throw new Error(
|
|
4350
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4351
|
-
);
|
|
4139
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4352
4140
|
if (!pendingSwap.response.claimDetails.swapTree)
|
|
4353
|
-
throw new Error(
|
|
4354
|
-
`Swap ${pendingSwap.id}: missing swap tree in claim details`
|
|
4355
|
-
);
|
|
4141
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in claim details`);
|
|
4356
4142
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4357
|
-
throw new Error(
|
|
4358
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4359
|
-
);
|
|
4143
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4360
4144
|
const swapStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4361
4145
|
if (!swapStatus.transaction?.hex)
|
|
4362
|
-
throw new Error(
|
|
4363
|
-
|
|
4364
|
-
);
|
|
4365
|
-
const lockupTx = Transaction6.fromRaw(
|
|
4366
|
-
hex8.decode(swapStatus.transaction.hex)
|
|
4367
|
-
);
|
|
4146
|
+
throw new Error(`Swap ${pendingSwap.id}: BTC transaction hex is required`);
|
|
4147
|
+
const lockupTx = Transaction6.fromRaw(hex8.decode(swapStatus.transaction.hex));
|
|
4368
4148
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4369
4149
|
const network = arkInfo.network === "bitcoin" ? NETWORK : arkInfo.network === "mutinynet" ? MUTINYNET_NETWORK : REGTEST_NETWORK;
|
|
4370
|
-
const swapTree = deserializeSwapTree(
|
|
4371
|
-
pendingSwap.response.claimDetails.swapTree
|
|
4372
|
-
);
|
|
4150
|
+
const swapTree = deserializeSwapTree(pendingSwap.response.claimDetails.swapTree);
|
|
4373
4151
|
const musig = tweakMusig(
|
|
4374
4152
|
create(hex8.decode(pendingSwap.ephemeralKey), [
|
|
4375
4153
|
hex8.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
@@ -4390,19 +4168,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4390
4168
|
vout: swapOutput.vout,
|
|
4391
4169
|
transactionId: lockupTx.id
|
|
4392
4170
|
},
|
|
4393
|
-
OutScript2.encode(
|
|
4394
|
-
Address2(network).decode(pendingSwap.toAddress)
|
|
4395
|
-
),
|
|
4171
|
+
OutScript2.encode(Address2(network).decode(pendingSwap.toAddress)),
|
|
4396
4172
|
feeToDeliverExactAmount > fee ? feeToDeliverExactAmount : fee
|
|
4397
4173
|
)
|
|
4398
4174
|
);
|
|
4399
4175
|
const musigMessage = musig.message(
|
|
4400
|
-
claimTx.preimageWitnessV1(
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
SigHash2.DEFAULT,
|
|
4404
|
-
[swapOutput.amount]
|
|
4405
|
-
)
|
|
4176
|
+
claimTx.preimageWitnessV1(0, [swapOutput.script], SigHash2.DEFAULT, [
|
|
4177
|
+
swapOutput.amount
|
|
4178
|
+
])
|
|
4406
4179
|
).generateNonce();
|
|
4407
4180
|
const signedTxData = await this.swapProvider.postChainClaimDetails(
|
|
4408
4181
|
pendingSwap.response.id,
|
|
@@ -4416,14 +4189,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4416
4189
|
}
|
|
4417
4190
|
);
|
|
4418
4191
|
if (!signedTxData.pubNonce || !signedTxData.partialSignature)
|
|
4419
|
-
throw new Error(
|
|
4420
|
-
`Swap ${pendingSwap.id}: invalid signature data from server`
|
|
4421
|
-
);
|
|
4192
|
+
throw new Error(`Swap ${pendingSwap.id}: invalid signature data from server`);
|
|
4422
4193
|
const musigSession = musigMessage.aggregateNonces([
|
|
4423
4194
|
[
|
|
4424
|
-
hex8.decode(
|
|
4425
|
-
pendingSwap.response.claimDetails.serverPublicKey
|
|
4426
|
-
),
|
|
4195
|
+
hex8.decode(pendingSwap.response.claimDetails.serverPublicKey),
|
|
4427
4196
|
hex8.decode(signedTxData.pubNonce)
|
|
4428
4197
|
]
|
|
4429
4198
|
]).initializeSession();
|
|
@@ -4443,13 +4212,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4443
4212
|
*/
|
|
4444
4213
|
async refundArk(pendingSwap) {
|
|
4445
4214
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4446
|
-
throw new Error(
|
|
4447
|
-
`Swap ${pendingSwap.id}: missing server public key in lockup details`
|
|
4448
|
-
);
|
|
4215
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4449
4216
|
if (!pendingSwap.response.lockupDetails.timeouts)
|
|
4450
|
-
throw new Error(
|
|
4451
|
-
`Swap ${pendingSwap.id}: missing timeouts in lockup details`
|
|
4452
|
-
);
|
|
4217
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in lockup details`);
|
|
4453
4218
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4454
4219
|
const address = await this.wallet.getAddress();
|
|
4455
4220
|
const ourXOnlyPublicKey = normalizeToXOnlyKey(
|
|
@@ -4491,9 +4256,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4491
4256
|
timeoutBlockHeights: pendingSwap.response.lockupDetails.timeouts
|
|
4492
4257
|
});
|
|
4493
4258
|
if (!vhtlcScript.refundScript)
|
|
4494
|
-
throw new Error(
|
|
4495
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4496
|
-
);
|
|
4259
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4497
4260
|
if (pendingSwap.response.lockupDetails.lockupAddress !== vhtlcAddress) {
|
|
4498
4261
|
throw new SwapError({
|
|
4499
4262
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4574,9 +4337,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4574
4337
|
*/
|
|
4575
4338
|
async waitAndClaimArk(pendingSwap) {
|
|
4576
4339
|
if (this.swapManager && await this.swapManager.hasSwap(pendingSwap.id)) {
|
|
4577
|
-
const { txid } = await this.swapManager.waitForSwapCompletion(
|
|
4578
|
-
pendingSwap.id
|
|
4579
|
-
);
|
|
4340
|
+
const { txid } = await this.swapManager.waitForSwapCompletion(pendingSwap.id);
|
|
4580
4341
|
return { txid };
|
|
4581
4342
|
}
|
|
4582
4343
|
return new Promise((resolve, reject) => {
|
|
@@ -4597,37 +4358,33 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4597
4358
|
break;
|
|
4598
4359
|
case "transaction.claimed":
|
|
4599
4360
|
await updateSwapStatus();
|
|
4600
|
-
const claimedStatus = await this.getSwapStatus(
|
|
4601
|
-
pendingSwap.id
|
|
4602
|
-
);
|
|
4361
|
+
const claimedStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4603
4362
|
resolve({
|
|
4604
4363
|
txid: claimedStatus.transaction?.id ?? ""
|
|
4605
4364
|
});
|
|
4606
4365
|
break;
|
|
4607
4366
|
case "transaction.claim.pending":
|
|
4608
4367
|
await updateSwapStatus();
|
|
4609
|
-
await this.signCooperativeClaimForServer(swap).catch(
|
|
4368
|
+
await this.signCooperativeClaimForServer(swap).catch((err) => {
|
|
4369
|
+
logger.error(`Failed to sign cooperative claim for ${swap.id}:`, err);
|
|
4370
|
+
});
|
|
4371
|
+
break;
|
|
4372
|
+
case "transaction.lockupFailed":
|
|
4373
|
+
await updateSwapStatus();
|
|
4374
|
+
await this.quoteSwap(swap.response.id, quoteOptionsForSwap(swap)).catch(
|
|
4610
4375
|
(err) => {
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4376
|
+
reject(
|
|
4377
|
+
new SwapError({
|
|
4378
|
+
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4379
|
+
isRefundable: false,
|
|
4380
|
+
// TODO btc refund not implemented yet
|
|
4381
|
+
pendingSwap: swap,
|
|
4382
|
+
cause: err
|
|
4383
|
+
})
|
|
4614
4384
|
);
|
|
4615
4385
|
}
|
|
4616
4386
|
);
|
|
4617
4387
|
break;
|
|
4618
|
-
case "transaction.lockupFailed":
|
|
4619
|
-
await updateSwapStatus();
|
|
4620
|
-
await this.quoteSwap(swap.response.id).catch((err) => {
|
|
4621
|
-
reject(
|
|
4622
|
-
new SwapError({
|
|
4623
|
-
message: `Failed to renegotiate quote: ${err.message}`,
|
|
4624
|
-
isRefundable: false,
|
|
4625
|
-
// TODO btc refund not implemented yet
|
|
4626
|
-
pendingSwap: swap
|
|
4627
|
-
})
|
|
4628
|
-
);
|
|
4629
|
-
});
|
|
4630
|
-
break;
|
|
4631
4388
|
case "swap.expired":
|
|
4632
4389
|
await updateSwapStatus();
|
|
4633
4390
|
reject(
|
|
@@ -4667,17 +4424,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4667
4424
|
*/
|
|
4668
4425
|
async claimArk(pendingSwap) {
|
|
4669
4426
|
if (!pendingSwap.toAddress)
|
|
4670
|
-
throw new Error(
|
|
4671
|
-
`Swap ${pendingSwap.id}: destination address is required`
|
|
4672
|
-
);
|
|
4427
|
+
throw new Error(`Swap ${pendingSwap.id}: destination address is required`);
|
|
4673
4428
|
if (!pendingSwap.response.claimDetails.serverPublicKey)
|
|
4674
|
-
throw new Error(
|
|
4675
|
-
`Swap ${pendingSwap.id}: missing server public key in claim details`
|
|
4676
|
-
);
|
|
4429
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in claim details`);
|
|
4677
4430
|
if (!pendingSwap.response.claimDetails.timeouts)
|
|
4678
|
-
throw new Error(
|
|
4679
|
-
`Swap ${pendingSwap.id}: missing timeouts in claim details`
|
|
4680
|
-
);
|
|
4431
|
+
throw new Error(`Swap ${pendingSwap.id}: missing timeouts in claim details`);
|
|
4681
4432
|
const arkInfo = await this.arkProvider.getInfo();
|
|
4682
4433
|
const preimage = hex8.decode(pendingSwap.preimage);
|
|
4683
4434
|
const address = await this.wallet.getAddress();
|
|
@@ -4689,10 +4440,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4689
4440
|
pendingSwap.response.claimDetails.serverPublicKey,
|
|
4690
4441
|
"sender"
|
|
4691
4442
|
);
|
|
4692
|
-
const serverXOnlyPublicKey = normalizeToXOnlyKey(
|
|
4693
|
-
arkInfo.signerPubkey,
|
|
4694
|
-
"server"
|
|
4695
|
-
);
|
|
4443
|
+
const serverXOnlyPublicKey = normalizeToXOnlyKey(arkInfo.signerPubkey, "server");
|
|
4696
4444
|
const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
|
|
4697
4445
|
network: arkInfo.network,
|
|
4698
4446
|
preimageHash: hex8.decode(pendingSwap.request.preimageHash),
|
|
@@ -4702,9 +4450,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4702
4450
|
timeoutBlockHeights: pendingSwap.response.claimDetails.timeouts
|
|
4703
4451
|
});
|
|
4704
4452
|
if (!vhtlcScript.claimScript)
|
|
4705
|
-
throw new Error(
|
|
4706
|
-
`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`
|
|
4707
|
-
);
|
|
4453
|
+
throw new Error(`Swap ${pendingSwap.id}: failed to create VHTLC script for chain swap`);
|
|
4708
4454
|
if (pendingSwap.response.claimDetails.lockupAddress !== vhtlcAddress) {
|
|
4709
4455
|
throw new SwapError({
|
|
4710
4456
|
message: "Unable to claim: invalid VHTLC address"
|
|
@@ -4721,15 +4467,11 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4721
4467
|
break;
|
|
4722
4468
|
}
|
|
4723
4469
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
4724
|
-
await new Promise(
|
|
4725
|
-
(resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS)
|
|
4726
|
-
);
|
|
4470
|
+
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
4727
4471
|
}
|
|
4728
4472
|
}
|
|
4729
4473
|
if (!vtxo) {
|
|
4730
|
-
throw new Error(
|
|
4731
|
-
`Swap ${pendingSwap.id}: no spendable virtual coins found`
|
|
4732
|
-
);
|
|
4474
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
4733
4475
|
}
|
|
4734
4476
|
const input = {
|
|
4735
4477
|
...vtxo,
|
|
@@ -4740,10 +4482,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4740
4482
|
amount: BigInt(vtxo.value),
|
|
4741
4483
|
script: ArkAddress2.decode(address).pkScript
|
|
4742
4484
|
};
|
|
4743
|
-
const vhtlcIdentity = claimVHTLCIdentity(
|
|
4744
|
-
this.wallet.identity,
|
|
4745
|
-
preimage
|
|
4746
|
-
);
|
|
4485
|
+
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
4747
4486
|
if (isRecoverable(vtxo)) {
|
|
4748
4487
|
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
4749
4488
|
} else {
|
|
@@ -4769,16 +4508,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4769
4508
|
*/
|
|
4770
4509
|
async signCooperativeClaimForServer(pendingSwap) {
|
|
4771
4510
|
if (!pendingSwap.response.lockupDetails.swapTree)
|
|
4772
|
-
throw new Error(
|
|
4773
|
-
`Swap ${pendingSwap.id}: missing swap tree in lockup details`
|
|
4774
|
-
);
|
|
4511
|
+
throw new Error(`Swap ${pendingSwap.id}: missing swap tree in lockup details`);
|
|
4775
4512
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
4776
|
-
throw new Error(
|
|
4777
|
-
|
|
4778
|
-
);
|
|
4779
|
-
const claimDetails = await this.swapProvider.getChainClaimDetails(
|
|
4780
|
-
pendingSwap.id
|
|
4781
|
-
);
|
|
4513
|
+
throw new Error(`Swap ${pendingSwap.id}: missing server public key in lockup details`);
|
|
4514
|
+
const claimDetails = await this.swapProvider.getChainClaimDetails(pendingSwap.id);
|
|
4782
4515
|
const serverPubKey = pendingSwap.response.lockupDetails.serverPublicKey;
|
|
4783
4516
|
if (claimDetails.publicKey !== serverPubKey) {
|
|
4784
4517
|
throw new Error(
|
|
@@ -4794,9 +4527,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4794
4527
|
);
|
|
4795
4528
|
const musigNonces = musig.message(hex8.decode(claimDetails.transactionHash)).generateNonce().aggregateNonces([
|
|
4796
4529
|
[
|
|
4797
|
-
hex8.decode(
|
|
4798
|
-
pendingSwap.response.lockupDetails.serverPublicKey
|
|
4799
|
-
),
|
|
4530
|
+
hex8.decode(pendingSwap.response.lockupDetails.serverPublicKey),
|
|
4800
4531
|
hex8.decode(claimDetails.pubNonce)
|
|
4801
4532
|
]
|
|
4802
4533
|
]).initializeSession();
|
|
@@ -4815,10 +4546,8 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4815
4546
|
* @returns The transaction ID of the claim.
|
|
4816
4547
|
*/
|
|
4817
4548
|
async waitAndClaimChain(pendingSwap) {
|
|
4818
|
-
if (pendingSwap.request.to === "ARK")
|
|
4819
|
-
|
|
4820
|
-
if (pendingSwap.request.to === "BTC")
|
|
4821
|
-
return this.waitAndClaimBtc(pendingSwap);
|
|
4549
|
+
if (pendingSwap.request.to === "ARK") return this.waitAndClaimArk(pendingSwap);
|
|
4550
|
+
if (pendingSwap.request.to === "BTC") return this.waitAndClaimBtc(pendingSwap);
|
|
4822
4551
|
throw new SwapError({
|
|
4823
4552
|
message: `Unsupported swap destination: ${pendingSwap.request.to}`
|
|
4824
4553
|
});
|
|
@@ -4833,11 +4562,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4833
4562
|
*/
|
|
4834
4563
|
async createChainSwap(args) {
|
|
4835
4564
|
const { to, from, receiverLockAmount, senderLockAmount, toAddress } = args;
|
|
4836
|
-
if (!toAddress)
|
|
4837
|
-
throw new SwapError({ message: "Destination address is required" });
|
|
4565
|
+
if (!toAddress) throw new SwapError({ message: "Destination address is required" });
|
|
4838
4566
|
const feeSatsPerByte = args.feeSatsPerByte ?? 1;
|
|
4839
|
-
if (feeSatsPerByte <= 0)
|
|
4840
|
-
throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4567
|
+
if (feeSatsPerByte <= 0) throw new SwapError({ message: "Invalid feeSatsPerByte" });
|
|
4841
4568
|
let amount, serverLockAmount, userLockAmount;
|
|
4842
4569
|
if (receiverLockAmount) {
|
|
4843
4570
|
amount = receiverLockAmount;
|
|
@@ -4852,8 +4579,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4852
4579
|
}
|
|
4853
4580
|
const preimage = randomBytes(32);
|
|
4854
4581
|
const preimageHash = hex8.encode(sha2563(preimage));
|
|
4855
|
-
if (!preimageHash)
|
|
4856
|
-
throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4582
|
+
if (!preimageHash) throw new SwapError({ message: "Failed to get preimage hash" });
|
|
4857
4583
|
const ephemeralKey = secp256k12.utils.randomSecretKey();
|
|
4858
4584
|
const refundPublicKey = to === "ARK" ? hex8.encode(secp256k12.getPublicKey(ephemeralKey)) : hex8.encode(await this.wallet.identity.compressedPublicKey());
|
|
4859
4585
|
if (!refundPublicKey)
|
|
@@ -4902,30 +4628,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4902
4628
|
const { to, from, swap, arkInfo } = args;
|
|
4903
4629
|
if (from === "ARK") {
|
|
4904
4630
|
if (!swap.response.lockupDetails.serverPublicKey)
|
|
4905
|
-
throw new Error(
|
|
4906
|
-
`Swap ${swap.id}: missing serverPublicKey in lockup details`
|
|
4907
|
-
);
|
|
4631
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in lockup details`);
|
|
4908
4632
|
if (!swap.response.lockupDetails.timeouts)
|
|
4909
|
-
throw new Error(
|
|
4910
|
-
`Swap ${swap.id}: missing timeouts in lockup details`
|
|
4911
|
-
);
|
|
4633
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in lockup details`);
|
|
4912
4634
|
}
|
|
4913
4635
|
if (to === "ARK") {
|
|
4914
4636
|
if (!swap.response.claimDetails.serverPublicKey)
|
|
4915
|
-
throw new Error(
|
|
4916
|
-
`Swap ${swap.id}: missing serverPublicKey in claim details`
|
|
4917
|
-
);
|
|
4637
|
+
throw new Error(`Swap ${swap.id}: missing serverPublicKey in claim details`);
|
|
4918
4638
|
if (!swap.response.claimDetails.timeouts)
|
|
4919
|
-
throw new Error(
|
|
4920
|
-
`Swap ${swap.id}: missing timeouts in claim details`
|
|
4921
|
-
);
|
|
4639
|
+
throw new Error(`Swap ${swap.id}: missing timeouts in claim details`);
|
|
4922
4640
|
}
|
|
4923
4641
|
const lockupAddress = to === "ARK" ? swap.response.claimDetails.lockupAddress : swap.response.lockupDetails.lockupAddress;
|
|
4924
4642
|
const receiverPubkey = to === "ARK" ? swap.request.claimPublicKey : swap.response.lockupDetails.serverPublicKey;
|
|
4925
4643
|
const senderPubkey = to === "ARK" ? swap.response.claimDetails.serverPublicKey : swap.request.refundPublicKey;
|
|
4926
|
-
const serverPubkey = hex8.encode(
|
|
4927
|
-
normalizeToXOnlyKey(arkInfo.signerPubkey, "server")
|
|
4928
|
-
);
|
|
4644
|
+
const serverPubkey = hex8.encode(normalizeToXOnlyKey(arkInfo.signerPubkey, "server"));
|
|
4929
4645
|
const vhtlcTimeouts = to === "ARK" ? swap.response.claimDetails.timeouts : swap.response.lockupDetails.timeouts;
|
|
4930
4646
|
const { vhtlcAddress } = this.createVHTLCScript({
|
|
4931
4647
|
network: arkInfo.network,
|
|
@@ -4943,15 +4659,110 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4943
4659
|
return true;
|
|
4944
4660
|
}
|
|
4945
4661
|
/**
|
|
4946
|
-
* Renegotiates the quote for an existing swap.
|
|
4662
|
+
* Renegotiates the quote for an existing chain swap. Convenience wrapper
|
|
4663
|
+
* over `getSwapQuote` + `acceptSwapQuote` with a safety floor.
|
|
4664
|
+
*
|
|
4665
|
+
* The floor is resolved in order:
|
|
4666
|
+
* 1. `options.minAcceptableAmount` if provided.
|
|
4667
|
+
* 2. The original `response.claimDetails.amount` of the stored
|
|
4668
|
+
* pending swap (Boltz-confirmed server-lock amount at creation).
|
|
4669
|
+
* 3. Otherwise throws `QuoteRejectedError({ reason: "no_baseline" })`.
|
|
4670
|
+
*
|
|
4671
|
+
* `options.maxSlippageBps` (default 0) relaxes the floor by basis points.
|
|
4672
|
+
* Quotes ≤ 0 are always rejected. On rejection the acceptance is NOT
|
|
4673
|
+
* posted to Boltz.
|
|
4674
|
+
*
|
|
4675
|
+
* Prefer `getSwapQuote` / `acceptSwapQuote` for callers that want to
|
|
4676
|
+
* inspect the quote before committing.
|
|
4677
|
+
*
|
|
4947
4678
|
* @param swapId - The ID of the swap.
|
|
4679
|
+
* @param options - Optional floor and slippage configuration.
|
|
4948
4680
|
* @returns The accepted quote amount.
|
|
4681
|
+
* @throws QuoteRejectedError if the quote is non-positive, below the
|
|
4682
|
+
* effective floor, or no baseline is available.
|
|
4683
|
+
*/
|
|
4684
|
+
async quoteSwap(swapId, options) {
|
|
4685
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4686
|
+
const amount = await this.getSwapQuote(swapId);
|
|
4687
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4688
|
+
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4689
|
+
return amount;
|
|
4690
|
+
}
|
|
4691
|
+
/**
|
|
4692
|
+
* Fetches a renegotiated quote from Boltz without accepting it.
|
|
4693
|
+
* Pair with `acceptSwapQuote` to commit a specific value.
|
|
4949
4694
|
*/
|
|
4950
|
-
async
|
|
4695
|
+
async getSwapQuote(swapId) {
|
|
4951
4696
|
const { amount } = await this.swapProvider.getChainQuote(swapId);
|
|
4697
|
+
return amount;
|
|
4698
|
+
}
|
|
4699
|
+
/**
|
|
4700
|
+
* Accepts a quote amount for an existing chain swap, after validating it
|
|
4701
|
+
* against the configured floor. See `quoteSwap` for floor-resolution rules.
|
|
4702
|
+
*
|
|
4703
|
+
* @throws QuoteRejectedError if `amount` ≤ 0, below the effective floor,
|
|
4704
|
+
* or no baseline is available.
|
|
4705
|
+
*/
|
|
4706
|
+
async acceptSwapQuote(swapId, amount, options) {
|
|
4707
|
+
const effectiveFloor = await this.resolveEffectiveFloor(swapId, options);
|
|
4708
|
+
this.validateQuote(amount, effectiveFloor);
|
|
4952
4709
|
await this.swapProvider.postChainQuote(swapId, { amount });
|
|
4953
4710
|
return amount;
|
|
4954
4711
|
}
|
|
4712
|
+
async resolveEffectiveFloor(swapId, options) {
|
|
4713
|
+
this.validateQuoteOptions(options);
|
|
4714
|
+
const floor = await this.resolveQuoteFloor(swapId, options);
|
|
4715
|
+
const slippageBps = options?.maxSlippageBps ?? 0;
|
|
4716
|
+
return Math.floor(floor - floor * slippageBps / 1e4);
|
|
4717
|
+
}
|
|
4718
|
+
async resolveQuoteFloor(swapId, options) {
|
|
4719
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4720
|
+
return options.minAcceptableAmount;
|
|
4721
|
+
}
|
|
4722
|
+
const swaps = await this.swapRepository.getAllSwaps({
|
|
4723
|
+
id: swapId,
|
|
4724
|
+
type: "chain"
|
|
4725
|
+
});
|
|
4726
|
+
const stored = swaps[0];
|
|
4727
|
+
const amount = stored?.response?.claimDetails?.amount;
|
|
4728
|
+
if (typeof amount !== "number") {
|
|
4729
|
+
throw new QuoteRejectedError({ reason: "no_baseline" });
|
|
4730
|
+
}
|
|
4731
|
+
return amount;
|
|
4732
|
+
}
|
|
4733
|
+
validateQuoteOptions(options) {
|
|
4734
|
+
if (options?.minAcceptableAmount !== void 0) {
|
|
4735
|
+
const v = options.minAcceptableAmount;
|
|
4736
|
+
if (!Number.isInteger(v) || v <= 0) {
|
|
4737
|
+
throw new TypeError(
|
|
4738
|
+
`Invalid minAcceptableAmount: ${v} \u2014 must be a positive integer`
|
|
4739
|
+
);
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
if (options?.maxSlippageBps !== void 0) {
|
|
4743
|
+
const v = options.maxSlippageBps;
|
|
4744
|
+
if (!Number.isInteger(v) || v < 0 || v > 1e4) {
|
|
4745
|
+
throw new TypeError(
|
|
4746
|
+
`Invalid maxSlippageBps: ${v} \u2014 must be an integer in [0, 10000]`
|
|
4747
|
+
);
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
validateQuote(amount, effectiveFloor) {
|
|
4752
|
+
if (!(amount > 0)) {
|
|
4753
|
+
throw new QuoteRejectedError({
|
|
4754
|
+
reason: "non_positive",
|
|
4755
|
+
quotedAmount: amount
|
|
4756
|
+
});
|
|
4757
|
+
}
|
|
4758
|
+
if (amount < effectiveFloor) {
|
|
4759
|
+
throw new QuoteRejectedError({
|
|
4760
|
+
reason: "below_floor",
|
|
4761
|
+
quotedAmount: amount,
|
|
4762
|
+
floor: effectiveFloor
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4955
4766
|
// =========================================================================
|
|
4956
4767
|
// Shared utilities
|
|
4957
4768
|
// =========================================================================
|
|
@@ -4965,14 +4776,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4965
4776
|
* @returns The commitment transaction ID.
|
|
4966
4777
|
*/
|
|
4967
4778
|
async joinBatch(identity, input, output, arkInfo, isRecoverable2 = true) {
|
|
4968
|
-
return joinBatch(
|
|
4969
|
-
this.arkProvider,
|
|
4970
|
-
identity,
|
|
4971
|
-
input,
|
|
4972
|
-
output,
|
|
4973
|
-
arkInfo,
|
|
4974
|
-
isRecoverable2
|
|
4975
|
-
);
|
|
4779
|
+
return joinBatch(this.arkProvider, identity, input, output, arkInfo, isRecoverable2);
|
|
4976
4780
|
}
|
|
4977
4781
|
/**
|
|
4978
4782
|
* Creates a VHTLC script for the swap.
|
|
@@ -5010,9 +4814,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5010
4814
|
async getPendingSubmarineSwaps() {
|
|
5011
4815
|
const swaps = await this.getPendingSubmarineSwapsFromStorage();
|
|
5012
4816
|
if (!swaps) return [];
|
|
5013
|
-
return swaps.filter(
|
|
5014
|
-
(swap) => swap.status === "invoice.set"
|
|
5015
|
-
);
|
|
4817
|
+
return swaps.filter((swap) => swap.status === "invoice.set");
|
|
5016
4818
|
}
|
|
5017
4819
|
/**
|
|
5018
4820
|
* Returns pending reverse swaps (those with status `swap.created`).
|
|
@@ -5020,9 +4822,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5020
4822
|
async getPendingReverseSwaps() {
|
|
5021
4823
|
const swaps = await this.getPendingReverseSwapsFromStorage();
|
|
5022
4824
|
if (!swaps) return [];
|
|
5023
|
-
return swaps.filter(
|
|
5024
|
-
(swap) => swap.status === "swap.created"
|
|
5025
|
-
);
|
|
4825
|
+
return swaps.filter((swap) => swap.status === "swap.created");
|
|
5026
4826
|
}
|
|
5027
4827
|
/**
|
|
5028
4828
|
* Returns pending chain swaps (those with status `swap.created`).
|
|
@@ -5061,10 +4861,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5061
4861
|
this.savePendingReverseSwap.bind(this)
|
|
5062
4862
|
)
|
|
5063
4863
|
).catch((error) => {
|
|
5064
|
-
logger.error(
|
|
5065
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
5066
|
-
error
|
|
5067
|
-
);
|
|
4864
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5068
4865
|
})
|
|
5069
4866
|
);
|
|
5070
4867
|
}
|
|
@@ -5078,23 +4875,15 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5078
4875
|
this.savePendingSubmarineSwap.bind(this)
|
|
5079
4876
|
)
|
|
5080
4877
|
).catch((error) => {
|
|
5081
|
-
logger.error(
|
|
5082
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
5083
|
-
error
|
|
5084
|
-
);
|
|
4878
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5085
4879
|
})
|
|
5086
4880
|
);
|
|
5087
4881
|
}
|
|
5088
4882
|
for (const swap of await this.getPendingChainSwapsFromStorage()) {
|
|
5089
4883
|
if (isChainFinalStatus(swap.status)) continue;
|
|
5090
4884
|
promises.push(
|
|
5091
|
-
this.getSwapStatus(swap.id).then(
|
|
5092
|
-
(
|
|
5093
|
-
).catch((error) => {
|
|
5094
|
-
logger.error(
|
|
5095
|
-
`Failed to refresh swap status for ${swap.id}:`,
|
|
5096
|
-
error
|
|
5097
|
-
);
|
|
4885
|
+
this.getSwapStatus(swap.id).then(({ status }) => this.savePendingChainSwap({ ...swap, status })).catch((error) => {
|
|
4886
|
+
logger.error(`Failed to refresh swap status for ${swap.id}:`, error);
|
|
5098
4887
|
})
|
|
5099
4888
|
);
|
|
5100
4889
|
}
|
|
@@ -5111,9 +4900,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5111
4900
|
* display/monitoring and are not automatically wired into the SwapManager.
|
|
5112
4901
|
*/
|
|
5113
4902
|
async restoreSwaps(boltzFees) {
|
|
5114
|
-
const publicKey = hex8.encode(
|
|
5115
|
-
await this.wallet.identity.compressedPublicKey()
|
|
5116
|
-
);
|
|
4903
|
+
const publicKey = hex8.encode(await this.wallet.identity.compressedPublicKey());
|
|
5117
4904
|
if (!publicKey) throw new Error("Failed to get public key from wallet");
|
|
5118
4905
|
const fees = boltzFees ?? await this.swapProvider.getFees();
|
|
5119
4906
|
const chainSwaps = [];
|
|
@@ -5165,25 +4952,14 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5165
4952
|
preimage: ""
|
|
5166
4953
|
});
|
|
5167
4954
|
} else if (isRestoredSubmarineSwap(swap)) {
|
|
5168
|
-
const {
|
|
5169
|
-
amount,
|
|
5170
|
-
lockupAddress,
|
|
5171
|
-
serverPublicKey,
|
|
5172
|
-
tree,
|
|
5173
|
-
timeoutBlockHeights
|
|
5174
|
-
} = swap.refundDetails;
|
|
4955
|
+
const { amount, lockupAddress, serverPublicKey, tree, timeoutBlockHeights } = swap.refundDetails;
|
|
5175
4956
|
let preimage = "";
|
|
5176
4957
|
if (!isSubmarineFinalStatus(status)) {
|
|
5177
4958
|
try {
|
|
5178
|
-
const data = await this.swapProvider.getSwapPreimage(
|
|
5179
|
-
swap.id
|
|
5180
|
-
);
|
|
4959
|
+
const data = await this.swapProvider.getSwapPreimage(swap.id);
|
|
5181
4960
|
preimage = data.preimage;
|
|
5182
4961
|
} catch (error) {
|
|
5183
|
-
logger.warn(
|
|
5184
|
-
`Failed to restore preimage for submarine swap ${id}`,
|
|
5185
|
-
error
|
|
5186
|
-
);
|
|
4962
|
+
logger.warn(`Failed to restore preimage for submarine swap ${id}`, error);
|
|
5187
4963
|
}
|
|
5188
4964
|
}
|
|
5189
4965
|
submarineSwaps.push({
|
|
@@ -5221,12 +4997,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5221
4997
|
} else if (isRestoredChainSwap(swap)) {
|
|
5222
4998
|
const refundDetails = swap.refundDetails;
|
|
5223
4999
|
if (!refundDetails) continue;
|
|
5224
|
-
const {
|
|
5225
|
-
amount,
|
|
5226
|
-
lockupAddress,
|
|
5227
|
-
serverPublicKey,
|
|
5228
|
-
timeoutBlockHeight
|
|
5229
|
-
} = refundDetails;
|
|
5000
|
+
const { amount, lockupAddress, serverPublicKey, timeoutBlockHeight } = refundDetails;
|
|
5230
5001
|
chainSwaps.push({
|
|
5231
5002
|
id,
|
|
5232
5003
|
type: "chain",
|
|
@@ -5285,6 +5056,7 @@ export {
|
|
|
5285
5056
|
SwapExpiredError,
|
|
5286
5057
|
TransactionFailedError,
|
|
5287
5058
|
PreimageFetchError,
|
|
5059
|
+
QuoteRejectedError,
|
|
5288
5060
|
BoltzRefundError,
|
|
5289
5061
|
isSubmarineFailedStatus,
|
|
5290
5062
|
isSubmarineFinalStatus,
|