@arkade-os/boltz-swap 0.3.43 → 0.3.45
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-LvsGHtre.d.ts → arkade-swaps-C8CmzknM.d.cts} +28 -2
- package/dist/{arkade-swaps-C3sUFr5f.d.cts → arkade-swaps-gXHRbR09.d.ts} +28 -2
- package/dist/{chunk-UXYHW7KV.js → chunk-5GBBRBFS.js} +199 -161
- package/dist/{chunk-CWY37W4B.js → chunk-WPAI2ECT.js} +1 -1
- package/dist/expo/background.cjs +199 -161
- 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 +199 -161
- package/dist/expo/index.d.cts +4 -4
- package/dist/expo/index.d.ts +4 -4
- package/dist/expo/index.js +2 -2
- package/dist/index.cjs +200 -162
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- 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-CGMXUKPe.d.cts → swapsPollProcessor-B98F6MQF.d.cts} +1 -1
- package/dist/{swapsPollProcessor-CuDM6sxV.d.ts → swapsPollProcessor-Ov-wp17v.d.ts} +1 -1
- package/dist/{types-8NrCdOpS.d.cts → types-CS62-oEy.d.cts} +19 -2
- package/dist/{types-8NrCdOpS.d.ts → types-CS62-oEy.d.ts} +19 -2
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
|
|
2
|
-
import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-
|
|
2
|
+
import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-CS62-oEy.cjs';
|
|
3
3
|
import { TransactionOutput } from '@scure/btc-signer/psbt.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -324,7 +324,10 @@ declare class ArkadeSwaps {
|
|
|
324
324
|
* swap's ARK lockup address.
|
|
325
325
|
*
|
|
326
326
|
* Path selection per VTXO:
|
|
327
|
-
* - CLTV
|
|
327
|
+
* - CLTV elapsed, live VTXO → `refundWithoutReceiver` offchain (sender +
|
|
328
|
+
* server, no Boltz, no batch round).
|
|
329
|
+
* - CLTV elapsed, swept VTXO → `refundWithoutReceiver` via `joinBatch`
|
|
330
|
+
* (a swept VTXO is no longer a live leaf).
|
|
328
331
|
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign swept-batch refund).
|
|
329
332
|
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz.
|
|
330
333
|
*
|
|
@@ -463,6 +466,29 @@ declare class ArkadeSwaps {
|
|
|
463
466
|
* @returns The commitment transaction ID.
|
|
464
467
|
*/
|
|
465
468
|
joinBatch(identity: Identity, input: ArkTxInput, output: TransactionOutput, arkInfo: ArkInfo, isRecoverable?: boolean): Promise<string>;
|
|
469
|
+
/**
|
|
470
|
+
* Settle a `refundWithoutReceiver` (sender + server, no Boltz) refund for a
|
|
471
|
+
* single VTXO whose CLTV refund locktime has elapsed.
|
|
472
|
+
*
|
|
473
|
+
* A live VTXO settles the leaf with an offchain Ark tx — no batch round. A
|
|
474
|
+
* swept (recoverable) VTXO is no longer a live leaf, so it can only be
|
|
475
|
+
* reclaimed by re-registering it into a batch.
|
|
476
|
+
*/
|
|
477
|
+
private settleRefundWithoutReceiver;
|
|
478
|
+
/**
|
|
479
|
+
* Refund every VTXO at a swap's VHTLC address back to the wallet, shared by
|
|
480
|
+
* {@link ArkadeSwaps.refundVHTLC} (submarine) and {@link ArkadeSwaps.refundArk}
|
|
481
|
+
* (chain). Path selection per VTXO:
|
|
482
|
+
* - CLTV elapsed → `refundWithoutReceiver` (offchain for a live VTXO, via a
|
|
483
|
+
* batch round for a swept one — see {@link settleRefundWithoutReceiver}).
|
|
484
|
+
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign a swept-batch refund).
|
|
485
|
+
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz, falling
|
|
486
|
+
* back to `refundWithoutReceiver` offchain if Boltz rejects after the
|
|
487
|
+
* locktime has since elapsed.
|
|
488
|
+
*
|
|
489
|
+
* @returns Counts of VTXOs swept vs. deferred.
|
|
490
|
+
*/
|
|
491
|
+
private refundVtxos;
|
|
466
492
|
/**
|
|
467
493
|
* Creates a VHTLC script for the swap.
|
|
468
494
|
* Works for submarine, reverse, and chain swaps.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IWallet, ArkProvider, IndexerProvider, ArkInfo, Identity, ArkTxInput, VHTLC } from '@arkade-os/sdk';
|
|
2
|
-
import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-
|
|
2
|
+
import { r as BoltzSwapProvider, y as SwapManager, m as SwapRepository, q as ArkadeSwapsCreateConfig, A as ArkadeSwapsConfig, n as SwapManagerClient, C as CreateLightningInvoiceRequest, e as CreateLightningInvoiceResponse, b as BoltzReverseSwap, S as SendLightningPaymentRequest, o as SendLightningPaymentResponse, O as OptimisticSendLightningPaymentResponse, c as BoltzSubmarineSwap, f as SubmarineRefundOutcome, g as SubmarineRecoveryInfo, h as SubmarineRecoveryResult, j as ArkToBtcResponse, a as BoltzChainSwap, l as ChainArkRefundOutcome, k as BtcToArkResponse, d as Chain, F as FeesResponse, i as ChainFeesResponse, L as LimitsResponse, G as GetSwapStatusResponse, B as BoltzSwap } from './types-CS62-oEy.js';
|
|
3
3
|
import { TransactionOutput } from '@scure/btc-signer/psbt.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -324,7 +324,10 @@ declare class ArkadeSwaps {
|
|
|
324
324
|
* swap's ARK lockup address.
|
|
325
325
|
*
|
|
326
326
|
* Path selection per VTXO:
|
|
327
|
-
* - CLTV
|
|
327
|
+
* - CLTV elapsed, live VTXO → `refundWithoutReceiver` offchain (sender +
|
|
328
|
+
* server, no Boltz, no batch round).
|
|
329
|
+
* - CLTV elapsed, swept VTXO → `refundWithoutReceiver` via `joinBatch`
|
|
330
|
+
* (a swept VTXO is no longer a live leaf).
|
|
328
331
|
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign swept-batch refund).
|
|
329
332
|
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz.
|
|
330
333
|
*
|
|
@@ -463,6 +466,29 @@ declare class ArkadeSwaps {
|
|
|
463
466
|
* @returns The commitment transaction ID.
|
|
464
467
|
*/
|
|
465
468
|
joinBatch(identity: Identity, input: ArkTxInput, output: TransactionOutput, arkInfo: ArkInfo, isRecoverable?: boolean): Promise<string>;
|
|
469
|
+
/**
|
|
470
|
+
* Settle a `refundWithoutReceiver` (sender + server, no Boltz) refund for a
|
|
471
|
+
* single VTXO whose CLTV refund locktime has elapsed.
|
|
472
|
+
*
|
|
473
|
+
* A live VTXO settles the leaf with an offchain Ark tx — no batch round. A
|
|
474
|
+
* swept (recoverable) VTXO is no longer a live leaf, so it can only be
|
|
475
|
+
* reclaimed by re-registering it into a batch.
|
|
476
|
+
*/
|
|
477
|
+
private settleRefundWithoutReceiver;
|
|
478
|
+
/**
|
|
479
|
+
* Refund every VTXO at a swap's VHTLC address back to the wallet, shared by
|
|
480
|
+
* {@link ArkadeSwaps.refundVHTLC} (submarine) and {@link ArkadeSwaps.refundArk}
|
|
481
|
+
* (chain). Path selection per VTXO:
|
|
482
|
+
* - CLTV elapsed → `refundWithoutReceiver` (offchain for a live VTXO, via a
|
|
483
|
+
* batch round for a swept one — see {@link settleRefundWithoutReceiver}).
|
|
484
|
+
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign a swept-batch refund).
|
|
485
|
+
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz, falling
|
|
486
|
+
* back to `refundWithoutReceiver` offchain if Boltz rejects after the
|
|
487
|
+
* locktime has since elapsed.
|
|
488
|
+
*
|
|
489
|
+
* @returns Counts of VTXOs swept vs. deferred.
|
|
490
|
+
*/
|
|
491
|
+
private refundVtxos;
|
|
466
492
|
/**
|
|
467
493
|
* Creates a VHTLC script for the swap.
|
|
468
494
|
* Works for submarine, reverse, and chain swaps.
|
|
@@ -571,20 +571,26 @@ var BoltzSwapProvider = class {
|
|
|
571
571
|
invoiceAmount,
|
|
572
572
|
claimPublicKey,
|
|
573
573
|
preimageHash,
|
|
574
|
-
description
|
|
574
|
+
description,
|
|
575
|
+
descriptionHash
|
|
575
576
|
}) {
|
|
576
577
|
if (claimPublicKey.length != 66) {
|
|
577
578
|
throw new SwapError({
|
|
578
579
|
message: "claimPublicKey must be a compressed public key"
|
|
579
580
|
});
|
|
580
581
|
}
|
|
582
|
+
if (descriptionHash !== void 0 && !/^[0-9a-fA-F]{64}$/.test(descriptionHash)) {
|
|
583
|
+
throw new SwapError({
|
|
584
|
+
message: "descriptionHash must be a 32-byte SHA256 (64 hex chars)"
|
|
585
|
+
});
|
|
586
|
+
}
|
|
581
587
|
const requestBody = {
|
|
582
588
|
from: "BTC",
|
|
583
589
|
to: "ARK",
|
|
584
590
|
invoiceAmount,
|
|
585
591
|
claimPublicKey,
|
|
586
592
|
preimageHash,
|
|
587
|
-
description: description?.trim() || "Send to Arkade address",
|
|
593
|
+
...descriptionHash ? { descriptionHash } : { description: description?.trim() || "Send to Arkade address" },
|
|
588
594
|
...this.referralId ? { referralId: this.referralId } : {}
|
|
589
595
|
};
|
|
590
596
|
const response = await this.request(
|
|
@@ -997,6 +1003,12 @@ var decodeInvoice = (invoice) => {
|
|
|
997
1003
|
expiry: decoded.expiry ?? 3600,
|
|
998
1004
|
amountSats: Number(millisats / 1000n),
|
|
999
1005
|
description: decoded.sections.find((s) => s.name === "description")?.value ?? "",
|
|
1006
|
+
// description_hash (BOLT11 `h`) is missing from light-bolt11-decoder's
|
|
1007
|
+
// Section union even though the decoder emits it. Widen just this lookup
|
|
1008
|
+
// rather than patch the library's types (separate repo, out of scope).
|
|
1009
|
+
descriptionHash: decoded.sections.find(
|
|
1010
|
+
(s) => s.name === "description_hash"
|
|
1011
|
+
)?.value ?? "",
|
|
1000
1012
|
paymentHash: decoded.sections.find((s) => s.name === "payment_hash")?.value ?? ""
|
|
1001
1013
|
};
|
|
1002
1014
|
};
|
|
@@ -3100,6 +3112,40 @@ var claimVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKe
|
|
|
3100
3112
|
await arkProvider.finalizeTx(arkTxid, finalCheckpoints);
|
|
3101
3113
|
return arkTxid;
|
|
3102
3114
|
};
|
|
3115
|
+
var refundWithoutReceiverVHTLCwithOffchainTx = async (identity, vhtlcScript, serverXOnlyPublicKey, input, output, arkInfo, arkProvider) => {
|
|
3116
|
+
const rawCheckpointTapscript = hex7.decode(arkInfo.checkpointTapscript);
|
|
3117
|
+
const serverUnrollScript = CSVMultisigTapscript2.decode(rawCheckpointTapscript);
|
|
3118
|
+
const { arkTx, checkpoints } = buildOffchainTx([input], [output], serverUnrollScript);
|
|
3119
|
+
const signedArkTx = await identity.sign(arkTx);
|
|
3120
|
+
const { arkTxid, finalArkTx, signedCheckpointTxs } = await arkProvider.submitTx(
|
|
3121
|
+
base643.encode(signedArkTx.toPSBT()),
|
|
3122
|
+
checkpoints.map((c) => base643.encode(c.toPSBT()))
|
|
3123
|
+
);
|
|
3124
|
+
const finalTx = Transaction5.fromPSBT(base643.decode(finalArkTx));
|
|
3125
|
+
const serverPubkeyHex = hex7.encode(serverXOnlyPublicKey);
|
|
3126
|
+
const refundLeafHash = tapLeafHash3(
|
|
3127
|
+
scriptFromTapLeafScript(vhtlcScript.refundWithoutReceiver())
|
|
3128
|
+
);
|
|
3129
|
+
for (let i = 0; i < finalTx.inputsLength; i++) {
|
|
3130
|
+
if (!verifySignatures(finalTx, i, [serverPubkeyHex], refundLeafHash)) {
|
|
3131
|
+
throw new Error("Invalid final Ark transaction");
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
const finalCheckpoints = await Promise.all(
|
|
3135
|
+
signedCheckpointTxs.map(async (c, idx) => {
|
|
3136
|
+
const tx = Transaction5.fromPSBT(base643.decode(c));
|
|
3137
|
+
const checkpointLeaf = checkpoints[idx].getInput(0).tapLeafScript[0];
|
|
3138
|
+
const cpLeafHash = tapLeafHash3(scriptFromTapLeafScript(checkpointLeaf));
|
|
3139
|
+
if (!verifySignatures(tx, 0, [serverPubkeyHex], cpLeafHash)) {
|
|
3140
|
+
throw new Error("Invalid server signature in checkpoint transaction");
|
|
3141
|
+
}
|
|
3142
|
+
const signedCheckpoint = await identity.sign(tx, [0]);
|
|
3143
|
+
return base643.encode(signedCheckpoint.toPSBT());
|
|
3144
|
+
})
|
|
3145
|
+
);
|
|
3146
|
+
await arkProvider.finalizeTx(arkTxid, finalCheckpoints);
|
|
3147
|
+
return arkTxid;
|
|
3148
|
+
};
|
|
3103
3149
|
var refundVHTLCwithOffchainTx = async (swapId, identity, arkProvider, boltzXOnlyPublicKey, ourXOnlyPublicKey, serverXOnlyPublicKey, input, output, arkInfo, refundFunc) => {
|
|
3104
3150
|
const rawCheckpointTapscript = hex7.decode(arkInfo.checkpointTapscript);
|
|
3105
3151
|
const serverUnrollScript = CSVMultisigTapscript2.decode(rawCheckpointTapscript);
|
|
@@ -3406,7 +3452,7 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3406
3452
|
invoiceAmount: args.amount,
|
|
3407
3453
|
claimPublicKey,
|
|
3408
3454
|
preimageHash,
|
|
3409
|
-
...args.description?.trim() ? { description: args.description.trim() } : {}
|
|
3455
|
+
...args.descriptionHash !== void 0 ? { descriptionHash: args.descriptionHash } : args.description?.trim() ? { description: args.description.trim() } : {}
|
|
3410
3456
|
};
|
|
3411
3457
|
const swapResponse = await this.swapProvider.createReverseSwap(swapRequest);
|
|
3412
3458
|
const decodedInvoice = decodeInvoice(swapResponse.invoice);
|
|
@@ -3854,85 +3900,22 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
3854
3900
|
}
|
|
3855
3901
|
const outputScript = ArkAddress2.decode(address).pkScript;
|
|
3856
3902
|
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
3857
|
-
const
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
await this.joinBatch(
|
|
3874
|
-
this.wallet.identity,
|
|
3875
|
-
input2,
|
|
3876
|
-
output,
|
|
3877
|
-
arkInfo,
|
|
3878
|
-
isRecoverableVtxo
|
|
3879
|
-
);
|
|
3880
|
-
sweptCount++;
|
|
3881
|
-
continue;
|
|
3882
|
-
}
|
|
3883
|
-
if (isRecoverableVtxo) {
|
|
3884
|
-
logger.error(
|
|
3885
|
-
`Swap ${pendingSwap.id}: recoverable VTXO ${vtxo.txid}:${vtxo.vout} cannot be refunded yet \u2014 refundWithoutReceiver locktime has not passed (refundLocktime=${vhtlcTimeouts.refund}, currentTimestamp=${Math.floor(Date.now() / 1e3)}). Refund will be retried after locktime.`
|
|
3886
|
-
);
|
|
3887
|
-
skippedCount++;
|
|
3888
|
-
continue;
|
|
3889
|
-
}
|
|
3890
|
-
const input = {
|
|
3891
|
-
...vtxo,
|
|
3892
|
-
tapLeafScript: vhtlcScript.refund(),
|
|
3893
|
-
tapTree: vhtlcScript.encode()
|
|
3894
|
-
};
|
|
3895
|
-
try {
|
|
3896
|
-
if (boltzCallCount > 0) {
|
|
3897
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
3898
|
-
}
|
|
3899
|
-
boltzCallCount++;
|
|
3900
|
-
await refundVHTLCwithOffchainTx(
|
|
3901
|
-
pendingSwap.id,
|
|
3902
|
-
this.wallet.identity,
|
|
3903
|
-
this.arkProvider,
|
|
3904
|
-
boltzXOnlyPublicKey,
|
|
3905
|
-
ourXOnlyPublicKey,
|
|
3906
|
-
serverXOnlyPublicKey,
|
|
3907
|
-
input,
|
|
3908
|
-
output,
|
|
3909
|
-
arkInfo,
|
|
3910
|
-
this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3911
|
-
);
|
|
3912
|
-
sweptCount++;
|
|
3913
|
-
} catch (error) {
|
|
3914
|
-
if (!(error instanceof BoltzRefundError)) {
|
|
3915
|
-
throw error;
|
|
3916
|
-
}
|
|
3917
|
-
if (!isSubmarineRefundLocktimeReached(vhtlcTimeouts.refund)) {
|
|
3918
|
-
logger.error(
|
|
3919
|
-
`Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint and refundWithoutReceiver locktime has not passed yet (currentTimestamp=${Math.floor(Date.now() / 1e3)}, locktime=${vhtlcTimeouts.refund}). Refund will be retried after locktime.`
|
|
3920
|
-
);
|
|
3921
|
-
skippedCount++;
|
|
3922
|
-
continue;
|
|
3923
|
-
}
|
|
3924
|
-
logger.warn(
|
|
3925
|
-
`Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver via joinBatch`
|
|
3926
|
-
);
|
|
3927
|
-
const fallbackInput = {
|
|
3928
|
-
...vtxo,
|
|
3929
|
-
tapLeafScript: refundWithoutReceiverLeaf,
|
|
3930
|
-
tapTree: vhtlcScript.encode()
|
|
3931
|
-
};
|
|
3932
|
-
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
3933
|
-
sweptCount++;
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3903
|
+
const refundContext = {
|
|
3904
|
+
arkInfo,
|
|
3905
|
+
vhtlcScript,
|
|
3906
|
+
serverXOnlyPublicKey,
|
|
3907
|
+
refundWithoutReceiverLeaf,
|
|
3908
|
+
outputScript
|
|
3909
|
+
};
|
|
3910
|
+
const { swept: sweptCount, skipped: skippedCount } = await this.refundVtxos({
|
|
3911
|
+
swapId: pendingSwap.id,
|
|
3912
|
+
vtxos: refundableVtxos,
|
|
3913
|
+
refundLocktime: vhtlcTimeouts.refund,
|
|
3914
|
+
refundContext,
|
|
3915
|
+
boltzXOnlyPublicKey,
|
|
3916
|
+
ourXOnlyPublicKey,
|
|
3917
|
+
refundViaBoltz: this.swapProvider.refundSubmarineSwap.bind(this.swapProvider)
|
|
3918
|
+
});
|
|
3936
3919
|
if (!isSubmarineSuccessStatus(pendingSwap.status)) {
|
|
3937
3920
|
const fullyRefunded = skippedCount === 0;
|
|
3938
3921
|
await updateSubmarineSwapStatus(
|
|
@@ -4490,7 +4473,10 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4490
4473
|
* swap's ARK lockup address.
|
|
4491
4474
|
*
|
|
4492
4475
|
* Path selection per VTXO:
|
|
4493
|
-
* - CLTV
|
|
4476
|
+
* - CLTV elapsed, live VTXO → `refundWithoutReceiver` offchain (sender +
|
|
4477
|
+
* server, no Boltz, no batch round).
|
|
4478
|
+
* - CLTV elapsed, swept VTXO → `refundWithoutReceiver` via `joinBatch`
|
|
4479
|
+
* (a swept VTXO is no longer a live leaf).
|
|
4494
4480
|
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign swept-batch refund).
|
|
4495
4481
|
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz.
|
|
4496
4482
|
*
|
|
@@ -4547,84 +4533,22 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
4547
4533
|
const outputScript = ArkAddress2.decode(address).pkScript;
|
|
4548
4534
|
const refundWithoutReceiverLeaf = vhtlcScript.refundWithoutReceiver();
|
|
4549
4535
|
const refundLocktime = pendingSwap.response.lockupDetails.timeouts.refund;
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
this.wallet.identity,
|
|
4567
|
-
input2,
|
|
4568
|
-
output,
|
|
4569
|
-
arkInfo,
|
|
4570
|
-
isRecoverableVtxo
|
|
4571
|
-
);
|
|
4572
|
-
sweptCount++;
|
|
4573
|
-
continue;
|
|
4574
|
-
}
|
|
4575
|
-
if (isRecoverableVtxo) {
|
|
4576
|
-
logger.error(
|
|
4577
|
-
`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.`
|
|
4578
|
-
);
|
|
4579
|
-
skippedCount++;
|
|
4580
|
-
continue;
|
|
4581
|
-
}
|
|
4582
|
-
const input = {
|
|
4583
|
-
...vtxo,
|
|
4584
|
-
tapLeafScript: vhtlcScript.refund(),
|
|
4585
|
-
tapTree: vhtlcScript.encode()
|
|
4586
|
-
};
|
|
4587
|
-
try {
|
|
4588
|
-
if (boltzCallCount > 0) {
|
|
4589
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
4590
|
-
}
|
|
4591
|
-
boltzCallCount++;
|
|
4592
|
-
await refundVHTLCwithOffchainTx(
|
|
4593
|
-
pendingSwap.id,
|
|
4594
|
-
this.wallet.identity,
|
|
4595
|
-
this.arkProvider,
|
|
4596
|
-
boltzXOnlyPublicKey,
|
|
4597
|
-
ourXOnlyPublicKey,
|
|
4598
|
-
serverXOnlyPublicKey,
|
|
4599
|
-
input,
|
|
4600
|
-
output,
|
|
4601
|
-
arkInfo,
|
|
4602
|
-
this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4603
|
-
);
|
|
4604
|
-
sweptCount++;
|
|
4605
|
-
} catch (error) {
|
|
4606
|
-
if (!(error instanceof BoltzRefundError)) {
|
|
4607
|
-
throw error;
|
|
4608
|
-
}
|
|
4609
|
-
if (!isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
4610
|
-
logger.error(
|
|
4611
|
-
`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.`
|
|
4612
|
-
);
|
|
4613
|
-
skippedCount++;
|
|
4614
|
-
continue;
|
|
4615
|
-
}
|
|
4616
|
-
logger.warn(
|
|
4617
|
-
`Swap ${pendingSwap.id}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver via joinBatch`
|
|
4618
|
-
);
|
|
4619
|
-
const fallbackInput = {
|
|
4620
|
-
...vtxo,
|
|
4621
|
-
tapLeafScript: refundWithoutReceiverLeaf,
|
|
4622
|
-
tapTree: vhtlcScript.encode()
|
|
4623
|
-
};
|
|
4624
|
-
await this.joinBatch(this.wallet.identity, fallbackInput, output, arkInfo, false);
|
|
4625
|
-
sweptCount++;
|
|
4626
|
-
}
|
|
4627
|
-
}
|
|
4536
|
+
const refundContext = {
|
|
4537
|
+
arkInfo,
|
|
4538
|
+
vhtlcScript,
|
|
4539
|
+
serverXOnlyPublicKey,
|
|
4540
|
+
refundWithoutReceiverLeaf,
|
|
4541
|
+
outputScript
|
|
4542
|
+
};
|
|
4543
|
+
const { swept: sweptCount, skipped: skippedCount } = await this.refundVtxos({
|
|
4544
|
+
swapId: pendingSwap.id,
|
|
4545
|
+
vtxos: unspentVtxos,
|
|
4546
|
+
refundLocktime,
|
|
4547
|
+
refundContext,
|
|
4548
|
+
boltzXOnlyPublicKey,
|
|
4549
|
+
ourXOnlyPublicKey,
|
|
4550
|
+
refundViaBoltz: this.swapProvider.refundChainSwap.bind(this.swapProvider)
|
|
4551
|
+
});
|
|
4628
4552
|
const finalStatus = await this.getSwapStatus(pendingSwap.id);
|
|
4629
4553
|
await this.savePendingChainSwap({
|
|
4630
4554
|
...pendingSwap,
|
|
@@ -5157,6 +5081,120 @@ var ArkadeSwaps = class _ArkadeSwaps {
|
|
|
5157
5081
|
async joinBatch(identity, input, output, arkInfo, isRecoverable2 = true) {
|
|
5158
5082
|
return joinBatch(this.arkProvider, identity, input, output, arkInfo, isRecoverable2);
|
|
5159
5083
|
}
|
|
5084
|
+
/**
|
|
5085
|
+
* Settle a `refundWithoutReceiver` (sender + server, no Boltz) refund for a
|
|
5086
|
+
* single VTXO whose CLTV refund locktime has elapsed.
|
|
5087
|
+
*
|
|
5088
|
+
* A live VTXO settles the leaf with an offchain Ark tx — no batch round. A
|
|
5089
|
+
* swept (recoverable) VTXO is no longer a live leaf, so it can only be
|
|
5090
|
+
* reclaimed by re-registering it into a batch.
|
|
5091
|
+
*/
|
|
5092
|
+
async settleRefundWithoutReceiver(ctx, vtxo) {
|
|
5093
|
+
const input = {
|
|
5094
|
+
...vtxo,
|
|
5095
|
+
tapLeafScript: ctx.refundWithoutReceiverLeaf,
|
|
5096
|
+
tapTree: ctx.vhtlcScript.encode()
|
|
5097
|
+
};
|
|
5098
|
+
const output = { amount: BigInt(vtxo.value), script: ctx.outputScript };
|
|
5099
|
+
if (isRecoverable(vtxo)) {
|
|
5100
|
+
await this.joinBatch(this.wallet.identity, input, output, ctx.arkInfo, true);
|
|
5101
|
+
} else {
|
|
5102
|
+
await refundWithoutReceiverVHTLCwithOffchainTx(
|
|
5103
|
+
this.wallet.identity,
|
|
5104
|
+
ctx.vhtlcScript,
|
|
5105
|
+
ctx.serverXOnlyPublicKey,
|
|
5106
|
+
input,
|
|
5107
|
+
output,
|
|
5108
|
+
ctx.arkInfo,
|
|
5109
|
+
this.arkProvider
|
|
5110
|
+
);
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
/**
|
|
5114
|
+
* Refund every VTXO at a swap's VHTLC address back to the wallet, shared by
|
|
5115
|
+
* {@link ArkadeSwaps.refundVHTLC} (submarine) and {@link ArkadeSwaps.refundArk}
|
|
5116
|
+
* (chain). Path selection per VTXO:
|
|
5117
|
+
* - CLTV elapsed → `refundWithoutReceiver` (offchain for a live VTXO, via a
|
|
5118
|
+
* batch round for a swept one — see {@link settleRefundWithoutReceiver}).
|
|
5119
|
+
* - Pre-CLTV recoverable → skipped (Boltz can't co-sign a swept-batch refund).
|
|
5120
|
+
* - Pre-CLTV non-recoverable → cooperative 3-of-3 refund via Boltz, falling
|
|
5121
|
+
* back to `refundWithoutReceiver` offchain if Boltz rejects after the
|
|
5122
|
+
* locktime has since elapsed.
|
|
5123
|
+
*
|
|
5124
|
+
* @returns Counts of VTXOs swept vs. deferred.
|
|
5125
|
+
*/
|
|
5126
|
+
async refundVtxos(params) {
|
|
5127
|
+
const {
|
|
5128
|
+
swapId,
|
|
5129
|
+
vtxos,
|
|
5130
|
+
refundLocktime,
|
|
5131
|
+
refundContext,
|
|
5132
|
+
boltzXOnlyPublicKey,
|
|
5133
|
+
ourXOnlyPublicKey,
|
|
5134
|
+
refundViaBoltz
|
|
5135
|
+
} = params;
|
|
5136
|
+
const { vhtlcScript, serverXOnlyPublicKey, arkInfo, outputScript } = refundContext;
|
|
5137
|
+
let boltzCallCount = 0;
|
|
5138
|
+
let sweptCount = 0;
|
|
5139
|
+
let skippedCount = 0;
|
|
5140
|
+
for (const vtxo of vtxos) {
|
|
5141
|
+
const isRecoverableVtxo = isRecoverable(vtxo);
|
|
5142
|
+
const output = { amount: BigInt(vtxo.value), script: outputScript };
|
|
5143
|
+
if (isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
5144
|
+
await this.settleRefundWithoutReceiver(refundContext, vtxo);
|
|
5145
|
+
sweptCount++;
|
|
5146
|
+
continue;
|
|
5147
|
+
}
|
|
5148
|
+
if (isRecoverableVtxo) {
|
|
5149
|
+
logger.error(
|
|
5150
|
+
`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
|
+
);
|
|
5152
|
+
skippedCount++;
|
|
5153
|
+
continue;
|
|
5154
|
+
}
|
|
5155
|
+
const input = {
|
|
5156
|
+
...vtxo,
|
|
5157
|
+
tapLeafScript: vhtlcScript.refund(),
|
|
5158
|
+
tapTree: vhtlcScript.encode()
|
|
5159
|
+
};
|
|
5160
|
+
try {
|
|
5161
|
+
if (boltzCallCount > 0) {
|
|
5162
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
5163
|
+
}
|
|
5164
|
+
boltzCallCount++;
|
|
5165
|
+
await refundVHTLCwithOffchainTx(
|
|
5166
|
+
swapId,
|
|
5167
|
+
this.wallet.identity,
|
|
5168
|
+
this.arkProvider,
|
|
5169
|
+
boltzXOnlyPublicKey,
|
|
5170
|
+
ourXOnlyPublicKey,
|
|
5171
|
+
serverXOnlyPublicKey,
|
|
5172
|
+
input,
|
|
5173
|
+
output,
|
|
5174
|
+
arkInfo,
|
|
5175
|
+
refundViaBoltz
|
|
5176
|
+
);
|
|
5177
|
+
sweptCount++;
|
|
5178
|
+
} catch (error) {
|
|
5179
|
+
if (!(error instanceof BoltzRefundError)) {
|
|
5180
|
+
throw error;
|
|
5181
|
+
}
|
|
5182
|
+
if (!isSubmarineRefundLocktimeReached(refundLocktime)) {
|
|
5183
|
+
logger.error(
|
|
5184
|
+
`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
|
+
);
|
|
5186
|
+
skippedCount++;
|
|
5187
|
+
continue;
|
|
5188
|
+
}
|
|
5189
|
+
logger.warn(
|
|
5190
|
+
`Swap ${swapId}: Boltz rejected VTXO outpoint, falling back to refundWithoutReceiver offchain`
|
|
5191
|
+
);
|
|
5192
|
+
await this.settleRefundWithoutReceiver(refundContext, vtxo);
|
|
5193
|
+
sweptCount++;
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
return { swept: sweptCount, skipped: skippedCount };
|
|
5197
|
+
}
|
|
5160
5198
|
/**
|
|
5161
5199
|
* Creates a VHTLC script for the swap.
|
|
5162
5200
|
* Works for submarine, reverse, and chain swaps.
|