@arkade-os/boltz-swap 0.3.50 → 0.3.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{arkade-swaps-A180YN8g.d.ts → arkade-swaps-CoJKHu2Y.d.ts} +19 -4
- package/dist/{arkade-swaps-BG3xEK31.d.cts → arkade-swaps-qZmYvnVy.d.cts} +19 -4
- package/dist/{chunk-HIG2OOAN.js → chunk-76V3TKDL.js} +1 -1
- package/dist/{chunk-VKHFXGKL.js → chunk-CZTZTYZU.js} +224 -47
- package/dist/expo/background.cjs +222 -47
- package/dist/expo/background.d.cts +3 -3
- package/dist/expo/background.d.ts +3 -3
- package/dist/expo/background.js +2 -2
- package/dist/expo/index.cjs +224 -47
- package/dist/expo/index.d.cts +4 -4
- package/dist/expo/index.d.ts +4 -4
- package/dist/expo/index.js +4 -2
- package/dist/index.cjs +228 -48
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -2
- package/dist/repositories/realm/index.cjs +1 -0
- package/dist/repositories/realm/index.d.cts +1 -1
- package/dist/repositories/realm/index.d.ts +1 -1
- package/dist/repositories/realm/index.js +1 -0
- package/dist/repositories/sqlite/index.cjs +1 -0
- package/dist/repositories/sqlite/index.d.cts +1 -1
- package/dist/repositories/sqlite/index.d.ts +1 -1
- package/dist/repositories/sqlite/index.js +1 -0
- package/dist/{swapsPollProcessor-C4HQHP9Z.d.ts → swapsPollProcessor--BPV48QB.d.ts} +1 -1
- package/dist/{swapsPollProcessor-CIqTFkV1.d.cts → swapsPollProcessor-DCtJYSbJ.d.cts} +1 -1
- package/dist/{types-BKEkNZxK.d.cts → types-Db4JSdjk.d.cts} +69 -8
- package/dist/{types-BKEkNZxK.d.ts → types-Db4JSdjk.d.ts} +69 -8
- package/package.json +3 -3
package/dist/expo/index.cjs
CHANGED
|
@@ -225,6 +225,7 @@ var BoltzRefundError = class extends Error {
|
|
|
225
225
|
this.cause = cause;
|
|
226
226
|
this.name = "BoltzRefundError";
|
|
227
227
|
}
|
|
228
|
+
cause;
|
|
228
229
|
};
|
|
229
230
|
|
|
230
231
|
// src/arkade-swaps.ts
|
|
@@ -995,6 +996,13 @@ var MusigKeyAgg = class _MusigKeyAgg {
|
|
|
995
996
|
this.internalKey = internalKey;
|
|
996
997
|
this._tweak = _tweak;
|
|
997
998
|
}
|
|
999
|
+
privateKey;
|
|
1000
|
+
myPublicKey;
|
|
1001
|
+
publicKeys;
|
|
1002
|
+
myIndex;
|
|
1003
|
+
aggPubkey;
|
|
1004
|
+
internalKey;
|
|
1005
|
+
_tweak;
|
|
998
1006
|
xonlyTweakAdd(tweak) {
|
|
999
1007
|
if (this._tweak) throw new Error("musig key already tweaked");
|
|
1000
1008
|
return new _MusigKeyAgg(
|
|
@@ -1029,6 +1037,13 @@ var MusigWithMessage = class {
|
|
|
1029
1037
|
this.tweak = tweak;
|
|
1030
1038
|
this.msg = msg;
|
|
1031
1039
|
}
|
|
1040
|
+
privateKey;
|
|
1041
|
+
myPublicKey;
|
|
1042
|
+
publicKeys;
|
|
1043
|
+
myIndex;
|
|
1044
|
+
aggPubkey;
|
|
1045
|
+
tweak;
|
|
1046
|
+
msg;
|
|
1032
1047
|
generateNonce() {
|
|
1033
1048
|
const nonce = (0, import_musig2.nonceGen)(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
|
|
1034
1049
|
return new MusigWithNonce(
|
|
@@ -1052,6 +1067,13 @@ var MusigWithNonce = class {
|
|
|
1052
1067
|
this.msg = msg;
|
|
1053
1068
|
this.nonce = nonce;
|
|
1054
1069
|
}
|
|
1070
|
+
privateKey;
|
|
1071
|
+
myPublicKey;
|
|
1072
|
+
publicKeys;
|
|
1073
|
+
myIndex;
|
|
1074
|
+
tweak;
|
|
1075
|
+
msg;
|
|
1076
|
+
nonce;
|
|
1055
1077
|
get publicNonce() {
|
|
1056
1078
|
return this.nonce.public;
|
|
1057
1079
|
}
|
|
@@ -1100,6 +1122,14 @@ var MusigNoncesAggregated = class {
|
|
|
1100
1122
|
this.pubNonces = pubNonces;
|
|
1101
1123
|
this.aggregatedNonce = aggregatedNonce;
|
|
1102
1124
|
}
|
|
1125
|
+
privateKey;
|
|
1126
|
+
publicKeys;
|
|
1127
|
+
myIndex;
|
|
1128
|
+
tweak;
|
|
1129
|
+
msg;
|
|
1130
|
+
nonce;
|
|
1131
|
+
pubNonces;
|
|
1132
|
+
aggregatedNonce;
|
|
1103
1133
|
get publicNonce() {
|
|
1104
1134
|
return this.nonce.public;
|
|
1105
1135
|
}
|
|
@@ -1131,6 +1161,12 @@ var MusigSession = class {
|
|
|
1131
1161
|
this.session = session;
|
|
1132
1162
|
this.partialSignatures = Array(publicKeys.length).fill(null);
|
|
1133
1163
|
}
|
|
1164
|
+
privateKey;
|
|
1165
|
+
publicKeys;
|
|
1166
|
+
myIndex;
|
|
1167
|
+
nonce;
|
|
1168
|
+
pubNonces;
|
|
1169
|
+
session;
|
|
1134
1170
|
partialSignatures;
|
|
1135
1171
|
get publicNonce() {
|
|
1136
1172
|
return this.nonce.public;
|
|
@@ -1158,6 +1194,10 @@ var MusigSigned = class {
|
|
|
1158
1194
|
this.ourPartialSignature = ourPartialSignature;
|
|
1159
1195
|
this.publicNonce = publicNonce;
|
|
1160
1196
|
}
|
|
1197
|
+
session;
|
|
1198
|
+
partialSignatures;
|
|
1199
|
+
ourPartialSignature;
|
|
1200
|
+
publicNonce;
|
|
1161
1201
|
aggregatePartials() {
|
|
1162
1202
|
if (this.partialSignatures.some((s) => s === null)) {
|
|
1163
1203
|
throw new Error("not all partial signatures are set");
|
|
@@ -1425,12 +1465,20 @@ var SwapManager = class _SwapManager {
|
|
|
1425
1465
|
*/
|
|
1426
1466
|
static NOT_FOUND_THRESHOLD = 10;
|
|
1427
1467
|
/**
|
|
1428
|
-
*
|
|
1429
|
-
*
|
|
1430
|
-
*
|
|
1431
|
-
*
|
|
1468
|
+
* Floor on the delay between re-attempts of a refund that left VTXOs
|
|
1469
|
+
* deferred, and the delay used when the outcome names no retry time. Boltz
|
|
1470
|
+
* won't send another status update once the swap is refundable-and-final,
|
|
1471
|
+
* so the manager owns the local retry cadence.
|
|
1432
1472
|
*/
|
|
1433
1473
|
static REFUND_RETRY_DELAY_MS = 6e4;
|
|
1474
|
+
/**
|
|
1475
|
+
* Ceiling on that delay. A deferral can be hours out (a pre-CLTV VTXO waits
|
|
1476
|
+
* out the whole refund locktime), and `setTimeout` fires *immediately* past
|
|
1477
|
+
* its 32-bit millisecond range, so long waits are broken into re-armed
|
|
1478
|
+
* hops. Re-attempting costs an indexer lookup and re-defers, which also lets
|
|
1479
|
+
* the cadence recover from a suspended device or a clock jump.
|
|
1480
|
+
*/
|
|
1481
|
+
static MAX_REFUND_RETRY_DELAY_MS = 60 * 6e4;
|
|
1434
1482
|
swapProvider;
|
|
1435
1483
|
config;
|
|
1436
1484
|
// Event listeners storage (supports multiple listeners per event)
|
|
@@ -1449,10 +1497,9 @@ var SwapManager = class _SwapManager {
|
|
|
1449
1497
|
reconnectTimer = null;
|
|
1450
1498
|
initialPollTimer = null;
|
|
1451
1499
|
pollRetryTimers = /* @__PURE__ */ new Map();
|
|
1452
|
-
// Per-swap retry timers for
|
|
1453
|
-
//
|
|
1454
|
-
//
|
|
1455
|
-
// refund completes or the manager stops.
|
|
1500
|
+
// Per-swap retry timers for deferred refunds that left local work undone.
|
|
1501
|
+
// The swap is held in `monitoredSwaps` past its terminal Boltz status
|
|
1502
|
+
// until the local refund completes or the manager stops.
|
|
1456
1503
|
refundRetryTimers = /* @__PURE__ */ new Map();
|
|
1457
1504
|
// Per-swap counter of consecutive `SwapNotFoundError` responses from
|
|
1458
1505
|
// `getSwapStatus`. Reset on any successful poll. Once a swap reaches
|
|
@@ -1624,11 +1671,12 @@ var SwapManager = class _SwapManager {
|
|
|
1624
1671
|
this.initialSwaps.set(swap.id, swap);
|
|
1625
1672
|
}
|
|
1626
1673
|
for (const swap of pendingSwaps) {
|
|
1627
|
-
if (
|
|
1674
|
+
if (this.shouldMonitorOnStart(swap)) {
|
|
1628
1675
|
this.monitoredSwaps.set(swap.id, swap);
|
|
1629
1676
|
}
|
|
1630
1677
|
}
|
|
1631
1678
|
await this.tryConnectWebSocket();
|
|
1679
|
+
this.resumePersistedRefundRetries();
|
|
1632
1680
|
await this.resumeActionableSwaps();
|
|
1633
1681
|
}
|
|
1634
1682
|
/**
|
|
@@ -1711,6 +1759,7 @@ var SwapManager = class _SwapManager {
|
|
|
1711
1759
|
* Remove a swap from monitoring
|
|
1712
1760
|
*/
|
|
1713
1761
|
async removeSwap(swapId) {
|
|
1762
|
+
const swap = this.monitoredSwaps.get(swapId);
|
|
1714
1763
|
this.monitoredSwaps.delete(swapId);
|
|
1715
1764
|
this.swapSubscriptions.delete(swapId);
|
|
1716
1765
|
this.chainClaimPromises.delete(swapId);
|
|
@@ -1724,6 +1773,9 @@ var SwapManager = class _SwapManager {
|
|
|
1724
1773
|
clearTimeout(refundRetryTimer);
|
|
1725
1774
|
this.refundRetryTimers.delete(swapId);
|
|
1726
1775
|
}
|
|
1776
|
+
if (swap) {
|
|
1777
|
+
await this.clearRefundRetry(swap);
|
|
1778
|
+
}
|
|
1727
1779
|
this.notFoundCounts.delete(swapId);
|
|
1728
1780
|
logger.log(`Removed swap ${swapId} from monitoring`);
|
|
1729
1781
|
}
|
|
@@ -2062,12 +2114,69 @@ var SwapManager = class _SwapManager {
|
|
|
2062
2114
|
}
|
|
2063
2115
|
this.swapCompletedListeners.forEach((listener) => listener(swap));
|
|
2064
2116
|
}
|
|
2117
|
+
static isRefundRetryableSwap(swap) {
|
|
2118
|
+
return isPendingSubmarineSwap(swap) || isPendingChainSwap(swap);
|
|
2119
|
+
}
|
|
2120
|
+
static hasPendingRefundRetry(swap) {
|
|
2121
|
+
if (!_SwapManager.isRefundRetryableSwap(swap)) return false;
|
|
2122
|
+
return swap.refundRetry?.pending === true && Number.isFinite(swap.refundRetry.nextRetryAt);
|
|
2123
|
+
}
|
|
2124
|
+
shouldMonitorOnStart(swap) {
|
|
2125
|
+
if (!this.isFinalStatus(swap)) return true;
|
|
2126
|
+
return this.config.enableAutoActions === true && _SwapManager.hasPendingRefundRetry(swap);
|
|
2127
|
+
}
|
|
2065
2128
|
/**
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
2068
|
-
*
|
|
2129
|
+
* How long to wait before re-attempting a refund, given the `retryAt`
|
|
2130
|
+
* (Unix seconds) the outcome reported. Clamped at both ends: see
|
|
2131
|
+
* {@link SwapManager.REFUND_RETRY_DELAY_MS} and
|
|
2132
|
+
* {@link SwapManager.MAX_REFUND_RETRY_DELAY_MS}.
|
|
2069
2133
|
*/
|
|
2070
|
-
|
|
2134
|
+
static refundRetryDelayMs(retryAt) {
|
|
2135
|
+
if (retryAt === void 0) return _SwapManager.REFUND_RETRY_DELAY_MS;
|
|
2136
|
+
return Math.min(
|
|
2137
|
+
Math.max(retryAt * 1e3 - Date.now(), _SwapManager.REFUND_RETRY_DELAY_MS),
|
|
2138
|
+
_SwapManager.MAX_REFUND_RETRY_DELAY_MS
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
2141
|
+
static persistedRefundRetryDelayMs(nextRetryAt) {
|
|
2142
|
+
return Math.min(
|
|
2143
|
+
Math.max(nextRetryAt * 1e3 - Date.now(), 0),
|
|
2144
|
+
_SwapManager.MAX_REFUND_RETRY_DELAY_MS
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
static nextRefundRetryAt(delayMs) {
|
|
2148
|
+
return Math.ceil((Date.now() + delayMs) / 1e3);
|
|
2149
|
+
}
|
|
2150
|
+
async clearRefundRetry(swap) {
|
|
2151
|
+
if (!_SwapManager.isRefundRetryableSwap(swap) || swap.refundRetry === void 0) return;
|
|
2152
|
+
delete swap.refundRetry;
|
|
2153
|
+
await this.saveSwap(swap);
|
|
2154
|
+
}
|
|
2155
|
+
resumePersistedRefundRetries() {
|
|
2156
|
+
if (!this.config.enableAutoActions) return;
|
|
2157
|
+
for (const swap of this.monitoredSwaps.values()) {
|
|
2158
|
+
if (!_SwapManager.hasPendingRefundRetry(swap)) continue;
|
|
2159
|
+
const delayMs = _SwapManager.persistedRefundRetryDelayMs(swap.refundRetry.nextRetryAt);
|
|
2160
|
+
logger.log(
|
|
2161
|
+
`Swap ${swap.id}: resuming deferred refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2162
|
+
);
|
|
2163
|
+
this.armRefundRetryTimer(swap, delayMs);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Schedule another `executeAutonomousAction` run for a swap whose refund
|
|
2168
|
+
* left VTXOs deferred. The pending retry and its next attempt time are saved
|
|
2169
|
+
* with the swap so stop/start and application restart can re-arm it.
|
|
2170
|
+
*/
|
|
2171
|
+
async scheduleRefundRetry(swap, delayMs) {
|
|
2172
|
+
swap.refundRetry = {
|
|
2173
|
+
pending: true,
|
|
2174
|
+
nextRetryAt: _SwapManager.nextRefundRetryAt(delayMs)
|
|
2175
|
+
};
|
|
2176
|
+
await this.saveSwap(swap);
|
|
2177
|
+
this.armRefundRetryTimer(swap, delayMs);
|
|
2178
|
+
}
|
|
2179
|
+
armRefundRetryTimer(swap, delayMs) {
|
|
2071
2180
|
const existing = this.refundRetryTimers.get(swap.id);
|
|
2072
2181
|
if (existing) clearTimeout(existing);
|
|
2073
2182
|
this.refundRetryTimers.set(
|
|
@@ -2075,12 +2184,14 @@ var SwapManager = class _SwapManager {
|
|
|
2075
2184
|
setTimeout(async () => {
|
|
2076
2185
|
this.refundRetryTimers.delete(swap.id);
|
|
2077
2186
|
if (!this.isRunning) return;
|
|
2078
|
-
|
|
2187
|
+
const current = this.monitoredSwaps.get(swap.id);
|
|
2188
|
+
if (!current) return;
|
|
2189
|
+
let ran = false;
|
|
2079
2190
|
try {
|
|
2080
|
-
await this.executeAutonomousAction(
|
|
2191
|
+
ran = await this.executeAutonomousAction(current);
|
|
2081
2192
|
} finally {
|
|
2082
|
-
if (!this.refundRetryTimers.has(
|
|
2083
|
-
this.finalizeMonitoredSwap(
|
|
2193
|
+
if (ran && !this.refundRetryTimers.has(current.id) && this.isFinalStatus(current)) {
|
|
2194
|
+
this.finalizeMonitoredSwap(current);
|
|
2084
2195
|
}
|
|
2085
2196
|
}
|
|
2086
2197
|
}, delayMs)
|
|
@@ -2089,11 +2200,16 @@ var SwapManager = class _SwapManager {
|
|
|
2089
2200
|
/**
|
|
2090
2201
|
* Execute autonomous action based on swap status
|
|
2091
2202
|
* Uses locking to prevent race conditions with manual operations
|
|
2203
|
+
*
|
|
2204
|
+
* @returns `false` when another action for this swap already held the lock
|
|
2205
|
+
* and this call did nothing, `true` when it ran. Callers that finalize on
|
|
2206
|
+
* the result must not act on `false`: the in-flight action owns the swap's
|
|
2207
|
+
* outcome and will re-arm or finalize from its own result.
|
|
2092
2208
|
*/
|
|
2093
2209
|
async executeAutonomousAction(swap) {
|
|
2094
2210
|
if (this.swapsInProgress.has(swap.id)) {
|
|
2095
2211
|
logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
|
|
2096
|
-
return;
|
|
2212
|
+
return false;
|
|
2097
2213
|
}
|
|
2098
2214
|
try {
|
|
2099
2215
|
this.swapsInProgress.add(swap.id);
|
|
@@ -2102,7 +2218,7 @@ var SwapManager = class _SwapManager {
|
|
|
2102
2218
|
logger.log(
|
|
2103
2219
|
`Skipping claim for swap ${swap.id}: missing preimage (restored swap)`
|
|
2104
2220
|
);
|
|
2105
|
-
return;
|
|
2221
|
+
return true;
|
|
2106
2222
|
}
|
|
2107
2223
|
if (isReverseClaimableStatus(swap.status)) {
|
|
2108
2224
|
logger.log(`Auto-claiming reverse swap ${swap.id}`);
|
|
@@ -2114,11 +2230,26 @@ var SwapManager = class _SwapManager {
|
|
|
2114
2230
|
logger.log(
|
|
2115
2231
|
`Skipping refund for swap ${swap.id}: missing invoice (restored swap)`
|
|
2116
2232
|
);
|
|
2117
|
-
return;
|
|
2233
|
+
return true;
|
|
2118
2234
|
}
|
|
2119
2235
|
if (isSubmarineRefundableStatus(swap.status)) {
|
|
2120
2236
|
logger.log(`Auto-refunding submarine swap ${swap.id}`);
|
|
2121
|
-
|
|
2237
|
+
let outcome;
|
|
2238
|
+
try {
|
|
2239
|
+
outcome = await this.executeRefundAction(swap);
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
await this.clearRefundRetry(swap);
|
|
2242
|
+
throw error;
|
|
2243
|
+
}
|
|
2244
|
+
if (outcome && outcome.skipped > 0) {
|
|
2245
|
+
const delayMs = _SwapManager.refundRetryDelayMs(outcome.retryAt);
|
|
2246
|
+
logger.log(
|
|
2247
|
+
`Submarine swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2248
|
+
);
|
|
2249
|
+
await this.scheduleRefundRetry(swap, delayMs);
|
|
2250
|
+
} else if (outcome) {
|
|
2251
|
+
await this.clearRefundRetry(swap);
|
|
2252
|
+
}
|
|
2122
2253
|
this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
|
|
2123
2254
|
}
|
|
2124
2255
|
} else if (isPendingChainSwap(swap)) {
|
|
@@ -2142,10 +2273,13 @@ var SwapManager = class _SwapManager {
|
|
|
2142
2273
|
try {
|
|
2143
2274
|
const outcome = await this.executeRefundArkAction(swap);
|
|
2144
2275
|
if (outcome && outcome.skipped > 0) {
|
|
2276
|
+
const delayMs = _SwapManager.refundRetryDelayMs(outcome.retryAt);
|
|
2145
2277
|
logger.log(
|
|
2146
|
-
`Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry`
|
|
2278
|
+
`Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2147
2279
|
);
|
|
2148
|
-
this.scheduleRefundRetry(swap,
|
|
2280
|
+
await this.scheduleRefundRetry(swap, delayMs);
|
|
2281
|
+
} else if (outcome) {
|
|
2282
|
+
await this.clearRefundRetry(swap);
|
|
2149
2283
|
}
|
|
2150
2284
|
this.actionExecutedListeners.forEach(
|
|
2151
2285
|
(listener) => listener(swap, "refundArk")
|
|
@@ -2158,7 +2292,7 @@ var SwapManager = class _SwapManager {
|
|
|
2158
2292
|
this.swapFailedListeners.forEach(
|
|
2159
2293
|
(listener) => listener(swap, error)
|
|
2160
2294
|
);
|
|
2161
|
-
this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2295
|
+
await this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2162
2296
|
}
|
|
2163
2297
|
}
|
|
2164
2298
|
if (swap.request.from === "BTC") {
|
|
@@ -2191,6 +2325,7 @@ var SwapManager = class _SwapManager {
|
|
|
2191
2325
|
} finally {
|
|
2192
2326
|
this.swapsInProgress.delete(swap.id);
|
|
2193
2327
|
}
|
|
2328
|
+
return true;
|
|
2194
2329
|
}
|
|
2195
2330
|
/**
|
|
2196
2331
|
* Execute claim action for reverse swap
|
|
@@ -2204,13 +2339,15 @@ var SwapManager = class _SwapManager {
|
|
|
2204
2339
|
}
|
|
2205
2340
|
/**
|
|
2206
2341
|
* Execute refund action for submarine swap
|
|
2342
|
+
*
|
|
2343
|
+
* @returns The refund outcome, or `undefined` if no callback is wired.
|
|
2207
2344
|
*/
|
|
2208
2345
|
async executeRefundAction(swap) {
|
|
2209
2346
|
if (!this.refundCallback) {
|
|
2210
2347
|
logger.error("Refund callback not set");
|
|
2211
2348
|
return;
|
|
2212
2349
|
}
|
|
2213
|
-
|
|
2350
|
+
return this.refundCallback(swap);
|
|
2214
2351
|
}
|
|
2215
2352
|
/**
|
|
2216
2353
|
* Execute claim action for chain swap Btc to Ark
|
|
@@ -2292,6 +2429,7 @@ var SwapManager = class _SwapManager {
|
|
|
2292
2429
|
return;
|
|
2293
2430
|
}
|
|
2294
2431
|
for (const swap of this.monitoredSwaps.values()) {
|
|
2432
|
+
if (this.refundRetryTimers.has(swap.id)) continue;
|
|
2295
2433
|
try {
|
|
2296
2434
|
if (isPendingReverseSwap(swap) && isReverseClaimableStatus(swap.status)) {
|
|
2297
2435
|
logger.log(`Resuming claim for swap ${swap.id}`);
|
|
@@ -2616,6 +2754,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2616
2754
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
2617
2755
|
this.dbName = dbName;
|
|
2618
2756
|
}
|
|
2757
|
+
dbName;
|
|
2619
2758
|
version = 1;
|
|
2620
2759
|
db = null;
|
|
2621
2760
|
async getDB() {
|
|
@@ -3166,6 +3305,7 @@ var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
|
|
|
3166
3305
|
return refundableVtxos.some((vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo));
|
|
3167
3306
|
};
|
|
3168
3307
|
var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
|
|
3308
|
+
var CLTV_IMMATURE_RETRY_SEC = 60;
|
|
3169
3309
|
var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
|
|
3170
3310
|
var CLAIM_VTXO_RETRY_DELAY_MS = 500;
|
|
3171
3311
|
var quoteOptionsForSwap = (swap) => {
|
|
@@ -3238,9 +3378,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3238
3378
|
claim: async (swap) => {
|
|
3239
3379
|
await this.claimVHTLC(swap);
|
|
3240
3380
|
},
|
|
3241
|
-
refund:
|
|
3242
|
-
await this.refundVHTLC(swap);
|
|
3243
|
-
},
|
|
3381
|
+
refund: (swap) => this.refundVHTLC(swap),
|
|
3244
3382
|
claimArk: (swap) => this.claimArk(swap),
|
|
3245
3383
|
claimBtc: (swap) => this.claimBtc(swap),
|
|
3246
3384
|
refundArk: async (swap) => {
|
|
@@ -3846,7 +3984,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3846
3984
|
refundWithoutReceiverLeaf,
|
|
3847
3985
|
outputScript
|
|
3848
3986
|
};
|
|
3849
|
-
const
|
|
3987
|
+
const outcome = await this.refundVtxos({
|
|
3850
3988
|
swapId: pendingSwap.id,
|
|
3851
3989
|
vtxos: refundableVtxos,
|
|
3852
3990
|
refundLocktime: vhtlcTimeouts.refund,
|
|
@@ -3856,7 +3994,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3856
3994
|
refundViaBoltz: this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3857
3995
|
});
|
|
3858
3996
|
if (!isSubmarineSuccessStatus(pendingSwap.status)) {
|
|
3859
|
-
const fullyRefunded =
|
|
3997
|
+
const fullyRefunded = outcome.skipped === 0;
|
|
3998
|
+
pendingSwap.refundable = true;
|
|
3999
|
+
pendingSwap.refunded = fullyRefunded;
|
|
3860
4000
|
await updateSubmarineSwapStatus(
|
|
3861
4001
|
pendingSwap,
|
|
3862
4002
|
pendingSwap.status,
|
|
@@ -3865,7 +4005,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3865
4005
|
{ refundable: true, refunded: fullyRefunded }
|
|
3866
4006
|
);
|
|
3867
4007
|
}
|
|
3868
|
-
return
|
|
4008
|
+
return outcome;
|
|
3869
4009
|
}
|
|
3870
4010
|
/**
|
|
3871
4011
|
* Inspect a submarine swap's lockup address for recoverable funds.
|
|
@@ -4482,7 +4622,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4482
4622
|
refundWithoutReceiverLeaf,
|
|
4483
4623
|
outputScript
|
|
4484
4624
|
};
|
|
4485
|
-
const
|
|
4625
|
+
const outcome = await this.refundVtxos({
|
|
4486
4626
|
swapId: pendingSwap.id,
|
|
4487
4627
|
vtxos: unspentVtxos,
|
|
4488
4628
|
refundLocktime,
|
|
@@ -4492,11 +4632,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4492
4632
|
refundViaBoltz: this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4493
4633
|
});
|
|
4494
4634
|
const finalStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
});
|
|
4499
|
-
return { swept: sweptCount, skipped: skippedCount };
|
|
4635
|
+
pendingSwap.status = finalStatus.status;
|
|
4636
|
+
await this.savePendingChainSwap(pendingSwap);
|
|
4637
|
+
return outcome;
|
|
4500
4638
|
}
|
|
4501
4639
|
// =========================================================================
|
|
4502
4640
|
// Chain swaps: BTC -> ARK
|
|
@@ -5100,18 +5238,44 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5100
5238
|
);
|
|
5101
5239
|
}
|
|
5102
5240
|
}
|
|
5241
|
+
/**
|
|
5242
|
+
* {@link settleRefundWithoutReceiver}, deferring instead of failing when the server
|
|
5243
|
+
* rejects the spend as CLTV-immature ({@link ArkErrorName.FORFEIT_CLOSURE_LOCKED}):
|
|
5244
|
+
* we gate on the local wall clock, the server on the chain tip's, which lags. The
|
|
5245
|
+
* server-authoritative sibling of the wall-clock deferral the pre-CLTV branches
|
|
5246
|
+
* already implement. Anything else propagates.
|
|
5247
|
+
*
|
|
5248
|
+
* Only `submitTx` can raise it, so this is a pass-through for recoverable VTXOs,
|
|
5249
|
+
* which settle via a batch round.
|
|
5250
|
+
*
|
|
5251
|
+
* @returns `true` if settled, `false` if the server deferred it.
|
|
5252
|
+
*/
|
|
5253
|
+
async trySettleRefundWithoutReceiver(swapId, ctx, vtxo) {
|
|
5254
|
+
try {
|
|
5255
|
+
await this.settleRefundWithoutReceiver(ctx, vtxo);
|
|
5256
|
+
return true;
|
|
5257
|
+
} catch (error) {
|
|
5258
|
+
if (!(0, import_sdk8.isArkError)(error, import_sdk8.ArkErrorName.FORFEIT_CLOSURE_LOCKED)) throw error;
|
|
5259
|
+
logger.warn(
|
|
5260
|
+
`Swap ${swapId}: server deferred refundWithoutReceiver for VTXO ${vtxo.txid}:${vtxo.vout} \u2014 refund locktime has not matured against the chain tip (locktime=${error.metadata?.locktime}, currentLocktime=${error.metadata?.current_locktime}, type=${error.metadata?.type}). Refund will be retried once a later block carries the locktime.`
|
|
5261
|
+
);
|
|
5262
|
+
return false;
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5103
5265
|
/**
|
|
5104
5266
|
* Refund every VTXO at a swap's VHTLC address back to the wallet, shared by
|
|
5105
5267
|
* {@link ArkadeSwaps.refundVHTLC} (submarine) and {@link ArkadeSwaps.refundArk}
|
|
5106
5268
|
* (chain). Path selection per VTXO:
|
|
5107
|
-
* - CLTV elapsed → `refundWithoutReceiver` (offchain for a
|
|
5108
|
-
* batch round for a swept one — see
|
|
5269
|
+
* - CLTV elapsed by our wall clock → `refundWithoutReceiver` (offchain for a
|
|
5270
|
+
* live VTXO, via a batch round for a swept one — see
|
|
5271
|
+
* {@link settleRefundWithoutReceiver}), or skipped if the server defers it as
|
|
5272
|
+
* still immature (see {@link trySettleRefundWithoutReceiver}).
|
|
5109
5273
|
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign a swept-batch refund).
|
|
5110
5274
|
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz, falling
|
|
5111
5275
|
* back to `refundWithoutReceiver` offchain if Boltz rejects after the
|
|
5112
5276
|
* locktime has since elapsed.
|
|
5113
5277
|
*
|
|
5114
|
-
* @returns Counts of VTXOs swept vs. deferred.
|
|
5278
|
+
* @returns Counts of VTXOs swept vs. deferred, and when to retry the latter.
|
|
5115
5279
|
*/
|
|
5116
5280
|
async refundVtxos(params) {
|
|
5117
5281
|
const {
|
|
@@ -5127,19 +5291,27 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5127
5291
|
let boltzCallCount = 0;
|
|
5128
5292
|
let sweptCount = 0;
|
|
5129
5293
|
let skippedCount = 0;
|
|
5294
|
+
let retryAt;
|
|
5295
|
+
const defer = (candidate) => {
|
|
5296
|
+
skippedCount++;
|
|
5297
|
+
retryAt = retryAt === void 0 ? candidate : Math.min(retryAt, candidate);
|
|
5298
|
+
};
|
|
5130
5299
|
for (const vtxo of vtxos) {
|
|
5131
5300
|
const isRecoverableVtxo = (0, import_sdk8.isRecoverable)(vtxo);
|
|
5132
5301
|
const output = { amount: BigInt(vtxo.value), script: outputScript };
|
|
5133
5302
|
if (isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
5134
|
-
await this.
|
|
5135
|
-
|
|
5303
|
+
if (await this.trySettleRefundWithoutReceiver(swapId, refundContext, vtxo)) {
|
|
5304
|
+
sweptCount++;
|
|
5305
|
+
} else {
|
|
5306
|
+
defer(Math.floor(Date.now() / 1e3) + CLTV_IMMATURE_RETRY_SEC);
|
|
5307
|
+
}
|
|
5136
5308
|
continue;
|
|
5137
5309
|
}
|
|
5138
5310
|
if (isRecoverableVtxo) {
|
|
5139
5311
|
logger.error(
|
|
5140
5312
|
`Swap ${swapId}: recoverable VTXO ${vtxo.txid}:${vtxo.vout} cannot be refunded yet \u2014 refundWithoutReceiver locktime has not passed (refundLocktime=${refundLocktime}, currentTimestamp=${Math.floor(Date.now() / 1e3)}). Refund will be retried after locktime.`
|
|
5141
5313
|
);
|
|
5142
|
-
|
|
5314
|
+
defer(refundLocktime);
|
|
5143
5315
|
continue;
|
|
5144
5316
|
}
|
|
5145
5317
|
const input = {
|
|
@@ -5173,17 +5345,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5173
5345
|
logger.error(
|
|
5174
5346
|
`Swap ${swapId}: Boltz rejected VTXO outpoint and refundWithoutReceiver locktime has not passed yet (currentTimestamp=${Math.floor(Date.now() / 1e3)}, locktime=${refundLocktime}). Refund will be retried after locktime.`
|
|
5175
5347
|
);
|
|
5176
|
-
|
|
5348
|
+
defer(refundLocktime);
|
|
5177
5349
|
continue;
|
|
5178
5350
|
}
|
|
5179
5351
|
logger.warn(
|
|
5180
5352
|
`Swap ${swapId}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver offchain`
|
|
5181
5353
|
);
|
|
5182
|
-
await this.
|
|
5183
|
-
|
|
5354
|
+
if (await this.trySettleRefundWithoutReceiver(swapId, refundContext, vtxo)) {
|
|
5355
|
+
sweptCount++;
|
|
5356
|
+
} else {
|
|
5357
|
+
defer(Math.floor(Date.now() / 1e3) + CLTV_IMMATURE_RETRY_SEC);
|
|
5358
|
+
}
|
|
5184
5359
|
}
|
|
5185
5360
|
}
|
|
5186
|
-
return { swept: sweptCount, skipped: skippedCount };
|
|
5361
|
+
return { swept: sweptCount, skipped: skippedCount, retryAt };
|
|
5187
5362
|
}
|
|
5188
5363
|
/**
|
|
5189
5364
|
* Creates a VHTLC script for the swap.
|
|
@@ -5521,6 +5696,8 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
5521
5696
|
this.config = config;
|
|
5522
5697
|
this.swapRepository = inner.swapRepository;
|
|
5523
5698
|
}
|
|
5699
|
+
inner;
|
|
5700
|
+
config;
|
|
5524
5701
|
swapRepository;
|
|
5525
5702
|
foregroundIntervalId;
|
|
5526
5703
|
/**
|
package/dist/expo/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-
|
|
2
|
-
import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-
|
|
3
|
-
import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-
|
|
4
|
-
export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-
|
|
1
|
+
import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-qZmYvnVy.cjs';
|
|
2
|
+
import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-Db4JSdjk.cjs';
|
|
3
|
+
import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor-DCtJYSbJ.cjs';
|
|
4
|
+
export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor-DCtJYSbJ.cjs';
|
|
5
5
|
import { ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
|
|
6
6
|
import { TransactionOutput } from '@scure/btc-signer/psbt.js';
|
|
7
7
|
import '@arkade-os/sdk/worker/expo';
|
package/dist/expo/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-
|
|
2
|
-
import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-
|
|
3
|
-
import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor
|
|
4
|
-
export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor
|
|
1
|
+
import { I as IArkadeSwaps, A as ArkadeSwaps, Q as QuoteSwapOptions, V as VhtlcTimeouts } from '../arkade-swaps-CoJKHu2Y.js';
|
|
2
|
+
import { n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, b as BoltzReverseSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, F as FeesResponse, a as BoltzChainSwap, j as ArkToBtcResponse, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from '../types-Db4JSdjk.js';
|
|
3
|
+
import { E as ExpoArkadeSwapsConfig } from '../swapsPollProcessor--BPV48QB.js';
|
|
4
|
+
export { D as DefineSwapBackgroundTaskOptions, b as ExpoArkadeLightningConfig, c as ExpoSwapBackgroundConfig, P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies } from '../swapsPollProcessor--BPV48QB.js';
|
|
5
5
|
import { ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
|
|
6
6
|
import { TransactionOutput } from '@scure/btc-signer/psbt.js';
|
|
7
7
|
import '@arkade-os/sdk/worker/expo';
|
package/dist/expo/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SWAP_POLL_TASK_TYPE
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-76V3TKDL.js";
|
|
4
4
|
import {
|
|
5
5
|
ArkadeSwaps
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-CZTZTYZU.js";
|
|
7
7
|
import "../chunk-SJQJQO7P.js";
|
|
8
8
|
|
|
9
9
|
// src/expo/arkade-lightning.ts
|
|
@@ -28,6 +28,8 @@ var ExpoArkadeSwaps = class _ExpoArkadeSwaps {
|
|
|
28
28
|
this.config = config;
|
|
29
29
|
this.swapRepository = inner.swapRepository;
|
|
30
30
|
}
|
|
31
|
+
inner;
|
|
32
|
+
config;
|
|
31
33
|
swapRepository;
|
|
32
34
|
foregroundIntervalId;
|
|
33
35
|
/**
|