@atomiqlabs/sdk 8.6.5 → 8.6.6
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/SmartChainAssets.d.ts +181 -181
- package/dist/SmartChainAssets.js +181 -181
- package/dist/bitcoin/coinselect2/accumulative.d.ts +6 -6
- package/dist/bitcoin/coinselect2/accumulative.js +51 -52
- package/dist/bitcoin/coinselect2/blackjack.d.ts +6 -6
- package/dist/bitcoin/coinselect2/blackjack.js +37 -38
- package/dist/bitcoin/coinselect2/index.d.ts +17 -19
- package/dist/bitcoin/coinselect2/index.js +69 -69
- package/dist/bitcoin/coinselect2/utils.d.ts +75 -77
- package/dist/bitcoin/coinselect2/utils.js +123 -123
- package/dist/bitcoin/wallet/BitcoinWallet.d.ts +128 -130
- package/dist/bitcoin/wallet/BitcoinWallet.js +322 -322
- package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +78 -78
- package/dist/bitcoin/wallet/IBitcoinWallet.js +20 -21
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +99 -101
- package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +176 -176
- package/dist/enums/FeeType.d.ts +15 -15
- package/dist/enums/FeeType.js +19 -19
- package/dist/enums/SwapAmountType.d.ts +15 -15
- package/dist/enums/SwapAmountType.js +19 -19
- package/dist/enums/SwapDirection.d.ts +15 -15
- package/dist/enums/SwapDirection.js +19 -19
- package/dist/enums/SwapSide.d.ts +15 -15
- package/dist/enums/SwapSide.js +19 -19
- package/dist/enums/SwapType.d.ts +75 -75
- package/dist/enums/SwapType.js +79 -79
- package/dist/errors/IntermediaryError.d.ts +13 -13
- package/dist/errors/IntermediaryError.js +27 -27
- package/dist/errors/RequestError.d.ts +32 -32
- package/dist/errors/RequestError.js +54 -54
- package/dist/errors/UserError.d.ts +8 -8
- package/dist/errors/UserError.js +16 -16
- package/dist/events/UnifiedSwapEventListener.d.ts +23 -23
- package/dist/events/UnifiedSwapEventListener.js +130 -130
- package/dist/http/HttpUtils.d.ts +27 -27
- package/dist/http/HttpUtils.js +90 -91
- package/dist/http/paramcoders/IParamReader.d.ts +8 -8
- package/dist/http/paramcoders/IParamReader.js +2 -2
- package/dist/http/paramcoders/ParamDecoder.d.ts +42 -44
- package/dist/http/paramcoders/ParamDecoder.js +137 -137
- package/dist/http/paramcoders/ParamEncoder.d.ts +18 -20
- package/dist/http/paramcoders/ParamEncoder.js +36 -36
- package/dist/http/paramcoders/SchemaVerifier.d.ts +26 -26
- package/dist/http/paramcoders/SchemaVerifier.js +145 -145
- package/dist/http/paramcoders/client/ResponseParamDecoder.d.ts +11 -11
- package/dist/http/paramcoders/client/ResponseParamDecoder.js +57 -57
- package/dist/http/paramcoders/client/StreamParamEncoder.d.ts +11 -13
- package/dist/http/paramcoders/client/StreamParamEncoder.js +26 -26
- package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +16 -16
- package/dist/http/paramcoders/client/StreamingFetchPromise.js +173 -174
- package/dist/index.d.ts +85 -85
- package/dist/index.js +158 -158
- package/dist/intermediaries/Intermediary.d.ts +157 -157
- package/dist/intermediaries/Intermediary.js +142 -142
- package/dist/intermediaries/IntermediaryDiscovery.d.ts +198 -199
- package/dist/intermediaries/IntermediaryDiscovery.js +406 -406
- package/dist/intermediaries/apis/IntermediaryAPI.d.ts +437 -438
- package/dist/intermediaries/apis/IntermediaryAPI.js +603 -602
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.d.ts +155 -155
- package/dist/intermediaries/apis/TrustedIntermediaryAPI.js +137 -137
- package/dist/lnurl/LNURL.d.ts +102 -102
- package/dist/lnurl/LNURL.js +321 -321
- package/dist/prices/RedundantSwapPrice.d.ts +110 -110
- package/dist/prices/RedundantSwapPrice.js +222 -222
- package/dist/prices/SingleSwapPrice.d.ts +34 -34
- package/dist/prices/SingleSwapPrice.js +44 -44
- package/dist/prices/SwapPriceWithChain.d.ts +107 -107
- package/dist/prices/SwapPriceWithChain.js +128 -128
- package/dist/prices/abstract/ICachedSwapPrice.d.ts +28 -28
- package/dist/prices/abstract/ICachedSwapPrice.js +62 -62
- package/dist/prices/abstract/IPriceProvider.d.ts +81 -81
- package/dist/prices/abstract/IPriceProvider.js +74 -74
- package/dist/prices/abstract/ISwapPrice.d.ts +168 -168
- package/dist/prices/abstract/ISwapPrice.js +279 -279
- package/dist/prices/providers/BinancePriceProvider.d.ts +23 -23
- package/dist/prices/providers/BinancePriceProvider.js +30 -30
- package/dist/prices/providers/CoinGeckoPriceProvider.d.ts +23 -23
- package/dist/prices/providers/CoinGeckoPriceProvider.js +29 -29
- package/dist/prices/providers/CoinPaprikaPriceProvider.d.ts +25 -25
- package/dist/prices/providers/CoinPaprikaPriceProvider.js +29 -29
- package/dist/prices/providers/CustomPriceProvider.d.ts +24 -24
- package/dist/prices/providers/CustomPriceProvider.js +35 -35
- package/dist/prices/providers/KrakenPriceProvider.d.ts +38 -38
- package/dist/prices/providers/KrakenPriceProvider.js +45 -45
- package/dist/prices/providers/OKXPriceProvider.d.ts +34 -34
- package/dist/prices/providers/OKXPriceProvider.js +29 -29
- package/dist/prices/providers/abstract/ExchangePriceProvider.d.ts +17 -17
- package/dist/prices/providers/abstract/ExchangePriceProvider.js +21 -21
- package/dist/prices/providers/abstract/HttpPriceProvider.d.ts +7 -7
- package/dist/prices/providers/abstract/HttpPriceProvider.js +12 -12
- package/dist/storage/IUnifiedStorage.d.ts +85 -85
- package/dist/storage/IUnifiedStorage.js +2 -2
- package/dist/storage/UnifiedSwapStorage.d.ts +114 -114
- package/dist/storage/UnifiedSwapStorage.js +116 -116
- package/dist/storage-browser/IndexedDBUnifiedStorage.d.ts +63 -63
- package/dist/storage-browser/IndexedDBUnifiedStorage.js +298 -298
- package/dist/storage-browser/LocalStorageManager.d.ts +49 -49
- package/dist/storage-browser/LocalStorageManager.js +93 -93
- package/dist/swapper/Swapper.d.ts +686 -687
- package/dist/swapper/Swapper.js +1603 -1603
- package/dist/swapper/SwapperFactory.d.ts +135 -135
- package/dist/swapper/SwapperFactory.js +162 -162
- package/dist/swapper/SwapperUtils.d.ts +200 -200
- package/dist/swapper/SwapperUtils.js +467 -467
- package/dist/swapper/SwapperWithChain.d.ts +404 -404
- package/dist/swapper/SwapperWithChain.js +469 -469
- package/dist/swapper/SwapperWithSigner.d.ts +322 -322
- package/dist/swapper/SwapperWithSigner.js +318 -318
- package/dist/swaps/IAddressSwap.d.ts +22 -22
- package/dist/swaps/IAddressSwap.js +13 -14
- package/dist/swaps/IBTCWalletSwap.d.ts +73 -73
- package/dist/swaps/IBTCWalletSwap.js +17 -18
- package/dist/swaps/IClaimableSwap.d.ts +49 -49
- package/dist/swaps/IClaimableSwap.js +14 -15
- package/dist/swaps/IClaimableSwapWrapper.d.ts +15 -15
- package/dist/swaps/IClaimableSwapWrapper.js +2 -2
- package/dist/swaps/IRefundableSwap.d.ts +43 -43
- package/dist/swaps/IRefundableSwap.js +13 -14
- package/dist/swaps/ISwap.d.ts +386 -387
- package/dist/swaps/ISwap.js +346 -346
- package/dist/swaps/ISwapWithGasDrop.d.ts +21 -21
- package/dist/swaps/ISwapWithGasDrop.js +11 -12
- package/dist/swaps/ISwapWrapper.d.ts +283 -285
- package/dist/swaps/ISwapWrapper.js +353 -353
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.d.ts +98 -98
- package/dist/swaps/escrow_swaps/IEscrowSelfInitSwap.js +126 -126
- package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +133 -135
- package/dist/swaps/escrow_swaps/IEscrowSwap.js +169 -169
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +114 -115
- package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +134 -134
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +98 -101
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +130 -130
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.d.ts +162 -162
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCSelfInitSwap.js +190 -190
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.d.ts +58 -58
- package/dist/swaps/escrow_swaps/frombtc/IFromBTCWrapper.js +78 -78
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +529 -531
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +1285 -1285
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +181 -184
- package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +418 -418
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +581 -583
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +1371 -1371
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +225 -228
- package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +506 -506
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +458 -458
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +1126 -1126
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +190 -191
- package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +378 -378
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +403 -403
- package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +924 -924
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.d.ts +62 -62
- package/dist/swaps/escrow_swaps/tobtc/IToBTCWrapper.js +112 -112
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.d.ts +125 -127
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNSwap.js +256 -256
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +241 -242
- package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +520 -520
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.d.ts +73 -73
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCSwap.js +155 -155
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +127 -128
- package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +278 -278
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +630 -630
- package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +1443 -1443
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +213 -207
- package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +756 -755
- package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +261 -261
- package/dist/swaps/trusted/ln/LnForGasSwap.js +511 -511
- package/dist/swaps/trusted/ln/LnForGasWrapper.d.ts +40 -40
- package/dist/swaps/trusted/ln/LnForGasWrapper.js +82 -82
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +342 -342
- package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +715 -715
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +68 -69
- package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +92 -92
- package/dist/types/AmountData.d.ts +10 -10
- package/dist/types/AmountData.js +2 -2
- package/dist/types/CustomPriceFunction.d.ts +11 -11
- package/dist/types/CustomPriceFunction.js +2 -2
- package/dist/types/PriceInfoType.d.ts +28 -28
- package/dist/types/PriceInfoType.js +56 -57
- package/dist/types/SwapExecutionAction.d.ts +88 -88
- package/dist/types/SwapExecutionAction.js +2 -2
- package/dist/types/SwapStateInfo.d.ts +5 -5
- package/dist/types/SwapStateInfo.js +2 -2
- package/dist/types/SwapWithSigner.d.ts +17 -17
- package/dist/types/SwapWithSigner.js +42 -43
- package/dist/types/Token.d.ts +99 -99
- package/dist/types/Token.js +76 -76
- package/dist/types/TokenAmount.d.ts +69 -69
- package/dist/types/TokenAmount.js +59 -60
- package/dist/types/fees/Fee.d.ts +50 -50
- package/dist/types/fees/Fee.js +2 -2
- package/dist/types/fees/FeeBreakdown.d.ts +11 -11
- package/dist/types/fees/FeeBreakdown.js +2 -2
- package/dist/types/fees/PercentagePPM.d.ts +17 -17
- package/dist/types/fees/PercentagePPM.js +17 -18
- package/dist/types/lnurl/LNURLPay.d.ts +61 -61
- package/dist/types/lnurl/LNURLPay.js +30 -31
- package/dist/types/lnurl/LNURLWithdraw.d.ts +48 -48
- package/dist/types/lnurl/LNURLWithdraw.js +26 -27
- package/dist/types/wallets/LightningInvoiceCreateService.d.ts +24 -24
- package/dist/types/wallets/LightningInvoiceCreateService.js +14 -15
- package/dist/types/wallets/MinimalBitcoinWalletInterface.d.ts +23 -23
- package/dist/types/wallets/MinimalBitcoinWalletInterface.js +2 -2
- package/dist/types/wallets/MinimalLightningNetworkWalletInterface.d.ts +9 -9
- package/dist/types/wallets/MinimalLightningNetworkWalletInterface.js +2 -2
- package/dist/utils/AutomaticClockDriftCorrection.d.ts +1 -1
- package/dist/utils/AutomaticClockDriftCorrection.js +69 -70
- package/dist/utils/BitcoinUtils.d.ts +12 -14
- package/dist/utils/BitcoinUtils.js +101 -102
- package/dist/utils/BitcoinWalletUtils.d.ts +7 -7
- package/dist/utils/BitcoinWalletUtils.js +13 -14
- package/dist/utils/Logger.d.ts +7 -7
- package/dist/utils/Logger.js +11 -12
- package/dist/utils/RetryUtils.d.ts +22 -22
- package/dist/utils/RetryUtils.js +66 -67
- package/dist/utils/SwapUtils.d.ts +88 -88
- package/dist/utils/SwapUtils.js +72 -72
- package/dist/utils/TimeoutUtils.d.ts +17 -17
- package/dist/utils/TimeoutUtils.js +54 -55
- package/dist/utils/TokenUtils.d.ts +19 -19
- package/dist/utils/TokenUtils.js +36 -37
- package/dist/utils/TypeUtils.d.ts +7 -7
- package/dist/utils/TypeUtils.js +2 -2
- package/dist/utils/Utils.d.ts +56 -58
- package/dist/utils/Utils.js +193 -194
- package/package.json +1 -1
- package/src/intermediaries/apis/IntermediaryAPI.ts +4 -2
- package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +8 -0
|
@@ -1,1285 +1,1285 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FromBTCLNSwap = exports.
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
* the
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
[FromBTCLNSwapState.
|
|
72
|
-
[FromBTCLNSwapState.
|
|
73
|
-
[FromBTCLNSwapState.
|
|
74
|
-
[FromBTCLNSwapState.
|
|
75
|
-
[FromBTCLNSwapState.
|
|
76
|
-
[FromBTCLNSwapState.
|
|
77
|
-
[FromBTCLNSwapState.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
(obj.
|
|
83
|
-
(obj.
|
|
84
|
-
(obj.
|
|
85
|
-
(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Legacy escrow (HTLC) based swap for Bitcoin Lightning -> Smart chains, requires manual settlement
|
|
90
|
-
* of the swap on the destination network once the lightning network payment is received by the LP.
|
|
91
|
-
*
|
|
92
|
-
* @category Swaps/Legacy/Lightning → Smart chain
|
|
93
|
-
*/
|
|
94
|
-
class FromBTCLNSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
|
|
95
|
-
/**
|
|
96
|
-
* Sets the LNURL data for the swap
|
|
97
|
-
*
|
|
98
|
-
* @internal
|
|
99
|
-
*/
|
|
100
|
-
_setLNURLData(lnurl, lnurlK1, lnurlCallback) {
|
|
101
|
-
this.lnurl = lnurl;
|
|
102
|
-
this.lnurlK1 = lnurlK1;
|
|
103
|
-
this.lnurlCallback = lnurlCallback;
|
|
104
|
-
}
|
|
105
|
-
constructor(wrapper, initOrObject) {
|
|
106
|
-
if (isFromBTCLNSwapInit(initOrObject) && initOrObject.url != null)
|
|
107
|
-
initOrObject.url += "/frombtcln";
|
|
108
|
-
super(wrapper, initOrObject);
|
|
109
|
-
this.TYPE = SwapType_1.SwapType.FROM_BTCLN;
|
|
110
|
-
/**
|
|
111
|
-
* @internal
|
|
112
|
-
*/
|
|
113
|
-
this.swapStateName = (state) => FromBTCLNSwapState[state];
|
|
114
|
-
/**
|
|
115
|
-
* @internal
|
|
116
|
-
*/
|
|
117
|
-
this.swapStateDescription = FromBTCLNSwapStateDescription;
|
|
118
|
-
/**
|
|
119
|
-
* @internal
|
|
120
|
-
*/
|
|
121
|
-
this.inputToken = Token_1.BitcoinTokens.BTCLN;
|
|
122
|
-
this.lnurlFailSignal = new AbortController();
|
|
123
|
-
this.prPosted = false;
|
|
124
|
-
if (isFromBTCLNSwapInit(initOrObject)) {
|
|
125
|
-
this._state = FromBTCLNSwapState.PR_CREATED;
|
|
126
|
-
this.pr = initOrObject.pr;
|
|
127
|
-
this.secret = initOrObject.secret;
|
|
128
|
-
this.initialSwapData = initOrObject.initialSwapData;
|
|
129
|
-
this.lnurl = initOrObject.lnurl;
|
|
130
|
-
this.lnurlK1 = initOrObject.lnurlK1;
|
|
131
|
-
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
132
|
-
this.usesClaimHashAsId = true;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
this.pr = initOrObject.pr;
|
|
136
|
-
this.secret = initOrObject.secret;
|
|
137
|
-
if (initOrObject.initialSwapData == null) {
|
|
138
|
-
this.initialSwapData = this._data;
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
this.initialSwapData = base_1.SwapData.deserialize(initOrObject.initialSwapData);
|
|
142
|
-
}
|
|
143
|
-
this.lnurl = initOrObject.lnurl;
|
|
144
|
-
this.lnurlK1 = initOrObject.lnurlK1;
|
|
145
|
-
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
146
|
-
this.prPosted = initOrObject.prPosted;
|
|
147
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED && this._data != null) {
|
|
148
|
-
this.initialSwapData = this._data;
|
|
149
|
-
delete this._data;
|
|
150
|
-
}
|
|
151
|
-
this.usesClaimHashAsId = initOrObject.usesClaimHashAsId ?? false;
|
|
152
|
-
}
|
|
153
|
-
this.tryRecomputeSwapPrice();
|
|
154
|
-
this.logger = (0, Logger_1.getLogger)("FromBTCLN(" + this.getIdentifierHashString() + "): ");
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* @inheritDoc
|
|
158
|
-
* @internal
|
|
159
|
-
*/
|
|
160
|
-
getSwapData() {
|
|
161
|
-
return this._data ?? this.initialSwapData;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* @inheritDoc
|
|
165
|
-
* @internal
|
|
166
|
-
*/
|
|
167
|
-
upgradeVersion() {
|
|
168
|
-
if (this.version == null) {
|
|
169
|
-
switch (this._state) {
|
|
170
|
-
case -2:
|
|
171
|
-
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
172
|
-
break;
|
|
173
|
-
case -1:
|
|
174
|
-
this._state = FromBTCLNSwapState.FAILED;
|
|
175
|
-
break;
|
|
176
|
-
case 0:
|
|
177
|
-
this._state = FromBTCLNSwapState.PR_CREATED;
|
|
178
|
-
break;
|
|
179
|
-
case 1:
|
|
180
|
-
this._state = FromBTCLNSwapState.PR_PAID;
|
|
181
|
-
break;
|
|
182
|
-
case 2:
|
|
183
|
-
this._state = FromBTCLNSwapState.CLAIM_COMMITED;
|
|
184
|
-
break;
|
|
185
|
-
case 3:
|
|
186
|
-
this._state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
this.version = 1;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
//////////////////////////////
|
|
193
|
-
//// Getters & utils
|
|
194
|
-
/**
|
|
195
|
-
* @inheritDoc
|
|
196
|
-
* @internal
|
|
197
|
-
*/
|
|
198
|
-
getIdentifierHash() {
|
|
199
|
-
const idBuffer = this.usesClaimHashAsId
|
|
200
|
-
? buffer_1.Buffer.from(this.getClaimHash(), "hex")
|
|
201
|
-
: this.getPaymentHash();
|
|
202
|
-
if (this._randomNonce == null)
|
|
203
|
-
return idBuffer;
|
|
204
|
-
return buffer_1.Buffer.concat([idBuffer, buffer_1.Buffer.from(this._randomNonce, "hex")]);
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Returns the payment hash of the swap and lightning network invoice, or `null` if not known (i.e. if
|
|
208
|
-
* the swap was recovered from on-chain data, the payment hash might not be known)
|
|
209
|
-
*
|
|
210
|
-
* @internal
|
|
211
|
-
*/
|
|
212
|
-
getPaymentHash() {
|
|
213
|
-
if (this.pr == null)
|
|
214
|
-
return null;
|
|
215
|
-
if (this.pr.toLowerCase().startsWith("ln")) {
|
|
216
|
-
const parsed = (0, bolt11_1.decode)(this.pr);
|
|
217
|
-
if (parsed.tagsObject.payment_hash == null)
|
|
218
|
-
throw new Error("Swap invoice has no payment hash field!");
|
|
219
|
-
return buffer_1.Buffer.from(parsed.tagsObject.payment_hash, "hex");
|
|
220
|
-
}
|
|
221
|
-
return buffer_1.Buffer.from(this.pr, "hex");
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* @inheritDoc
|
|
225
|
-
* @internal
|
|
226
|
-
*/
|
|
227
|
-
canCommit(skipQuoteExpiryChecks) {
|
|
228
|
-
return this._state === FromBTCLNSwapState.PR_PAID || (!!skipQuoteExpiryChecks && this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED);
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* @inheritDoc
|
|
232
|
-
*/
|
|
233
|
-
getInputAddress() {
|
|
234
|
-
return this.lnurl ?? this.pr ?? null;
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* @inheritDoc
|
|
238
|
-
*/
|
|
239
|
-
getInputTxId() {
|
|
240
|
-
const paymentHash = this.getPaymentHash();
|
|
241
|
-
if (paymentHash == null)
|
|
242
|
-
return null;
|
|
243
|
-
return paymentHash.toString("hex");
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap.
|
|
247
|
-
*
|
|
248
|
-
* In case the swap is recovered from on-chain data, the address returned might be just a payment hash,
|
|
249
|
-
* as it is impossible to retrieve the actual lightning network invoice paid purely from on-chain
|
|
250
|
-
* data.
|
|
251
|
-
*/
|
|
252
|
-
getAddress() {
|
|
253
|
-
return this.pr ?? "";
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* A hyperlink representation of the address + amount that the user needs to sends on the source chain.
|
|
257
|
-
* This is suitable to be displayed in a form of QR code.
|
|
258
|
-
*
|
|
259
|
-
* @remarks
|
|
260
|
-
* In case the swap is recovered from on-chain data, the address returned might be just a payment hash,
|
|
261
|
-
* as it is impossible to retrieve the actual lightning network invoice paid purely from on-chain
|
|
262
|
-
* data.
|
|
263
|
-
*/
|
|
264
|
-
getHyperlink() {
|
|
265
|
-
return this.pr == null ? "" : "lightning:" + this.pr.toUpperCase();
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Returns the timeout time (in UNIX milliseconds) when the swap will definitelly be considered as expired
|
|
269
|
-
* if the LP doesn't make it expired sooner
|
|
270
|
-
*/
|
|
271
|
-
getDefinitiveExpiryTime() {
|
|
272
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
273
|
-
return 0;
|
|
274
|
-
const decoded = (0, bolt11_1.decode)(this.pr);
|
|
275
|
-
if (decoded.timeExpireDate == null)
|
|
276
|
-
throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
277
|
-
const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
|
|
278
|
-
const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper._options.bitcoinBlocktime * this.wrapper._options.safetyFactor;
|
|
279
|
-
return (decoded.timeExpireDate + finalCltvExpiryDelay) * 1000;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
|
|
283
|
-
*/
|
|
284
|
-
getHtlcTimeoutTime() {
|
|
285
|
-
if (this._data == null)
|
|
286
|
-
return null;
|
|
287
|
-
return Number(this.wrapper._getHtlcTimeout(this._data)) * 1000;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Returns timeout time (in UNIX milliseconds) when the LN invoice will expire
|
|
291
|
-
*/
|
|
292
|
-
getTimeoutTime() {
|
|
293
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
294
|
-
return 0;
|
|
295
|
-
const decoded = (0, bolt11_1.decode)(this.pr);
|
|
296
|
-
if (decoded.timeExpireDate == null)
|
|
297
|
-
throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
298
|
-
return (decoded.timeExpireDate * 1000);
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* @inheritDoc
|
|
302
|
-
*/
|
|
303
|
-
isFinished() {
|
|
304
|
-
return this._state === FromBTCLNSwapState.CLAIM_CLAIMED || this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.FAILED;
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* @inheritDoc
|
|
308
|
-
*/
|
|
309
|
-
isClaimable() {
|
|
310
|
-
return this._state === FromBTCLNSwapState.CLAIM_COMMITED;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* @inheritDoc
|
|
314
|
-
*/
|
|
315
|
-
isSuccessful() {
|
|
316
|
-
return this._state === FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* @inheritDoc
|
|
320
|
-
*/
|
|
321
|
-
isFailed() {
|
|
322
|
-
return this._state === FromBTCLNSwapState.FAILED || this._state === FromBTCLNSwapState.EXPIRED;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* @inheritDoc
|
|
326
|
-
*/
|
|
327
|
-
isInProgress() {
|
|
328
|
-
return (this._state === FromBTCLNSwapState.PR_CREATED && this.initiated) ||
|
|
329
|
-
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.initiated) ||
|
|
330
|
-
this._state === FromBTCLNSwapState.PR_PAID ||
|
|
331
|
-
this._state === FromBTCLNSwapState.CLAIM_COMMITED;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* @inheritDoc
|
|
335
|
-
*/
|
|
336
|
-
isQuoteExpired() {
|
|
337
|
-
return this._state === FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* @inheritDoc
|
|
341
|
-
*/
|
|
342
|
-
isQuoteSoftExpired() {
|
|
343
|
-
return this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* @inheritDoc
|
|
347
|
-
* @internal
|
|
348
|
-
*/
|
|
349
|
-
_verifyQuoteDefinitelyExpired() {
|
|
350
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
351
|
-
return Promise.resolve(this.getDefinitiveExpiryTime() < Date.now());
|
|
352
|
-
}
|
|
353
|
-
return super._verifyQuoteDefinitelyExpired();
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* @inheritDoc
|
|
357
|
-
* @internal
|
|
358
|
-
*/
|
|
359
|
-
_verifyQuoteValid() {
|
|
360
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED ||
|
|
361
|
-
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
362
|
-
return Promise.resolve(this.getTimeoutTime() > Date.now());
|
|
363
|
-
}
|
|
364
|
-
return super._verifyQuoteValid();
|
|
365
|
-
}
|
|
366
|
-
//////////////////////////////
|
|
367
|
-
//// Amounts & fees
|
|
368
|
-
/**
|
|
369
|
-
* @inheritDoc
|
|
370
|
-
*/
|
|
371
|
-
getInputToken() {
|
|
372
|
-
return Token_1.BitcoinTokens.BTCLN;
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* @inheritDoc
|
|
376
|
-
*/
|
|
377
|
-
getInput() {
|
|
378
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
379
|
-
return (0, TokenAmount_1.toTokenAmount)(null, this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
380
|
-
const parsed = (0, bolt11_1.decode)(this.pr);
|
|
381
|
-
if (parsed.millisatoshis == null)
|
|
382
|
-
throw new Error("Swap invoice doesn't contain msat amount field!");
|
|
383
|
-
const amount = (BigInt(parsed.millisatoshis) + 999n) / 1000n;
|
|
384
|
-
return (0, TokenAmount_1.toTokenAmount)(amount, this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* @inheritDoc
|
|
388
|
-
*/
|
|
389
|
-
getSmartChainNetworkFee() {
|
|
390
|
-
return this.getCommitAndClaimNetworkFee();
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* @inheritDoc
|
|
394
|
-
*/
|
|
395
|
-
async hasEnoughForTxFees() {
|
|
396
|
-
const [balance, feeRate] = await Promise.all([
|
|
397
|
-
this.wrapper._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
|
|
398
|
-
this.feeRate != null ? Promise.resolve(this.feeRate) : this.wrapper._contract.getInitFeeRate(this.getSwapData().getOfferer(), this.getSwapData().getClaimer(), this.getSwapData().getToken(), this.getSwapData().getClaimHash())
|
|
399
|
-
]);
|
|
400
|
-
const commitFee = await this.wrapper._contract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
401
|
-
const claimFee = await this.wrapper._contract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
402
|
-
const totalFee = commitFee + claimFee + this.getSwapData().getTotalDeposit();
|
|
403
|
-
return {
|
|
404
|
-
enoughBalance: balance >= totalFee,
|
|
405
|
-
balance: (0, TokenAmount_1.toTokenAmount)(balance, this.wrapper._getNativeToken(), this.wrapper._prices, this.pricingInfo),
|
|
406
|
-
required: (0, TokenAmount_1.toTokenAmount)(totalFee, this.wrapper._getNativeToken(), this.wrapper._prices, this.pricingInfo)
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
isValidSecretPreimage(secret) {
|
|
410
|
-
const paymentHash = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex")));
|
|
411
|
-
const claimHash = this.wrapper._contract.getHashForHtlc(paymentHash).toString("hex");
|
|
412
|
-
return this.getSwapData().getClaimHash() === claimHash;
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Sets the secret preimage for the swap, in case it is not known already
|
|
416
|
-
*
|
|
417
|
-
* @param secret Secret preimage that matches the expected payment hash
|
|
418
|
-
*
|
|
419
|
-
* @throws {Error} If an invalid secret preimage is provided
|
|
420
|
-
*/
|
|
421
|
-
setSecretPreimage(secret) {
|
|
422
|
-
if (!this.isValidSecretPreimage(secret))
|
|
423
|
-
throw new Error("Invalid secret preimage provided, hash doesn't match!");
|
|
424
|
-
this.secret = secret;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Returns whether the secret preimage for this swap is known
|
|
428
|
-
*/
|
|
429
|
-
hasSecretPreimage() {
|
|
430
|
-
return this.secret != null;
|
|
431
|
-
}
|
|
432
|
-
//////////////////////////////
|
|
433
|
-
//// Execution
|
|
434
|
-
/**
|
|
435
|
-
* Executes the swap with the provided bitcoin lightning network wallet or LNURL
|
|
436
|
-
*
|
|
437
|
-
* @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
|
|
438
|
-
* quote was created, this is required for legacy swaps because the destination wallet needs to actively claim
|
|
439
|
-
* the swap funds on the destination (this also means you need native token to cover gas costs)
|
|
440
|
-
* @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
|
|
441
|
-
* link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
|
|
442
|
-
* @param callbacks Callbacks to track the progress of the swap
|
|
443
|
-
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
444
|
-
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
445
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
446
|
-
*/
|
|
447
|
-
async execute(dstSigner, walletOrLnurlWithdraw, callbacks, options) {
|
|
448
|
-
if (this._state === FromBTCLNSwapState.FAILED)
|
|
449
|
-
throw new Error("Swap failed!");
|
|
450
|
-
if (this._state === FromBTCLNSwapState.EXPIRED)
|
|
451
|
-
throw new Error("Swap HTLC expired!");
|
|
452
|
-
if (this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED)
|
|
453
|
-
throw new Error("Swap quote expired!");
|
|
454
|
-
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
455
|
-
throw new Error("Swap already settled!");
|
|
456
|
-
let abortSignal = options?.abortSignal;
|
|
457
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED) {
|
|
458
|
-
if (walletOrLnurlWithdraw != null && this.lnurl == null) {
|
|
459
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
460
|
-
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
461
|
-
if (typeof (walletOrLnurlWithdraw) === "string" || (0, LNURLWithdraw_1.isLNURLWithdraw)(walletOrLnurlWithdraw)) {
|
|
462
|
-
await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
const paymentPromise = walletOrLnurlWithdraw.payInvoice(this.pr);
|
|
466
|
-
const abortController = new AbortController();
|
|
467
|
-
paymentPromise.catch(e => abortController.abort(e));
|
|
468
|
-
if (options?.abortSignal != null)
|
|
469
|
-
options.abortSignal.addEventListener("abort", () => abortController.abort(options?.abortSignal?.reason));
|
|
470
|
-
abortSignal = abortController.signal;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
|
|
474
|
-
if (!paymentSuccess)
|
|
475
|
-
throw new Error("Failed to receive lightning network payment");
|
|
476
|
-
}
|
|
477
|
-
if (this._state === FromBTCLNSwapState.PR_PAID || this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
478
|
-
if (this.canCommitAndClaimInOneShot()) {
|
|
479
|
-
await this.commitAndClaim(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent, callbacks?.onDestinationClaimSent, options?.secret);
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
if (this._state === FromBTCLNSwapState.PR_PAID) {
|
|
483
|
-
await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
|
|
484
|
-
if (options?.delayBetweenCommitAndClaimSeconds != null)
|
|
485
|
-
await (0, TimeoutUtils_1.timeoutPromise)(options.delayBetweenCommitAndClaimSeconds * 1000, options?.abortSignal);
|
|
486
|
-
}
|
|
487
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
488
|
-
await this.claim(dstSigner, options?.abortSignal, callbacks?.onDestinationClaimSent, options?.secret);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
// @ts-ignore
|
|
493
|
-
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
494
|
-
if (callbacks?.onSwapSettled != null)
|
|
495
|
-
callbacks.onSwapSettled(this.getOutputTxId());
|
|
496
|
-
}
|
|
497
|
-
return true;
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* @inheritDoc
|
|
501
|
-
*
|
|
502
|
-
* @param options
|
|
503
|
-
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap
|
|
504
|
-
* wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
|
|
505
|
-
* can use `skipChecks=true`)
|
|
506
|
-
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
507
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
508
|
-
*/
|
|
509
|
-
async txsExecute(options) {
|
|
510
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED) {
|
|
511
|
-
if (!await this._verifyQuoteValid())
|
|
512
|
-
throw new Error("Quote already expired or close to expiry!");
|
|
513
|
-
return [
|
|
514
|
-
{
|
|
515
|
-
name: "Payment",
|
|
516
|
-
description: "Initiates the swap by paying up the lightning network invoice",
|
|
517
|
-
chain: "LIGHTNING",
|
|
518
|
-
txs: [
|
|
519
|
-
{
|
|
520
|
-
type: "BOLT11_PAYMENT_REQUEST",
|
|
521
|
-
address: this.getAddress(),
|
|
522
|
-
hyperlink: this.getHyperlink()
|
|
523
|
-
}
|
|
524
|
-
]
|
|
525
|
-
}
|
|
526
|
-
];
|
|
527
|
-
}
|
|
528
|
-
if (this._state === FromBTCLNSwapState.PR_PAID) {
|
|
529
|
-
if (!await this._verifyQuoteValid())
|
|
530
|
-
throw new Error("Quote already expired or close to expiry!");
|
|
531
|
-
const txsCommit = await this.txsCommit(options?.skipChecks);
|
|
532
|
-
const txsClaim = await this._txsClaim(undefined, options?.secret);
|
|
533
|
-
return [
|
|
534
|
-
{
|
|
535
|
-
name: "Commit",
|
|
536
|
-
description: `Creates the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
537
|
-
chain: this.chainIdentifier,
|
|
538
|
-
txs: txsCommit
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
name: "Claim",
|
|
542
|
-
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
543
|
-
chain: this.chainIdentifier,
|
|
544
|
-
txs: txsClaim
|
|
545
|
-
},
|
|
546
|
-
];
|
|
547
|
-
}
|
|
548
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
549
|
-
const txsClaim = await this.txsClaim(undefined, options?.secret);
|
|
550
|
-
return [
|
|
551
|
-
{
|
|
552
|
-
name: "Claim",
|
|
553
|
-
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
554
|
-
chain: this.chainIdentifier,
|
|
555
|
-
txs: txsClaim
|
|
556
|
-
},
|
|
557
|
-
];
|
|
558
|
-
}
|
|
559
|
-
throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED, PR_PAID or CLAIM_COMMITED");
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* @inheritDoc
|
|
563
|
-
*
|
|
564
|
-
* @param options
|
|
565
|
-
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap
|
|
566
|
-
* wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
|
|
567
|
-
* can use `skipChecks=true`)
|
|
568
|
-
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
569
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
570
|
-
*/
|
|
571
|
-
async getCurrentActions(options) {
|
|
572
|
-
try {
|
|
573
|
-
return await this.txsExecute(options);
|
|
574
|
-
}
|
|
575
|
-
catch (e) {
|
|
576
|
-
return [];
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
//////////////////////////////
|
|
580
|
-
//// Payment
|
|
581
|
-
/**
|
|
582
|
-
* Checks whether the LP received the LN payment and we can continue by committing & claiming the HTLC on-chain
|
|
583
|
-
*
|
|
584
|
-
* @param save If the new swap state should be saved
|
|
585
|
-
*
|
|
586
|
-
* @internal
|
|
587
|
-
*/
|
|
588
|
-
async _checkIntermediaryPaymentReceived(save = true) {
|
|
589
|
-
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
590
|
-
this._state === FromBTCLNSwapState.CLAIM_COMMITED ||
|
|
591
|
-
this._state === FromBTCLNSwapState.CLAIM_CLAIMED ||
|
|
592
|
-
this._state === FromBTCLNSwapState.FAILED ||
|
|
593
|
-
this._state === FromBTCLNSwapState.EXPIRED)
|
|
594
|
-
return true;
|
|
595
|
-
if (this._state === FromBTCLNSwapState.QUOTE_EXPIRED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null))
|
|
596
|
-
return false;
|
|
597
|
-
if (this.url == null)
|
|
598
|
-
return false;
|
|
599
|
-
const paymentHash = this.getPaymentHash();
|
|
600
|
-
if (paymentHash == null)
|
|
601
|
-
throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
|
|
602
|
-
const resp = await IntermediaryAPI_1.IntermediaryAPI.getPaymentAuthorization(this.url, paymentHash.toString("hex"));
|
|
603
|
-
switch (resp.code) {
|
|
604
|
-
case IntermediaryAPI_1.PaymentAuthorizationResponseCodes.AUTH_DATA:
|
|
605
|
-
const data = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
606
|
-
try {
|
|
607
|
-
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), data, resp.data);
|
|
608
|
-
this.expiry = await this.wrapper._contract.getInitAuthorizationExpiry(data, resp.data);
|
|
609
|
-
this._state = FromBTCLNSwapState.PR_PAID;
|
|
610
|
-
this._data = data;
|
|
611
|
-
this.signatureData = {
|
|
612
|
-
prefix: resp.data.prefix,
|
|
613
|
-
timeout: resp.data.timeout,
|
|
614
|
-
signature: resp.data.signature
|
|
615
|
-
};
|
|
616
|
-
this.initiated = true;
|
|
617
|
-
if (save)
|
|
618
|
-
await this._saveAndEmit();
|
|
619
|
-
return true;
|
|
620
|
-
}
|
|
621
|
-
catch (e) { }
|
|
622
|
-
return null;
|
|
623
|
-
case IntermediaryAPI_1.PaymentAuthorizationResponseCodes.EXPIRED:
|
|
624
|
-
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
625
|
-
this.initiated = true;
|
|
626
|
-
if (save)
|
|
627
|
-
await this._saveAndEmit();
|
|
628
|
-
return false;
|
|
629
|
-
default:
|
|
630
|
-
return null;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
/**
|
|
634
|
-
* Checks the data returned by the intermediary in the payment auth request
|
|
635
|
-
*
|
|
636
|
-
* @param signer Smart chain signer's address initiating the swap
|
|
637
|
-
* @param data Parsed swap data as returned by the intermediary
|
|
638
|
-
* @param signature Signature data as returned by the intermediary
|
|
639
|
-
*
|
|
640
|
-
* @throws {IntermediaryError} If the returned are not valid
|
|
641
|
-
* @throws {SignatureVerificationError} If the returned signature is not valid
|
|
642
|
-
* @throws {Error} If the swap is already committed on-chain
|
|
643
|
-
*
|
|
644
|
-
* @internal
|
|
645
|
-
*/
|
|
646
|
-
async checkIntermediaryReturnedAuthData(signer, data, signature) {
|
|
647
|
-
data.setClaimer(signer);
|
|
648
|
-
if (data.getType() !== base_1.ChainSwapType.HTLC)
|
|
649
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid swap type");
|
|
650
|
-
if (!data.isOfferer(this.getSwapData().getOfferer()))
|
|
651
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid offerer used");
|
|
652
|
-
if (!data.isClaimer(this._getInitiator()))
|
|
653
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid claimer used");
|
|
654
|
-
if (!data.isToken(this.getSwapData().getToken()))
|
|
655
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid token used");
|
|
656
|
-
if (data.getSecurityDeposit() > this.getSwapData().getSecurityDeposit())
|
|
657
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
|
|
658
|
-
if (data.getClaimerBounty() !== 0n)
|
|
659
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid claimer bounty!");
|
|
660
|
-
if (data.getAmount() < this.getSwapData().getAmount())
|
|
661
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid amount received!");
|
|
662
|
-
if (data.getClaimHash() !== this.getSwapData().getClaimHash())
|
|
663
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid payment hash used!");
|
|
664
|
-
if (!data.isDepositToken(this.getSwapData().getDepositToken()))
|
|
665
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid deposit token used!");
|
|
666
|
-
if (data.hasSuccessAction())
|
|
667
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid has success action");
|
|
668
|
-
await Promise.all([
|
|
669
|
-
this.wrapper._contract.isValidInitAuthorization(this._getInitiator(), data, signature, this.feeRate),
|
|
670
|
-
this.wrapper._contract.getCommitStatus(data.getClaimer(), data)
|
|
671
|
-
.then(status => {
|
|
672
|
-
if (status?.type !== base_1.SwapCommitStateType.NOT_COMMITED)
|
|
673
|
-
throw new Error("Swap already committed on-chain!");
|
|
674
|
-
})
|
|
675
|
-
]);
|
|
676
|
-
}
|
|
677
|
-
/**
|
|
678
|
-
* Waits till a lightning network payment is received by the intermediary and client
|
|
679
|
-
* can continue by initiating (committing) & settling (claiming) the HTLC by calling
|
|
680
|
-
* either the {@link commitAndClaim} function (if the underlying chain allows commit
|
|
681
|
-
* and claim in a single transaction - check with {@link canCommitAndClaimInOneShot}).
|
|
682
|
-
* Or call {@link commit} and then {@link claim} separately.
|
|
683
|
-
*
|
|
684
|
-
* If this swap is using an LNURL-withdraw link as input, it automatically posts the
|
|
685
|
-
* generated invoice to the LNURL service to pay it.
|
|
686
|
-
*
|
|
687
|
-
* @param onPaymentReceived Callback as for when the LP reports having received the ln payment
|
|
688
|
-
* @param abortSignal Abort signal to stop waiting for payment
|
|
689
|
-
* @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
|
|
690
|
-
*/
|
|
691
|
-
async waitForPayment(onPaymentReceived, checkIntervalSeconds, abortSignal) {
|
|
692
|
-
checkIntervalSeconds ??= 5;
|
|
693
|
-
if (this._state !== FromBTCLNSwapState.PR_CREATED &&
|
|
694
|
-
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData != null))
|
|
695
|
-
throw new Error("Must be in PR_CREATED state!");
|
|
696
|
-
if (this.url == null)
|
|
697
|
-
throw new Error("LP URL not known, cannot await the payment!");
|
|
698
|
-
const abortController = new AbortController();
|
|
699
|
-
if (abortSignal != null)
|
|
700
|
-
abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
701
|
-
let save = false;
|
|
702
|
-
if (this.lnurl != null && this.lnurlK1 != null && this.lnurlCallback != null && !this.prPosted) {
|
|
703
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
704
|
-
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
705
|
-
LNURL_1.LNURL.postInvoiceToLNURLWithdraw({ k1: this.lnurlK1, callback: this.lnurlCallback }, this.pr).catch(e => {
|
|
706
|
-
this.lnurlFailSignal.abort(e);
|
|
707
|
-
});
|
|
708
|
-
this.prPosted = true;
|
|
709
|
-
save ||= true;
|
|
710
|
-
}
|
|
711
|
-
if (!this.initiated) {
|
|
712
|
-
this.initiated = true;
|
|
713
|
-
save ||= true;
|
|
714
|
-
}
|
|
715
|
-
if (save)
|
|
716
|
-
await this._saveAndEmit();
|
|
717
|
-
let lnurlFailListener = () => abortController.abort(this.lnurlFailSignal.signal.reason);
|
|
718
|
-
this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
|
|
719
|
-
this.lnurlFailSignal.signal.throwIfAborted();
|
|
720
|
-
const paymentHash = this.getPaymentHash();
|
|
721
|
-
if (paymentHash == null)
|
|
722
|
-
throw new Error("Swap payment hash not available, the swap was probably recovered!");
|
|
723
|
-
let resp = { code: IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING, msg: "" };
|
|
724
|
-
while (!abortController.signal.aborted && resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING) {
|
|
725
|
-
resp = await IntermediaryAPI_1.IntermediaryAPI.getPaymentAuthorization(this.url, paymentHash.toString("hex"));
|
|
726
|
-
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING)
|
|
727
|
-
await (0, TimeoutUtils_1.timeoutPromise)(checkIntervalSeconds * 1000, abortController.signal);
|
|
728
|
-
}
|
|
729
|
-
this.lnurlFailSignal.signal.removeEventListener("abort", lnurlFailListener);
|
|
730
|
-
abortController.signal.throwIfAborted();
|
|
731
|
-
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.AUTH_DATA) {
|
|
732
|
-
const sigData = resp.data;
|
|
733
|
-
const swapData = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
734
|
-
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), swapData, sigData);
|
|
735
|
-
this.expiry = await this.wrapper._contract.getInitAuthorizationExpiry(swapData, sigData);
|
|
736
|
-
if (onPaymentReceived != null)
|
|
737
|
-
onPaymentReceived(this.getInputTxId());
|
|
738
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
739
|
-
this._data = swapData;
|
|
740
|
-
this.signatureData = {
|
|
741
|
-
prefix: sigData.prefix,
|
|
742
|
-
timeout: sigData.timeout,
|
|
743
|
-
signature: sigData.signature
|
|
744
|
-
};
|
|
745
|
-
await this._saveAndEmit(FromBTCLNSwapState.PR_PAID);
|
|
746
|
-
}
|
|
747
|
-
return true;
|
|
748
|
-
}
|
|
749
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
750
|
-
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.EXPIRED) {
|
|
751
|
-
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
752
|
-
}
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
throw new IntermediaryError_1.IntermediaryError("Invalid response from the LP");
|
|
756
|
-
}
|
|
757
|
-
//////////////////////////////
|
|
758
|
-
//// Commit
|
|
759
|
-
/**
|
|
760
|
-
* @inheritDoc
|
|
761
|
-
*
|
|
762
|
-
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
763
|
-
*/
|
|
764
|
-
async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
|
|
765
|
-
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
766
|
-
this.checkSigner(signer);
|
|
767
|
-
let txCount = 0;
|
|
768
|
-
const txs = await this.txsCommit(skipChecks);
|
|
769
|
-
const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
|
|
770
|
-
txCount++;
|
|
771
|
-
if (onBeforeTxSent != null && txCount === txs.length)
|
|
772
|
-
onBeforeTxSent(txId);
|
|
773
|
-
return Promise.resolve();
|
|
774
|
-
});
|
|
775
|
-
this._commitTxId = result[result.length - 1];
|
|
776
|
-
if (this._state === FromBTCLNSwapState.PR_PAID || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_EXPIRED) {
|
|
777
|
-
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
778
|
-
}
|
|
779
|
-
return this._commitTxId;
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* @inheritDoc
|
|
783
|
-
*/
|
|
784
|
-
async waitTillCommited(abortSignal) {
|
|
785
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
786
|
-
return Promise.resolve();
|
|
787
|
-
if (this._state !== FromBTCLNSwapState.PR_PAID && (this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null))
|
|
788
|
-
throw new Error("Invalid state");
|
|
789
|
-
const abortController = (0, Utils_1.extendAbortController)(abortSignal);
|
|
790
|
-
const result = await Promise.race([
|
|
791
|
-
this.watchdogWaitTillCommited(undefined, abortController.signal),
|
|
792
|
-
this.waitTillState(FromBTCLNSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
|
|
793
|
-
]);
|
|
794
|
-
abortController.abort();
|
|
795
|
-
if (result === 0)
|
|
796
|
-
this.logger.debug("waitTillCommited(): Resolved from state changed");
|
|
797
|
-
if (result === true)
|
|
798
|
-
this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
799
|
-
if (result === false) {
|
|
800
|
-
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
|
|
801
|
-
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
802
|
-
this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
803
|
-
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
804
|
-
}
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
808
|
-
this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
809
|
-
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
//////////////////////////////
|
|
813
|
-
//// Claim
|
|
814
|
-
/**
|
|
815
|
-
* Unsafe txs claim getter without state checking!
|
|
816
|
-
*
|
|
817
|
-
* @param _signer
|
|
818
|
-
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
819
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
820
|
-
*
|
|
821
|
-
* @internal
|
|
822
|
-
*/
|
|
823
|
-
async _txsClaim(_signer, secret) {
|
|
824
|
-
let address = undefined;
|
|
825
|
-
if (_signer != null) {
|
|
826
|
-
if (typeof (_signer) === "string") {
|
|
827
|
-
address = _signer;
|
|
828
|
-
}
|
|
829
|
-
else if ((0, base_1.isAbstractSigner)(_signer)) {
|
|
830
|
-
address = _signer.getAddress();
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
if (this._data == null)
|
|
837
|
-
throw new Error("Unknown data, wrong state?");
|
|
838
|
-
const useSecret = secret ?? this.secret;
|
|
839
|
-
if (useSecret == null)
|
|
840
|
-
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
|
|
841
|
-
if (!this.isValidSecretPreimage(useSecret))
|
|
842
|
-
throw new Error("Invalid swap secret pre-image provided!");
|
|
843
|
-
return this.wrapper._contract.txsClaimWithSecret(address ?? this._getInitiator(), this._data, useSecret, true, true);
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* @inheritDoc
|
|
847
|
-
*
|
|
848
|
-
* @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
|
|
849
|
-
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
850
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
851
|
-
*
|
|
852
|
-
* @throws {Error} If in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
|
|
853
|
-
*/
|
|
854
|
-
async txsClaim(_signer, secret) {
|
|
855
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED)
|
|
856
|
-
throw new Error("Must be in CLAIM_COMMITED state!");
|
|
857
|
-
return this._txsClaim(_signer, secret);
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* @inheritDoc
|
|
861
|
-
*
|
|
862
|
-
* @param _signer
|
|
863
|
-
* @param abortSignal
|
|
864
|
-
* @param onBeforeTxSent
|
|
865
|
-
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
866
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
867
|
-
*/
|
|
868
|
-
async claim(_signer, abortSignal, onBeforeTxSent, secret) {
|
|
869
|
-
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
870
|
-
let txCount = 0;
|
|
871
|
-
const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsClaim(_signer, secret), true, abortSignal, undefined, (txId) => {
|
|
872
|
-
txCount++;
|
|
873
|
-
if (onBeforeTxSent != null && txCount === 1)
|
|
874
|
-
onBeforeTxSent(txId);
|
|
875
|
-
return Promise.resolve();
|
|
876
|
-
});
|
|
877
|
-
this._claimTxId = result[0];
|
|
878
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.EXPIRED || this._state === FromBTCLNSwapState.FAILED) {
|
|
879
|
-
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
880
|
-
}
|
|
881
|
-
return result[0];
|
|
882
|
-
}
|
|
883
|
-
/**
|
|
884
|
-
* @inheritDoc
|
|
885
|
-
*
|
|
886
|
-
* @throws {Error} If swap is in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
|
|
887
|
-
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
888
|
-
*/
|
|
889
|
-
async waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
|
|
890
|
-
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
891
|
-
return Promise.resolve(true);
|
|
892
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED)
|
|
893
|
-
throw new Error("Invalid state (not CLAIM_COMMITED)");
|
|
894
|
-
const abortController = new AbortController();
|
|
895
|
-
if (abortSignal != null)
|
|
896
|
-
abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
897
|
-
let timedOut = false;
|
|
898
|
-
if (maxWaitTimeSeconds != null) {
|
|
899
|
-
const timeout = setTimeout(() => {
|
|
900
|
-
timedOut = true;
|
|
901
|
-
abortController.abort();
|
|
902
|
-
}, maxWaitTimeSeconds * 1000);
|
|
903
|
-
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
904
|
-
}
|
|
905
|
-
let res;
|
|
906
|
-
try {
|
|
907
|
-
res = await Promise.race([
|
|
908
|
-
this.watchdogWaitTillResult(undefined, abortController.signal),
|
|
909
|
-
this.waitTillState(FromBTCLNSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
|
|
910
|
-
this.waitTillState(FromBTCLNSwapState.EXPIRED, "eq", abortController.signal).then(() => 1),
|
|
911
|
-
]);
|
|
912
|
-
abortController.abort();
|
|
913
|
-
}
|
|
914
|
-
catch (e) {
|
|
915
|
-
abortController.abort();
|
|
916
|
-
if (timedOut)
|
|
917
|
-
return false;
|
|
918
|
-
throw e;
|
|
919
|
-
}
|
|
920
|
-
if (res === 0) {
|
|
921
|
-
this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
|
|
922
|
-
return true;
|
|
923
|
-
}
|
|
924
|
-
if (res === 1) {
|
|
925
|
-
this.logger.debug("waitTillClaimed(): Resolved from state change (EXPIRED)");
|
|
926
|
-
throw new Error("Swap expired during claiming");
|
|
927
|
-
}
|
|
928
|
-
this.logger.debug("waitTillClaimed(): Resolved from watchdog");
|
|
929
|
-
if (res?.type === base_1.SwapCommitStateType.PAID) {
|
|
930
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
931
|
-
this._claimTxId = await res.getClaimTxId();
|
|
932
|
-
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
|
|
936
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED &&
|
|
937
|
-
this._state !== FromBTCLNSwapState.FAILED) {
|
|
938
|
-
if (res.getRefundTxId != null)
|
|
939
|
-
this._refundTxId = await res.getRefundTxId();
|
|
940
|
-
await this._saveAndEmit(FromBTCLNSwapState.FAILED);
|
|
941
|
-
}
|
|
942
|
-
throw new Error("Swap expired while waiting for claim!");
|
|
943
|
-
}
|
|
944
|
-
return true;
|
|
945
|
-
}
|
|
946
|
-
//////////////////////////////
|
|
947
|
-
//// Commit & claim
|
|
948
|
-
/**
|
|
949
|
-
* Estimated transaction fee for commit & claim transactions combined, required
|
|
950
|
-
* to settle the swap on the smart chain destination side.
|
|
951
|
-
*/
|
|
952
|
-
async getCommitAndClaimNetworkFee() {
|
|
953
|
-
const swapContract = this.wrapper._contract;
|
|
954
|
-
const feeRate = this.feeRate ?? await swapContract.getInitFeeRate(this.getSwapData().getOfferer(), this.getSwapData().getClaimer(), this.getSwapData().getToken(), this.getSwapData().getClaimHash());
|
|
955
|
-
const commitFee = await (swapContract.getRawCommitFee != null ?
|
|
956
|
-
swapContract.getRawCommitFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
957
|
-
swapContract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate));
|
|
958
|
-
const claimFee = await (swapContract.getRawClaimFee != null ?
|
|
959
|
-
swapContract.getRawClaimFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
960
|
-
swapContract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate));
|
|
961
|
-
return (0, TokenAmount_1.toTokenAmount)(commitFee + claimFee, this.wrapper._getNativeToken(), this.wrapper._prices);
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Returns whether the underlying chain supports calling commit and claim in a single call,
|
|
965
|
-
* such that you can use the {@link commitAndClaim} function. If not you have to manually
|
|
966
|
-
* call {@link commit} first and then {@link claim}.
|
|
967
|
-
*/
|
|
968
|
-
canCommitAndClaimInOneShot() {
|
|
969
|
-
return this.wrapper._contract.initAndClaimWithSecret != null;
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* Returns transactions for both commit & claim operation together, such that they can be signed all at once by
|
|
973
|
-
* the wallet. **WARNING**: transactions must be sent sequentially, such that the claim (2nd) transaction is only
|
|
974
|
-
* sent after the commit (1st) transaction confirms. Failure to do so can reveal the HTLC pre-image too soon,
|
|
975
|
-
* opening a possibility for the LP to steal funds!
|
|
976
|
-
*
|
|
977
|
-
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
978
|
-
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
979
|
-
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
980
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
981
|
-
*
|
|
982
|
-
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
983
|
-
*/
|
|
984
|
-
async txsCommitAndClaim(skipChecks, secret) {
|
|
985
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED)
|
|
986
|
-
return await this.txsClaim(undefined, secret);
|
|
987
|
-
if (this._state !== FromBTCLNSwapState.PR_PAID &&
|
|
988
|
-
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData == null))
|
|
989
|
-
throw new Error("Must be in PR_PAID state!");
|
|
990
|
-
if (this._data == null)
|
|
991
|
-
throw new Error("Unknown data, wrong state?");
|
|
992
|
-
const useSecret = secret ?? this.secret;
|
|
993
|
-
if (useSecret == null)
|
|
994
|
-
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as second argument");
|
|
995
|
-
if (!this.isValidSecretPreimage(useSecret))
|
|
996
|
-
throw new Error("Invalid swap secret pre-image provided!");
|
|
997
|
-
const initTxs = await this.txsCommit(skipChecks);
|
|
998
|
-
const claimTxs = await this.wrapper._contract.txsClaimWithSecret(this._getInitiator(), this._data, useSecret, true, true, undefined, true);
|
|
999
|
-
return initTxs.concat(claimTxs);
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Commits and claims the swap, in a way that the transactions can be signed together by the provided signer and
|
|
1003
|
-
* then automatically sent sequentially by the SDK. To check if the underlying chain supports this flow check
|
|
1004
|
-
* the {@link canCommitAndClaimInOneShot} function.
|
|
1005
|
-
*
|
|
1006
|
-
* @param _signer Signer to sign the transactions with, must be the same as used in the initialization
|
|
1007
|
-
* @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
|
|
1008
|
-
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
1009
|
-
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
1010
|
-
* @param onBeforeCommitTxSent Optional callback called before the initialization (commit) transaction is
|
|
1011
|
-
* broadcasted
|
|
1012
|
-
* @param onBeforeClaimTxSent Optional callback called before the settlement (claim) transaction is
|
|
1013
|
-
* broadcasted
|
|
1014
|
-
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
1015
|
-
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
1016
|
-
*
|
|
1017
|
-
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
1018
|
-
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
1019
|
-
*/
|
|
1020
|
-
async commitAndClaim(_signer, abortSignal, skipChecks, onBeforeCommitTxSent, onBeforeClaimTxSent, secret) {
|
|
1021
|
-
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
1022
|
-
if (!this.canCommitAndClaimInOneShot())
|
|
1023
|
-
throw new Error("Cannot commitAndClaim in single action, please run commit and claim separately!");
|
|
1024
|
-
this.checkSigner(signer);
|
|
1025
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED)
|
|
1026
|
-
return [await this.claim(signer, abortSignal, onBeforeClaimTxSent, secret)];
|
|
1027
|
-
let txCount = 0;
|
|
1028
|
-
const txs = await this.txsCommitAndClaim(skipChecks, secret);
|
|
1029
|
-
const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
|
|
1030
|
-
txCount++;
|
|
1031
|
-
if (onBeforeCommitTxSent != null && txCount === 1)
|
|
1032
|
-
onBeforeCommitTxSent(txId);
|
|
1033
|
-
if (onBeforeClaimTxSent != null && txCount === txs.length)
|
|
1034
|
-
onBeforeClaimTxSent(txId);
|
|
1035
|
-
return Promise.resolve();
|
|
1036
|
-
});
|
|
1037
|
-
this._commitTxId = result[0] ?? this._commitTxId;
|
|
1038
|
-
this._claimTxId = result[result.length - 1] ?? this._claimTxId;
|
|
1039
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
1040
|
-
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
1041
|
-
}
|
|
1042
|
-
return result;
|
|
1043
|
-
}
|
|
1044
|
-
//////////////////////////////
|
|
1045
|
-
//// LNURL
|
|
1046
|
-
/**
|
|
1047
|
-
* Whether this swap uses an LNURL-withdraw link
|
|
1048
|
-
*/
|
|
1049
|
-
isLNURL() {
|
|
1050
|
-
return this.lnurl != null;
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Gets the used LNURL or `null` if this is not an LNURL-withdraw swap
|
|
1054
|
-
*/
|
|
1055
|
-
getLNURL() {
|
|
1056
|
-
return this.lnurl ?? null;
|
|
1057
|
-
}
|
|
1058
|
-
/**
|
|
1059
|
-
* Pay the generated lightning network invoice with an LNURL-withdraw link, this
|
|
1060
|
-
* is useful when you want to display a lightning payment QR code and also want to
|
|
1061
|
-
* allow payments using LNURL-withdraw NFC cards.
|
|
1062
|
-
*
|
|
1063
|
-
* Note that the swap needs to be created **without** an LNURL to begin with for this function
|
|
1064
|
-
* to work. If this swap is already using an LNURL-withdraw link, this function throws.
|
|
1065
|
-
*/
|
|
1066
|
-
async settleWithLNURLWithdraw(lnurl) {
|
|
1067
|
-
if (this._state !== FromBTCLNSwapState.PR_CREATED &&
|
|
1068
|
-
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData != null))
|
|
1069
|
-
throw new Error("Must be in PR_CREATED state!");
|
|
1070
|
-
if (this.lnurl != null)
|
|
1071
|
-
throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
|
|
1072
|
-
let lnurlParams;
|
|
1073
|
-
if (typeof (lnurl) === "string") {
|
|
1074
|
-
const parsedLNURL = await LNURL_1.LNURL.getLNURL(lnurl);
|
|
1075
|
-
if (parsedLNURL == null || parsedLNURL.tag !== "withdrawRequest")
|
|
1076
|
-
throw new UserError_1.UserError("Invalid LNURL-withdraw to settle the swap");
|
|
1077
|
-
lnurlParams = parsedLNURL;
|
|
1078
|
-
}
|
|
1079
|
-
else {
|
|
1080
|
-
lnurlParams = lnurl.params;
|
|
1081
|
-
}
|
|
1082
|
-
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
1083
|
-
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
1084
|
-
LNURL_1.LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
|
|
1085
|
-
this.lnurl = lnurlParams.url;
|
|
1086
|
-
this.lnurlCallback = lnurlParams.callback;
|
|
1087
|
-
this.lnurlK1 = lnurlParams.k1;
|
|
1088
|
-
this.prPosted = true;
|
|
1089
|
-
await this._saveAndEmit();
|
|
1090
|
-
}
|
|
1091
|
-
//////////////////////////////
|
|
1092
|
-
//// Storage
|
|
1093
|
-
/**
|
|
1094
|
-
* @inheritDoc
|
|
1095
|
-
*/
|
|
1096
|
-
serialize() {
|
|
1097
|
-
return {
|
|
1098
|
-
...super.serialize(),
|
|
1099
|
-
pr: this.pr,
|
|
1100
|
-
secret: this.secret,
|
|
1101
|
-
lnurl: this.lnurl,
|
|
1102
|
-
lnurlK1: this.lnurlK1,
|
|
1103
|
-
lnurlCallback: this.lnurlCallback,
|
|
1104
|
-
prPosted: this.prPosted,
|
|
1105
|
-
initialSwapData: this.initialSwapData.serialize(),
|
|
1106
|
-
usesClaimHashAsId: this.usesClaimHashAsId
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
//////////////////////////////
|
|
1110
|
-
//// Swap ticks & sync
|
|
1111
|
-
/**
|
|
1112
|
-
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
1113
|
-
* data
|
|
1114
|
-
*
|
|
1115
|
-
* @private
|
|
1116
|
-
*/
|
|
1117
|
-
async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
|
|
1118
|
-
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
1119
|
-
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null) ||
|
|
1120
|
-
this._state === FromBTCLNSwapState.CLAIM_COMMITED ||
|
|
1121
|
-
this._state === FromBTCLNSwapState.EXPIRED) {
|
|
1122
|
-
//Check for expiry before the getCommitStatus to prevent race conditions
|
|
1123
|
-
let quoteExpired = false;
|
|
1124
|
-
if (this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null)) {
|
|
1125
|
-
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
1126
|
-
}
|
|
1127
|
-
//Check if it's already successfully paid
|
|
1128
|
-
commitStatus ??= await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
1129
|
-
if (commitStatus != null && await this._forciblySetOnchainState(commitStatus))
|
|
1130
|
-
return true;
|
|
1131
|
-
//Set the state on expiry here
|
|
1132
|
-
if (this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null)) {
|
|
1133
|
-
if (quoteExpired) {
|
|
1134
|
-
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1135
|
-
return true;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
return false;
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* @inheritDoc
|
|
1143
|
-
* @internal
|
|
1144
|
-
*/
|
|
1145
|
-
_shouldFetchExpiryStatus() {
|
|
1146
|
-
return this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null);
|
|
1147
|
-
}
|
|
1148
|
-
/**
|
|
1149
|
-
* @inheritDoc
|
|
1150
|
-
* @internal
|
|
1151
|
-
*/
|
|
1152
|
-
_shouldFetchOnchainState() {
|
|
1153
|
-
return this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null) ||
|
|
1154
|
-
this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.EXPIRED;
|
|
1155
|
-
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Whether an intermediary (LP) should be contacted to get the state of this swap.
|
|
1158
|
-
*
|
|
1159
|
-
* @internal
|
|
1160
|
-
*/
|
|
1161
|
-
_shouldCheckIntermediary() {
|
|
1162
|
-
return this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null);
|
|
1163
|
-
}
|
|
1164
|
-
/**
|
|
1165
|
-
* @inheritDoc
|
|
1166
|
-
* @internal
|
|
1167
|
-
*/
|
|
1168
|
-
async _sync(save, quoteDefinitelyExpired, commitStatus, skipLpCheck) {
|
|
1169
|
-
let changed = false;
|
|
1170
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
1171
|
-
if (this._state != FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.getTimeoutTime() < Date.now()) {
|
|
1172
|
-
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1173
|
-
changed ||= true;
|
|
1174
|
-
}
|
|
1175
|
-
if (!skipLpCheck)
|
|
1176
|
-
try {
|
|
1177
|
-
const result = await this._checkIntermediaryPaymentReceived(false);
|
|
1178
|
-
if (result !== null)
|
|
1179
|
-
changed ||= true;
|
|
1180
|
-
}
|
|
1181
|
-
catch (e) {
|
|
1182
|
-
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1183
|
-
}
|
|
1184
|
-
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
1185
|
-
if (await this._verifyQuoteDefinitelyExpired()) {
|
|
1186
|
-
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1187
|
-
changed ||= true;
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
if (await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus))
|
|
1192
|
-
changed = true;
|
|
1193
|
-
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
1194
|
-
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data);
|
|
1195
|
-
if (expired) {
|
|
1196
|
-
this._state = FromBTCLNSwapState.EXPIRED;
|
|
1197
|
-
changed = true;
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
if (save && changed)
|
|
1201
|
-
await this._saveAndEmit();
|
|
1202
|
-
return changed;
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* @inheritDoc
|
|
1206
|
-
* @internal
|
|
1207
|
-
*/
|
|
1208
|
-
async _forciblySetOnchainState(commitStatus) {
|
|
1209
|
-
switch (commitStatus.type) {
|
|
1210
|
-
case base_1.SwapCommitStateType.PAID:
|
|
1211
|
-
if (this._claimTxId == null)
|
|
1212
|
-
this._claimTxId = await commitStatus.getClaimTxId();
|
|
1213
|
-
if (this.secret == null || this.pr == null)
|
|
1214
|
-
this._setSwapSecret(await commitStatus.getClaimResult());
|
|
1215
|
-
this._state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
1216
|
-
return true;
|
|
1217
|
-
case base_1.SwapCommitStateType.NOT_COMMITED:
|
|
1218
|
-
if (this._refundTxId == null && commitStatus.getRefundTxId)
|
|
1219
|
-
this._refundTxId = await commitStatus.getRefundTxId();
|
|
1220
|
-
if (this._refundTxId != null) {
|
|
1221
|
-
this._state = FromBTCLNSwapState.FAILED;
|
|
1222
|
-
return true;
|
|
1223
|
-
}
|
|
1224
|
-
break;
|
|
1225
|
-
case base_1.SwapCommitStateType.EXPIRED:
|
|
1226
|
-
if (this._refundTxId == null && commitStatus.getRefundTxId)
|
|
1227
|
-
this._refundTxId = await commitStatus.getRefundTxId();
|
|
1228
|
-
this._state = this._refundTxId == null ? FromBTCLNSwapState.QUOTE_EXPIRED : FromBTCLNSwapState.FAILED;
|
|
1229
|
-
return true;
|
|
1230
|
-
case base_1.SwapCommitStateType.COMMITED:
|
|
1231
|
-
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED && this._state !== FromBTCLNSwapState.EXPIRED) {
|
|
1232
|
-
this._state = FromBTCLNSwapState.CLAIM_COMMITED;
|
|
1233
|
-
return true;
|
|
1234
|
-
}
|
|
1235
|
-
break;
|
|
1236
|
-
}
|
|
1237
|
-
return false;
|
|
1238
|
-
}
|
|
1239
|
-
/**
|
|
1240
|
-
* @inheritDoc
|
|
1241
|
-
* @internal
|
|
1242
|
-
*/
|
|
1243
|
-
async _tick(save) {
|
|
1244
|
-
switch (this._state) {
|
|
1245
|
-
case FromBTCLNSwapState.PR_CREATED:
|
|
1246
|
-
if (this.getTimeoutTime() < Date.now()) {
|
|
1247
|
-
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1248
|
-
if (save)
|
|
1249
|
-
await this._saveAndEmit();
|
|
1250
|
-
return true;
|
|
1251
|
-
}
|
|
1252
|
-
break;
|
|
1253
|
-
case FromBTCLNSwapState.PR_PAID:
|
|
1254
|
-
if (this.expiry < Date.now()) {
|
|
1255
|
-
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1256
|
-
if (save)
|
|
1257
|
-
await this._saveAndEmit();
|
|
1258
|
-
return true;
|
|
1259
|
-
}
|
|
1260
|
-
break;
|
|
1261
|
-
case FromBTCLNSwapState.CLAIM_COMMITED:
|
|
1262
|
-
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data);
|
|
1263
|
-
if (expired) {
|
|
1264
|
-
this._state = FromBTCLNSwapState.EXPIRED;
|
|
1265
|
-
if (save)
|
|
1266
|
-
await this._saveAndEmit();
|
|
1267
|
-
return true;
|
|
1268
|
-
}
|
|
1269
|
-
break;
|
|
1270
|
-
}
|
|
1271
|
-
return false;
|
|
1272
|
-
}
|
|
1273
|
-
/**
|
|
1274
|
-
* Forcibly sets the swap secret pre-image from on-chain data
|
|
1275
|
-
*
|
|
1276
|
-
* @internal
|
|
1277
|
-
*/
|
|
1278
|
-
_setSwapSecret(secret) {
|
|
1279
|
-
this.secret = secret;
|
|
1280
|
-
if (this.pr == null) {
|
|
1281
|
-
this.pr = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex"))).toString("hex");
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
exports.FromBTCLNSwap = FromBTCLNSwap;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FromBTCLNSwap = exports.FromBTCLNSwapState = void 0;
|
|
4
|
+
exports.isFromBTCLNSwapInit = isFromBTCLNSwapInit;
|
|
5
|
+
const bolt11_1 = require("@atomiqlabs/bolt11");
|
|
6
|
+
const IFromBTCSelfInitSwap_1 = require("../IFromBTCSelfInitSwap");
|
|
7
|
+
const SwapType_1 = require("../../../../enums/SwapType");
|
|
8
|
+
const base_1 = require("@atomiqlabs/base");
|
|
9
|
+
const buffer_1 = require("buffer");
|
|
10
|
+
const LNURL_1 = require("../../../../lnurl/LNURL");
|
|
11
|
+
const UserError_1 = require("../../../../errors/UserError");
|
|
12
|
+
const IntermediaryAPI_1 = require("../../../../intermediaries/apis/IntermediaryAPI");
|
|
13
|
+
const IntermediaryError_1 = require("../../../../errors/IntermediaryError");
|
|
14
|
+
const Utils_1 = require("../../../../utils/Utils");
|
|
15
|
+
const IEscrowSelfInitSwap_1 = require("../../IEscrowSelfInitSwap");
|
|
16
|
+
const TokenAmount_1 = require("../../../../types/TokenAmount");
|
|
17
|
+
const Token_1 = require("../../../../types/Token");
|
|
18
|
+
const Logger_1 = require("../../../../utils/Logger");
|
|
19
|
+
const TimeoutUtils_1 = require("../../../../utils/TimeoutUtils");
|
|
20
|
+
const LNURLWithdraw_1 = require("../../../../types/lnurl/LNURLWithdraw");
|
|
21
|
+
const sha2_1 = require("@noble/hashes/sha2");
|
|
22
|
+
/**
|
|
23
|
+
* State enum for legacy Lightning -> Smart chain swaps
|
|
24
|
+
* @category Swaps/Legacy/Lightning → Smart chain
|
|
25
|
+
*/
|
|
26
|
+
var FromBTCLNSwapState;
|
|
27
|
+
(function (FromBTCLNSwapState) {
|
|
28
|
+
/**
|
|
29
|
+
* Swap has failed as the user didn't settle the HTLC on the destination before expiration
|
|
30
|
+
*/
|
|
31
|
+
FromBTCLNSwapState[FromBTCLNSwapState["FAILED"] = -4] = "FAILED";
|
|
32
|
+
/**
|
|
33
|
+
* Swap has expired for good and there is no way how it can be executed anymore
|
|
34
|
+
*/
|
|
35
|
+
FromBTCLNSwapState[FromBTCLNSwapState["QUOTE_EXPIRED"] = -3] = "QUOTE_EXPIRED";
|
|
36
|
+
/**
|
|
37
|
+
* A swap is almost expired, and it should be presented to the user as expired, though
|
|
38
|
+
* there is still a chance that it will be processed
|
|
39
|
+
*/
|
|
40
|
+
FromBTCLNSwapState[FromBTCLNSwapState["QUOTE_SOFT_EXPIRED"] = -2] = "QUOTE_SOFT_EXPIRED";
|
|
41
|
+
/**
|
|
42
|
+
* Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the
|
|
43
|
+
* swap on the destination smart chain.
|
|
44
|
+
*/
|
|
45
|
+
FromBTCLNSwapState[FromBTCLNSwapState["EXPIRED"] = -1] = "EXPIRED";
|
|
46
|
+
/**
|
|
47
|
+
* Swap quote was created, use {@link FromBTCLNSwap.getAddress} or {@link FromBTCLNSwap.getHyperlink}
|
|
48
|
+
* to get the bolt11 lightning network invoice to pay to initiate the swap, then use the
|
|
49
|
+
* {@link FromBTCLNSwap.waitForPayment} to wait till the lightning network payment is received
|
|
50
|
+
* by the intermediary (LP)
|
|
51
|
+
*/
|
|
52
|
+
FromBTCLNSwapState[FromBTCLNSwapState["PR_CREATED"] = 0] = "PR_CREATED";
|
|
53
|
+
/**
|
|
54
|
+
* Lightning network payment has been received by the intermediary (LP), the user can now settle
|
|
55
|
+
* the swap on the destination smart chain side with {@link FromBTCLNSwap.commitAndClaim} (if
|
|
56
|
+
* the underlying chain supports it - check with {@link FromBTCLNSwap.canCommitAndClaimInOneShot}),
|
|
57
|
+
* or by calling {@link FromBTCLNSwap.commit} and {@link FromBTCLNSwap.claim} separately.
|
|
58
|
+
*/
|
|
59
|
+
FromBTCLNSwapState[FromBTCLNSwapState["PR_PAID"] = 1] = "PR_PAID";
|
|
60
|
+
/**
|
|
61
|
+
* Swap escrow HTLC has been created on the destination chain. Continue by claiming it with the
|
|
62
|
+
* {@link FromBTCLNSwap.claim} or {@link FromBTCLNSwap.txsClaim} function.
|
|
63
|
+
*/
|
|
64
|
+
FromBTCLNSwapState[FromBTCLNSwapState["CLAIM_COMMITED"] = 2] = "CLAIM_COMMITED";
|
|
65
|
+
/**
|
|
66
|
+
* Swap successfully settled and funds received on the destination chain
|
|
67
|
+
*/
|
|
68
|
+
FromBTCLNSwapState[FromBTCLNSwapState["CLAIM_CLAIMED"] = 3] = "CLAIM_CLAIMED";
|
|
69
|
+
})(FromBTCLNSwapState || (exports.FromBTCLNSwapState = FromBTCLNSwapState = {}));
|
|
70
|
+
const FromBTCLNSwapStateDescription = {
|
|
71
|
+
[FromBTCLNSwapState.FAILED]: "Swap has failed as the user didn't settle the HTLC on the destination before expiration",
|
|
72
|
+
[FromBTCLNSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
|
|
73
|
+
[FromBTCLNSwapState.QUOTE_SOFT_EXPIRED]: "Swap is expired, though there is still a chance that it will be processed",
|
|
74
|
+
[FromBTCLNSwapState.EXPIRED]: "Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the swap on the destination smart chain.",
|
|
75
|
+
[FromBTCLNSwapState.PR_CREATED]: "Swap quote was created, pay the bolt11 lightning network invoice to initiate the swap, then use the wait till the lightning network payment is received by the intermediary (LP)",
|
|
76
|
+
[FromBTCLNSwapState.PR_PAID]: "Lightning network payment has been received by the intermediary (LP), the user can now settle the swap on the destination smart chain side.",
|
|
77
|
+
[FromBTCLNSwapState.CLAIM_COMMITED]: "Swap escrow HTLC has been created on the destination chain. Continue by claiming it.",
|
|
78
|
+
[FromBTCLNSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
|
|
79
|
+
};
|
|
80
|
+
function isFromBTCLNSwapInit(obj) {
|
|
81
|
+
return (obj.pr == null || typeof obj.pr === "string") &&
|
|
82
|
+
(obj.secret == null || typeof obj.secret === "string") &&
|
|
83
|
+
(obj.lnurl == null || typeof (obj.lnurl) === "string") &&
|
|
84
|
+
(obj.lnurlK1 == null || typeof (obj.lnurlK1) === "string") &&
|
|
85
|
+
(obj.lnurlCallback == null || typeof (obj.lnurlCallback) === "string") &&
|
|
86
|
+
(0, IEscrowSelfInitSwap_1.isIEscrowSelfInitSwapInit)(obj);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Legacy escrow (HTLC) based swap for Bitcoin Lightning -> Smart chains, requires manual settlement
|
|
90
|
+
* of the swap on the destination network once the lightning network payment is received by the LP.
|
|
91
|
+
*
|
|
92
|
+
* @category Swaps/Legacy/Lightning → Smart chain
|
|
93
|
+
*/
|
|
94
|
+
class FromBTCLNSwap extends IFromBTCSelfInitSwap_1.IFromBTCSelfInitSwap {
|
|
95
|
+
/**
|
|
96
|
+
* Sets the LNURL data for the swap
|
|
97
|
+
*
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
_setLNURLData(lnurl, lnurlK1, lnurlCallback) {
|
|
101
|
+
this.lnurl = lnurl;
|
|
102
|
+
this.lnurlK1 = lnurlK1;
|
|
103
|
+
this.lnurlCallback = lnurlCallback;
|
|
104
|
+
}
|
|
105
|
+
constructor(wrapper, initOrObject) {
|
|
106
|
+
if (isFromBTCLNSwapInit(initOrObject) && initOrObject.url != null)
|
|
107
|
+
initOrObject.url += "/frombtcln";
|
|
108
|
+
super(wrapper, initOrObject);
|
|
109
|
+
this.TYPE = SwapType_1.SwapType.FROM_BTCLN;
|
|
110
|
+
/**
|
|
111
|
+
* @internal
|
|
112
|
+
*/
|
|
113
|
+
this.swapStateName = (state) => FromBTCLNSwapState[state];
|
|
114
|
+
/**
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
this.swapStateDescription = FromBTCLNSwapStateDescription;
|
|
118
|
+
/**
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
this.inputToken = Token_1.BitcoinTokens.BTCLN;
|
|
122
|
+
this.lnurlFailSignal = new AbortController();
|
|
123
|
+
this.prPosted = false;
|
|
124
|
+
if (isFromBTCLNSwapInit(initOrObject)) {
|
|
125
|
+
this._state = FromBTCLNSwapState.PR_CREATED;
|
|
126
|
+
this.pr = initOrObject.pr;
|
|
127
|
+
this.secret = initOrObject.secret;
|
|
128
|
+
this.initialSwapData = initOrObject.initialSwapData;
|
|
129
|
+
this.lnurl = initOrObject.lnurl;
|
|
130
|
+
this.lnurlK1 = initOrObject.lnurlK1;
|
|
131
|
+
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
132
|
+
this.usesClaimHashAsId = true;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.pr = initOrObject.pr;
|
|
136
|
+
this.secret = initOrObject.secret;
|
|
137
|
+
if (initOrObject.initialSwapData == null) {
|
|
138
|
+
this.initialSwapData = this._data;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.initialSwapData = base_1.SwapData.deserialize(initOrObject.initialSwapData);
|
|
142
|
+
}
|
|
143
|
+
this.lnurl = initOrObject.lnurl;
|
|
144
|
+
this.lnurlK1 = initOrObject.lnurlK1;
|
|
145
|
+
this.lnurlCallback = initOrObject.lnurlCallback;
|
|
146
|
+
this.prPosted = initOrObject.prPosted;
|
|
147
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED && this._data != null) {
|
|
148
|
+
this.initialSwapData = this._data;
|
|
149
|
+
delete this._data;
|
|
150
|
+
}
|
|
151
|
+
this.usesClaimHashAsId = initOrObject.usesClaimHashAsId ?? false;
|
|
152
|
+
}
|
|
153
|
+
this.tryRecomputeSwapPrice();
|
|
154
|
+
this.logger = (0, Logger_1.getLogger)("FromBTCLN(" + this.getIdentifierHashString() + "): ");
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* @inheritDoc
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
getSwapData() {
|
|
161
|
+
return this._data ?? this.initialSwapData;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* @inheritDoc
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
167
|
+
upgradeVersion() {
|
|
168
|
+
if (this.version == null) {
|
|
169
|
+
switch (this._state) {
|
|
170
|
+
case -2:
|
|
171
|
+
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
172
|
+
break;
|
|
173
|
+
case -1:
|
|
174
|
+
this._state = FromBTCLNSwapState.FAILED;
|
|
175
|
+
break;
|
|
176
|
+
case 0:
|
|
177
|
+
this._state = FromBTCLNSwapState.PR_CREATED;
|
|
178
|
+
break;
|
|
179
|
+
case 1:
|
|
180
|
+
this._state = FromBTCLNSwapState.PR_PAID;
|
|
181
|
+
break;
|
|
182
|
+
case 2:
|
|
183
|
+
this._state = FromBTCLNSwapState.CLAIM_COMMITED;
|
|
184
|
+
break;
|
|
185
|
+
case 3:
|
|
186
|
+
this._state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
this.version = 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//////////////////////////////
|
|
193
|
+
//// Getters & utils
|
|
194
|
+
/**
|
|
195
|
+
* @inheritDoc
|
|
196
|
+
* @internal
|
|
197
|
+
*/
|
|
198
|
+
getIdentifierHash() {
|
|
199
|
+
const idBuffer = this.usesClaimHashAsId
|
|
200
|
+
? buffer_1.Buffer.from(this.getClaimHash(), "hex")
|
|
201
|
+
: this.getPaymentHash();
|
|
202
|
+
if (this._randomNonce == null)
|
|
203
|
+
return idBuffer;
|
|
204
|
+
return buffer_1.Buffer.concat([idBuffer, buffer_1.Buffer.from(this._randomNonce, "hex")]);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Returns the payment hash of the swap and lightning network invoice, or `null` if not known (i.e. if
|
|
208
|
+
* the swap was recovered from on-chain data, the payment hash might not be known)
|
|
209
|
+
*
|
|
210
|
+
* @internal
|
|
211
|
+
*/
|
|
212
|
+
getPaymentHash() {
|
|
213
|
+
if (this.pr == null)
|
|
214
|
+
return null;
|
|
215
|
+
if (this.pr.toLowerCase().startsWith("ln")) {
|
|
216
|
+
const parsed = (0, bolt11_1.decode)(this.pr);
|
|
217
|
+
if (parsed.tagsObject.payment_hash == null)
|
|
218
|
+
throw new Error("Swap invoice has no payment hash field!");
|
|
219
|
+
return buffer_1.Buffer.from(parsed.tagsObject.payment_hash, "hex");
|
|
220
|
+
}
|
|
221
|
+
return buffer_1.Buffer.from(this.pr, "hex");
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* @inheritDoc
|
|
225
|
+
* @internal
|
|
226
|
+
*/
|
|
227
|
+
canCommit(skipQuoteExpiryChecks) {
|
|
228
|
+
return this._state === FromBTCLNSwapState.PR_PAID || (!!skipQuoteExpiryChecks && this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* @inheritDoc
|
|
232
|
+
*/
|
|
233
|
+
getInputAddress() {
|
|
234
|
+
return this.lnurl ?? this.pr ?? null;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* @inheritDoc
|
|
238
|
+
*/
|
|
239
|
+
getInputTxId() {
|
|
240
|
+
const paymentHash = this.getPaymentHash();
|
|
241
|
+
if (paymentHash == null)
|
|
242
|
+
return null;
|
|
243
|
+
return paymentHash.toString("hex");
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Returns the lightning network BOLT11 invoice that needs to be paid as an input to the swap.
|
|
247
|
+
*
|
|
248
|
+
* In case the swap is recovered from on-chain data, the address returned might be just a payment hash,
|
|
249
|
+
* as it is impossible to retrieve the actual lightning network invoice paid purely from on-chain
|
|
250
|
+
* data.
|
|
251
|
+
*/
|
|
252
|
+
getAddress() {
|
|
253
|
+
return this.pr ?? "";
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* A hyperlink representation of the address + amount that the user needs to sends on the source chain.
|
|
257
|
+
* This is suitable to be displayed in a form of QR code.
|
|
258
|
+
*
|
|
259
|
+
* @remarks
|
|
260
|
+
* In case the swap is recovered from on-chain data, the address returned might be just a payment hash,
|
|
261
|
+
* as it is impossible to retrieve the actual lightning network invoice paid purely from on-chain
|
|
262
|
+
* data.
|
|
263
|
+
*/
|
|
264
|
+
getHyperlink() {
|
|
265
|
+
return this.pr == null ? "" : "lightning:" + this.pr.toUpperCase();
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Returns the timeout time (in UNIX milliseconds) when the swap will definitelly be considered as expired
|
|
269
|
+
* if the LP doesn't make it expired sooner
|
|
270
|
+
*/
|
|
271
|
+
getDefinitiveExpiryTime() {
|
|
272
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
273
|
+
return 0;
|
|
274
|
+
const decoded = (0, bolt11_1.decode)(this.pr);
|
|
275
|
+
if (decoded.timeExpireDate == null)
|
|
276
|
+
throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
277
|
+
const finalCltvExpiryDelta = decoded.tagsObject.min_final_cltv_expiry ?? 144;
|
|
278
|
+
const finalCltvExpiryDelay = finalCltvExpiryDelta * this.wrapper._options.bitcoinBlocktime * this.wrapper._options.safetyFactor;
|
|
279
|
+
return (decoded.timeExpireDate + finalCltvExpiryDelay) * 1000;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Returns timeout time (in UNIX milliseconds) when the swap htlc will expire
|
|
283
|
+
*/
|
|
284
|
+
getHtlcTimeoutTime() {
|
|
285
|
+
if (this._data == null)
|
|
286
|
+
return null;
|
|
287
|
+
return Number(this.wrapper._getHtlcTimeout(this._data)) * 1000;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Returns timeout time (in UNIX milliseconds) when the LN invoice will expire
|
|
291
|
+
*/
|
|
292
|
+
getTimeoutTime() {
|
|
293
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
294
|
+
return 0;
|
|
295
|
+
const decoded = (0, bolt11_1.decode)(this.pr);
|
|
296
|
+
if (decoded.timeExpireDate == null)
|
|
297
|
+
throw new Error("Swap invoice doesn't contain expiry date field!");
|
|
298
|
+
return (decoded.timeExpireDate * 1000);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* @inheritDoc
|
|
302
|
+
*/
|
|
303
|
+
isFinished() {
|
|
304
|
+
return this._state === FromBTCLNSwapState.CLAIM_CLAIMED || this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.FAILED;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* @inheritDoc
|
|
308
|
+
*/
|
|
309
|
+
isClaimable() {
|
|
310
|
+
return this._state === FromBTCLNSwapState.CLAIM_COMMITED;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* @inheritDoc
|
|
314
|
+
*/
|
|
315
|
+
isSuccessful() {
|
|
316
|
+
return this._state === FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* @inheritDoc
|
|
320
|
+
*/
|
|
321
|
+
isFailed() {
|
|
322
|
+
return this._state === FromBTCLNSwapState.FAILED || this._state === FromBTCLNSwapState.EXPIRED;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* @inheritDoc
|
|
326
|
+
*/
|
|
327
|
+
isInProgress() {
|
|
328
|
+
return (this._state === FromBTCLNSwapState.PR_CREATED && this.initiated) ||
|
|
329
|
+
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.initiated) ||
|
|
330
|
+
this._state === FromBTCLNSwapState.PR_PAID ||
|
|
331
|
+
this._state === FromBTCLNSwapState.CLAIM_COMMITED;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* @inheritDoc
|
|
335
|
+
*/
|
|
336
|
+
isQuoteExpired() {
|
|
337
|
+
return this._state === FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* @inheritDoc
|
|
341
|
+
*/
|
|
342
|
+
isQuoteSoftExpired() {
|
|
343
|
+
return this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* @inheritDoc
|
|
347
|
+
* @internal
|
|
348
|
+
*/
|
|
349
|
+
_verifyQuoteDefinitelyExpired() {
|
|
350
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
351
|
+
return Promise.resolve(this.getDefinitiveExpiryTime() < Date.now());
|
|
352
|
+
}
|
|
353
|
+
return super._verifyQuoteDefinitelyExpired();
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* @inheritDoc
|
|
357
|
+
* @internal
|
|
358
|
+
*/
|
|
359
|
+
_verifyQuoteValid() {
|
|
360
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED ||
|
|
361
|
+
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
362
|
+
return Promise.resolve(this.getTimeoutTime() > Date.now());
|
|
363
|
+
}
|
|
364
|
+
return super._verifyQuoteValid();
|
|
365
|
+
}
|
|
366
|
+
//////////////////////////////
|
|
367
|
+
//// Amounts & fees
|
|
368
|
+
/**
|
|
369
|
+
* @inheritDoc
|
|
370
|
+
*/
|
|
371
|
+
getInputToken() {
|
|
372
|
+
return Token_1.BitcoinTokens.BTCLN;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* @inheritDoc
|
|
376
|
+
*/
|
|
377
|
+
getInput() {
|
|
378
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
379
|
+
return (0, TokenAmount_1.toTokenAmount)(null, this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
380
|
+
const parsed = (0, bolt11_1.decode)(this.pr);
|
|
381
|
+
if (parsed.millisatoshis == null)
|
|
382
|
+
throw new Error("Swap invoice doesn't contain msat amount field!");
|
|
383
|
+
const amount = (BigInt(parsed.millisatoshis) + 999n) / 1000n;
|
|
384
|
+
return (0, TokenAmount_1.toTokenAmount)(amount, this.inputToken, this.wrapper._prices, this.pricingInfo);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* @inheritDoc
|
|
388
|
+
*/
|
|
389
|
+
getSmartChainNetworkFee() {
|
|
390
|
+
return this.getCommitAndClaimNetworkFee();
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* @inheritDoc
|
|
394
|
+
*/
|
|
395
|
+
async hasEnoughForTxFees() {
|
|
396
|
+
const [balance, feeRate] = await Promise.all([
|
|
397
|
+
this.wrapper._contract.getBalance(this._getInitiator(), this.wrapper._chain.getNativeCurrencyAddress(), false),
|
|
398
|
+
this.feeRate != null ? Promise.resolve(this.feeRate) : this.wrapper._contract.getInitFeeRate(this.getSwapData().getOfferer(), this.getSwapData().getClaimer(), this.getSwapData().getToken(), this.getSwapData().getClaimHash())
|
|
399
|
+
]);
|
|
400
|
+
const commitFee = await this.wrapper._contract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
401
|
+
const claimFee = await this.wrapper._contract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate);
|
|
402
|
+
const totalFee = commitFee + claimFee + this.getSwapData().getTotalDeposit();
|
|
403
|
+
return {
|
|
404
|
+
enoughBalance: balance >= totalFee,
|
|
405
|
+
balance: (0, TokenAmount_1.toTokenAmount)(balance, this.wrapper._getNativeToken(), this.wrapper._prices, this.pricingInfo),
|
|
406
|
+
required: (0, TokenAmount_1.toTokenAmount)(totalFee, this.wrapper._getNativeToken(), this.wrapper._prices, this.pricingInfo)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
isValidSecretPreimage(secret) {
|
|
410
|
+
const paymentHash = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex")));
|
|
411
|
+
const claimHash = this.wrapper._contract.getHashForHtlc(paymentHash).toString("hex");
|
|
412
|
+
return this.getSwapData().getClaimHash() === claimHash;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Sets the secret preimage for the swap, in case it is not known already
|
|
416
|
+
*
|
|
417
|
+
* @param secret Secret preimage that matches the expected payment hash
|
|
418
|
+
*
|
|
419
|
+
* @throws {Error} If an invalid secret preimage is provided
|
|
420
|
+
*/
|
|
421
|
+
setSecretPreimage(secret) {
|
|
422
|
+
if (!this.isValidSecretPreimage(secret))
|
|
423
|
+
throw new Error("Invalid secret preimage provided, hash doesn't match!");
|
|
424
|
+
this.secret = secret;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Returns whether the secret preimage for this swap is known
|
|
428
|
+
*/
|
|
429
|
+
hasSecretPreimage() {
|
|
430
|
+
return this.secret != null;
|
|
431
|
+
}
|
|
432
|
+
//////////////////////////////
|
|
433
|
+
//// Execution
|
|
434
|
+
/**
|
|
435
|
+
* Executes the swap with the provided bitcoin lightning network wallet or LNURL
|
|
436
|
+
*
|
|
437
|
+
* @param dstSigner Signer on the destination network, needs to have the same address as the one specified when
|
|
438
|
+
* quote was created, this is required for legacy swaps because the destination wallet needs to actively claim
|
|
439
|
+
* the swap funds on the destination (this also means you need native token to cover gas costs)
|
|
440
|
+
* @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
|
|
441
|
+
* link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
|
|
442
|
+
* @param callbacks Callbacks to track the progress of the swap
|
|
443
|
+
* @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
|
|
444
|
+
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
445
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
446
|
+
*/
|
|
447
|
+
async execute(dstSigner, walletOrLnurlWithdraw, callbacks, options) {
|
|
448
|
+
if (this._state === FromBTCLNSwapState.FAILED)
|
|
449
|
+
throw new Error("Swap failed!");
|
|
450
|
+
if (this._state === FromBTCLNSwapState.EXPIRED)
|
|
451
|
+
throw new Error("Swap HTLC expired!");
|
|
452
|
+
if (this._state === FromBTCLNSwapState.QUOTE_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED)
|
|
453
|
+
throw new Error("Swap quote expired!");
|
|
454
|
+
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
455
|
+
throw new Error("Swap already settled!");
|
|
456
|
+
let abortSignal = options?.abortSignal;
|
|
457
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED) {
|
|
458
|
+
if (walletOrLnurlWithdraw != null && this.lnurl == null) {
|
|
459
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
460
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
461
|
+
if (typeof (walletOrLnurlWithdraw) === "string" || (0, LNURLWithdraw_1.isLNURLWithdraw)(walletOrLnurlWithdraw)) {
|
|
462
|
+
await this.settleWithLNURLWithdraw(walletOrLnurlWithdraw);
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
const paymentPromise = walletOrLnurlWithdraw.payInvoice(this.pr);
|
|
466
|
+
const abortController = new AbortController();
|
|
467
|
+
paymentPromise.catch(e => abortController.abort(e));
|
|
468
|
+
if (options?.abortSignal != null)
|
|
469
|
+
options.abortSignal.addEventListener("abort", () => abortController.abort(options?.abortSignal?.reason));
|
|
470
|
+
abortSignal = abortController.signal;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
const paymentSuccess = await this.waitForPayment(callbacks?.onSourceTransactionReceived, options?.lightningTxCheckIntervalSeconds, abortSignal);
|
|
474
|
+
if (!paymentSuccess)
|
|
475
|
+
throw new Error("Failed to receive lightning network payment");
|
|
476
|
+
}
|
|
477
|
+
if (this._state === FromBTCLNSwapState.PR_PAID || this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
478
|
+
if (this.canCommitAndClaimInOneShot()) {
|
|
479
|
+
await this.commitAndClaim(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent, callbacks?.onDestinationClaimSent, options?.secret);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
if (this._state === FromBTCLNSwapState.PR_PAID) {
|
|
483
|
+
await this.commit(dstSigner, options?.abortSignal, undefined, callbacks?.onDestinationCommitSent);
|
|
484
|
+
if (options?.delayBetweenCommitAndClaimSeconds != null)
|
|
485
|
+
await (0, TimeoutUtils_1.timeoutPromise)(options.delayBetweenCommitAndClaimSeconds * 1000, options?.abortSignal);
|
|
486
|
+
}
|
|
487
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
488
|
+
await this.claim(dstSigner, options?.abortSignal, callbacks?.onDestinationClaimSent, options?.secret);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// @ts-ignore
|
|
493
|
+
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
494
|
+
if (callbacks?.onSwapSettled != null)
|
|
495
|
+
callbacks.onSwapSettled(this.getOutputTxId());
|
|
496
|
+
}
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* @inheritDoc
|
|
501
|
+
*
|
|
502
|
+
* @param options
|
|
503
|
+
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap
|
|
504
|
+
* wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
|
|
505
|
+
* can use `skipChecks=true`)
|
|
506
|
+
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
507
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
508
|
+
*/
|
|
509
|
+
async txsExecute(options) {
|
|
510
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED) {
|
|
511
|
+
if (!await this._verifyQuoteValid())
|
|
512
|
+
throw new Error("Quote already expired or close to expiry!");
|
|
513
|
+
return [
|
|
514
|
+
{
|
|
515
|
+
name: "Payment",
|
|
516
|
+
description: "Initiates the swap by paying up the lightning network invoice",
|
|
517
|
+
chain: "LIGHTNING",
|
|
518
|
+
txs: [
|
|
519
|
+
{
|
|
520
|
+
type: "BOLT11_PAYMENT_REQUEST",
|
|
521
|
+
address: this.getAddress(),
|
|
522
|
+
hyperlink: this.getHyperlink()
|
|
523
|
+
}
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
];
|
|
527
|
+
}
|
|
528
|
+
if (this._state === FromBTCLNSwapState.PR_PAID) {
|
|
529
|
+
if (!await this._verifyQuoteValid())
|
|
530
|
+
throw new Error("Quote already expired or close to expiry!");
|
|
531
|
+
const txsCommit = await this.txsCommit(options?.skipChecks);
|
|
532
|
+
const txsClaim = await this._txsClaim(undefined, options?.secret);
|
|
533
|
+
return [
|
|
534
|
+
{
|
|
535
|
+
name: "Commit",
|
|
536
|
+
description: `Creates the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
537
|
+
chain: this.chainIdentifier,
|
|
538
|
+
txs: txsCommit
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
name: "Claim",
|
|
542
|
+
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
543
|
+
chain: this.chainIdentifier,
|
|
544
|
+
txs: txsClaim
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
}
|
|
548
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
549
|
+
const txsClaim = await this.txsClaim(undefined, options?.secret);
|
|
550
|
+
return [
|
|
551
|
+
{
|
|
552
|
+
name: "Claim",
|
|
553
|
+
description: `Settles & claims the funds from the HTLC escrow on the ${this.chainIdentifier} side`,
|
|
554
|
+
chain: this.chainIdentifier,
|
|
555
|
+
txs: txsClaim
|
|
556
|
+
},
|
|
557
|
+
];
|
|
558
|
+
}
|
|
559
|
+
throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED, PR_PAID or CLAIM_COMMITED");
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* @inheritDoc
|
|
563
|
+
*
|
|
564
|
+
* @param options
|
|
565
|
+
* @param options.skipChecks Skip checks like making sure init signature is still valid and swap
|
|
566
|
+
* wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
|
|
567
|
+
* can use `skipChecks=true`)
|
|
568
|
+
* @param options.secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
569
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
570
|
+
*/
|
|
571
|
+
async getCurrentActions(options) {
|
|
572
|
+
try {
|
|
573
|
+
return await this.txsExecute(options);
|
|
574
|
+
}
|
|
575
|
+
catch (e) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
//////////////////////////////
|
|
580
|
+
//// Payment
|
|
581
|
+
/**
|
|
582
|
+
* Checks whether the LP received the LN payment and we can continue by committing & claiming the HTLC on-chain
|
|
583
|
+
*
|
|
584
|
+
* @param save If the new swap state should be saved
|
|
585
|
+
*
|
|
586
|
+
* @internal
|
|
587
|
+
*/
|
|
588
|
+
async _checkIntermediaryPaymentReceived(save = true) {
|
|
589
|
+
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
590
|
+
this._state === FromBTCLNSwapState.CLAIM_COMMITED ||
|
|
591
|
+
this._state === FromBTCLNSwapState.CLAIM_CLAIMED ||
|
|
592
|
+
this._state === FromBTCLNSwapState.FAILED ||
|
|
593
|
+
this._state === FromBTCLNSwapState.EXPIRED)
|
|
594
|
+
return true;
|
|
595
|
+
if (this._state === FromBTCLNSwapState.QUOTE_EXPIRED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null))
|
|
596
|
+
return false;
|
|
597
|
+
if (this.url == null)
|
|
598
|
+
return false;
|
|
599
|
+
const paymentHash = this.getPaymentHash();
|
|
600
|
+
if (paymentHash == null)
|
|
601
|
+
throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
|
|
602
|
+
const resp = await IntermediaryAPI_1.IntermediaryAPI.getPaymentAuthorization(this.url, paymentHash.toString("hex"));
|
|
603
|
+
switch (resp.code) {
|
|
604
|
+
case IntermediaryAPI_1.PaymentAuthorizationResponseCodes.AUTH_DATA:
|
|
605
|
+
const data = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
606
|
+
try {
|
|
607
|
+
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), data, resp.data);
|
|
608
|
+
this.expiry = await this.wrapper._contract.getInitAuthorizationExpiry(data, resp.data);
|
|
609
|
+
this._state = FromBTCLNSwapState.PR_PAID;
|
|
610
|
+
this._data = data;
|
|
611
|
+
this.signatureData = {
|
|
612
|
+
prefix: resp.data.prefix,
|
|
613
|
+
timeout: resp.data.timeout,
|
|
614
|
+
signature: resp.data.signature
|
|
615
|
+
};
|
|
616
|
+
this.initiated = true;
|
|
617
|
+
if (save)
|
|
618
|
+
await this._saveAndEmit();
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
catch (e) { }
|
|
622
|
+
return null;
|
|
623
|
+
case IntermediaryAPI_1.PaymentAuthorizationResponseCodes.EXPIRED:
|
|
624
|
+
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
625
|
+
this.initiated = true;
|
|
626
|
+
if (save)
|
|
627
|
+
await this._saveAndEmit();
|
|
628
|
+
return false;
|
|
629
|
+
default:
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Checks the data returned by the intermediary in the payment auth request
|
|
635
|
+
*
|
|
636
|
+
* @param signer Smart chain signer's address initiating the swap
|
|
637
|
+
* @param data Parsed swap data as returned by the intermediary
|
|
638
|
+
* @param signature Signature data as returned by the intermediary
|
|
639
|
+
*
|
|
640
|
+
* @throws {IntermediaryError} If the returned are not valid
|
|
641
|
+
* @throws {SignatureVerificationError} If the returned signature is not valid
|
|
642
|
+
* @throws {Error} If the swap is already committed on-chain
|
|
643
|
+
*
|
|
644
|
+
* @internal
|
|
645
|
+
*/
|
|
646
|
+
async checkIntermediaryReturnedAuthData(signer, data, signature) {
|
|
647
|
+
data.setClaimer(signer);
|
|
648
|
+
if (data.getType() !== base_1.ChainSwapType.HTLC)
|
|
649
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid swap type");
|
|
650
|
+
if (!data.isOfferer(this.getSwapData().getOfferer()))
|
|
651
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid offerer used");
|
|
652
|
+
if (!data.isClaimer(this._getInitiator()))
|
|
653
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid claimer used");
|
|
654
|
+
if (!data.isToken(this.getSwapData().getToken()))
|
|
655
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid token used");
|
|
656
|
+
if (data.getSecurityDeposit() > this.getSwapData().getSecurityDeposit())
|
|
657
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid security deposit!");
|
|
658
|
+
if (data.getClaimerBounty() !== 0n)
|
|
659
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid claimer bounty!");
|
|
660
|
+
if (data.getAmount() < this.getSwapData().getAmount())
|
|
661
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid amount received!");
|
|
662
|
+
if (data.getClaimHash() !== this.getSwapData().getClaimHash())
|
|
663
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid payment hash used!");
|
|
664
|
+
if (!data.isDepositToken(this.getSwapData().getDepositToken()))
|
|
665
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid deposit token used!");
|
|
666
|
+
if (data.hasSuccessAction())
|
|
667
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid has success action");
|
|
668
|
+
await Promise.all([
|
|
669
|
+
this.wrapper._contract.isValidInitAuthorization(this._getInitiator(), data, signature, this.feeRate),
|
|
670
|
+
this.wrapper._contract.getCommitStatus(data.getClaimer(), data)
|
|
671
|
+
.then(status => {
|
|
672
|
+
if (status?.type !== base_1.SwapCommitStateType.NOT_COMMITED)
|
|
673
|
+
throw new Error("Swap already committed on-chain!");
|
|
674
|
+
})
|
|
675
|
+
]);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Waits till a lightning network payment is received by the intermediary and client
|
|
679
|
+
* can continue by initiating (committing) & settling (claiming) the HTLC by calling
|
|
680
|
+
* either the {@link commitAndClaim} function (if the underlying chain allows commit
|
|
681
|
+
* and claim in a single transaction - check with {@link canCommitAndClaimInOneShot}).
|
|
682
|
+
* Or call {@link commit} and then {@link claim} separately.
|
|
683
|
+
*
|
|
684
|
+
* If this swap is using an LNURL-withdraw link as input, it automatically posts the
|
|
685
|
+
* generated invoice to the LNURL service to pay it.
|
|
686
|
+
*
|
|
687
|
+
* @param onPaymentReceived Callback as for when the LP reports having received the ln payment
|
|
688
|
+
* @param abortSignal Abort signal to stop waiting for payment
|
|
689
|
+
* @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
|
|
690
|
+
*/
|
|
691
|
+
async waitForPayment(onPaymentReceived, checkIntervalSeconds, abortSignal) {
|
|
692
|
+
checkIntervalSeconds ??= 5;
|
|
693
|
+
if (this._state !== FromBTCLNSwapState.PR_CREATED &&
|
|
694
|
+
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData != null))
|
|
695
|
+
throw new Error("Must be in PR_CREATED state!");
|
|
696
|
+
if (this.url == null)
|
|
697
|
+
throw new Error("LP URL not known, cannot await the payment!");
|
|
698
|
+
const abortController = new AbortController();
|
|
699
|
+
if (abortSignal != null)
|
|
700
|
+
abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
701
|
+
let save = false;
|
|
702
|
+
if (this.lnurl != null && this.lnurlK1 != null && this.lnurlCallback != null && !this.prPosted) {
|
|
703
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
704
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
705
|
+
LNURL_1.LNURL.postInvoiceToLNURLWithdraw({ k1: this.lnurlK1, callback: this.lnurlCallback }, this.pr).catch(e => {
|
|
706
|
+
this.lnurlFailSignal.abort(e);
|
|
707
|
+
});
|
|
708
|
+
this.prPosted = true;
|
|
709
|
+
save ||= true;
|
|
710
|
+
}
|
|
711
|
+
if (!this.initiated) {
|
|
712
|
+
this.initiated = true;
|
|
713
|
+
save ||= true;
|
|
714
|
+
}
|
|
715
|
+
if (save)
|
|
716
|
+
await this._saveAndEmit();
|
|
717
|
+
let lnurlFailListener = () => abortController.abort(this.lnurlFailSignal.signal.reason);
|
|
718
|
+
this.lnurlFailSignal.signal.addEventListener("abort", lnurlFailListener);
|
|
719
|
+
this.lnurlFailSignal.signal.throwIfAborted();
|
|
720
|
+
const paymentHash = this.getPaymentHash();
|
|
721
|
+
if (paymentHash == null)
|
|
722
|
+
throw new Error("Swap payment hash not available, the swap was probably recovered!");
|
|
723
|
+
let resp = { code: IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING, msg: "" };
|
|
724
|
+
while (!abortController.signal.aborted && resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING) {
|
|
725
|
+
resp = await IntermediaryAPI_1.IntermediaryAPI.getPaymentAuthorization(this.url, paymentHash.toString("hex"));
|
|
726
|
+
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.PENDING)
|
|
727
|
+
await (0, TimeoutUtils_1.timeoutPromise)(checkIntervalSeconds * 1000, abortController.signal);
|
|
728
|
+
}
|
|
729
|
+
this.lnurlFailSignal.signal.removeEventListener("abort", lnurlFailListener);
|
|
730
|
+
abortController.signal.throwIfAborted();
|
|
731
|
+
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.AUTH_DATA) {
|
|
732
|
+
const sigData = resp.data;
|
|
733
|
+
const swapData = new this.wrapper._swapDataDeserializer(resp.data.data);
|
|
734
|
+
await this.checkIntermediaryReturnedAuthData(this._getInitiator(), swapData, sigData);
|
|
735
|
+
this.expiry = await this.wrapper._contract.getInitAuthorizationExpiry(swapData, sigData);
|
|
736
|
+
if (onPaymentReceived != null)
|
|
737
|
+
onPaymentReceived(this.getInputTxId());
|
|
738
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
739
|
+
this._data = swapData;
|
|
740
|
+
this.signatureData = {
|
|
741
|
+
prefix: sigData.prefix,
|
|
742
|
+
timeout: sigData.timeout,
|
|
743
|
+
signature: sigData.signature
|
|
744
|
+
};
|
|
745
|
+
await this._saveAndEmit(FromBTCLNSwapState.PR_PAID);
|
|
746
|
+
}
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
750
|
+
if (resp.code === IntermediaryAPI_1.PaymentAuthorizationResponseCodes.EXPIRED) {
|
|
751
|
+
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
752
|
+
}
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
throw new IntermediaryError_1.IntermediaryError("Invalid response from the LP");
|
|
756
|
+
}
|
|
757
|
+
//////////////////////////////
|
|
758
|
+
//// Commit
|
|
759
|
+
/**
|
|
760
|
+
* @inheritDoc
|
|
761
|
+
*
|
|
762
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
763
|
+
*/
|
|
764
|
+
async commit(_signer, abortSignal, skipChecks, onBeforeTxSent) {
|
|
765
|
+
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
766
|
+
this.checkSigner(signer);
|
|
767
|
+
let txCount = 0;
|
|
768
|
+
const txs = await this.txsCommit(skipChecks);
|
|
769
|
+
const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
|
|
770
|
+
txCount++;
|
|
771
|
+
if (onBeforeTxSent != null && txCount === txs.length)
|
|
772
|
+
onBeforeTxSent(txId);
|
|
773
|
+
return Promise.resolve();
|
|
774
|
+
});
|
|
775
|
+
this._commitTxId = result[result.length - 1];
|
|
776
|
+
if (this._state === FromBTCLNSwapState.PR_PAID || this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this._state === FromBTCLNSwapState.QUOTE_EXPIRED) {
|
|
777
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
778
|
+
}
|
|
779
|
+
return this._commitTxId;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* @inheritDoc
|
|
783
|
+
*/
|
|
784
|
+
async waitTillCommited(abortSignal) {
|
|
785
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
786
|
+
return Promise.resolve();
|
|
787
|
+
if (this._state !== FromBTCLNSwapState.PR_PAID && (this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null))
|
|
788
|
+
throw new Error("Invalid state");
|
|
789
|
+
const abortController = (0, Utils_1.extendAbortController)(abortSignal);
|
|
790
|
+
const result = await Promise.race([
|
|
791
|
+
this.watchdogWaitTillCommited(undefined, abortController.signal),
|
|
792
|
+
this.waitTillState(FromBTCLNSwapState.CLAIM_COMMITED, "gte", abortController.signal).then(() => 0)
|
|
793
|
+
]);
|
|
794
|
+
abortController.abort();
|
|
795
|
+
if (result === 0)
|
|
796
|
+
this.logger.debug("waitTillCommited(): Resolved from state changed");
|
|
797
|
+
if (result === true)
|
|
798
|
+
this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
|
|
799
|
+
if (result === false) {
|
|
800
|
+
this.logger.debug("waitTillCommited(): Resolved from watchdog - signature expired");
|
|
801
|
+
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
802
|
+
this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
803
|
+
await this._saveAndEmit(FromBTCLNSwapState.QUOTE_EXPIRED);
|
|
804
|
+
}
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
808
|
+
this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED) {
|
|
809
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_COMMITED);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
//////////////////////////////
|
|
813
|
+
//// Claim
|
|
814
|
+
/**
|
|
815
|
+
* Unsafe txs claim getter without state checking!
|
|
816
|
+
*
|
|
817
|
+
* @param _signer
|
|
818
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
819
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
820
|
+
*
|
|
821
|
+
* @internal
|
|
822
|
+
*/
|
|
823
|
+
async _txsClaim(_signer, secret) {
|
|
824
|
+
let address = undefined;
|
|
825
|
+
if (_signer != null) {
|
|
826
|
+
if (typeof (_signer) === "string") {
|
|
827
|
+
address = _signer;
|
|
828
|
+
}
|
|
829
|
+
else if ((0, base_1.isAbstractSigner)(_signer)) {
|
|
830
|
+
address = _signer.getAddress();
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
address = (await this.wrapper._chain.wrapSigner(_signer)).getAddress();
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
if (this._data == null)
|
|
837
|
+
throw new Error("Unknown data, wrong state?");
|
|
838
|
+
const useSecret = secret ?? this.secret;
|
|
839
|
+
if (useSecret == null)
|
|
840
|
+
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as an argument");
|
|
841
|
+
if (!this.isValidSecretPreimage(useSecret))
|
|
842
|
+
throw new Error("Invalid swap secret pre-image provided!");
|
|
843
|
+
return this.wrapper._contract.txsClaimWithSecret(address ?? this._getInitiator(), this._data, useSecret, true, true);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* @inheritDoc
|
|
847
|
+
*
|
|
848
|
+
* @param _signer Optional signer address to use for claiming the swap, can also be different from the initializer
|
|
849
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
850
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
851
|
+
*
|
|
852
|
+
* @throws {Error} If in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
|
|
853
|
+
*/
|
|
854
|
+
async txsClaim(_signer, secret) {
|
|
855
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED)
|
|
856
|
+
throw new Error("Must be in CLAIM_COMMITED state!");
|
|
857
|
+
return this._txsClaim(_signer, secret);
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* @inheritDoc
|
|
861
|
+
*
|
|
862
|
+
* @param _signer
|
|
863
|
+
* @param abortSignal
|
|
864
|
+
* @param onBeforeTxSent
|
|
865
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
866
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
867
|
+
*/
|
|
868
|
+
async claim(_signer, abortSignal, onBeforeTxSent, secret) {
|
|
869
|
+
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
870
|
+
let txCount = 0;
|
|
871
|
+
const result = await this.wrapper._chain.sendAndConfirm(signer, await this.txsClaim(_signer, secret), true, abortSignal, undefined, (txId) => {
|
|
872
|
+
txCount++;
|
|
873
|
+
if (onBeforeTxSent != null && txCount === 1)
|
|
874
|
+
onBeforeTxSent(txId);
|
|
875
|
+
return Promise.resolve();
|
|
876
|
+
});
|
|
877
|
+
this._claimTxId = result[0];
|
|
878
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.EXPIRED || this._state === FromBTCLNSwapState.FAILED) {
|
|
879
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
880
|
+
}
|
|
881
|
+
return result[0];
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* @inheritDoc
|
|
885
|
+
*
|
|
886
|
+
* @throws {Error} If swap is in invalid state (must be {@link FromBTCLNSwapState.CLAIM_COMMITED})
|
|
887
|
+
* @throws {Error} If the LP refunded sooner than we were able to claim
|
|
888
|
+
*/
|
|
889
|
+
async waitTillClaimed(maxWaitTimeSeconds, abortSignal) {
|
|
890
|
+
if (this._state === FromBTCLNSwapState.CLAIM_CLAIMED)
|
|
891
|
+
return Promise.resolve(true);
|
|
892
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED)
|
|
893
|
+
throw new Error("Invalid state (not CLAIM_COMMITED)");
|
|
894
|
+
const abortController = new AbortController();
|
|
895
|
+
if (abortSignal != null)
|
|
896
|
+
abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
|
|
897
|
+
let timedOut = false;
|
|
898
|
+
if (maxWaitTimeSeconds != null) {
|
|
899
|
+
const timeout = setTimeout(() => {
|
|
900
|
+
timedOut = true;
|
|
901
|
+
abortController.abort();
|
|
902
|
+
}, maxWaitTimeSeconds * 1000);
|
|
903
|
+
abortController.signal.addEventListener("abort", () => clearTimeout(timeout));
|
|
904
|
+
}
|
|
905
|
+
let res;
|
|
906
|
+
try {
|
|
907
|
+
res = await Promise.race([
|
|
908
|
+
this.watchdogWaitTillResult(undefined, abortController.signal),
|
|
909
|
+
this.waitTillState(FromBTCLNSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0),
|
|
910
|
+
this.waitTillState(FromBTCLNSwapState.EXPIRED, "eq", abortController.signal).then(() => 1),
|
|
911
|
+
]);
|
|
912
|
+
abortController.abort();
|
|
913
|
+
}
|
|
914
|
+
catch (e) {
|
|
915
|
+
abortController.abort();
|
|
916
|
+
if (timedOut)
|
|
917
|
+
return false;
|
|
918
|
+
throw e;
|
|
919
|
+
}
|
|
920
|
+
if (res === 0) {
|
|
921
|
+
this.logger.debug("waitTillClaimed(): Resolved from state change (CLAIM_CLAIMED)");
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
924
|
+
if (res === 1) {
|
|
925
|
+
this.logger.debug("waitTillClaimed(): Resolved from state change (EXPIRED)");
|
|
926
|
+
throw new Error("Swap expired during claiming");
|
|
927
|
+
}
|
|
928
|
+
this.logger.debug("waitTillClaimed(): Resolved from watchdog");
|
|
929
|
+
if (res?.type === base_1.SwapCommitStateType.PAID) {
|
|
930
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
931
|
+
this._claimTxId = await res.getClaimTxId();
|
|
932
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (res?.type === base_1.SwapCommitStateType.NOT_COMMITED || res?.type === base_1.SwapCommitStateType.EXPIRED) {
|
|
936
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED &&
|
|
937
|
+
this._state !== FromBTCLNSwapState.FAILED) {
|
|
938
|
+
if (res.getRefundTxId != null)
|
|
939
|
+
this._refundTxId = await res.getRefundTxId();
|
|
940
|
+
await this._saveAndEmit(FromBTCLNSwapState.FAILED);
|
|
941
|
+
}
|
|
942
|
+
throw new Error("Swap expired while waiting for claim!");
|
|
943
|
+
}
|
|
944
|
+
return true;
|
|
945
|
+
}
|
|
946
|
+
//////////////////////////////
|
|
947
|
+
//// Commit & claim
|
|
948
|
+
/**
|
|
949
|
+
* Estimated transaction fee for commit & claim transactions combined, required
|
|
950
|
+
* to settle the swap on the smart chain destination side.
|
|
951
|
+
*/
|
|
952
|
+
async getCommitAndClaimNetworkFee() {
|
|
953
|
+
const swapContract = this.wrapper._contract;
|
|
954
|
+
const feeRate = this.feeRate ?? await swapContract.getInitFeeRate(this.getSwapData().getOfferer(), this.getSwapData().getClaimer(), this.getSwapData().getToken(), this.getSwapData().getClaimHash());
|
|
955
|
+
const commitFee = await (swapContract.getRawCommitFee != null ?
|
|
956
|
+
swapContract.getRawCommitFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
957
|
+
swapContract.getCommitFee(this._getInitiator(), this.getSwapData(), feeRate));
|
|
958
|
+
const claimFee = await (swapContract.getRawClaimFee != null ?
|
|
959
|
+
swapContract.getRawClaimFee(this._getInitiator(), this.getSwapData(), feeRate) :
|
|
960
|
+
swapContract.getClaimFee(this._getInitiator(), this.getSwapData(), feeRate));
|
|
961
|
+
return (0, TokenAmount_1.toTokenAmount)(commitFee + claimFee, this.wrapper._getNativeToken(), this.wrapper._prices);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Returns whether the underlying chain supports calling commit and claim in a single call,
|
|
965
|
+
* such that you can use the {@link commitAndClaim} function. If not you have to manually
|
|
966
|
+
* call {@link commit} first and then {@link claim}.
|
|
967
|
+
*/
|
|
968
|
+
canCommitAndClaimInOneShot() {
|
|
969
|
+
return this.wrapper._contract.initAndClaimWithSecret != null;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Returns transactions for both commit & claim operation together, such that they can be signed all at once by
|
|
973
|
+
* the wallet. **WARNING**: transactions must be sent sequentially, such that the claim (2nd) transaction is only
|
|
974
|
+
* sent after the commit (1st) transaction confirms. Failure to do so can reveal the HTLC pre-image too soon,
|
|
975
|
+
* opening a possibility for the LP to steal funds!
|
|
976
|
+
*
|
|
977
|
+
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
978
|
+
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
979
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
980
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
981
|
+
*
|
|
982
|
+
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
983
|
+
*/
|
|
984
|
+
async txsCommitAndClaim(skipChecks, secret) {
|
|
985
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED)
|
|
986
|
+
return await this.txsClaim(undefined, secret);
|
|
987
|
+
if (this._state !== FromBTCLNSwapState.PR_PAID &&
|
|
988
|
+
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData == null))
|
|
989
|
+
throw new Error("Must be in PR_PAID state!");
|
|
990
|
+
if (this._data == null)
|
|
991
|
+
throw new Error("Unknown data, wrong state?");
|
|
992
|
+
const useSecret = secret ?? this.secret;
|
|
993
|
+
if (useSecret == null)
|
|
994
|
+
throw new Error("Swap secret pre-image not known and not provided, please provide the swap secret pre-image as second argument");
|
|
995
|
+
if (!this.isValidSecretPreimage(useSecret))
|
|
996
|
+
throw new Error("Invalid swap secret pre-image provided!");
|
|
997
|
+
const initTxs = await this.txsCommit(skipChecks);
|
|
998
|
+
const claimTxs = await this.wrapper._contract.txsClaimWithSecret(this._getInitiator(), this._data, useSecret, true, true, undefined, true);
|
|
999
|
+
return initTxs.concat(claimTxs);
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Commits and claims the swap, in a way that the transactions can be signed together by the provided signer and
|
|
1003
|
+
* then automatically sent sequentially by the SDK. To check if the underlying chain supports this flow check
|
|
1004
|
+
* the {@link canCommitAndClaimInOneShot} function.
|
|
1005
|
+
*
|
|
1006
|
+
* @param _signer Signer to sign the transactions with, must be the same as used in the initialization
|
|
1007
|
+
* @param abortSignal Abort signal to stop waiting for the transaction confirmation and abort
|
|
1008
|
+
* @param skipChecks Skip checks like making sure init signature is still valid and swap wasn't commited yet
|
|
1009
|
+
* (this is handled when swap is created (quoted), if you commit right after quoting, you can use skipChecks=true)
|
|
1010
|
+
* @param onBeforeCommitTxSent Optional callback called before the initialization (commit) transaction is
|
|
1011
|
+
* broadcasted
|
|
1012
|
+
* @param onBeforeClaimTxSent Optional callback called before the settlement (claim) transaction is
|
|
1013
|
+
* broadcasted
|
|
1014
|
+
* @param secret A swap secret to use for the claim transaction, generally only needed if the swap
|
|
1015
|
+
* was recovered from on-chain data, or the pre-image was generated outside the SDK
|
|
1016
|
+
*
|
|
1017
|
+
* @throws {Error} If in invalid state (must be PR_PAID or CLAIM_COMMITED)
|
|
1018
|
+
* @throws {Error} If invalid signer is provided that doesn't match the swap data
|
|
1019
|
+
*/
|
|
1020
|
+
async commitAndClaim(_signer, abortSignal, skipChecks, onBeforeCommitTxSent, onBeforeClaimTxSent, secret) {
|
|
1021
|
+
const signer = (0, base_1.isAbstractSigner)(_signer) ? _signer : await this.wrapper._chain.wrapSigner(_signer);
|
|
1022
|
+
if (!this.canCommitAndClaimInOneShot())
|
|
1023
|
+
throw new Error("Cannot commitAndClaim in single action, please run commit and claim separately!");
|
|
1024
|
+
this.checkSigner(signer);
|
|
1025
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED)
|
|
1026
|
+
return [await this.claim(signer, abortSignal, onBeforeClaimTxSent, secret)];
|
|
1027
|
+
let txCount = 0;
|
|
1028
|
+
const txs = await this.txsCommitAndClaim(skipChecks, secret);
|
|
1029
|
+
const result = await this.wrapper._chain.sendAndConfirm(signer, txs, true, abortSignal, undefined, (txId) => {
|
|
1030
|
+
txCount++;
|
|
1031
|
+
if (onBeforeCommitTxSent != null && txCount === 1)
|
|
1032
|
+
onBeforeCommitTxSent(txId);
|
|
1033
|
+
if (onBeforeClaimTxSent != null && txCount === txs.length)
|
|
1034
|
+
onBeforeClaimTxSent(txId);
|
|
1035
|
+
return Promise.resolve();
|
|
1036
|
+
});
|
|
1037
|
+
this._commitTxId = result[0] ?? this._commitTxId;
|
|
1038
|
+
this._claimTxId = result[result.length - 1] ?? this._claimTxId;
|
|
1039
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_CLAIMED) {
|
|
1040
|
+
await this._saveAndEmit(FromBTCLNSwapState.CLAIM_CLAIMED);
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
//////////////////////////////
|
|
1045
|
+
//// LNURL
|
|
1046
|
+
/**
|
|
1047
|
+
* Whether this swap uses an LNURL-withdraw link
|
|
1048
|
+
*/
|
|
1049
|
+
isLNURL() {
|
|
1050
|
+
return this.lnurl != null;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Gets the used LNURL or `null` if this is not an LNURL-withdraw swap
|
|
1054
|
+
*/
|
|
1055
|
+
getLNURL() {
|
|
1056
|
+
return this.lnurl ?? null;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Pay the generated lightning network invoice with an LNURL-withdraw link, this
|
|
1060
|
+
* is useful when you want to display a lightning payment QR code and also want to
|
|
1061
|
+
* allow payments using LNURL-withdraw NFC cards.
|
|
1062
|
+
*
|
|
1063
|
+
* Note that the swap needs to be created **without** an LNURL to begin with for this function
|
|
1064
|
+
* to work. If this swap is already using an LNURL-withdraw link, this function throws.
|
|
1065
|
+
*/
|
|
1066
|
+
async settleWithLNURLWithdraw(lnurl) {
|
|
1067
|
+
if (this._state !== FromBTCLNSwapState.PR_CREATED &&
|
|
1068
|
+
(this._state !== FromBTCLNSwapState.QUOTE_SOFT_EXPIRED || this.signatureData != null))
|
|
1069
|
+
throw new Error("Must be in PR_CREATED state!");
|
|
1070
|
+
if (this.lnurl != null)
|
|
1071
|
+
throw new Error("Cannot settle LNURL-withdraw swap with different LNURL");
|
|
1072
|
+
let lnurlParams;
|
|
1073
|
+
if (typeof (lnurl) === "string") {
|
|
1074
|
+
const parsedLNURL = await LNURL_1.LNURL.getLNURL(lnurl);
|
|
1075
|
+
if (parsedLNURL == null || parsedLNURL.tag !== "withdrawRequest")
|
|
1076
|
+
throw new UserError_1.UserError("Invalid LNURL-withdraw to settle the swap");
|
|
1077
|
+
lnurlParams = parsedLNURL;
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
lnurlParams = lnurl.params;
|
|
1081
|
+
}
|
|
1082
|
+
if (this.pr == null || !this.pr.toLowerCase().startsWith("ln"))
|
|
1083
|
+
throw new Error("Input lightning network invoice not available, the swap was probably recovered!");
|
|
1084
|
+
LNURL_1.LNURL.useLNURLWithdraw(lnurlParams, this.pr).catch(e => this.lnurlFailSignal.abort(e));
|
|
1085
|
+
this.lnurl = lnurlParams.url;
|
|
1086
|
+
this.lnurlCallback = lnurlParams.callback;
|
|
1087
|
+
this.lnurlK1 = lnurlParams.k1;
|
|
1088
|
+
this.prPosted = true;
|
|
1089
|
+
await this._saveAndEmit();
|
|
1090
|
+
}
|
|
1091
|
+
//////////////////////////////
|
|
1092
|
+
//// Storage
|
|
1093
|
+
/**
|
|
1094
|
+
* @inheritDoc
|
|
1095
|
+
*/
|
|
1096
|
+
serialize() {
|
|
1097
|
+
return {
|
|
1098
|
+
...super.serialize(),
|
|
1099
|
+
pr: this.pr,
|
|
1100
|
+
secret: this.secret,
|
|
1101
|
+
lnurl: this.lnurl,
|
|
1102
|
+
lnurlK1: this.lnurlK1,
|
|
1103
|
+
lnurlCallback: this.lnurlCallback,
|
|
1104
|
+
prPosted: this.prPosted,
|
|
1105
|
+
initialSwapData: this.initialSwapData.serialize(),
|
|
1106
|
+
usesClaimHashAsId: this.usesClaimHashAsId
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
//////////////////////////////
|
|
1110
|
+
//// Swap ticks & sync
|
|
1111
|
+
/**
|
|
1112
|
+
* Checks the swap's state on-chain and compares it to its internal state, updates/changes it according to on-chain
|
|
1113
|
+
* data
|
|
1114
|
+
*
|
|
1115
|
+
* @private
|
|
1116
|
+
*/
|
|
1117
|
+
async syncStateFromChain(quoteDefinitelyExpired, commitStatus) {
|
|
1118
|
+
if (this._state === FromBTCLNSwapState.PR_PAID ||
|
|
1119
|
+
(this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null) ||
|
|
1120
|
+
this._state === FromBTCLNSwapState.CLAIM_COMMITED ||
|
|
1121
|
+
this._state === FromBTCLNSwapState.EXPIRED) {
|
|
1122
|
+
//Check for expiry before the getCommitStatus to prevent race conditions
|
|
1123
|
+
let quoteExpired = false;
|
|
1124
|
+
if (this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null)) {
|
|
1125
|
+
quoteExpired = quoteDefinitelyExpired ?? await this._verifyQuoteDefinitelyExpired();
|
|
1126
|
+
}
|
|
1127
|
+
//Check if it's already successfully paid
|
|
1128
|
+
commitStatus ??= await this.wrapper._contract.getCommitStatus(this._getInitiator(), this._data);
|
|
1129
|
+
if (commitStatus != null && await this._forciblySetOnchainState(commitStatus))
|
|
1130
|
+
return true;
|
|
1131
|
+
//Set the state on expiry here
|
|
1132
|
+
if (this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null)) {
|
|
1133
|
+
if (quoteExpired) {
|
|
1134
|
+
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1135
|
+
return true;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* @inheritDoc
|
|
1143
|
+
* @internal
|
|
1144
|
+
*/
|
|
1145
|
+
_shouldFetchExpiryStatus() {
|
|
1146
|
+
return this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* @inheritDoc
|
|
1150
|
+
* @internal
|
|
1151
|
+
*/
|
|
1152
|
+
_shouldFetchOnchainState() {
|
|
1153
|
+
return this._state === FromBTCLNSwapState.PR_PAID || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData != null) ||
|
|
1154
|
+
this._state === FromBTCLNSwapState.CLAIM_COMMITED || this._state === FromBTCLNSwapState.EXPIRED;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Whether an intermediary (LP) should be contacted to get the state of this swap.
|
|
1158
|
+
*
|
|
1159
|
+
* @internal
|
|
1160
|
+
*/
|
|
1161
|
+
_shouldCheckIntermediary() {
|
|
1162
|
+
return this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null);
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* @inheritDoc
|
|
1166
|
+
* @internal
|
|
1167
|
+
*/
|
|
1168
|
+
async _sync(save, quoteDefinitelyExpired, commitStatus, skipLpCheck) {
|
|
1169
|
+
let changed = false;
|
|
1170
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
1171
|
+
if (this._state != FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.getTimeoutTime() < Date.now()) {
|
|
1172
|
+
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1173
|
+
changed ||= true;
|
|
1174
|
+
}
|
|
1175
|
+
if (!skipLpCheck)
|
|
1176
|
+
try {
|
|
1177
|
+
const result = await this._checkIntermediaryPaymentReceived(false);
|
|
1178
|
+
if (result !== null)
|
|
1179
|
+
changed ||= true;
|
|
1180
|
+
}
|
|
1181
|
+
catch (e) {
|
|
1182
|
+
this.logger.error("_sync(): Failed to synchronize swap, error: ", e);
|
|
1183
|
+
}
|
|
1184
|
+
if (this._state === FromBTCLNSwapState.PR_CREATED || (this._state === FromBTCLNSwapState.QUOTE_SOFT_EXPIRED && this.signatureData == null)) {
|
|
1185
|
+
if (await this._verifyQuoteDefinitelyExpired()) {
|
|
1186
|
+
this._state = FromBTCLNSwapState.QUOTE_EXPIRED;
|
|
1187
|
+
changed ||= true;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
if (await this.syncStateFromChain(quoteDefinitelyExpired, commitStatus))
|
|
1192
|
+
changed = true;
|
|
1193
|
+
if (this._state === FromBTCLNSwapState.CLAIM_COMMITED) {
|
|
1194
|
+
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data);
|
|
1195
|
+
if (expired) {
|
|
1196
|
+
this._state = FromBTCLNSwapState.EXPIRED;
|
|
1197
|
+
changed = true;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (save && changed)
|
|
1201
|
+
await this._saveAndEmit();
|
|
1202
|
+
return changed;
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* @inheritDoc
|
|
1206
|
+
* @internal
|
|
1207
|
+
*/
|
|
1208
|
+
async _forciblySetOnchainState(commitStatus) {
|
|
1209
|
+
switch (commitStatus.type) {
|
|
1210
|
+
case base_1.SwapCommitStateType.PAID:
|
|
1211
|
+
if (this._claimTxId == null)
|
|
1212
|
+
this._claimTxId = await commitStatus.getClaimTxId();
|
|
1213
|
+
if (this.secret == null || this.pr == null)
|
|
1214
|
+
this._setSwapSecret(await commitStatus.getClaimResult());
|
|
1215
|
+
this._state = FromBTCLNSwapState.CLAIM_CLAIMED;
|
|
1216
|
+
return true;
|
|
1217
|
+
case base_1.SwapCommitStateType.NOT_COMMITED:
|
|
1218
|
+
if (this._refundTxId == null && commitStatus.getRefundTxId)
|
|
1219
|
+
this._refundTxId = await commitStatus.getRefundTxId();
|
|
1220
|
+
if (this._refundTxId != null) {
|
|
1221
|
+
this._state = FromBTCLNSwapState.FAILED;
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
break;
|
|
1225
|
+
case base_1.SwapCommitStateType.EXPIRED:
|
|
1226
|
+
if (this._refundTxId == null && commitStatus.getRefundTxId)
|
|
1227
|
+
this._refundTxId = await commitStatus.getRefundTxId();
|
|
1228
|
+
this._state = this._refundTxId == null ? FromBTCLNSwapState.QUOTE_EXPIRED : FromBTCLNSwapState.FAILED;
|
|
1229
|
+
return true;
|
|
1230
|
+
case base_1.SwapCommitStateType.COMMITED:
|
|
1231
|
+
if (this._state !== FromBTCLNSwapState.CLAIM_COMMITED && this._state !== FromBTCLNSwapState.EXPIRED) {
|
|
1232
|
+
this._state = FromBTCLNSwapState.CLAIM_COMMITED;
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* @inheritDoc
|
|
1241
|
+
* @internal
|
|
1242
|
+
*/
|
|
1243
|
+
async _tick(save) {
|
|
1244
|
+
switch (this._state) {
|
|
1245
|
+
case FromBTCLNSwapState.PR_CREATED:
|
|
1246
|
+
if (this.getTimeoutTime() < Date.now()) {
|
|
1247
|
+
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1248
|
+
if (save)
|
|
1249
|
+
await this._saveAndEmit();
|
|
1250
|
+
return true;
|
|
1251
|
+
}
|
|
1252
|
+
break;
|
|
1253
|
+
case FromBTCLNSwapState.PR_PAID:
|
|
1254
|
+
if (this.expiry < Date.now()) {
|
|
1255
|
+
this._state = FromBTCLNSwapState.QUOTE_SOFT_EXPIRED;
|
|
1256
|
+
if (save)
|
|
1257
|
+
await this._saveAndEmit();
|
|
1258
|
+
return true;
|
|
1259
|
+
}
|
|
1260
|
+
break;
|
|
1261
|
+
case FromBTCLNSwapState.CLAIM_COMMITED:
|
|
1262
|
+
const expired = await this.wrapper._contract.isExpired(this._getInitiator(), this._data);
|
|
1263
|
+
if (expired) {
|
|
1264
|
+
this._state = FromBTCLNSwapState.EXPIRED;
|
|
1265
|
+
if (save)
|
|
1266
|
+
await this._saveAndEmit();
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1271
|
+
return false;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Forcibly sets the swap secret pre-image from on-chain data
|
|
1275
|
+
*
|
|
1276
|
+
* @internal
|
|
1277
|
+
*/
|
|
1278
|
+
_setSwapSecret(secret) {
|
|
1279
|
+
this.secret = secret;
|
|
1280
|
+
if (this.pr == null) {
|
|
1281
|
+
this.pr = buffer_1.Buffer.from((0, sha2_1.sha256)(buffer_1.Buffer.from(secret, "hex"))).toString("hex");
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
exports.FromBTCLNSwap = FromBTCLNSwap;
|