@atomiqlabs/sdk 8.3.6 → 8.4.4

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.
Files changed (58) hide show
  1. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +3 -2
  2. package/dist/bitcoin/wallet/BitcoinWallet.js +15 -1
  3. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +25 -3
  4. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +51 -2
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +2 -0
  8. package/dist/intermediaries/apis/IntermediaryAPI.js +2 -0
  9. package/dist/swapper/Swapper.d.ts +2 -1
  10. package/dist/swapper/Swapper.js +4 -3
  11. package/dist/swaps/ISwap.d.ts +34 -0
  12. package/dist/swaps/ISwap.js +10 -0
  13. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +12 -0
  14. package/dist/swaps/escrow_swaps/IEscrowSwap.js +18 -0
  15. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +54 -4
  16. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +72 -8
  17. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +4 -1
  18. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +23 -4
  19. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +89 -3
  20. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +118 -5
  21. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +2 -0
  22. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +30 -4
  23. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +48 -3
  24. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +69 -4
  25. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +40 -2
  26. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +72 -2
  27. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +51 -1
  28. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +71 -3
  29. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +28 -2
  30. package/dist/swaps/trusted/ln/LnForGasSwap.js +39 -1
  31. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +28 -3
  32. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +44 -1
  33. package/dist/types/SwapStateInfo.d.ts +5 -0
  34. package/dist/types/SwapStateInfo.js +2 -0
  35. package/dist/types/lnurl/LNURLPay.d.ts +0 -1
  36. package/dist/types/lnurl/LNURLPay.js +0 -1
  37. package/dist/types/lnurl/LNURLWithdraw.d.ts +0 -1
  38. package/dist/types/lnurl/LNURLWithdraw.js +0 -1
  39. package/package.json +3 -1
  40. package/src/bitcoin/wallet/BitcoinWallet.ts +21 -4
  41. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +60 -5
  42. package/src/index.ts +1 -0
  43. package/src/intermediaries/apis/IntermediaryAPI.ts +5 -1
  44. package/src/swapper/Swapper.ts +5 -2
  45. package/src/swaps/ISwap.ts +45 -0
  46. package/src/swaps/escrow_swaps/IEscrowSwap.ts +20 -0
  47. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +86 -11
  48. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +28 -3
  49. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +137 -9
  50. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +35 -4
  51. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +83 -6
  52. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +77 -4
  53. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +90 -4
  54. package/src/swaps/trusted/ln/LnForGasSwap.ts +51 -3
  55. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +58 -3
  56. package/src/types/SwapStateInfo.ts +6 -0
  57. package/src/types/lnurl/LNURLPay.ts +0 -1
  58. package/src/types/lnurl/LNURLWithdraw.ts +0 -1
@@ -40,22 +40,67 @@ import {
40
40
  serializePriceInfoType
41
41
  } from "../../../../types/PriceInfoType";
42
42
  import {sha256} from "@noble/hashes/sha2";
43
+ import {SwapExecutionAction} from "../../../../types/SwapExecutionAction";
43
44
 
44
45
  /**
45
46
  * State enum for FromBTCLNAuto swaps
46
47
  * @category Swaps/Lightning → Smart chain
47
48
  */
48
49
  export enum FromBTCLNAutoSwapState {
50
+ /**
51
+ * Swap has failed as the user didn't settle the HTLC on the destination before expiration
52
+ */
49
53
  FAILED = -4,
54
+ /**
55
+ * Swap has expired for good and there is no way how it can be executed anymore
56
+ */
50
57
  QUOTE_EXPIRED = -3,
58
+ /**
59
+ * A swap is almost expired, and it should be presented to the user as expired, though
60
+ * there is still a chance that it will be processed
61
+ */
51
62
  QUOTE_SOFT_EXPIRED = -2,
63
+ /**
64
+ * Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the
65
+ * swap on the destination smart chain.
66
+ */
52
67
  EXPIRED = -1,
68
+ /**
69
+ * Swap quote was created, use {@link FromBTCLNAutoSwap.getAddress} or {@link FromBTCLNAutoSwap.getHyperlink}
70
+ * to get the bolt11 lightning network invoice to pay to initiate the swap, then use the
71
+ * {@link FromBTCLNAutoSwap.waitForPayment} to wait till the lightning network payment is received
72
+ * by the intermediary (LP) and the destination HTLC escrow is created
73
+ */
53
74
  PR_CREATED = 0,
75
+ /**
76
+ * Lightning network payment has been received by the intermediary (LP), but the destination chain
77
+ * HTLC escrow hasn't been created yet. Use {@link FromBTCLNAutoSwap.waitForPayment} to continue waiting
78
+ * till the destination HTLC escrow is created.
79
+ */
54
80
  PR_PAID = 1,
81
+ /**
82
+ * Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers
83
+ * using the {@link FromBTCLNAutoSwap.waitTillClaimed} function or settle manually using the
84
+ * {@link FromBTCLNAutoSwap.claim} or {@link FromBTCLNAutoSwap.txsClaim} function.
85
+ */
55
86
  CLAIM_COMMITED = 2,
87
+ /**
88
+ * Swap successfully settled and funds received on the destination chain
89
+ */
56
90
  CLAIM_CLAIMED = 3
57
91
  }
58
92
 
93
+ const FromBTCLNAutoSwapStateDescription = {
94
+ [FromBTCLNAutoSwapState.FAILED]: "Swap has failed as the user didn't settle the HTLC on the destination before expiration",
95
+ [FromBTCLNAutoSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
96
+ [FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED]: "A swap is expired, though there is still a chance that it will be processed",
97
+ [FromBTCLNAutoSwapState.EXPIRED]: "Swap HTLC on the destination chain has expired, it is not safe anymore to settle (claim) the swap on the destination smart chain.",
98
+ [FromBTCLNAutoSwapState.PR_CREATED]: "Swap quote was created, pay the bolt11 lightning network invoice to initiate the swap, then wait till the lightning network payment is received by the intermediary (LP) and the destination HTLC escrow is created",
99
+ [FromBTCLNAutoSwapState.PR_PAID]: "Lightning network payment has been received by the intermediary (LP), but the destination chain HTLC escrow hasn't been created yet. Continue waiting till the destination HTLC escrow is created.",
100
+ [FromBTCLNAutoSwapState.CLAIM_COMMITED]: "Swap escrow HTLC has been created on the destination chain, wait for automatic settlement by the watchtowers or settle manually.",
101
+ [FromBTCLNAutoSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
102
+ };
103
+
59
104
  export type FromBTCLNAutoSwapInit<T extends SwapData> = IEscrowSwapInit<T> & {
60
105
  pr?: string,
61
106
  secret?: string,
@@ -100,6 +145,14 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
100
145
  implements IAddressSwap, ISwapWithGasDrop<T>, IClaimableSwap<T, FromBTCLNAutoDefinition<T>, FromBTCLNAutoSwapState> {
101
146
 
102
147
  protected readonly TYPE: SwapType.FROM_BTCLN_AUTO = SwapType.FROM_BTCLN_AUTO;
148
+ /**
149
+ * @internal
150
+ */
151
+ protected readonly swapStateName = (state: number) => FromBTCLNAutoSwapState[state];
152
+ /**
153
+ * @internal
154
+ */
155
+ protected readonly swapStateDescription = FromBTCLNAutoSwapStateDescription;
103
156
  /**
104
157
  * @internal
105
158
  */
@@ -109,6 +162,12 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
109
162
  */
110
163
  protected readonly inputToken: BtcToken<true> = BitcoinTokens.BTCLN;
111
164
 
165
+ /**
166
+ * Timestamp at which the HTLC was commited on the smart chain side
167
+ * @internal
168
+ */
169
+ _commitedAt?: number;
170
+
112
171
  private readonly lnurlFailSignal: AbortController = new AbortController();
113
172
  private readonly usesClaimHashAsId: boolean;
114
173
  private readonly initialSwapData: T["Data"];
@@ -135,7 +194,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
135
194
  private lnurlCallback?: string;
136
195
  private prPosted?: boolean = false;
137
196
 
138
- private broadcastTickCounter: number = 0;
197
+ private broadcastTickCounter: number = 0
139
198
 
140
199
  /**
141
200
  * Sets the LNURL data for the swap
@@ -188,6 +247,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
188
247
 
189
248
  this._commitTxId = initOrObject.commitTxId;
190
249
  this._claimTxId = initOrObject.claimTxId;
250
+ this._commitedAt = initOrObject.commitedAt;
191
251
 
192
252
  this.lnurl = initOrObject.lnurl;
193
253
  this.lnurlK1 = initOrObject.lnurlK1;
@@ -410,6 +470,16 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
410
470
  return this._state===FromBTCLNAutoSwapState.FAILED || this._state===FromBTCLNAutoSwapState.EXPIRED;
411
471
  }
412
472
 
473
+ /**
474
+ * @inheritDoc
475
+ */
476
+ isInProgress(): boolean {
477
+ return (this._state===FromBTCLNAutoSwapState.PR_CREATED && this.initiated) ||
478
+ (this._state===FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED && this.initiated) ||
479
+ this._state===FromBTCLNAutoSwapState.PR_PAID ||
480
+ this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED;
481
+ }
482
+
413
483
  /**
414
484
  * @inheritDoc
415
485
  */
@@ -662,6 +732,25 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
662
732
  return this.getSwapData().getClaimHash()===claimHash;
663
733
  }
664
734
 
735
+ /**
736
+ * Sets the secret preimage for the swap, in case it is not known already
737
+ *
738
+ * @param secret Secret preimage that matches the expected payment hash
739
+ *
740
+ * @throws {Error} If an invalid secret preimage is provided
741
+ */
742
+ setSecretPreimage(secret: string) {
743
+ if(!this.isValidSecretPreimage(secret)) throw new Error("Invalid secret preimage provided, hash doesn't match!");
744
+ this.secret = secret;
745
+ }
746
+
747
+ /**
748
+ * Returns whether the secret preimage for this swap is known
749
+ */
750
+ hasSecretPreimage(): boolean {
751
+ return this.secret != null;
752
+ }
753
+
665
754
 
666
755
  //////////////////////////////
667
756
  //// Execution
@@ -672,8 +761,8 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
672
761
  * @param walletOrLnurlWithdraw Bitcoin lightning wallet to use to pay the lightning network invoice, or an LNURL-withdraw
673
762
  * link, wallet is not required and the LN invoice can be paid externally as well (just pass null or undefined here)
674
763
  * @param callbacks Callbacks to track the progress of the swap
675
- * @param options Optional options for the swap like feeRate, AbortSignal, and timeouts/intervals
676
- * @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
764
+ * @param options Optional options for the swap like AbortSignal, and timeouts/intervals
765
+ * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
677
766
  * was recovered from on-chain data, or the pre-image was generated outside the SDK
678
767
  *
679
768
  * @returns {boolean} Whether a swap was settled automatically by swap watchtowers or requires manual claim by the
@@ -688,9 +777,9 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
688
777
  options?: {
689
778
  abortSignal?: AbortSignal,
690
779
  lightningTxCheckIntervalSeconds?: number,
691
- maxWaitTillAutomaticSettlementSeconds?: number
692
- },
693
- secret?: string
780
+ maxWaitTillAutomaticSettlementSeconds?: number,
781
+ secret?: string
782
+ }
694
783
  ): Promise<boolean> {
695
784
  if(this._state===FromBTCLNAutoSwapState.FAILED) throw new Error("Swap failed!");
696
785
  if(this._state===FromBTCLNAutoSwapState.EXPIRED) throw new Error("Swap HTLC expired!");
@@ -725,9 +814,9 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
725
814
  if((this._state as FromBTCLNAutoSwapState)===FromBTCLNAutoSwapState.CLAIM_CLAIMED) return true;
726
815
 
727
816
  if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED) {
728
- if(this.secret==null && secret==null)
817
+ if(this.secret==null && options?.secret==null)
729
818
  throw new Error("Tried to wait till settlement, but no secret pre-image is known, please pass the secret pre-image as an argument!");
730
- const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal, secret);
819
+ const success = await this.waitTillClaimed(options?.maxWaitTillAutomaticSettlementSeconds ?? 60, options?.abortSignal, options?.secret);
731
820
  if (success && callbacks?.onSwapSettled != null) callbacks.onSwapSettled(this.getOutputTxId()!);
732
821
  return success;
733
822
  }
@@ -760,6 +849,42 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
760
849
  throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED");
761
850
  }
762
851
 
852
+ /**
853
+ *
854
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
855
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
856
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
857
+ * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
858
+ * was recovered from on-chain data, or the pre-image was generated outside the SDK
859
+ */
860
+ async getCurrentActions(options?: {
861
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
862
+ maxWaitTillAutomaticSettlementSeconds?: number,
863
+ secret?: string
864
+ }): Promise<SwapExecutionAction<T>[]> {
865
+ if(options?.secret!=null) this.setSecretPreimage(options.secret);
866
+ if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
867
+ try {
868
+ return await this.txsExecute();
869
+ } catch (e) {}
870
+ }
871
+ if(this.isClaimable()) {
872
+ if(
873
+ this._commitedAt==null ||
874
+ options?.maxWaitTillAutomaticSettlementSeconds===0 ||
875
+ (Date.now() - this._commitedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
876
+ ) {
877
+ return [{
878
+ name: "Claim" as const,
879
+ description: "Manually settle (claim) the swap on the destination smart chain",
880
+ chain: this.chainIdentifier,
881
+ txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
882
+ }];
883
+ }
884
+ }
885
+ return [];
886
+ }
887
+
763
888
 
764
889
  //////////////////////////////
765
890
  //// Payment
@@ -993,6 +1118,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
993
1118
  if(
994
1119
  this._state===FromBTCLNAutoSwapState.PR_PAID
995
1120
  ) {
1121
+ this._commitedAt ??= Date.now();
996
1122
  await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_COMMITED);
997
1123
  }
998
1124
 
@@ -1207,6 +1333,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1207
1333
  data: this._data==null ? null : this._data.serialize(),
1208
1334
  commitTxId: this._commitTxId,
1209
1335
  claimTxId: this._claimTxId,
1336
+ commitedAt: this._commitedAt,
1210
1337
  btcAmountSwap: this.btcAmountSwap==null ? null : this.btcAmountSwap.toString(10),
1211
1338
  btcAmountGas: this.btcAmountGas==null ? null : this.btcAmountGas.toString(10),
1212
1339
  gasSwapFeeBtc: this.gasSwapFeeBtc==null ? null : this.gasSwapFeeBtc.toString(10),
@@ -1355,6 +1482,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1355
1482
  return true;
1356
1483
  case SwapCommitStateType.COMMITED:
1357
1484
  if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED && this._state!==FromBTCLNAutoSwapState.EXPIRED) {
1485
+ this._commitedAt ??= Date.now();
1358
1486
  this._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
1359
1487
  return true;
1360
1488
  }
@@ -1440,4 +1568,4 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1440
1568
  }
1441
1569
  }
1442
1570
 
1443
- }
1571
+ }
@@ -37,6 +37,8 @@ import {AllOptional} from "../../../../utils/TypeUtils";
37
37
  import {sha256} from "@noble/hashes/sha2";
38
38
 
39
39
  export type FromBTCLNAutoOptions = {
40
+ paymentHash?: Buffer,
41
+ description?: string,
40
42
  descriptionHash?: Buffer,
41
43
  unsafeSkipLnNodeCheck?: boolean,
42
44
  gasAmount?: bigint,
@@ -167,6 +169,7 @@ export class FromBTCLNAutoWrapper<
167
169
  }
168
170
 
169
171
  swap._commitTxId = event.meta?.txId;
172
+ swap._commitedAt ??= Date.now();
170
173
  swap._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
171
174
  swap._broadcastSecret().catch(e => {
172
175
  this.logger.error("processEventInitialize("+swap.getId()+"): Error when broadcasting swap secret: ", e);
@@ -257,7 +260,7 @@ export class FromBTCLNAutoWrapper<
257
260
  resp: FromBTCLNAutoResponseType,
258
261
  amountData: AmountData,
259
262
  lp: Intermediary,
260
- options: {gasAmount: bigint, descriptionHash?: Buffer},
263
+ options: {gasAmount: bigint, description?: string, descriptionHash?: Buffer},
261
264
  decodedPr: PaymentRequestObject & {tagsObject: TagsObject},
262
265
  paymentHash: Buffer,
263
266
  claimerBounty: bigint
@@ -267,6 +270,9 @@ export class FromBTCLNAutoWrapper<
267
270
  if(options.descriptionHash!=null && decodedPr.tagsObject.purpose_commit_hash!==options.descriptionHash.toString("hex"))
268
271
  throw new IntermediaryError("Invalid pr returned - description hash");
269
272
 
273
+ if(options.description!=null && decodedPr.tagsObject.description!==options.description)
274
+ throw new IntermediaryError("Invalid pr returned - description");
275
+
270
276
  if(
271
277
  decodedPr.tagsObject.payment_hash==null ||
272
278
  !Buffer.from(decodedPr.tagsObject.payment_hash, "hex").equals(paymentHash)
@@ -317,20 +323,36 @@ export class FromBTCLNAutoWrapper<
317
323
  quote: Promise<FromBTCLNAutoSwap<T>>,
318
324
  intermediary: Intermediary
319
325
  }[] {
326
+ if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
327
+
320
328
  const _options = {
329
+ paymentHash: options?.paymentHash,
321
330
  unsafeSkipLnNodeCheck: options?.unsafeSkipLnNodeCheck ?? this._options.unsafeSkipLnNodeCheck,
322
331
  gasAmount: options?.gasAmount ?? 0n,
323
332
  feeSafetyFactor: options?.feeSafetyFactor ?? 1.25, //No need to add much of a margin, since the claim should happen rather soon
324
333
  unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
334
+ description: options?.description,
325
335
  descriptionHash: options?.descriptionHash
326
336
  };
327
337
 
328
- if(preFetches==null) preFetches = {};
338
+ if(_options.paymentHash!=null && _options.paymentHash.length!==32)
339
+ throw new UserError("Invalid payment hash length, must be exactly 32 bytes!");
329
340
 
330
341
  if(_options.descriptionHash!=null && _options.descriptionHash.length!==32)
331
342
  throw new UserError("Invalid description hash length");
332
343
 
333
- const {secret, paymentHash} = this.getSecretAndHash();
344
+ if(_options.description!=null && Buffer.byteLength(_options.description, "utf8") > 500)
345
+ throw new UserError("Invalid description length");
346
+
347
+ if(preFetches==null) preFetches = {};
348
+
349
+ let secret: Buffer | undefined;
350
+ let paymentHash: Buffer;
351
+ if(_options?.paymentHash!=null) {
352
+ paymentHash = _options.paymentHash;
353
+ } else {
354
+ ({secret, paymentHash} = this.getSecretAndHash());
355
+ }
334
356
  const claimHash = this._contract.getHashForHtlc(paymentHash);
335
357
 
336
358
  const nativeTokenAddress = this._chain.getNativeCurrencyAddress();
@@ -363,6 +385,7 @@ export class FromBTCLNAutoWrapper<
363
385
  amount: amountData.amount,
364
386
  claimer: recipient,
365
387
  token: amountData.token.toString(),
388
+ description: _options.description,
366
389
  descriptionHash: _options.descriptionHash,
367
390
  exactOut: !amountData.exactIn,
368
391
  additionalParams,
@@ -427,7 +450,7 @@ export class FromBTCLNAutoWrapper<
427
450
  _options.gasAmount + resp.claimerBounty, resp.claimerBounty, nativeTokenAddress
428
451
  ),
429
452
  pr: resp.pr,
430
- secret: secret.toString("hex"),
453
+ secret: secret?.toString("hex"),
431
454
  exactIn: amountData.exactIn ?? true
432
455
  };
433
456
  const quote = new FromBTCLNAutoSwap<T>(this, swapInit);
@@ -471,14 +494,21 @@ export class FromBTCLNAutoWrapper<
471
494
  quote: Promise<FromBTCLNAutoSwap<T>>,
472
495
  intermediary: Intermediary
473
496
  }[]> {
497
+ if(!this.isInitialized) throw new Error("Not initialized, call init() first!");
498
+
474
499
  const _options = {
500
+ paymentHash: options?.paymentHash,
475
501
  unsafeSkipLnNodeCheck: options?.unsafeSkipLnNodeCheck ?? this._options.unsafeSkipLnNodeCheck,
476
502
  gasAmount: options?.gasAmount ?? 0n,
477
503
  feeSafetyFactor: options?.feeSafetyFactor ?? 1.25, //No need to add much of a margin, since the claim should happen rather soon
478
504
  unsafeZeroWatchtowerFee: options?.unsafeZeroWatchtowerFee ?? false,
505
+ description: options?.description,
479
506
  descriptionHash: options?.descriptionHash
480
507
  };
481
508
 
509
+ if(_options.paymentHash!=null && _options.paymentHash.length!==32)
510
+ throw new UserError("Invalid payment hash length, must be exactly 32 bytes!");
511
+
482
512
  const abortController = extendAbortController(abortSignal);
483
513
  const preFetches = {
484
514
  pricePrefetchPromise: this.preFetchPrice(amountData, abortController.signal),
@@ -641,6 +671,7 @@ export class FromBTCLNAutoWrapper<
641
671
  swap._commitTxId = await init.getInitTxId();
642
672
  const blockData = await init.getTxBlock();
643
673
  swap.createdAt = blockData.blockTime * 1000;
674
+ swap._commitedAt = blockData.blockTime * 1000;
644
675
  swap._setInitiated();
645
676
  swap._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
646
677
  await swap._sync(false, false, state);
@@ -34,6 +34,7 @@ import {TokenAmount, toTokenAmount} from "../../../../types/TokenAmount";
34
34
  import {BitcoinTokens, BtcToken, SCToken} from "../../../../types/Token";
35
35
  import {getLogger, LoggerType} from "../../../../utils/Logger";
36
36
  import {toBitcoinWallet} from "../../../../utils/BitcoinWalletUtils";
37
+ import {SwapExecutionAction} from "../../../../types/SwapExecutionAction";
37
38
 
38
39
  /**
39
40
  * State enum for legacy escrow based Bitcoin -> Smart chain swaps.
@@ -73,9 +74,9 @@ export enum FromBTCSwapState {
73
74
  */
74
75
  CLAIM_COMMITED = 1,
75
76
  /**
76
- * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower
77
- * or settle manually using the {@link FromBTCSwap.claim} or {@link FromBTCSwap.txsClaim}
78
- * function.
77
+ * Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtowers
78
+ * using the {@link FromBTCSwap.waitTillClaimed} function or settle manually using the {@link FromBTCSwap.claim}
79
+ * or {@link FromBTCSwap.txsClaim} function.
79
80
  */
80
81
  BTC_TX_CONFIRMED = 2,
81
82
  /**
@@ -84,6 +85,17 @@ export enum FromBTCSwapState {
84
85
  CLAIM_CLAIMED = 3
85
86
  }
86
87
 
88
+ const FromBTCSwapStateDescription = {
89
+ [FromBTCSwapState.FAILED]: "Bitcoin swap address has expired and the intermediary (LP) has already refunded its funds. No BTC should be sent anymore!",
90
+ [FromBTCSwapState.EXPIRED]: "Bitcoin swap address has expired, user should not send any BTC anymore! Though the intermediary (LP) hasn't refunded yet. So if there is a transaction already in-flight the swap might still succeed.",
91
+ [FromBTCSwapState.QUOTE_EXPIRED]: "Swap has expired for good and there is no way how it can be executed anymore",
92
+ [FromBTCSwapState.QUOTE_SOFT_EXPIRED]: "The swap is expired, though there is still a chance that it will be processed",
93
+ [FromBTCSwapState.PR_CREATED]: "Swap quote was created, initiate it by creating the swap escrow on the destination smart chain",
94
+ [FromBTCSwapState.CLAIM_COMMITED]: "Swap escrow was initiated (committed) on the destination chain, user can send the BTC to the Bitcoin swap address.",
95
+ [FromBTCSwapState.BTC_TX_CONFIRMED]: "Input bitcoin transaction was confirmed, wait for automatic settlement by the watchtower or settle manually.",
96
+ [FromBTCSwapState.CLAIM_CLAIMED]: "Swap successfully settled and funds received on the destination chain"
97
+ };
98
+
87
99
  export type FromBTCSwapInit<T extends SwapData> = IEscrowSelfInitSwapInit<T> & {
88
100
  data: T;
89
101
  address?: string;
@@ -110,7 +122,14 @@ export class FromBTCSwap<T extends ChainType = ChainType>
110
122
  implements IBTCWalletSwap, IClaimableSwap<T, FromBTCDefinition<T>, FromBTCSwapState>, IAddressSwap {
111
123
 
112
124
  protected readonly TYPE: SwapType.FROM_BTC = SwapType.FROM_BTC;
113
-
125
+ /**
126
+ * @internal
127
+ */
128
+ protected readonly swapStateName = (state: number) => FromBTCSwapState[state];
129
+ /**
130
+ * @internal
131
+ */
132
+ protected readonly swapStateDescription = FromBTCSwapStateDescription;
114
133
  /**
115
134
  * @internal
116
135
  */
@@ -137,6 +156,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
137
156
  private txId?: string;
138
157
  private vout?: number;
139
158
 
159
+ private btcTxConfirmedAt?: number;
160
+
140
161
  constructor(wrapper: FromBTCWrapper<T>, init: FromBTCSwapInit<T["Data"]>);
141
162
  constructor(wrapper: FromBTCWrapper<T>, obj: any);
142
163
  constructor(wrapper: FromBTCWrapper<T>, initOrObject: FromBTCSwapInit<T["Data"]> | any) {
@@ -156,6 +177,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
156
177
  this.txId = initOrObject.txId;
157
178
  this.vout = initOrObject.vout;
158
179
  this.requiredConfirmations = initOrObject.requiredConfirmations ?? this._data.getConfirmationsHint();
180
+ this.btcTxConfirmedAt = initOrObject.btcTxConfirmedAt;
159
181
  }
160
182
  this.tryRecomputeSwapPrice();
161
183
  this.logger = getLogger("FromBTC("+this.getIdentifierHashString()+"): ");
@@ -285,6 +307,14 @@ export class FromBTCSwap<T extends ChainType = ChainType>
285
307
  return this._state===FromBTCSwapState.FAILED || this._state===FromBTCSwapState.EXPIRED;
286
308
  }
287
309
 
310
+ /**
311
+ * @inheritDoc
312
+ */
313
+ isInProgress(): boolean {
314
+ return this._state===FromBTCSwapState.CLAIM_COMMITED ||
315
+ this._state===FromBTCSwapState.BTC_TX_CONFIRMED;
316
+ }
317
+
288
318
  /**
289
319
  * @inheritDoc
290
320
  */
@@ -519,6 +549,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
519
549
  (this._state as FromBTCSwapState)!==FromBTCSwapState.CLAIM_CLAIMED &&
520
550
  (this._state as FromBTCSwapState)!==FromBTCSwapState.FAILED
521
551
  ) {
552
+ this.btcTxConfirmedAt ??= Date.now();
522
553
  this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
523
554
  }
524
555
 
@@ -737,6 +768,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
737
768
  /**
738
769
  * @inheritDoc
739
770
  *
771
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
740
772
  * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
741
773
  * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
742
774
  * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
@@ -746,6 +778,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
746
778
  * @throws {Error} if the swap or quote is expired, or if triggered in invalid state
747
779
  */
748
780
  async txsExecute(options?: {
781
+ bitcoinFeeRate?: number,
749
782
  bitcoinWallet?: MinimalBitcoinWalletInterface,
750
783
  skipChecks?: boolean
751
784
  }) {
@@ -803,6 +836,47 @@ export class FromBTCSwap<T extends ChainType = ChainType>
803
836
  throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED or CLAIM_COMMITED");
804
837
  }
805
838
 
839
+ /**
840
+ * @inheritDoc
841
+ *
842
+ * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
843
+ * @param options.bitcoinWallet Bitcoin wallet to use, when provided the function returns a funded
844
+ * psbt (`"FUNDED_PSBT"`), if not passed just a bitcoin receive address is returned (`"ADDRESS"`)
845
+ * @param options.skipChecks Skip checks like making sure init signature is still valid and swap
846
+ * wasn't commited yet (this is handled on swap creation, if you commit right after quoting, you
847
+ * can use `skipChecks=true`)
848
+ * @param options.manualSettlementSmartChainSigner Optional smart chain signer to create a manual claim (settlement) transaction
849
+ * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
850
+ * the bitcoin transaction is confirmed (defaults to 60 seconds)
851
+ */
852
+ async getCurrentActions(options?: {
853
+ bitcoinFeeRate?: number,
854
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
855
+ skipChecks?: boolean,
856
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
857
+ maxWaitTillAutomaticSettlementSeconds?: number
858
+ }): Promise<SwapExecutionAction<T>[]> {
859
+ if(this._state===FromBTCSwapState.PR_CREATED || this._state===FromBTCSwapState.CLAIM_COMMITED) {
860
+ try {
861
+ return await this.txsExecute(options);
862
+ } catch (e) {}
863
+ }
864
+ if(this.isClaimable()) {
865
+ if(
866
+ this.btcTxConfirmedAt==null ||
867
+ options?.maxWaitTillAutomaticSettlementSeconds===0 ||
868
+ (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
869
+ ) {
870
+ return [{
871
+ name: "Claim" as const,
872
+ description: "Manually settle (claim) the swap on the destination smart chain",
873
+ chain: this.chainIdentifier,
874
+ txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
875
+ }];
876
+ }
877
+ }
878
+ return[];
879
+ }
806
880
 
807
881
  //////////////////////////////
808
882
  //// Commit
@@ -1047,7 +1121,8 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1047
1121
  requiredConfirmations: this.requiredConfirmations,
1048
1122
  senderAddress: this.senderAddress,
1049
1123
  txId: this.txId,
1050
- vout: this.vout
1124
+ vout: this.vout,
1125
+ btcTxConfirmedAt: this.btcTxConfirmedAt
1051
1126
  };
1052
1127
  }
1053
1128
 
@@ -1156,6 +1231,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1156
1231
  save = true;
1157
1232
  }
1158
1233
  if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
1234
+ this.btcTxConfirmedAt ??= Date.now();
1159
1235
  this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1160
1236
  save = true;
1161
1237
  }
@@ -1198,6 +1274,7 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1198
1274
  shouldSave = true;
1199
1275
  }
1200
1276
  if(this.requiredConfirmations!=null && res.confirmations>=this.requiredConfirmations) {
1277
+ this.btcTxConfirmedAt ??= Date.now();
1201
1278
  this._state = FromBTCSwapState.BTC_TX_CONFIRMED;
1202
1279
  if(save) await this._saveAndEmit();
1203
1280
  shouldSave = true;
@@ -1215,4 +1292,4 @@ export class FromBTCSwap<T extends ChainType = ChainType>
1215
1292
  return false;
1216
1293
  }
1217
1294
 
1218
- }
1295
+ }