@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/background.cjs
CHANGED
|
@@ -235,6 +235,7 @@ var BoltzRefundError = class extends Error {
|
|
|
235
235
|
this.cause = cause;
|
|
236
236
|
this.name = "BoltzRefundError";
|
|
237
237
|
}
|
|
238
|
+
cause;
|
|
238
239
|
};
|
|
239
240
|
|
|
240
241
|
// src/boltz-swap-provider.ts
|
|
@@ -1005,6 +1006,13 @@ var MusigKeyAgg = class _MusigKeyAgg {
|
|
|
1005
1006
|
this.internalKey = internalKey;
|
|
1006
1007
|
this._tweak = _tweak;
|
|
1007
1008
|
}
|
|
1009
|
+
privateKey;
|
|
1010
|
+
myPublicKey;
|
|
1011
|
+
publicKeys;
|
|
1012
|
+
myIndex;
|
|
1013
|
+
aggPubkey;
|
|
1014
|
+
internalKey;
|
|
1015
|
+
_tweak;
|
|
1008
1016
|
xonlyTweakAdd(tweak) {
|
|
1009
1017
|
if (this._tweak) throw new Error("musig key already tweaked");
|
|
1010
1018
|
return new _MusigKeyAgg(
|
|
@@ -1039,6 +1047,13 @@ var MusigWithMessage = class {
|
|
|
1039
1047
|
this.tweak = tweak;
|
|
1040
1048
|
this.msg = msg;
|
|
1041
1049
|
}
|
|
1050
|
+
privateKey;
|
|
1051
|
+
myPublicKey;
|
|
1052
|
+
publicKeys;
|
|
1053
|
+
myIndex;
|
|
1054
|
+
aggPubkey;
|
|
1055
|
+
tweak;
|
|
1056
|
+
msg;
|
|
1042
1057
|
generateNonce() {
|
|
1043
1058
|
const nonce = (0, import_musig2.nonceGen)(this.myPublicKey, this.privateKey, this.aggPubkey, this.msg);
|
|
1044
1059
|
return new MusigWithNonce(
|
|
@@ -1062,6 +1077,13 @@ var MusigWithNonce = class {
|
|
|
1062
1077
|
this.msg = msg;
|
|
1063
1078
|
this.nonce = nonce;
|
|
1064
1079
|
}
|
|
1080
|
+
privateKey;
|
|
1081
|
+
myPublicKey;
|
|
1082
|
+
publicKeys;
|
|
1083
|
+
myIndex;
|
|
1084
|
+
tweak;
|
|
1085
|
+
msg;
|
|
1086
|
+
nonce;
|
|
1065
1087
|
get publicNonce() {
|
|
1066
1088
|
return this.nonce.public;
|
|
1067
1089
|
}
|
|
@@ -1110,6 +1132,14 @@ var MusigNoncesAggregated = class {
|
|
|
1110
1132
|
this.pubNonces = pubNonces;
|
|
1111
1133
|
this.aggregatedNonce = aggregatedNonce;
|
|
1112
1134
|
}
|
|
1135
|
+
privateKey;
|
|
1136
|
+
publicKeys;
|
|
1137
|
+
myIndex;
|
|
1138
|
+
tweak;
|
|
1139
|
+
msg;
|
|
1140
|
+
nonce;
|
|
1141
|
+
pubNonces;
|
|
1142
|
+
aggregatedNonce;
|
|
1113
1143
|
get publicNonce() {
|
|
1114
1144
|
return this.nonce.public;
|
|
1115
1145
|
}
|
|
@@ -1141,6 +1171,12 @@ var MusigSession = class {
|
|
|
1141
1171
|
this.session = session;
|
|
1142
1172
|
this.partialSignatures = Array(publicKeys.length).fill(null);
|
|
1143
1173
|
}
|
|
1174
|
+
privateKey;
|
|
1175
|
+
publicKeys;
|
|
1176
|
+
myIndex;
|
|
1177
|
+
nonce;
|
|
1178
|
+
pubNonces;
|
|
1179
|
+
session;
|
|
1144
1180
|
partialSignatures;
|
|
1145
1181
|
get publicNonce() {
|
|
1146
1182
|
return this.nonce.public;
|
|
@@ -1168,6 +1204,10 @@ var MusigSigned = class {
|
|
|
1168
1204
|
this.ourPartialSignature = ourPartialSignature;
|
|
1169
1205
|
this.publicNonce = publicNonce;
|
|
1170
1206
|
}
|
|
1207
|
+
session;
|
|
1208
|
+
partialSignatures;
|
|
1209
|
+
ourPartialSignature;
|
|
1210
|
+
publicNonce;
|
|
1171
1211
|
aggregatePartials() {
|
|
1172
1212
|
if (this.partialSignatures.some((s) => s === null)) {
|
|
1173
1213
|
throw new Error("not all partial signatures are set");
|
|
@@ -1435,12 +1475,20 @@ var SwapManager = class _SwapManager {
|
|
|
1435
1475
|
*/
|
|
1436
1476
|
static NOT_FOUND_THRESHOLD = 10;
|
|
1437
1477
|
/**
|
|
1438
|
-
*
|
|
1439
|
-
*
|
|
1440
|
-
*
|
|
1441
|
-
*
|
|
1478
|
+
* Floor on the delay between re-attempts of a refund that left VTXOs
|
|
1479
|
+
* deferred, and the delay used when the outcome names no retry time. Boltz
|
|
1480
|
+
* won't send another status update once the swap is refundable-and-final,
|
|
1481
|
+
* so the manager owns the local retry cadence.
|
|
1442
1482
|
*/
|
|
1443
1483
|
static REFUND_RETRY_DELAY_MS = 6e4;
|
|
1484
|
+
/**
|
|
1485
|
+
* Ceiling on that delay. A deferral can be hours out (a pre-CLTV VTXO waits
|
|
1486
|
+
* out the whole refund locktime), and `setTimeout` fires *immediately* past
|
|
1487
|
+
* its 32-bit millisecond range, so long waits are broken into re-armed
|
|
1488
|
+
* hops. Re-attempting costs an indexer lookup and re-defers, which also lets
|
|
1489
|
+
* the cadence recover from a suspended device or a clock jump.
|
|
1490
|
+
*/
|
|
1491
|
+
static MAX_REFUND_RETRY_DELAY_MS = 60 * 6e4;
|
|
1444
1492
|
swapProvider;
|
|
1445
1493
|
config;
|
|
1446
1494
|
// Event listeners storage (supports multiple listeners per event)
|
|
@@ -1459,10 +1507,9 @@ var SwapManager = class _SwapManager {
|
|
|
1459
1507
|
reconnectTimer = null;
|
|
1460
1508
|
initialPollTimer = null;
|
|
1461
1509
|
pollRetryTimers = /* @__PURE__ */ new Map();
|
|
1462
|
-
// Per-swap retry timers for
|
|
1463
|
-
//
|
|
1464
|
-
//
|
|
1465
|
-
// refund completes or the manager stops.
|
|
1510
|
+
// Per-swap retry timers for deferred refunds that left local work undone.
|
|
1511
|
+
// The swap is held in `monitoredSwaps` past its terminal Boltz status
|
|
1512
|
+
// until the local refund completes or the manager stops.
|
|
1466
1513
|
refundRetryTimers = /* @__PURE__ */ new Map();
|
|
1467
1514
|
// Per-swap counter of consecutive `SwapNotFoundError` responses from
|
|
1468
1515
|
// `getSwapStatus`. Reset on any successful poll. Once a swap reaches
|
|
@@ -1634,11 +1681,12 @@ var SwapManager = class _SwapManager {
|
|
|
1634
1681
|
this.initialSwaps.set(swap.id, swap);
|
|
1635
1682
|
}
|
|
1636
1683
|
for (const swap of pendingSwaps) {
|
|
1637
|
-
if (
|
|
1684
|
+
if (this.shouldMonitorOnStart(swap)) {
|
|
1638
1685
|
this.monitoredSwaps.set(swap.id, swap);
|
|
1639
1686
|
}
|
|
1640
1687
|
}
|
|
1641
1688
|
await this.tryConnectWebSocket();
|
|
1689
|
+
this.resumePersistedRefundRetries();
|
|
1642
1690
|
await this.resumeActionableSwaps();
|
|
1643
1691
|
}
|
|
1644
1692
|
/**
|
|
@@ -1721,6 +1769,7 @@ var SwapManager = class _SwapManager {
|
|
|
1721
1769
|
* Remove a swap from monitoring
|
|
1722
1770
|
*/
|
|
1723
1771
|
async removeSwap(swapId) {
|
|
1772
|
+
const swap = this.monitoredSwaps.get(swapId);
|
|
1724
1773
|
this.monitoredSwaps.delete(swapId);
|
|
1725
1774
|
this.swapSubscriptions.delete(swapId);
|
|
1726
1775
|
this.chainClaimPromises.delete(swapId);
|
|
@@ -1734,6 +1783,9 @@ var SwapManager = class _SwapManager {
|
|
|
1734
1783
|
clearTimeout(refundRetryTimer);
|
|
1735
1784
|
this.refundRetryTimers.delete(swapId);
|
|
1736
1785
|
}
|
|
1786
|
+
if (swap) {
|
|
1787
|
+
await this.clearRefundRetry(swap);
|
|
1788
|
+
}
|
|
1737
1789
|
this.notFoundCounts.delete(swapId);
|
|
1738
1790
|
logger.log(`Removed swap ${swapId} from monitoring`);
|
|
1739
1791
|
}
|
|
@@ -2072,12 +2124,69 @@ var SwapManager = class _SwapManager {
|
|
|
2072
2124
|
}
|
|
2073
2125
|
this.swapCompletedListeners.forEach((listener) => listener(swap));
|
|
2074
2126
|
}
|
|
2127
|
+
static isRefundRetryableSwap(swap) {
|
|
2128
|
+
return isPendingSubmarineSwap(swap) || isPendingChainSwap(swap);
|
|
2129
|
+
}
|
|
2130
|
+
static hasPendingRefundRetry(swap) {
|
|
2131
|
+
if (!_SwapManager.isRefundRetryableSwap(swap)) return false;
|
|
2132
|
+
return swap.refundRetry?.pending === true && Number.isFinite(swap.refundRetry.nextRetryAt);
|
|
2133
|
+
}
|
|
2134
|
+
shouldMonitorOnStart(swap) {
|
|
2135
|
+
if (!this.isFinalStatus(swap)) return true;
|
|
2136
|
+
return this.config.enableAutoActions === true && _SwapManager.hasPendingRefundRetry(swap);
|
|
2137
|
+
}
|
|
2138
|
+
/**
|
|
2139
|
+
* How long to wait before re-attempting a refund, given the `retryAt`
|
|
2140
|
+
* (Unix seconds) the outcome reported. Clamped at both ends: see
|
|
2141
|
+
* {@link SwapManager.REFUND_RETRY_DELAY_MS} and
|
|
2142
|
+
* {@link SwapManager.MAX_REFUND_RETRY_DELAY_MS}.
|
|
2143
|
+
*/
|
|
2144
|
+
static refundRetryDelayMs(retryAt) {
|
|
2145
|
+
if (retryAt === void 0) return _SwapManager.REFUND_RETRY_DELAY_MS;
|
|
2146
|
+
return Math.min(
|
|
2147
|
+
Math.max(retryAt * 1e3 - Date.now(), _SwapManager.REFUND_RETRY_DELAY_MS),
|
|
2148
|
+
_SwapManager.MAX_REFUND_RETRY_DELAY_MS
|
|
2149
|
+
);
|
|
2150
|
+
}
|
|
2151
|
+
static persistedRefundRetryDelayMs(nextRetryAt) {
|
|
2152
|
+
return Math.min(
|
|
2153
|
+
Math.max(nextRetryAt * 1e3 - Date.now(), 0),
|
|
2154
|
+
_SwapManager.MAX_REFUND_RETRY_DELAY_MS
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
static nextRefundRetryAt(delayMs) {
|
|
2158
|
+
return Math.ceil((Date.now() + delayMs) / 1e3);
|
|
2159
|
+
}
|
|
2160
|
+
async clearRefundRetry(swap) {
|
|
2161
|
+
if (!_SwapManager.isRefundRetryableSwap(swap) || swap.refundRetry === void 0) return;
|
|
2162
|
+
delete swap.refundRetry;
|
|
2163
|
+
await this.saveSwap(swap);
|
|
2164
|
+
}
|
|
2165
|
+
resumePersistedRefundRetries() {
|
|
2166
|
+
if (!this.config.enableAutoActions) return;
|
|
2167
|
+
for (const swap of this.monitoredSwaps.values()) {
|
|
2168
|
+
if (!_SwapManager.hasPendingRefundRetry(swap)) continue;
|
|
2169
|
+
const delayMs = _SwapManager.persistedRefundRetryDelayMs(swap.refundRetry.nextRetryAt);
|
|
2170
|
+
logger.log(
|
|
2171
|
+
`Swap ${swap.id}: resuming deferred refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2172
|
+
);
|
|
2173
|
+
this.armRefundRetryTimer(swap, delayMs);
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2075
2176
|
/**
|
|
2076
|
-
* Schedule another `executeAutonomousAction` run for a
|
|
2077
|
-
*
|
|
2078
|
-
*
|
|
2177
|
+
* Schedule another `executeAutonomousAction` run for a swap whose refund
|
|
2178
|
+
* left VTXOs deferred. The pending retry and its next attempt time are saved
|
|
2179
|
+
* with the swap so stop/start and application restart can re-arm it.
|
|
2079
2180
|
*/
|
|
2080
|
-
scheduleRefundRetry(swap, delayMs) {
|
|
2181
|
+
async scheduleRefundRetry(swap, delayMs) {
|
|
2182
|
+
swap.refundRetry = {
|
|
2183
|
+
pending: true,
|
|
2184
|
+
nextRetryAt: _SwapManager.nextRefundRetryAt(delayMs)
|
|
2185
|
+
};
|
|
2186
|
+
await this.saveSwap(swap);
|
|
2187
|
+
this.armRefundRetryTimer(swap, delayMs);
|
|
2188
|
+
}
|
|
2189
|
+
armRefundRetryTimer(swap, delayMs) {
|
|
2081
2190
|
const existing = this.refundRetryTimers.get(swap.id);
|
|
2082
2191
|
if (existing) clearTimeout(existing);
|
|
2083
2192
|
this.refundRetryTimers.set(
|
|
@@ -2085,12 +2194,14 @@ var SwapManager = class _SwapManager {
|
|
|
2085
2194
|
setTimeout(async () => {
|
|
2086
2195
|
this.refundRetryTimers.delete(swap.id);
|
|
2087
2196
|
if (!this.isRunning) return;
|
|
2088
|
-
|
|
2197
|
+
const current = this.monitoredSwaps.get(swap.id);
|
|
2198
|
+
if (!current) return;
|
|
2199
|
+
let ran = false;
|
|
2089
2200
|
try {
|
|
2090
|
-
await this.executeAutonomousAction(
|
|
2201
|
+
ran = await this.executeAutonomousAction(current);
|
|
2091
2202
|
} finally {
|
|
2092
|
-
if (!this.refundRetryTimers.has(
|
|
2093
|
-
this.finalizeMonitoredSwap(
|
|
2203
|
+
if (ran && !this.refundRetryTimers.has(current.id) && this.isFinalStatus(current)) {
|
|
2204
|
+
this.finalizeMonitoredSwap(current);
|
|
2094
2205
|
}
|
|
2095
2206
|
}
|
|
2096
2207
|
}, delayMs)
|
|
@@ -2099,11 +2210,16 @@ var SwapManager = class _SwapManager {
|
|
|
2099
2210
|
/**
|
|
2100
2211
|
* Execute autonomous action based on swap status
|
|
2101
2212
|
* Uses locking to prevent race conditions with manual operations
|
|
2213
|
+
*
|
|
2214
|
+
* @returns `false` when another action for this swap already held the lock
|
|
2215
|
+
* and this call did nothing, `true` when it ran. Callers that finalize on
|
|
2216
|
+
* the result must not act on `false`: the in-flight action owns the swap's
|
|
2217
|
+
* outcome and will re-arm or finalize from its own result.
|
|
2102
2218
|
*/
|
|
2103
2219
|
async executeAutonomousAction(swap) {
|
|
2104
2220
|
if (this.swapsInProgress.has(swap.id)) {
|
|
2105
2221
|
logger.log(`Swap ${swap.id} is already being processed, skipping autonomous action`);
|
|
2106
|
-
return;
|
|
2222
|
+
return false;
|
|
2107
2223
|
}
|
|
2108
2224
|
try {
|
|
2109
2225
|
this.swapsInProgress.add(swap.id);
|
|
@@ -2112,7 +2228,7 @@ var SwapManager = class _SwapManager {
|
|
|
2112
2228
|
logger.log(
|
|
2113
2229
|
`Skipping claim for swap ${swap.id}: missing preimage (restored swap)`
|
|
2114
2230
|
);
|
|
2115
|
-
return;
|
|
2231
|
+
return true;
|
|
2116
2232
|
}
|
|
2117
2233
|
if (isReverseClaimableStatus(swap.status)) {
|
|
2118
2234
|
logger.log(`Auto-claiming reverse swap ${swap.id}`);
|
|
@@ -2124,11 +2240,26 @@ var SwapManager = class _SwapManager {
|
|
|
2124
2240
|
logger.log(
|
|
2125
2241
|
`Skipping refund for swap ${swap.id}: missing invoice (restored swap)`
|
|
2126
2242
|
);
|
|
2127
|
-
return;
|
|
2243
|
+
return true;
|
|
2128
2244
|
}
|
|
2129
2245
|
if (isSubmarineRefundableStatus(swap.status)) {
|
|
2130
2246
|
logger.log(`Auto-refunding submarine swap ${swap.id}`);
|
|
2131
|
-
|
|
2247
|
+
let outcome;
|
|
2248
|
+
try {
|
|
2249
|
+
outcome = await this.executeRefundAction(swap);
|
|
2250
|
+
} catch (error) {
|
|
2251
|
+
await this.clearRefundRetry(swap);
|
|
2252
|
+
throw error;
|
|
2253
|
+
}
|
|
2254
|
+
if (outcome && outcome.skipped > 0) {
|
|
2255
|
+
const delayMs = _SwapManager.refundRetryDelayMs(outcome.retryAt);
|
|
2256
|
+
logger.log(
|
|
2257
|
+
`Submarine swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2258
|
+
);
|
|
2259
|
+
await this.scheduleRefundRetry(swap, delayMs);
|
|
2260
|
+
} else if (outcome) {
|
|
2261
|
+
await this.clearRefundRetry(swap);
|
|
2262
|
+
}
|
|
2132
2263
|
this.actionExecutedListeners.forEach((listener) => listener(swap, "refund"));
|
|
2133
2264
|
}
|
|
2134
2265
|
} else if (isPendingChainSwap(swap)) {
|
|
@@ -2152,10 +2283,13 @@ var SwapManager = class _SwapManager {
|
|
|
2152
2283
|
try {
|
|
2153
2284
|
const outcome = await this.executeRefundArkAction(swap);
|
|
2154
2285
|
if (outcome && outcome.skipped > 0) {
|
|
2286
|
+
const delayMs = _SwapManager.refundRetryDelayMs(outcome.retryAt);
|
|
2155
2287
|
logger.log(
|
|
2156
|
-
`Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry`
|
|
2288
|
+
`Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry in ${Math.round(delayMs / 1e3)}s`
|
|
2157
2289
|
);
|
|
2158
|
-
this.scheduleRefundRetry(swap,
|
|
2290
|
+
await this.scheduleRefundRetry(swap, delayMs);
|
|
2291
|
+
} else if (outcome) {
|
|
2292
|
+
await this.clearRefundRetry(swap);
|
|
2159
2293
|
}
|
|
2160
2294
|
this.actionExecutedListeners.forEach(
|
|
2161
2295
|
(listener) => listener(swap, "refundArk")
|
|
@@ -2168,7 +2302,7 @@ var SwapManager = class _SwapManager {
|
|
|
2168
2302
|
this.swapFailedListeners.forEach(
|
|
2169
2303
|
(listener) => listener(swap, error)
|
|
2170
2304
|
);
|
|
2171
|
-
this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2305
|
+
await this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2172
2306
|
}
|
|
2173
2307
|
}
|
|
2174
2308
|
if (swap.request.from === "BTC") {
|
|
@@ -2201,6 +2335,7 @@ var SwapManager = class _SwapManager {
|
|
|
2201
2335
|
} finally {
|
|
2202
2336
|
this.swapsInProgress.delete(swap.id);
|
|
2203
2337
|
}
|
|
2338
|
+
return true;
|
|
2204
2339
|
}
|
|
2205
2340
|
/**
|
|
2206
2341
|
* Execute claim action for reverse swap
|
|
@@ -2214,13 +2349,15 @@ var SwapManager = class _SwapManager {
|
|
|
2214
2349
|
}
|
|
2215
2350
|
/**
|
|
2216
2351
|
* Execute refund action for submarine swap
|
|
2352
|
+
*
|
|
2353
|
+
* @returns The refund outcome, or `undefined` if no callback is wired.
|
|
2217
2354
|
*/
|
|
2218
2355
|
async executeRefundAction(swap) {
|
|
2219
2356
|
if (!this.refundCallback) {
|
|
2220
2357
|
logger.error("Refund callback not set");
|
|
2221
2358
|
return;
|
|
2222
2359
|
}
|
|
2223
|
-
|
|
2360
|
+
return this.refundCallback(swap);
|
|
2224
2361
|
}
|
|
2225
2362
|
/**
|
|
2226
2363
|
* Execute claim action for chain swap Btc to Ark
|
|
@@ -2302,6 +2439,7 @@ var SwapManager = class _SwapManager {
|
|
|
2302
2439
|
return;
|
|
2303
2440
|
}
|
|
2304
2441
|
for (const swap of this.monitoredSwaps.values()) {
|
|
2442
|
+
if (this.refundRetryTimers.has(swap.id)) continue;
|
|
2305
2443
|
try {
|
|
2306
2444
|
if (isPendingReverseSwap(swap) && isReverseClaimableStatus(swap.status)) {
|
|
2307
2445
|
logger.log(`Resuming claim for swap ${swap.id}`);
|
|
@@ -2626,6 +2764,7 @@ var IndexedDbSwapRepository = class {
|
|
|
2626
2764
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
2627
2765
|
this.dbName = dbName;
|
|
2628
2766
|
}
|
|
2767
|
+
dbName;
|
|
2629
2768
|
version = 1;
|
|
2630
2769
|
db = null;
|
|
2631
2770
|
async getDB() {
|
|
@@ -3176,6 +3315,7 @@ var canRecoverViaBoltz3of3 = (refundableVtxos, swap) => {
|
|
|
3176
3315
|
return refundableVtxos.some((vtxo) => !vtxo.isSpent && !(0, import_sdk8.isRecoverable)(vtxo));
|
|
3177
3316
|
};
|
|
3178
3317
|
var isSubmarineRefundLocktimeReached = (refundTimestamp) => Math.floor(Date.now() / 1e3) >= refundTimestamp;
|
|
3318
|
+
var CLTV_IMMATURE_RETRY_SEC = 60;
|
|
3179
3319
|
var CLAIM_VTXO_RETRY_ATTEMPTS = 3;
|
|
3180
3320
|
var CLAIM_VTXO_RETRY_DELAY_MS = 500;
|
|
3181
3321
|
var quoteOptionsForSwap = (swap) => {
|
|
@@ -3248,9 +3388,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3248
3388
|
claim: async (swap) => {
|
|
3249
3389
|
await this.claimVHTLC(swap);
|
|
3250
3390
|
},
|
|
3251
|
-
refund:
|
|
3252
|
-
await this.refundVHTLC(swap);
|
|
3253
|
-
},
|
|
3391
|
+
refund: (swap) => this.refundVHTLC(swap),
|
|
3254
3392
|
claimArk: (swap) => this.claimArk(swap),
|
|
3255
3393
|
claimBtc: (swap) => this.claimBtc(swap),
|
|
3256
3394
|
refundArk: async (swap) => {
|
|
@@ -3856,7 +3994,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3856
3994
|
refundWithoutReceiverLeaf,
|
|
3857
3995
|
outputScript
|
|
3858
3996
|
};
|
|
3859
|
-
const
|
|
3997
|
+
const outcome = await this.refundVtxos({
|
|
3860
3998
|
swapId: pendingSwap.id,
|
|
3861
3999
|
vtxos: refundableVtxos,
|
|
3862
4000
|
refundLocktime: vhtlcTimeouts.refund,
|
|
@@ -3866,7 +4004,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3866
4004
|
refundViaBoltz: this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3867
4005
|
});
|
|
3868
4006
|
if (!isSubmarineSuccessStatus(pendingSwap.status)) {
|
|
3869
|
-
const fullyRefunded =
|
|
4007
|
+
const fullyRefunded = outcome.skipped === 0;
|
|
4008
|
+
pendingSwap.refundable = true;
|
|
4009
|
+
pendingSwap.refunded = fullyRefunded;
|
|
3870
4010
|
await updateSubmarineSwapStatus(
|
|
3871
4011
|
pendingSwap,
|
|
3872
4012
|
pendingSwap.status,
|
|
@@ -3875,7 +4015,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3875
4015
|
{ refundable: true, refunded: fullyRefunded }
|
|
3876
4016
|
);
|
|
3877
4017
|
}
|
|
3878
|
-
return
|
|
4018
|
+
return outcome;
|
|
3879
4019
|
}
|
|
3880
4020
|
/**
|
|
3881
4021
|
* Inspect a submarine swap's lockup address for recoverable funds.
|
|
@@ -4492,7 +4632,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4492
4632
|
refundWithoutReceiverLeaf,
|
|
4493
4633
|
outputScript
|
|
4494
4634
|
};
|
|
4495
|
-
const
|
|
4635
|
+
const outcome = await this.refundVtxos({
|
|
4496
4636
|
swapId: pendingSwap.id,
|
|
4497
4637
|
vtxos: unspentVtxos,
|
|
4498
4638
|
refundLocktime,
|
|
@@ -4502,11 +4642,9 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4502
4642
|
refundViaBoltz: this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4503
4643
|
});
|
|
4504
4644
|
const finalStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
});
|
|
4509
|
-
return { swept: sweptCount, skipped: skippedCount };
|
|
4645
|
+
pendingSwap.status = finalStatus.status;
|
|
4646
|
+
await this.savePendingChainSwap(pendingSwap);
|
|
4647
|
+
return outcome;
|
|
4510
4648
|
}
|
|
4511
4649
|
// =========================================================================
|
|
4512
4650
|
// Chain swaps: BTC -> ARK
|
|
@@ -5110,18 +5248,44 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5110
5248
|
);
|
|
5111
5249
|
}
|
|
5112
5250
|
}
|
|
5251
|
+
/**
|
|
5252
|
+
* {@link settleRefundWithoutReceiver}, deferring instead of failing when the server
|
|
5253
|
+
* rejects the spend as CLTV-immature ({@link ArkErrorName.FORFEIT_CLOSURE_LOCKED}):
|
|
5254
|
+
* we gate on the local wall clock, the server on the chain tip's, which lags. The
|
|
5255
|
+
* server-authoritative sibling of the wall-clock deferral the pre-CLTV branches
|
|
5256
|
+
* already implement. Anything else propagates.
|
|
5257
|
+
*
|
|
5258
|
+
* Only `submitTx` can raise it, so this is a pass-through for recoverable VTXOs,
|
|
5259
|
+
* which settle via a batch round.
|
|
5260
|
+
*
|
|
5261
|
+
* @returns `true` if settled, `false` if the server deferred it.
|
|
5262
|
+
*/
|
|
5263
|
+
async trySettleRefundWithoutReceiver(swapId, ctx, vtxo) {
|
|
5264
|
+
try {
|
|
5265
|
+
await this.settleRefundWithoutReceiver(ctx, vtxo);
|
|
5266
|
+
return true;
|
|
5267
|
+
} catch (error) {
|
|
5268
|
+
if (!(0, import_sdk8.isArkError)(error, import_sdk8.ArkErrorName.FORFEIT_CLOSURE_LOCKED)) throw error;
|
|
5269
|
+
logger.warn(
|
|
5270
|
+
`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.`
|
|
5271
|
+
);
|
|
5272
|
+
return false;
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5113
5275
|
/**
|
|
5114
5276
|
* Refund every VTXO at a swap's VHTLC address back to the wallet, shared by
|
|
5115
5277
|
* {@link ArkadeSwaps.refundVHTLC} (submarine) and {@link ArkadeSwaps.refundArk}
|
|
5116
5278
|
* (chain). Path selection per VTXO:
|
|
5117
|
-
* - CLTV elapsed → `refundWithoutReceiver` (offchain for a
|
|
5118
|
-
* batch round for a swept one — see
|
|
5279
|
+
* - CLTV elapsed by our wall clock → `refundWithoutReceiver` (offchain for a
|
|
5280
|
+
* live VTXO, via a batch round for a swept one — see
|
|
5281
|
+
* {@link settleRefundWithoutReceiver}), or skipped if the server defers it as
|
|
5282
|
+
* still immature (see {@link trySettleRefundWithoutReceiver}).
|
|
5119
5283
|
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign a swept-batch refund).
|
|
5120
5284
|
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz, falling
|
|
5121
5285
|
* back to `refundWithoutReceiver` offchain if Boltz rejects after the
|
|
5122
5286
|
* locktime has since elapsed.
|
|
5123
5287
|
*
|
|
5124
|
-
* @returns Counts of VTXOs swept vs. deferred.
|
|
5288
|
+
* @returns Counts of VTXOs swept vs. deferred, and when to retry the latter.
|
|
5125
5289
|
*/
|
|
5126
5290
|
async refundVtxos(params) {
|
|
5127
5291
|
const {
|
|
@@ -5137,19 +5301,27 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5137
5301
|
let boltzCallCount = 0;
|
|
5138
5302
|
let sweptCount = 0;
|
|
5139
5303
|
let skippedCount = 0;
|
|
5304
|
+
let retryAt;
|
|
5305
|
+
const defer = (candidate) => {
|
|
5306
|
+
skippedCount++;
|
|
5307
|
+
retryAt = retryAt === void 0 ? candidate : Math.min(retryAt, candidate);
|
|
5308
|
+
};
|
|
5140
5309
|
for (const vtxo of vtxos) {
|
|
5141
5310
|
const isRecoverableVtxo = (0, import_sdk8.isRecoverable)(vtxo);
|
|
5142
5311
|
const output = { amount: BigInt(vtxo.value), script: outputScript };
|
|
5143
5312
|
if (isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
5144
|
-
await this.
|
|
5145
|
-
|
|
5313
|
+
if (await this.trySettleRefundWithoutReceiver(swapId, refundContext, vtxo)) {
|
|
5314
|
+
sweptCount++;
|
|
5315
|
+
} else {
|
|
5316
|
+
defer(Math.floor(Date.now() / 1e3) + CLTV_IMMATURE_RETRY_SEC);
|
|
5317
|
+
}
|
|
5146
5318
|
continue;
|
|
5147
5319
|
}
|
|
5148
5320
|
if (isRecoverableVtxo) {
|
|
5149
5321
|
logger.error(
|
|
5150
5322
|
`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.`
|
|
5151
5323
|
);
|
|
5152
|
-
|
|
5324
|
+
defer(refundLocktime);
|
|
5153
5325
|
continue;
|
|
5154
5326
|
}
|
|
5155
5327
|
const input = {
|
|
@@ -5183,17 +5355,20 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5183
5355
|
logger.error(
|
|
5184
5356
|
`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.`
|
|
5185
5357
|
);
|
|
5186
|
-
|
|
5358
|
+
defer(refundLocktime);
|
|
5187
5359
|
continue;
|
|
5188
5360
|
}
|
|
5189
5361
|
logger.warn(
|
|
5190
5362
|
`Swap ${swapId}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver offchain`
|
|
5191
5363
|
);
|
|
5192
|
-
await this.
|
|
5193
|
-
|
|
5364
|
+
if (await this.trySettleRefundWithoutReceiver(swapId, refundContext, vtxo)) {
|
|
5365
|
+
sweptCount++;
|
|
5366
|
+
} else {
|
|
5367
|
+
defer(Math.floor(Date.now() / 1e3) + CLTV_IMMATURE_RETRY_SEC);
|
|
5368
|
+
}
|
|
5194
5369
|
}
|
|
5195
5370
|
}
|
|
5196
|
-
return { swept: sweptCount, skipped: skippedCount };
|
|
5371
|
+
return { swept: sweptCount, skipped: skippedCount, retryAt };
|
|
5197
5372
|
}
|
|
5198
5373
|
/**
|
|
5199
5374
|
* Creates a VHTLC script for the swap.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-
|
|
2
|
-
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-
|
|
1
|
+
import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor-DCtJYSbJ.cjs';
|
|
2
|
+
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-DCtJYSbJ.cjs';
|
|
3
3
|
import '@arkade-os/sdk/worker/expo';
|
|
4
4
|
import '@arkade-os/sdk';
|
|
5
|
-
import '../types-
|
|
5
|
+
import '../types-Db4JSdjk.cjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Define the Expo background task handler for swap polling.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor
|
|
2
|
-
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor
|
|
1
|
+
import { D as DefineSwapBackgroundTaskOptions } from '../swapsPollProcessor--BPV48QB.js';
|
|
2
|
+
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor--BPV48QB.js';
|
|
3
3
|
import '@arkade-os/sdk/worker/expo';
|
|
4
4
|
import '@arkade-os/sdk';
|
|
5
|
-
import '../types-
|
|
5
|
+
import '../types-Db4JSdjk.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Define the Expo background task handler for swap polling.
|
package/dist/expo/background.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SWAP_POLL_TASK_TYPE,
|
|
3
3
|
swapsPollProcessor
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-76V3TKDL.js";
|
|
5
5
|
import {
|
|
6
6
|
BoltzSwapProvider
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-CZTZTYZU.js";
|
|
8
8
|
import "../chunk-SJQJQO7P.js";
|
|
9
9
|
|
|
10
10
|
// src/expo/background.ts
|