@arkade-os/boltz-swap 0.3.33 → 0.3.34
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-9M7FRuq1.d.cts → arkade-swaps-BXAD1s8j.d.ts} +13 -4
- package/dist/{arkade-swaps-DNsyWeFr.d.ts → arkade-swaps-CfMets16.d.cts} +13 -4
- package/dist/{chunk-HNQDJOLM.js → chunk-5K2FS2FE.js} +1 -1
- package/dist/{chunk-SJ5SYSMK.js → chunk-TDBUZE4N.js} +269 -90
- package/dist/expo/background.cjs +270 -90
- package/dist/expo/background.d.cts +3 -3
- package/dist/expo/background.d.ts +3 -3
- package/dist/expo/background.js +3 -2
- package/dist/expo/index.cjs +269 -90
- package/dist/expo/index.d.cts +5 -5
- package/dist/expo/index.d.ts +5 -5
- package/dist/expo/index.js +2 -2
- package/dist/index.cjs +305 -94
- package/dist/index.d.cts +21 -7
- package/dist/index.d.ts +21 -7
- package/dist/index.js +40 -6
- package/dist/repositories/realm/index.d.cts +1 -1
- package/dist/repositories/realm/index.d.ts +1 -1
- package/dist/repositories/sqlite/index.d.cts +1 -1
- package/dist/repositories/sqlite/index.d.ts +1 -1
- package/dist/{swapsPollProcessor-CuITxZie.d.cts → swapsPollProcessor-BpAqG0V6.d.cts} +1 -1
- package/dist/{swapsPollProcessor-CEgeGlbP.d.ts → swapsPollProcessor-DFVOAy_-.d.ts} +1 -1
- package/dist/{types-CrKkVzBB.d.cts → types--axEWA8c.d.cts} +34 -2
- package/dist/{types-CrKkVzBB.d.ts → types--axEWA8c.d.ts} +34 -2
- package/package.json +2 -2
package/dist/expo/background.cjs
CHANGED
|
@@ -155,6 +155,8 @@ var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
|
|
|
155
155
|
return `Boltz quote ${options.quotedAmount} is below acceptable floor ${options.floor}`;
|
|
156
156
|
case "non_positive":
|
|
157
157
|
return `Boltz quote ${options.quotedAmount} is not positive`;
|
|
158
|
+
case "non_safe_integer":
|
|
159
|
+
return `Boltz quote ${options.quotedAmount} is not a safe positive satoshi integer`;
|
|
158
160
|
case "no_baseline":
|
|
159
161
|
return "Cannot accept quote: no minAcceptableAmount and no stored pending swap";
|
|
160
162
|
}
|
|
@@ -208,6 +210,7 @@ var QuoteRejectedError = class _QuoteRejectedError extends SwapError {
|
|
|
208
210
|
message
|
|
209
211
|
});
|
|
210
212
|
case "non_positive":
|
|
213
|
+
case "non_safe_integer":
|
|
211
214
|
if (quotedAmount === null) return null;
|
|
212
215
|
return new _QuoteRejectedError({
|
|
213
216
|
reason,
|
|
@@ -223,6 +226,7 @@ var QUOTE_REJECTION_TRANSPORT_PREFIX = "QUOTE_REJECTED::";
|
|
|
223
226
|
var QUOTE_REJECTION_REASONS = /* @__PURE__ */ new Set([
|
|
224
227
|
"below_floor",
|
|
225
228
|
"non_positive",
|
|
229
|
+
"non_safe_integer",
|
|
226
230
|
"no_baseline"
|
|
227
231
|
]);
|
|
228
232
|
var BoltzRefundError = class extends Error {
|
|
@@ -1383,6 +1387,13 @@ var SwapManager = class _SwapManager {
|
|
|
1383
1387
|
* enough that a real "swap unknown to this provider" surfaces quickly.
|
|
1384
1388
|
*/
|
|
1385
1389
|
static NOT_FOUND_THRESHOLD = 10;
|
|
1390
|
+
/**
|
|
1391
|
+
* Delay between re-attempts of a chain refund that left VTXOs deferred
|
|
1392
|
+
* (e.g. pre-CLTV recoverable VTXO, or Boltz 3-of-3 rejected before CLTV
|
|
1393
|
+
* has elapsed). Boltz won't send another status update once the swap
|
|
1394
|
+
* is `swap.expired`, so the manager owns the local retry cadence.
|
|
1395
|
+
*/
|
|
1396
|
+
static REFUND_RETRY_DELAY_MS = 6e4;
|
|
1386
1397
|
swapProvider;
|
|
1387
1398
|
config;
|
|
1388
1399
|
// Event listeners storage (supports multiple listeners per event)
|
|
@@ -1401,6 +1412,11 @@ var SwapManager = class _SwapManager {
|
|
|
1401
1412
|
reconnectTimer = null;
|
|
1402
1413
|
initialPollTimer = null;
|
|
1403
1414
|
pollRetryTimers = /* @__PURE__ */ new Map();
|
|
1415
|
+
// Per-swap retry timers for chain refunds that left work undone
|
|
1416
|
+
// (refundArk returned `skipped > 0`). The swap is held in
|
|
1417
|
+
// `monitoredSwaps` past its terminal Boltz status until the local
|
|
1418
|
+
// refund completes or the manager stops.
|
|
1419
|
+
refundRetryTimers = /* @__PURE__ */ new Map();
|
|
1404
1420
|
// Per-swap counter of consecutive `SwapNotFoundError` responses from
|
|
1405
1421
|
// `getSwapStatus`. Reset on any successful poll. Once a swap reaches
|
|
1406
1422
|
// `NOT_FOUND_THRESHOLD` consecutive 404s the safety net trips and the
|
|
@@ -1597,6 +1613,10 @@ var SwapManager = class _SwapManager {
|
|
|
1597
1613
|
clearTimeout(timer);
|
|
1598
1614
|
}
|
|
1599
1615
|
this.pollRetryTimers.clear();
|
|
1616
|
+
for (const timer of this.refundRetryTimers.values()) {
|
|
1617
|
+
clearTimeout(timer);
|
|
1618
|
+
}
|
|
1619
|
+
this.refundRetryTimers.clear();
|
|
1600
1620
|
this.notFoundCounts.clear();
|
|
1601
1621
|
}
|
|
1602
1622
|
/**
|
|
@@ -1653,6 +1673,11 @@ var SwapManager = class _SwapManager {
|
|
|
1653
1673
|
clearTimeout(retryTimer);
|
|
1654
1674
|
this.pollRetryTimers.delete(swapId);
|
|
1655
1675
|
}
|
|
1676
|
+
const refundRetryTimer = this.refundRetryTimers.get(swapId);
|
|
1677
|
+
if (refundRetryTimer) {
|
|
1678
|
+
clearTimeout(refundRetryTimer);
|
|
1679
|
+
this.refundRetryTimers.delete(swapId);
|
|
1680
|
+
}
|
|
1656
1681
|
this.notFoundCounts.delete(swapId);
|
|
1657
1682
|
logger.log(`Removed swap ${swapId} from monitoring`);
|
|
1658
1683
|
}
|
|
@@ -1924,15 +1949,57 @@ var SwapManager = class _SwapManager {
|
|
|
1924
1949
|
await this.executeAutonomousAction(swap);
|
|
1925
1950
|
}
|
|
1926
1951
|
if (this.isFinalStatus(swap)) {
|
|
1927
|
-
this.
|
|
1928
|
-
|
|
1929
|
-
const retryTimer = this.pollRetryTimers.get(swap.id);
|
|
1930
|
-
if (retryTimer) {
|
|
1931
|
-
clearTimeout(retryTimer);
|
|
1932
|
-
this.pollRetryTimers.delete(swap.id);
|
|
1952
|
+
if (this.refundRetryTimers.has(swap.id)) {
|
|
1953
|
+
return;
|
|
1933
1954
|
}
|
|
1934
|
-
this.
|
|
1955
|
+
this.finalizeMonitoredSwap(swap);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Drop a swap from monitoring and emit the terminal completion event.
|
|
1960
|
+
* Shared between the on-status-update finalization path and the
|
|
1961
|
+
* refund-retry finalization path (used when a previously-deferred
|
|
1962
|
+
* chain refund has finished its remaining work).
|
|
1963
|
+
*/
|
|
1964
|
+
finalizeMonitoredSwap(swap) {
|
|
1965
|
+
if (!this.monitoredSwaps.has(swap.id)) return;
|
|
1966
|
+
this.monitoredSwaps.delete(swap.id);
|
|
1967
|
+
this.swapSubscriptions.delete(swap.id);
|
|
1968
|
+
const retryTimer = this.pollRetryTimers.get(swap.id);
|
|
1969
|
+
if (retryTimer) {
|
|
1970
|
+
clearTimeout(retryTimer);
|
|
1971
|
+
this.pollRetryTimers.delete(swap.id);
|
|
1972
|
+
}
|
|
1973
|
+
const refundRetry = this.refundRetryTimers.get(swap.id);
|
|
1974
|
+
if (refundRetry) {
|
|
1975
|
+
clearTimeout(refundRetry);
|
|
1976
|
+
this.refundRetryTimers.delete(swap.id);
|
|
1935
1977
|
}
|
|
1978
|
+
this.swapCompletedListeners.forEach((listener) => listener(swap));
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Schedule another `executeAutonomousAction` run for a chain swap whose
|
|
1982
|
+
* refund left VTXOs deferred. After the retry completes, if no further
|
|
1983
|
+
* deferral was reported, finalize monitoring cleanup.
|
|
1984
|
+
*/
|
|
1985
|
+
scheduleRefundRetry(swap, delayMs) {
|
|
1986
|
+
const existing = this.refundRetryTimers.get(swap.id);
|
|
1987
|
+
if (existing) clearTimeout(existing);
|
|
1988
|
+
this.refundRetryTimers.set(
|
|
1989
|
+
swap.id,
|
|
1990
|
+
setTimeout(async () => {
|
|
1991
|
+
this.refundRetryTimers.delete(swap.id);
|
|
1992
|
+
if (!this.isRunning) return;
|
|
1993
|
+
if (!this.monitoredSwaps.has(swap.id)) return;
|
|
1994
|
+
try {
|
|
1995
|
+
await this.executeAutonomousAction(swap);
|
|
1996
|
+
} finally {
|
|
1997
|
+
if (!this.refundRetryTimers.has(swap.id) && this.isFinalStatus(swap)) {
|
|
1998
|
+
this.finalizeMonitoredSwap(swap);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}, delayMs)
|
|
2002
|
+
);
|
|
1936
2003
|
}
|
|
1937
2004
|
/**
|
|
1938
2005
|
* Execute autonomous action based on swap status
|
|
@@ -1987,10 +2054,27 @@ var SwapManager = class _SwapManager {
|
|
|
1987
2054
|
} else if (isChainRefundableStatus(swap.status)) {
|
|
1988
2055
|
if (swap.request.from === "ARK") {
|
|
1989
2056
|
logger.log(`Auto-refunding ARK chain swap ${swap.id}`);
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
(
|
|
1993
|
-
|
|
2057
|
+
try {
|
|
2058
|
+
const outcome = await this.executeRefundArkAction(swap);
|
|
2059
|
+
if (outcome && outcome.skipped > 0) {
|
|
2060
|
+
logger.log(
|
|
2061
|
+
`Chain swap ${swap.id}: ${outcome.skipped} VTXO(s) deferred \u2014 scheduling refund retry`
|
|
2062
|
+
);
|
|
2063
|
+
this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2064
|
+
}
|
|
2065
|
+
this.actionExecutedListeners.forEach(
|
|
2066
|
+
(listener) => listener(swap, "refundArk")
|
|
2067
|
+
);
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
logger.error(
|
|
2070
|
+
`Auto-refunding ARK chain swap ${swap.id} failed; scheduling retry`,
|
|
2071
|
+
error
|
|
2072
|
+
);
|
|
2073
|
+
this.swapFailedListeners.forEach(
|
|
2074
|
+
(listener) => listener(swap, error)
|
|
2075
|
+
);
|
|
2076
|
+
this.scheduleRefundRetry(swap, _SwapManager.REFUND_RETRY_DELAY_MS);
|
|
2077
|
+
}
|
|
1994
2078
|
}
|
|
1995
2079
|
if (swap.request.from === "BTC") {
|
|
1996
2080
|
logger.warn(
|
|
@@ -2071,7 +2155,7 @@ var SwapManager = class _SwapManager {
|
|
|
2071
2155
|
logger.error("refundArk callback not set");
|
|
2072
2156
|
return;
|
|
2073
2157
|
}
|
|
2074
|
-
|
|
2158
|
+
return this.refundArkCallback(swap);
|
|
2075
2159
|
}
|
|
2076
2160
|
/**
|
|
2077
2161
|
* Execute sign server claim action for chain swap.
|
|
@@ -2171,9 +2255,7 @@ var SwapManager = class _SwapManager {
|
|
|
2171
2255
|
*/
|
|
2172
2256
|
async pollAllSwaps() {
|
|
2173
2257
|
if (this.monitoredSwaps.size === 0) return;
|
|
2174
|
-
const pollPromises = Array.from(this.monitoredSwaps.values()).map(
|
|
2175
|
-
(swap) => this.pollSingleSwap(swap)
|
|
2176
|
-
);
|
|
2258
|
+
const pollPromises = Array.from(this.monitoredSwaps.values()).filter((swap) => !this.refundRetryTimers.has(swap.id)).map((swap) => this.pollSingleSwap(swap));
|
|
2177
2259
|
await Promise.allSettled(pollPromises);
|
|
2178
2260
|
}
|
|
2179
2261
|
async pollSingleSwap(swap) {
|
|
@@ -2226,6 +2308,7 @@ var SwapManager = class _SwapManager {
|
|
|
2226
2308
|
* Boltz endpoint).
|
|
2227
2309
|
*/
|
|
2228
2310
|
async handleSwapNotFound(swap) {
|
|
2311
|
+
if (this.refundRetryTimers.has(swap.id)) return;
|
|
2229
2312
|
const count = (this.notFoundCounts.get(swap.id) ?? 0) + 1;
|
|
2230
2313
|
this.notFoundCounts.set(swap.id, count);
|
|
2231
2314
|
logger.warn(
|
|
@@ -2246,6 +2329,7 @@ var SwapManager = class _SwapManager {
|
|
|
2246
2329
|
* 404s without recovering anything.
|
|
2247
2330
|
*/
|
|
2248
2331
|
async markSwapAsUnknownToProvider(swap) {
|
|
2332
|
+
if (this.refundRetryTimers.has(swap.id)) return;
|
|
2249
2333
|
if (!this.monitoredSwaps.has(swap.id)) {
|
|
2250
2334
|
this.notFoundCounts.delete(swap.id);
|
|
2251
2335
|
return;
|
|
@@ -2258,6 +2342,11 @@ var SwapManager = class _SwapManager {
|
|
|
2258
2342
|
clearTimeout(retryTimer);
|
|
2259
2343
|
this.pollRetryTimers.delete(swap.id);
|
|
2260
2344
|
}
|
|
2345
|
+
const refundRetryTimer = this.refundRetryTimers.get(swap.id);
|
|
2346
|
+
if (refundRetryTimer) {
|
|
2347
|
+
clearTimeout(refundRetryTimer);
|
|
2348
|
+
this.refundRetryTimers.delete(swap.id);
|
|
2349
|
+
}
|
|
2261
2350
|
this.notFoundCounts.delete(swap.id);
|
|
2262
2351
|
this.swapUpdateListeners.forEach((listener) => listener(swap, oldStatus));
|
|
2263
2352
|
const subscribers = this.swapSubscriptions.get(swap.id);
|
|
@@ -3008,7 +3097,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3008
3097
|
await this.claimBtc(swap);
|
|
3009
3098
|
},
|
|
3010
3099
|
refundArk: async (swap) => {
|
|
3011
|
-
|
|
3100
|
+
return this.refundArk(swap);
|
|
3012
3101
|
},
|
|
3013
3102
|
signServerClaim: async (swap) => {
|
|
3014
3103
|
await this.signCooperativeClaimForServer(swap);
|
|
@@ -3221,51 +3310,67 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3221
3310
|
throw new Error(
|
|
3222
3311
|
`Swap ${pendingSwap.id}: VHTLC address mismatch. Expected ${lockupAddress}, got ${vhtlcAddress}`
|
|
3223
3312
|
);
|
|
3224
|
-
let
|
|
3313
|
+
let unspentVtxos = [];
|
|
3314
|
+
let rawVtxos = [];
|
|
3225
3315
|
for (let attempt = 1; attempt <= CLAIM_VTXO_RETRY_ATTEMPTS; attempt++) {
|
|
3226
|
-
const
|
|
3316
|
+
const result = await this.indexerProvider.getVtxos({
|
|
3227
3317
|
scripts: [import_base9.hex.encode(vhtlcScript.pkScript)]
|
|
3228
3318
|
});
|
|
3229
|
-
|
|
3230
|
-
|
|
3319
|
+
rawVtxos = result.vtxos;
|
|
3320
|
+
unspentVtxos = result.vtxos.filter((vtxo) => !vtxo.isSpent);
|
|
3321
|
+
if (unspentVtxos.length > 0) {
|
|
3231
3322
|
break;
|
|
3232
3323
|
}
|
|
3233
3324
|
if (attempt < CLAIM_VTXO_RETRY_ATTEMPTS) {
|
|
3234
3325
|
await new Promise((resolve) => setTimeout(resolve, CLAIM_VTXO_RETRY_DELAY_MS));
|
|
3235
3326
|
}
|
|
3236
3327
|
}
|
|
3237
|
-
if (
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3328
|
+
if (unspentVtxos.length === 0) {
|
|
3329
|
+
if (rawVtxos.length === 0) {
|
|
3330
|
+
throw new Error(`Swap ${pendingSwap.id}: no spendable virtual coins found`);
|
|
3331
|
+
}
|
|
3241
3332
|
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
3242
3333
|
}
|
|
3243
|
-
const input = {
|
|
3244
|
-
...vtxo,
|
|
3245
|
-
tapLeafScript: vhtlcScript.claim(),
|
|
3246
|
-
tapTree: vhtlcScript.encode()
|
|
3247
|
-
};
|
|
3248
|
-
const output = {
|
|
3249
|
-
amount: BigInt(vtxo.value),
|
|
3250
|
-
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
3251
|
-
};
|
|
3252
3334
|
const vhtlcIdentity = claimVHTLCIdentity(this.wallet.identity, preimage);
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
vhtlcScript
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3335
|
+
const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
|
|
3336
|
+
const claimErrors = [];
|
|
3337
|
+
let usedOffchainClaim = false;
|
|
3338
|
+
for (const vtxo of unspentVtxos) {
|
|
3339
|
+
const input = {
|
|
3340
|
+
...vtxo,
|
|
3341
|
+
tapLeafScript: vhtlcScript.claim(),
|
|
3342
|
+
tapTree: vhtlcScript.encode()
|
|
3343
|
+
};
|
|
3344
|
+
const output = {
|
|
3345
|
+
amount: BigInt(vtxo.value),
|
|
3346
|
+
script: outputScript
|
|
3347
|
+
};
|
|
3348
|
+
try {
|
|
3349
|
+
if ((0, import_sdk8.isRecoverable)(vtxo)) {
|
|
3350
|
+
await this.joinBatch(vhtlcIdentity, input, output, arkInfo);
|
|
3351
|
+
} else {
|
|
3352
|
+
await claimVHTLCwithOffchainTx(
|
|
3353
|
+
vhtlcIdentity,
|
|
3354
|
+
vhtlcScript,
|
|
3355
|
+
serverXOnly,
|
|
3356
|
+
input,
|
|
3357
|
+
output,
|
|
3358
|
+
arkInfo,
|
|
3359
|
+
this.arkProvider
|
|
3360
|
+
);
|
|
3361
|
+
usedOffchainClaim = true;
|
|
3362
|
+
}
|
|
3363
|
+
} catch (error) {
|
|
3364
|
+
claimErrors.push({ vtxo, error });
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
if (claimErrors.length > 0) {
|
|
3368
|
+
const details = claimErrors.map(({ vtxo, error }) => `${vtxo.txid}:${vtxo.vout} (${error.message})`).join("; ");
|
|
3369
|
+
throw new Error(
|
|
3370
|
+
`Swap ${pendingSwap.id}: failed to claim ${claimErrors.length}/${unspentVtxos.length} VTXOs: ${details}`
|
|
3266
3371
|
);
|
|
3267
|
-
finalStatus = (await this.getSwapStatus(pendingSwap.id)).status;
|
|
3268
3372
|
}
|
|
3373
|
+
const finalStatus = usedOffchainClaim ? (await this.getSwapStatus(pendingSwap.id)).status : "transaction.claimed";
|
|
3269
3374
|
await updateReverseSwapStatus(
|
|
3270
3375
|
pendingSwap,
|
|
3271
3376
|
finalStatus,
|
|
@@ -3640,6 +3745,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3640
3745
|
if (boltzCallCount > 0) {
|
|
3641
3746
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3642
3747
|
}
|
|
3748
|
+
boltzCallCount++;
|
|
3643
3749
|
await refundVHTLCwithOffchainTx(
|
|
3644
3750
|
pendingSwap.id,
|
|
3645
3751
|
this.wallet.identity,
|
|
@@ -3652,7 +3758,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3652
3758
|
arkInfo,
|
|
3653
3759
|
this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3654
3760
|
);
|
|
3655
|
-
boltzCallCount++;
|
|
3656
3761
|
sweptCount++;
|
|
3657
3762
|
} catch (error) {
|
|
3658
3763
|
if (!(error instanceof BoltzRefundError)) {
|
|
@@ -4150,8 +4255,17 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4150
4255
|
await this.swapProvider.postBtcTransaction(claimTx.hex);
|
|
4151
4256
|
}
|
|
4152
4257
|
/**
|
|
4153
|
-
* When an ARK to BTC swap fails, refund
|
|
4258
|
+
* When an ARK to BTC swap fails, refund every unspent VTXO at the chain
|
|
4259
|
+
* swap's ARK lockup address.
|
|
4260
|
+
*
|
|
4261
|
+
* Path selection per VTXO:
|
|
4262
|
+
* - CLTV has elapsed → `refundWithoutReceiver` via `joinBatch` (no Boltz).
|
|
4263
|
+
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign swept-batch refund).
|
|
4264
|
+
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz.
|
|
4265
|
+
*
|
|
4154
4266
|
* @param pendingSwap - The pending chain swap to refund.
|
|
4267
|
+
* @returns Counts of VTXOs swept vs. deferred. A `swept: 0` outcome means
|
|
4268
|
+
* the call was a no-op — callers should retry after CLTV.
|
|
4155
4269
|
*/
|
|
4156
4270
|
async refundArk(pendingSwap) {
|
|
4157
4271
|
if (!pendingSwap.response.lockupDetails.serverPublicKey)
|
|
@@ -4175,21 +4289,6 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4175
4289
|
"boltz",
|
|
4176
4290
|
pendingSwap.id
|
|
4177
4291
|
);
|
|
4178
|
-
const vhtlcPkScript = import_sdk8.ArkAddress.decode(
|
|
4179
|
-
pendingSwap.response.lockupDetails.lockupAddress
|
|
4180
|
-
).pkScript;
|
|
4181
|
-
const { vtxos } = await this.indexerProvider.getVtxos({
|
|
4182
|
-
scripts: [import_base9.hex.encode(vhtlcPkScript)]
|
|
4183
|
-
});
|
|
4184
|
-
if (vtxos.length === 0) {
|
|
4185
|
-
throw new Error(
|
|
4186
|
-
`Swap ${pendingSwap.id}: VHTLC not found for address ${pendingSwap.response.lockupDetails.lockupAddress}`
|
|
4187
|
-
);
|
|
4188
|
-
}
|
|
4189
|
-
const vtxo = vtxos[0];
|
|
4190
|
-
if (vtxo.isSpent) {
|
|
4191
|
-
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
4192
|
-
}
|
|
4193
4292
|
const { vhtlcAddress, vhtlcScript } = this.createVHTLCScript({
|
|
4194
4293
|
network: arkInfo.network,
|
|
4195
4294
|
preimageHash: import_base9.hex.decode(pendingSwap.request.preimageHash),
|
|
@@ -4205,37 +4304,105 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4205
4304
|
message: "Unable to claim: invalid VHTLC address"
|
|
4206
4305
|
});
|
|
4207
4306
|
}
|
|
4208
|
-
const
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
const output = {
|
|
4215
|
-
amount: BigInt(vtxo.value),
|
|
4216
|
-
script: import_sdk8.ArkAddress.decode(address).pkScript
|
|
4217
|
-
};
|
|
4218
|
-
if (isRecoverableVtxo) {
|
|
4219
|
-
await this.joinBatch(this.wallet.identity, input, output, arkInfo);
|
|
4220
|
-
} else {
|
|
4221
|
-
await refundVHTLCwithOffchainTx(
|
|
4222
|
-
pendingSwap.id,
|
|
4223
|
-
this.wallet.identity,
|
|
4224
|
-
this.arkProvider,
|
|
4225
|
-
boltzXOnlyPublicKey,
|
|
4226
|
-
ourXOnlyPublicKey,
|
|
4227
|
-
serverXOnlyPublicKey,
|
|
4228
|
-
input,
|
|
4229
|
-
output,
|
|
4230
|
-
arkInfo,
|
|
4231
|
-
this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4307
|
+
const { vtxos } = await this.indexerProvider.getVtxos({
|
|
4308
|
+
scripts: [import_base9.hex.encode(vhtlcScript.pkScript)]
|
|
4309
|
+
});
|
|
4310
|
+
if (vtxos.length === 0) {
|
|
4311
|
+
throw new Error(
|
|
4312
|
+
`Swap ${pendingSwap.id}: VHTLC not found for address ${pendingSwap.response.lockupDetails.lockupAddress}`
|
|
4232
4313
|
);
|
|
4233
4314
|
}
|
|
4315
|
+
const unspentVtxos = vtxos.filter((vtxo) => !vtxo.isSpent);
|
|
4316
|
+
if (unspentVtxos.length === 0) {
|
|
4317
|
+
throw new Error(`Swap ${pendingSwap.id}: VHTLC is already spent`);
|
|
4318
|
+
}
|
|
4319
|
+
const outputScript = import_sdk8.ArkAddress.decode(address).pkScript;
|
|
4320
|
+
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
4321
|
+
const refundLocktime = pendingSwap.response.lockupDetails.timeouts.refund;
|
|
4322
|
+
let boltzCallCount = 0;
|
|
4323
|
+
let sweptCount = 0;
|
|
4324
|
+
let skippedCount = 0;
|
|
4325
|
+
for (const vtxo of unspentVtxos) {
|
|
4326
|
+
const isRecoverableVtxo = (0, import_sdk8.isRecoverable)(vtxo);
|
|
4327
|
+
const output = {
|
|
4328
|
+
amount: BigInt(vtxo.value),
|
|
4329
|
+
script: outputScript
|
|
4330
|
+
};
|
|
4331
|
+
if (isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
4332
|
+
const input2 = {
|
|
4333
|
+
...vtxo,
|
|
4334
|
+
tapLeafScript: refundWithoutReceiverLeaf,
|
|
4335
|
+
tapTree: vhtlcScript.encode()
|
|
4336
|
+
};
|
|
4337
|
+
await this.joinBatch(
|
|
4338
|
+
this.wallet.identity,
|
|
4339
|
+
input2,
|
|
4340
|
+
output,
|
|
4341
|
+
arkInfo,
|
|
4342
|
+
isRecoverableVtxo
|
|
4343
|
+
);
|
|
4344
|
+
sweptCount++;
|
|
4345
|
+
continue;
|
|
4346
|
+
}
|
|
4347
|
+
if (isRecoverableVtxo) {
|
|
4348
|
+
logger.error(
|
|
4349
|
+
`Swap ${pendingSwap.id}: 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.`
|
|
4350
|
+
);
|
|
4351
|
+
skippedCount++;
|
|
4352
|
+
continue;
|
|
4353
|
+
}
|
|
4354
|
+
const input = {
|
|
4355
|
+
...vtxo,
|
|
4356
|
+
tapLeafScript: vhtlcScript.refund(),
|
|
4357
|
+
tapTree: vhtlcScript.encode()
|
|
4358
|
+
};
|
|
4359
|
+
try {
|
|
4360
|
+
if (boltzCallCount > 0) {
|
|
4361
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
4362
|
+
}
|
|
4363
|
+
boltzCallCount++;
|
|
4364
|
+
await refundVHTLCwithOffchainTx(
|
|
4365
|
+
pendingSwap.id,
|
|
4366
|
+
this.wallet.identity,
|
|
4367
|
+
this.arkProvider,
|
|
4368
|
+
boltzXOnlyPublicKey,
|
|
4369
|
+
ourXOnlyPublicKey,
|
|
4370
|
+
serverXOnlyPublicKey,
|
|
4371
|
+
input,
|
|
4372
|
+
output,
|
|
4373
|
+
arkInfo,
|
|
4374
|
+
this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4375
|
+
);
|
|
4376
|
+
sweptCount++;
|
|
4377
|
+
} catch (error) {
|
|
4378
|
+
if (!(error instanceof BoltzRefundError)) {
|
|
4379
|
+
throw error;
|
|
4380
|
+
}
|
|
4381
|
+
if (!isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
4382
|
+
logger.error(
|
|
4383
|
+
`Swap ${pendingSwap.id}: 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.`
|
|
4384
|
+
);
|
|
4385
|
+
skippedCount++;
|
|
4386
|
+
continue;
|
|
4387
|
+
}
|
|
4388
|
+
logger.warn(
|
|
4389
|
+
`Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver via joinBatch`
|
|
4390
|
+
);
|
|
4391
|
+
const fallbackInput = {
|
|
4392
|
+
...vtxo,
|
|
4393
|
+
tapLeafScript: refundWithoutReceiverLeaf,
|
|
4394
|
+
tapTree: vhtlcScript.encode()
|
|
4395
|
+
};
|
|
4396
|
+
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
4397
|
+
sweptCount++;
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4234
4400
|
const finalStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4235
4401
|
await this.savePendingChainSwap({
|
|
4236
4402
|
...pendingSwap,
|
|
4237
4403
|
status: finalStatus.status
|
|
4238
4404
|
});
|
|
4405
|
+
return { swept: sweptCount, skipped: skippedCount };
|
|
4239
4406
|
}
|
|
4240
4407
|
// =========================================================================
|
|
4241
4408
|
// Chain swaps: BTC -> ARK
|
|
@@ -4656,7 +4823,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4656
4823
|
this.validateQuoteOptions(options);
|
|
4657
4824
|
const floor = await this.resolveQuoteFloor(swapId, options);
|
|
4658
4825
|
const slippageBps = options?.maxSlippageBps ?? 0;
|
|
4659
|
-
|
|
4826
|
+
const effectiveFloor = Math.floor(floor - floor * slippageBps / 1e4);
|
|
4827
|
+
if (effectiveFloor < 1) {
|
|
4828
|
+
throw new TypeError(
|
|
4829
|
+
`Invalid quote configuration: maxSlippageBps=${slippageBps} reduces floor ${floor} below 1 sat`
|
|
4830
|
+
);
|
|
4831
|
+
}
|
|
4832
|
+
return effectiveFloor;
|
|
4660
4833
|
}
|
|
4661
4834
|
async resolveQuoteFloor(swapId, options) {
|
|
4662
4835
|
if (options?.minAcceptableAmount !== void 0) {
|
|
@@ -4692,7 +4865,13 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4692
4865
|
}
|
|
4693
4866
|
}
|
|
4694
4867
|
validateQuote(amount, effectiveFloor) {
|
|
4695
|
-
if (!(amount
|
|
4868
|
+
if (!Number.isSafeInteger(amount)) {
|
|
4869
|
+
throw new QuoteRejectedError({
|
|
4870
|
+
reason: "non_safe_integer",
|
|
4871
|
+
quotedAmount: amount
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
if (amount <= 0) {
|
|
4696
4875
|
throw new QuoteRejectedError({
|
|
4697
4876
|
reason: "non_positive",
|
|
4698
4877
|
quotedAmount: amount
|
|
@@ -5087,6 +5266,7 @@ function createBackgroundWalletShim(args) {
|
|
|
5087
5266
|
getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
|
|
5088
5267
|
getTransactionHistory: async () => notImplemented("getTransactionHistory"),
|
|
5089
5268
|
getContractManager: async () => notImplemented("getContractManager"),
|
|
5269
|
+
getDelegateManager: async () => notImplemented("getDelegateManager"),
|
|
5090
5270
|
getDelegatorManager: async () => notImplemented("getDelegatorManager"),
|
|
5091
5271
|
sendBitcoin: async () => notImplemented("sendBitcoin"),
|
|
5092
5272
|
send: async () => notImplemented("send"),
|
|
@@ -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-BpAqG0V6.cjs';
|
|
2
|
+
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-BpAqG0V6.cjs';
|
|
3
3
|
import '@arkade-os/sdk/worker/expo';
|
|
4
4
|
import '@arkade-os/sdk';
|
|
5
|
-
import '../types
|
|
5
|
+
import '../types--axEWA8c.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-DFVOAy_-.js';
|
|
2
|
+
export { P as PersistedSwapBackgroundConfig, S as SWAP_POLL_TASK_TYPE, a as SwapTaskDependencies, s as swapsPollProcessor } from '../swapsPollProcessor-DFVOAy_-.js';
|
|
3
3
|
import '@arkade-os/sdk/worker/expo';
|
|
4
4
|
import '@arkade-os/sdk';
|
|
5
|
-
import '../types
|
|
5
|
+
import '../types--axEWA8c.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-5K2FS2FE.js";
|
|
5
5
|
import {
|
|
6
6
|
BoltzSwapProvider
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-TDBUZE4N.js";
|
|
8
8
|
import "../chunk-SJQJQO7P.js";
|
|
9
9
|
|
|
10
10
|
// src/expo/background.ts
|
|
@@ -28,6 +28,7 @@ function createBackgroundWalletShim(args) {
|
|
|
28
28
|
getBoardingUtxos: async () => notImplemented("getBoardingUtxos"),
|
|
29
29
|
getTransactionHistory: async () => notImplemented("getTransactionHistory"),
|
|
30
30
|
getContractManager: async () => notImplemented("getContractManager"),
|
|
31
|
+
getDelegateManager: async () => notImplemented("getDelegateManager"),
|
|
31
32
|
getDelegatorManager: async () => notImplemented("getDelegatorManager"),
|
|
32
33
|
sendBitcoin: async () => notImplemented("sendBitcoin"),
|
|
33
34
|
send: async () => notImplemented("send"),
|