@atomiqlabs/sdk 8.7.7 → 8.9.0

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 (152) hide show
  1. package/api/index.d.ts +1 -0
  2. package/api/index.js +3 -0
  3. package/dist/ApiList.d.ts +37 -0
  4. package/dist/ApiList.js +30 -0
  5. package/dist/api/ApiEndpoints.d.ts +393 -0
  6. package/dist/api/ApiEndpoints.js +2 -0
  7. package/dist/api/ApiParser.d.ts +10 -0
  8. package/dist/api/ApiParser.js +134 -0
  9. package/dist/api/ApiTypes.d.ts +157 -0
  10. package/dist/api/ApiTypes.js +75 -0
  11. package/dist/api/SerializedAction.d.ts +40 -0
  12. package/dist/api/SerializedAction.js +59 -0
  13. package/dist/api/SwapperApi.d.ts +50 -0
  14. package/dist/api/SwapperApi.js +431 -0
  15. package/dist/api/index.d.ts +5 -0
  16. package/dist/api/index.js +24 -0
  17. package/dist/bitcoin/coinselect2/accumulative.d.ts +1 -0
  18. package/dist/bitcoin/coinselect2/accumulative.js +1 -1
  19. package/dist/bitcoin/coinselect2/blackjack.d.ts +1 -0
  20. package/dist/bitcoin/coinselect2/blackjack.js +1 -1
  21. package/dist/bitcoin/coinselect2/index.d.ts +3 -2
  22. package/dist/bitcoin/coinselect2/index.js +2 -2
  23. package/dist/bitcoin/coinselect2/utils.d.ts +7 -2
  24. package/dist/bitcoin/coinselect2/utils.js +45 -10
  25. package/dist/bitcoin/wallet/BitcoinWallet.d.ts +8 -25
  26. package/dist/bitcoin/wallet/BitcoinWallet.js +31 -18
  27. package/dist/bitcoin/wallet/IBitcoinWallet.d.ts +40 -2
  28. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.d.ts +7 -2
  29. package/dist/bitcoin/wallet/SingleAddressBitcoinWallet.js +10 -4
  30. package/dist/events/UnifiedSwapEventListener.d.ts +4 -3
  31. package/dist/events/UnifiedSwapEventListener.js +8 -2
  32. package/dist/http/HttpUtils.d.ts +4 -2
  33. package/dist/http/HttpUtils.js +10 -4
  34. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
  35. package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
  39. package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
  40. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +182 -15
  41. package/dist/intermediaries/apis/IntermediaryAPI.js +192 -31
  42. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
  43. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
  44. package/dist/storage/IUnifiedStorage.d.ts +45 -3
  45. package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
  46. package/dist/storage/UnifiedSwapStorage.js +46 -8
  47. package/dist/swapper/Swapper.d.ts +77 -4
  48. package/dist/swapper/Swapper.js +117 -25
  49. package/dist/swapper/SwapperUtils.d.ts +18 -2
  50. package/dist/swapper/SwapperUtils.js +39 -1
  51. package/dist/swaps/ISwap.d.ts +70 -9
  52. package/dist/swaps/ISwap.js +28 -6
  53. package/dist/swaps/ISwapWrapper.d.ts +11 -1
  54. package/dist/swaps/ISwapWrapper.js +23 -3
  55. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
  56. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
  57. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
  58. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
  59. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
  60. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
  61. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
  62. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
  63. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
  64. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
  65. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
  66. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
  67. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
  68. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
  69. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
  70. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
  71. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
  72. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
  73. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
  74. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
  75. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
  76. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
  77. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
  78. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
  79. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +85 -22
  80. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +299 -56
  81. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +41 -7
  82. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +183 -58
  83. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
  84. package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
  85. package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
  86. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
  87. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
  88. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
  89. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
  90. package/dist/types/SwapExecutionAction.d.ts +141 -34
  91. package/dist/types/SwapExecutionAction.js +104 -0
  92. package/dist/types/SwapExecutionStep.d.ts +144 -0
  93. package/dist/types/SwapExecutionStep.js +87 -0
  94. package/dist/types/TokenAmount.d.ts +6 -0
  95. package/dist/types/TokenAmount.js +26 -1
  96. package/dist/utils/BitcoinUtils.d.ts +4 -0
  97. package/dist/utils/BitcoinUtils.js +73 -1
  98. package/dist/utils/BitcoinWalletUtils.d.ts +2 -2
  99. package/dist/utils/Utils.d.ts +3 -1
  100. package/dist/utils/Utils.js +7 -1
  101. package/package.json +7 -4
  102. package/src/api/ApiEndpoints.ts +427 -0
  103. package/src/api/ApiParser.ts +138 -0
  104. package/src/api/ApiTypes.ts +229 -0
  105. package/src/api/SerializedAction.ts +97 -0
  106. package/src/api/SwapperApi.ts +545 -0
  107. package/src/api/index.ts +5 -0
  108. package/src/bitcoin/coinselect2/accumulative.ts +2 -1
  109. package/src/bitcoin/coinselect2/blackjack.ts +2 -1
  110. package/src/bitcoin/coinselect2/index.ts +5 -4
  111. package/src/bitcoin/coinselect2/utils.ts +55 -14
  112. package/src/bitcoin/wallet/BitcoinWallet.ts +69 -57
  113. package/src/bitcoin/wallet/IBitcoinWallet.ts +44 -3
  114. package/src/bitcoin/wallet/SingleAddressBitcoinWallet.ts +12 -4
  115. package/src/events/UnifiedSwapEventListener.ts +11 -3
  116. package/src/http/HttpUtils.ts +10 -4
  117. package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
  118. package/src/index.ts +1 -0
  119. package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
  120. package/src/intermediaries/apis/IntermediaryAPI.ts +335 -35
  121. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
  122. package/src/storage/IUnifiedStorage.ts +45 -4
  123. package/src/storage/UnifiedSwapStorage.ts +42 -8
  124. package/src/swapper/Swapper.ts +165 -24
  125. package/src/swapper/SwapperUtils.ts +42 -2
  126. package/src/swaps/ISwap.ts +88 -16
  127. package/src/swaps/ISwapWrapper.ts +28 -3
  128. package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
  129. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
  130. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
  131. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
  132. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
  133. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
  134. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
  135. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
  136. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
  137. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
  138. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
  139. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
  140. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +413 -64
  141. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +239 -61
  142. package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
  143. package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
  144. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
  145. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
  146. package/src/types/SwapExecutionAction.ts +266 -43
  147. package/src/types/SwapExecutionStep.ts +224 -0
  148. package/src/types/TokenAmount.ts +36 -2
  149. package/src/utils/BitcoinUtils.ts +73 -0
  150. package/src/utils/BitcoinWalletUtils.ts +2 -2
  151. package/src/utils/Utils.ts +10 -1
  152. package/src/intermediaries/apis/TrustedIntermediaryAPI.ts +0 -258
@@ -1,5 +1,6 @@
1
1
  import {isISwapInit, ISwap, ISwapInit} from "../ISwap";
2
2
  import {
3
+ BtcTx,
3
4
  BtcTxWithBlockheight,
4
5
  ChainType,
5
6
  isAbstractSigner,
@@ -16,7 +17,7 @@ import {parsePsbtTransaction, toCoinselectAddressType, toOutputScript} from "../
16
17
  import {getInputType, Transaction} from "@scure/btc-signer";
17
18
  import {Buffer} from "buffer";
18
19
  import {Fee} from "../../types/fees/Fee";
19
- import {IBitcoinWallet, isIBitcoinWallet} from "../../bitcoin/wallet/IBitcoinWallet";
20
+ import {BitcoinWalletUtxo, IBitcoinWallet, isIBitcoinWallet} from "../../bitcoin/wallet/IBitcoinWallet";
20
21
  import {IntermediaryAPI} from "../../intermediaries/apis/IntermediaryAPI";
21
22
  import {IBTCWalletSwap} from "../IBTCWalletSwap";
22
23
  import {ISwapWithGasDrop} from "../ISwapWithGasDrop";
@@ -38,7 +39,16 @@ import {
38
39
  serializePriceInfoType
39
40
  } from "../../types/PriceInfoType";
40
41
  import {toBitcoinWallet} from "../../utils/BitcoinWalletUtils";
41
- import {SwapExecutionAction, SwapExecutionActionBitcoin} from "../../types/SwapExecutionAction";
42
+ import {
43
+ SwapExecutionAction,
44
+ SwapExecutionActionSignPSBT, SwapExecutionActionSignSmartChainTx,
45
+ SwapExecutionActionWait
46
+ } from "../../types/SwapExecutionAction";
47
+ import {
48
+ SwapExecutionStepPayment,
49
+ SwapExecutionStepSettlement
50
+ } from "../../types/SwapExecutionStep";
51
+ import {SwapStateInfo} from "../../types/SwapStateInfo";
42
52
 
43
53
  /**
44
54
  * State enum for SPV vault (UTXO-controlled vault) based swaps
@@ -499,7 +509,7 @@ export class SpvFromBTCSwap<T extends ChainType>
499
509
  * @inheritDoc
500
510
  */
501
511
  isFinished(): boolean {
502
- return this._state===SpvFromBTCSwapState.CLAIMED || this._state===SpvFromBTCSwapState.QUOTE_EXPIRED || this._state===SpvFromBTCSwapState.FAILED;
512
+ return this.isSuccessful() || this.isFailed() || this.isQuoteExpired();
503
513
  }
504
514
 
505
515
  /**
@@ -822,13 +832,15 @@ export class SpvFromBTCSwap<T extends ChainType>
822
832
  /**
823
833
  * Returns the raw PSBT (not funded), the wallet should fund the PSBT (add its inputs) and importantly **set the nSequence field of the
824
834
  * 2nd input** (input 1 - indexing from 0) to the value returned in `in1sequence`, sign the PSBT and then pass
825
- * it back to the swap with {@link submitPsbt} function.
835
+ * it back to the swap with {@link submitPsbt} function. The transaction should use at least the returned `feeRate`
836
+ * sats/vB as the transaction fee.
826
837
  */
827
838
  async getPsbt(): Promise<{
828
839
  psbt: Transaction,
829
840
  psbtHex: string,
830
841
  psbtBase64: string,
831
- in1sequence: number
842
+ in1sequence: number,
843
+ feeRate: number
832
844
  }> {
833
845
  const res = await this.getTransactionDetails();
834
846
  const psbt = new Transaction({
@@ -862,7 +874,8 @@ export class SpvFromBTCSwap<T extends ChainType>
862
874
  psbt,
863
875
  psbtHex: serializedPsbt.toString("hex"),
864
876
  psbtBase64: serializedPsbt.toString("base64"),
865
- in1sequence: res.in1sequence
877
+ in1sequence: res.in1sequence,
878
+ feeRate: this.minimumBtcFeeRate
866
879
  };
867
880
  }
868
881
 
@@ -877,16 +890,23 @@ export class SpvFromBTCSwap<T extends ChainType>
877
890
  * @param _bitcoinWallet Sender's bitcoin wallet
878
891
  * @param feeRate Optional fee rate in sats/vB for the transaction
879
892
  * @param additionalOutputs additional outputs to add to the PSBT - can be used to collect fees from users
893
+ * @param utxos Pre-fetched list of UTXOs to spend from
894
+ * @param spendFully Instructs the wallet to spend all the passed UTXOs in the transaction without creating any
895
+ * change output, if the `feeRate` is passed, it will also enforce that the feeRate in sats/vB for the resulting
896
+ * transaction is not more than 50% and 10 sats/vB larger (considering also the CPFP adjustments)
880
897
  */
881
898
  async getFundedPsbt(
882
899
  _bitcoinWallet: IBitcoinWallet | MinimalBitcoinWalletInterface,
883
900
  feeRate?: number,
884
- additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[]
901
+ additionalOutputs?: ({amount: bigint, outputScript: Uint8Array} | {amount: bigint, address: string})[],
902
+ utxos?: BitcoinWalletUtxo[],
903
+ spendFully?: boolean
885
904
  ): Promise<{
886
905
  psbt: Transaction,
887
906
  psbtHex: string,
888
907
  psbtBase64: string,
889
- signInputs: number[]
908
+ signInputs: number[],
909
+ feeRate: number
890
910
  }> {
891
911
  const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
892
912
  if(feeRate!=null) {
@@ -901,7 +921,7 @@ export class SpvFromBTCSwap<T extends ChainType>
901
921
  script: (output as {outputScript: Uint8Array}).outputScript ?? toOutputScript(this.wrapper._options.bitcoinNetwork, (output as {address: string}).address)
902
922
  });
903
923
  });
904
- psbt = await bitcoinWallet.fundPsbt(psbt, feeRate);
924
+ psbt = await bitcoinWallet.fundPsbt(psbt, feeRate, utxos, spendFully);
905
925
  psbt.updateInput(1, {sequence: in1sequence});
906
926
  //Sign every input except the first one
907
927
  const signInputs: number[] = [];
@@ -913,7 +933,8 @@ export class SpvFromBTCSwap<T extends ChainType>
913
933
  psbt,
914
934
  psbtHex: serializedPsbt.toString("hex"),
915
935
  psbtBase64: serializedPsbt.toString("base64"),
916
- signInputs
936
+ signInputs,
937
+ feeRate
917
938
  };
918
939
  }
919
940
 
@@ -994,7 +1015,7 @@ export class SpvFromBTCSwap<T extends ChainType>
994
1015
  await this._saveAndEmit(SpvFromBTCSwapState.SIGNED);
995
1016
 
996
1017
  try {
997
- await IntermediaryAPI.initSpvFromBTC(
1018
+ await this.wrapper._lpApi.initSpvFromBTC(
998
1019
  this.chainIdentifier,
999
1020
  this.url,
1000
1021
  {
@@ -1024,8 +1045,13 @@ export class SpvFromBTCSwap<T extends ChainType>
1024
1045
  /**
1025
1046
  * @inheritDoc
1026
1047
  */
1027
- async sendBitcoinTransaction(wallet: IBitcoinWallet | MinimalBitcoinWalletInterfaceWithSigner, feeRate?: number): Promise<string> {
1028
- const {psbt, psbtBase64, psbtHex, signInputs} = await this.getFundedPsbt(wallet, feeRate);
1048
+ async sendBitcoinTransaction(
1049
+ wallet: IBitcoinWallet | MinimalBitcoinWalletInterfaceWithSigner,
1050
+ feeRate?: number,
1051
+ utxos?: BitcoinWalletUtxo[],
1052
+ spendFully?: boolean
1053
+ ): Promise<string> {
1054
+ const {psbt, psbtBase64, psbtHex, signInputs} = await this.getFundedPsbt(wallet, feeRate, undefined, utxos, spendFully);
1029
1055
  let signedPsbt: Transaction | string;
1030
1056
  if(isIBitcoinWallet(wallet)) {
1031
1057
  signedPsbt = await wallet.signPsbt(psbt, signInputs);
@@ -1060,7 +1086,9 @@ export class SpvFromBTCSwap<T extends ChainType>
1060
1086
  feeRate?: number,
1061
1087
  abortSignal?: AbortSignal,
1062
1088
  btcTxCheckIntervalSeconds?: number,
1063
- maxWaitTillAutomaticSettlementSeconds?: number
1089
+ maxWaitTillAutomaticSettlementSeconds?: number,
1090
+ utxos?: BitcoinWalletUtxo[],
1091
+ spendFully?: boolean
1064
1092
  }
1065
1093
  ): Promise<boolean> {
1066
1094
  if(this._state===SpvFromBTCSwapState.CLOSED) throw new Error("Swap encountered a catastrophic failure!");
@@ -1070,7 +1098,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1070
1098
  if(this._state===SpvFromBTCSwapState.CLAIMED || this._state===SpvFromBTCSwapState.FRONTED) throw new Error("Swap already settled or fronted!");
1071
1099
 
1072
1100
  if(this._state===SpvFromBTCSwapState.CREATED) {
1073
- const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate);
1101
+ const txId = await this.sendBitcoinTransaction(wallet, options?.feeRate, options?.utxos, options?.spendFully);
1074
1102
  if(callbacks?.onSourceTransactionSent!=null) callbacks.onSourceTransactionSent(txId);
1075
1103
  }
1076
1104
  if(this._state===SpvFromBTCSwapState.POSTED || this._state===SpvFromBTCSwapState.BROADCASTED) {
@@ -1089,39 +1117,329 @@ export class SpvFromBTCSwap<T extends ChainType>
1089
1117
  }
1090
1118
 
1091
1119
  /**
1092
- * @inheritDoc
1093
- *
1094
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
1095
- * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
1096
- * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
1097
- * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
1098
- * indexing from 0) to the value returned in `in1sequence`
1120
+ * @internal
1099
1121
  */
1100
- async txsExecute(options?: {
1122
+ protected async _getExecutionStatus(options?: {
1101
1123
  bitcoinFeeRate?: number,
1102
1124
  bitcoinWallet?: MinimalBitcoinWalletInterface,
1103
- }): Promise<[
1104
- SwapExecutionActionBitcoin<"RAW_PSBT" | "FUNDED_PSBT">
1105
- ]> {
1106
- if(this._state===SpvFromBTCSwapState.CREATED) {
1107
- if (!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
1108
- return [
1125
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1126
+ maxWaitTillAutomaticSettlementSeconds?: number
1127
+ }) {
1128
+ const state = this._state;
1129
+ const now = Date.now();
1130
+
1131
+ let confirmations: {
1132
+ current: number,
1133
+ target: number,
1134
+ etaSeconds: number
1135
+ } | undefined;
1136
+
1137
+ let bitcoinPaymentStatus: SwapExecutionStepPayment<"BITCOIN">["status"] = "inactive";
1138
+ let destinationSettlementStatus: SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">["status"] = "inactive";
1139
+ let buildCurrentAction: (actionOptions?: {
1140
+ bitcoinFeeRate?: number,
1141
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
1142
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1143
+ maxWaitTillAutomaticSettlementSeconds?: number
1144
+ }) => Promise<
1145
+ SwapExecutionActionSignPSBT |
1146
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
1147
+ SwapExecutionActionSignSmartChainTx<T> |
1148
+ undefined
1149
+ > = async () => undefined;
1150
+
1151
+ switch(state) {
1152
+ case SpvFromBTCSwapState.QUOTE_EXPIRED:
1153
+ case SpvFromBTCSwapState.DECLINED:
1154
+ case SpvFromBTCSwapState.FAILED:
1155
+ bitcoinPaymentStatus = "expired";
1156
+ break;
1157
+ case SpvFromBTCSwapState.CREATED: {
1158
+ const quoteValid = await this._verifyQuoteValid();
1159
+ bitcoinPaymentStatus = quoteValid ? "awaiting" : "soft_expired";
1160
+ if(quoteValid) {
1161
+ buildCurrentAction = this._buildDepositPsbtAction.bind(this);
1162
+ }
1163
+ break;
1164
+ }
1165
+ case SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED:
1166
+ case SpvFromBTCSwapState.SIGNED:
1167
+ case SpvFromBTCSwapState.POSTED:
1168
+ case SpvFromBTCSwapState.BROADCASTED:
1169
+ case SpvFromBTCSwapState.FRONTED: {
1170
+ const bitcoinPayment = await this.getBitcoinPayment();
1171
+ let bitcoinConfirmationDelay = -1;
1172
+ let knownBitcoinPaymentStatus: "received" | "confirmed" | undefined;
1173
+ if(bitcoinPayment!=null) {
1174
+ if(bitcoinPayment.confirmations >= bitcoinPayment.targetConfirmations) {
1175
+ knownBitcoinPaymentStatus = "confirmed";
1176
+ } else {
1177
+ const result = await this.wrapper._btcRpc.getConfirmationDelay(
1178
+ bitcoinPayment.btcTx,
1179
+ bitcoinPayment.targetConfirmations
1180
+ );
1181
+ confirmations = {
1182
+ current: bitcoinPayment.confirmations,
1183
+ target: bitcoinPayment.targetConfirmations,
1184
+ etaSeconds: result ?? -1
1185
+ };
1186
+ knownBitcoinPaymentStatus = "received";
1187
+ bitcoinConfirmationDelay = result ?? -1;
1188
+ }
1189
+ }
1190
+ if(state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED)
1191
+ bitcoinPaymentStatus = knownBitcoinPaymentStatus ?? "soft_expired";
1192
+ if(state===SpvFromBTCSwapState.POSTED || state===SpvFromBTCSwapState.SIGNED)
1193
+ bitcoinPaymentStatus = knownBitcoinPaymentStatus ?? "awaiting";
1194
+ if(state===SpvFromBTCSwapState.BROADCASTED)
1195
+ bitcoinPaymentStatus = knownBitcoinPaymentStatus ?? "received";
1196
+ destinationSettlementStatus = "inactive";
1197
+
1198
+ if(state===SpvFromBTCSwapState.FRONTED) {
1199
+ bitcoinPaymentStatus = knownBitcoinPaymentStatus ?? "received";
1200
+ destinationSettlementStatus = "settled";
1201
+ }
1202
+
1203
+ if(
1204
+ state===SpvFromBTCSwapState.SIGNED ||
1205
+ state===SpvFromBTCSwapState.POSTED ||
1206
+ state===SpvFromBTCSwapState.BROADCASTED
1207
+ ) {
1208
+ buildCurrentAction = this._buildWaitBitcoinConfirmationsAction.bind(this, bitcoinConfirmationDelay);
1209
+ }
1210
+ break;
1211
+ }
1212
+ case SpvFromBTCSwapState.BTC_TX_CONFIRMED:
1213
+ bitcoinPaymentStatus = "confirmed";
1214
+ if(
1215
+ this.btcTxConfirmedAt==null ||
1216
+ options?.maxWaitTillAutomaticSettlementSeconds===0 ||
1217
+ (now - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
1218
+ ) {
1219
+ destinationSettlementStatus = "awaiting_manual";
1220
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
1221
+ } else {
1222
+ destinationSettlementStatus = "awaiting_automatic";
1223
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
1224
+ }
1225
+ break;
1226
+ case SpvFromBTCSwapState.CLAIMED:
1227
+ bitcoinPaymentStatus = "confirmed";
1228
+ destinationSettlementStatus = "settled";
1229
+ break;
1230
+ case SpvFromBTCSwapState.CLOSED:
1231
+ bitcoinPaymentStatus = "confirmed";
1232
+ destinationSettlementStatus = "expired";
1233
+ break;
1234
+ }
1235
+
1236
+ if(bitcoinPaymentStatus==="confirmed") {
1237
+ confirmations = {
1238
+ current: this.getRequiredConfirmationsCount(),
1239
+ target: this.getRequiredConfirmationsCount(),
1240
+ etaSeconds: 0
1241
+ };
1242
+ }
1243
+
1244
+ return {
1245
+ steps: [
1109
1246
  {
1110
- name: "Payment",
1111
- description: "Send funds to the bitcoin swap address",
1247
+ type: "Payment",
1248
+ side: "source",
1112
1249
  chain: "BITCOIN",
1113
- txs: [
1114
- options?.bitcoinWallet==null
1115
- ? {...await this.getPsbt(), type: "RAW_PSBT"}
1116
- : {...await this.getFundedPsbt(options.bitcoinWallet, options?.bitcoinFeeRate), type: "FUNDED_PSBT"}
1117
- ]
1250
+ title: "Bitcoin payment",
1251
+ description: "Sign and submit the Bitcoin swap PSBT, then wait for the bitcoin transaction to confirm",
1252
+ status: bitcoinPaymentStatus,
1253
+ confirmations,
1254
+ initTxId: this._data?.btcTx?.txid,
1255
+ settleTxId: bitcoinPaymentStatus==="confirmed" ? this._data?.btcTx?.txid : undefined
1256
+ },
1257
+ {
1258
+ type: "Settlement",
1259
+ side: "destination",
1260
+ chain: this.chainIdentifier,
1261
+ title: "Destination settlement",
1262
+ description: `Wait for automatic settlement on the ${this.chainIdentifier} side, or settle manually if it takes too long`,
1263
+ status: destinationSettlementStatus,
1264
+ initTxId: this._frontTxId ?? this._claimTxId,
1265
+ settleTxId: this._frontTxId ?? this._claimTxId
1266
+ }
1267
+ ] as [
1268
+ SwapExecutionStepPayment<"BITCOIN">,
1269
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1270
+ ],
1271
+ buildCurrentAction,
1272
+ state
1273
+ };
1274
+ }
1275
+
1276
+ /**
1277
+ * @inheritDoc
1278
+ * @internal
1279
+ */
1280
+ async _submitExecutionTransactions(txs: (T["SignedTXType"] | Transaction | string)[], abortSignal?: AbortSignal, requiredStates?: SpvFromBTCSwapState[], idempotent?: boolean): Promise<string[]> {
1281
+ if(txs.length===0) throw new Error("Need to submit at least 1 transaction in the array, submitted empty array of transactions!");
1282
+
1283
+ // Handle idempotent calls
1284
+ if(idempotent) {
1285
+ let idempotencyTriggered = false;
1286
+ const txIds: string[] = [];
1287
+ for(let tx of txs) {
1288
+ let parsedTx: T["SignedTXType"] | Transaction | undefined;
1289
+ if(typeof(tx)==="string") {
1290
+ try {
1291
+ parsedTx = await this.wrapper._chain.deserializeSignedTx(tx);
1292
+ } catch (e) {}
1293
+ try {
1294
+ parsedTx = parsePsbtTransaction(tx);
1295
+ } catch (e) {}
1296
+ } else {
1297
+ parsedTx = tx;
1298
+ }
1299
+
1300
+ if(parsedTx==null) {
1301
+ this.logger.debug("_submitExecutionTransactions(): Failed to parse provided execution transaction: ", tx);
1302
+ continue;
1303
+ }
1304
+
1305
+ if(parsedTx instanceof Transaction) {
1306
+ // Bitcoin tx
1307
+ const btcTx = await this.wrapper._btcRpc.parseTransaction(Buffer.from(parsedTx.toBytes(true)).toString("hex"));
1308
+ if(btcTx.txid===this._data?.getTxId()) {
1309
+ if(this._state!==SpvFromBTCSwapState.SIGNED && this._state!==SpvFromBTCSwapState.DECLINED)
1310
+ idempotencyTriggered = true;
1311
+ }
1312
+ txIds.push(btcTx.txid);
1313
+ } else {
1314
+ // SC tx
1315
+ if(this.wrapper._chain.getTxId!=null) {
1316
+ const txId = await this.wrapper._chain.getTxId(parsedTx);
1317
+ if(this._claimTxId===txId) idempotencyTriggered = true;
1318
+ txIds.push(txId);
1319
+ }
1118
1320
  }
1119
- ];
1321
+ }
1322
+ if(idempotencyTriggered) return txIds;
1323
+ }
1324
+
1325
+ if(requiredStates!=null && !requiredStates.includes(this._state)) throw new Error("Swap state has changed before transactions were submitted!");
1326
+
1327
+ if(this._state===SpvFromBTCSwapState.CREATED || this._state===SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED) {
1328
+ let psbt: string | Transaction;
1329
+ if(txs.length!==1) throw new Error("Need to submit exactly 1 signed PSBT!");
1330
+ if(typeof(txs[0])!=="string" && !(txs[0] instanceof Transaction))
1331
+ throw new Error("Must submit a valid PSBT as hex/base64 string or `@scure/btc-signer` Transaction object!");
1332
+ psbt = txs[0];
1333
+ return [await this.submitPsbt(psbt)];
1120
1334
  }
1121
1335
 
1122
- throw new Error("Invalid swap state to obtain execution txns, required CREATED");
1336
+ if(this._state===SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1337
+ const parsedTxs: T["SignedTXType"][] = [];
1338
+ for(let tx of txs) {
1339
+ parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
1340
+ }
1341
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
1342
+ await this.waitTillClaimed(undefined, abortSignal);
1343
+ return txIds;
1344
+ }
1345
+
1346
+ throw new Error("Invalid swap state for transaction submission!");
1347
+ }
1348
+
1349
+ /**
1350
+ * @internal
1351
+ */
1352
+ private async _buildDepositPsbtAction(actionOptions?: {
1353
+ bitcoinFeeRate?: number,
1354
+ bitcoinWallet?: MinimalBitcoinWalletInterface
1355
+ }): Promise<SwapExecutionActionSignPSBT> {
1356
+ return {
1357
+ type: "SignPSBT",
1358
+ name: "Deposit on Bitcoin",
1359
+ description: "Send funds to the bitcoin swap address",
1360
+ chain: "BITCOIN",
1361
+ txs: [
1362
+ actionOptions?.bitcoinWallet==null
1363
+ ? {...await this.getPsbt(), type: "RAW_PSBT"}
1364
+ : {...await this.getFundedPsbt(actionOptions.bitcoinWallet, actionOptions?.bitcoinFeeRate), type: "FUNDED_PSBT"}
1365
+ ],
1366
+ submitPsbt: async (signedPsbt: string | Transaction | (string | Transaction)[], idempotent) => {
1367
+ return this._submitExecutionTransactions(
1368
+ Array.isArray(signedPsbt) ? signedPsbt : [signedPsbt],
1369
+ undefined,
1370
+ [SpvFromBTCSwapState.CREATED, SpvFromBTCSwapState.QUOTE_SOFT_EXPIRED],
1371
+ idempotent
1372
+ );
1373
+ }
1374
+ } as SwapExecutionActionSignPSBT;
1375
+ }
1376
+
1377
+ /**
1378
+ * @internal
1379
+ */
1380
+ private async _buildWaitBitcoinConfirmationsAction(confirmationDelay: number): Promise<SwapExecutionActionWait<"BITCOIN_CONFS">> {
1381
+ return {
1382
+ type: "Wait",
1383
+ name: "Bitcoin confirmations",
1384
+ description: "Wait for bitcoin transaction to confirm",
1385
+ pollTimeSeconds: 10,
1386
+ expectedTimeSeconds: confirmationDelay===-1 ? -1 : Math.floor(confirmationDelay/1000),
1387
+ wait: async (
1388
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal,
1389
+ btcConfirmationsCallback?: (txId?: string, confirmations?: number, targetConfirmations?: number, txEtaMs?: number) => void
1390
+ ) => {
1391
+ const abortController = extendAbortController(
1392
+ abortSignal, maxWaitTimeSeconds, "Timed out waiting for bitcoin transaction"
1393
+ );
1394
+ await this.waitForBitcoinTransaction(btcConfirmationsCallback, pollIntervalSeconds, abortController.signal);
1395
+ }
1396
+ } as SwapExecutionActionWait<"BITCOIN_CONFS">;
1397
+ }
1398
+
1399
+ /**
1400
+ * @internal
1401
+ */
1402
+ private async _buildWaitSettlementAction(maxWaitTillAutomaticSettlementSeconds?: number): Promise<SwapExecutionActionWait<"SETTLEMENT">> {
1403
+ return {
1404
+ type: "Wait",
1405
+ name: "Automatic settlement",
1406
+ description: "Wait for bitcoin transaction to confirm",
1407
+ pollTimeSeconds: 5,
1408
+ expectedTimeSeconds: 10,
1409
+ wait: async (
1410
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
1411
+ ) => {
1412
+ await this.waitTillClaimedOrFronted(maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60, abortSignal, pollIntervalSeconds);
1413
+ }
1414
+ } as SwapExecutionActionWait<"SETTLEMENT">;
1123
1415
  }
1124
1416
 
1417
+ /**
1418
+ * @internal
1419
+ */
1420
+ private async _buildClaimSmartChainTxAction(actionOptions?: {
1421
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
1422
+ }): Promise<SwapExecutionActionSignSmartChainTx<T>> {
1423
+ const signerAddress =
1424
+ await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
1425
+
1426
+ return {
1427
+ type: "SignSmartChainTransaction",
1428
+ name: "Settle manually",
1429
+ description: "Manually settle the swap on the destination smart chain",
1430
+ chain: this.chainIdentifier,
1431
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner)),
1432
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
1433
+ return this._submitExecutionTransactions(
1434
+ txs,
1435
+ abortSignal,
1436
+ [SpvFromBTCSwapState.BTC_TX_CONFIRMED],
1437
+ idempotent
1438
+ );
1439
+ },
1440
+ requiredSigner: signerAddress ?? this._getInitiator()
1441
+ } as SwapExecutionActionSignSmartChainTx<T>;
1442
+ }
1125
1443
 
1126
1444
  /**
1127
1445
  * @inheritDoc
@@ -1135,32 +1453,60 @@ export class SpvFromBTCSwap<T extends ChainType>
1135
1453
  * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
1136
1454
  * the bitcoin transaction is confirmed (defaults to 60 seconds)
1137
1455
  */
1138
- async getCurrentActions(options?: {
1456
+ async getExecutionAction(options?: {
1139
1457
  bitcoinFeeRate?: number,
1140
1458
  bitcoinWallet?: MinimalBitcoinWalletInterface,
1141
1459
  manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1142
1460
  maxWaitTillAutomaticSettlementSeconds?: number
1143
- }): Promise<SwapExecutionAction<T>[]> {
1144
- if(this._state===SpvFromBTCSwapState.CREATED) {
1145
- try {
1146
- return await this.txsExecute(options);
1147
- } catch (e) {}
1148
- }
1149
- if(this._state===SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1150
- if(
1151
- this.btcTxConfirmedAt==null ||
1152
- options?.maxWaitTillAutomaticSettlementSeconds===0 ||
1153
- (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
1154
- ) {
1155
- return [{
1156
- name: "Claim" as const,
1157
- description: "Manually settle (claim) the swap on the destination smart chain",
1158
- chain: this.chainIdentifier,
1159
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
1160
- }];
1161
- }
1162
- }
1163
- return [];
1461
+ }): Promise<
1462
+ SwapExecutionActionSignPSBT |
1463
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
1464
+ SwapExecutionActionSignSmartChainTx<T> |
1465
+ undefined
1466
+ > {
1467
+ const executionStatus = await this._getExecutionStatus(options);
1468
+ return executionStatus.buildCurrentAction(options);
1469
+ }
1470
+
1471
+ /**
1472
+ * @inheritDoc
1473
+ */
1474
+ async getExecutionStatus(options?: {
1475
+ skipBuildingAction?: boolean,
1476
+ bitcoinFeeRate?: number,
1477
+ bitcoinWallet?: MinimalBitcoinWalletInterface,
1478
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1479
+ maxWaitTillAutomaticSettlementSeconds?: number
1480
+ }): Promise<{
1481
+ steps: [
1482
+ SwapExecutionStepPayment<"BITCOIN">,
1483
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1484
+ ],
1485
+ currentAction:
1486
+ SwapExecutionActionSignPSBT |
1487
+ SwapExecutionActionWait<"BITCOIN_CONFS" | "SETTLEMENT"> |
1488
+ SwapExecutionActionSignSmartChainTx<T> |
1489
+ undefined,
1490
+ stateInfo: SwapStateInfo<SpvFromBTCSwapState>
1491
+ }> {
1492
+ const executionStatus = await this._getExecutionStatus(options);
1493
+ return {
1494
+ steps: executionStatus.steps,
1495
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1496
+ stateInfo: this._getStateInfo(executionStatus.state)
1497
+ };
1498
+ }
1499
+
1500
+ /**
1501
+ * @inheritDoc
1502
+ */
1503
+ async getExecutionSteps(options?: {
1504
+ maxWaitTillAutomaticSettlementSeconds?: number
1505
+ }): Promise<[
1506
+ SwapExecutionStepPayment<"BITCOIN">,
1507
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1508
+ ]> {
1509
+ return (await this._getExecutionStatus(options)).steps;
1164
1510
  }
1165
1511
 
1166
1512
 
@@ -1175,6 +1521,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1175
1521
  txId: string,
1176
1522
  confirmations: number,
1177
1523
  targetConfirmations: number,
1524
+ btcTx: BtcTx,
1178
1525
  inputAddresses?: string[]
1179
1526
  } | null> {
1180
1527
  if(this._data?.btcTx?.txid==null) return null;
@@ -1186,6 +1533,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1186
1533
  txId: result.txid,
1187
1534
  confirmations: result.confirmations ?? 0,
1188
1535
  targetConfirmations: this.vaultRequiredConfirmations,
1536
+ btcTx: result,
1189
1537
  inputAddresses: result.inputAddresses
1190
1538
  }
1191
1539
  }
@@ -1366,11 +1714,11 @@ export class SpvFromBTCSwap<T extends ChainType>
1366
1714
  /**
1367
1715
  * Periodically checks the chain to see whether the swap was finished (claimed or refunded)
1368
1716
  *
1369
- * @param abortSignal
1370
1717
  * @param interval How often to check (in seconds), default to 5s
1718
+ * @param abortSignal
1371
1719
  * @internal
1372
1720
  */
1373
- protected async watchdogWaitTillResult(abortSignal?: AbortSignal, interval: number = 5): Promise<
1721
+ protected async watchdogWaitTillResult(interval: number = 5, abortSignal?: AbortSignal): Promise<
1374
1722
  SpvWithdrawalClaimedState | SpvWithdrawalFrontedState | SpvWithdrawalClosedState
1375
1723
  > {
1376
1724
  if(this._data==null) throw new Error("Cannot await the result before the btc transaction is sent!");
@@ -1416,11 +1764,12 @@ export class SpvFromBTCSwap<T extends ChainType>
1416
1764
  * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled (by default
1417
1765
  * it waits indefinitely)
1418
1766
  * @param abortSignal Abort signal
1767
+ * @param pollIntervalSeconds How often to poll via the watchdog
1419
1768
  *
1420
1769
  * @returns {boolean} whether the swap was claimed or fronted automatically or not, if the swap was not claimed
1421
1770
  * the user can claim manually through the {@link claim} function
1422
1771
  */
1423
- async waitTillClaimedOrFronted(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
1772
+ async waitTillClaimedOrFronted(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, pollIntervalSeconds?: number): Promise<boolean> {
1424
1773
  if(this._state===SpvFromBTCSwapState.CLAIMED || this._state===SpvFromBTCSwapState.FRONTED) return Promise.resolve(true);
1425
1774
 
1426
1775
  const abortController = extendAbortController(abortSignal);
@@ -1437,7 +1786,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1437
1786
  let res: number | SpvWithdrawalState;
1438
1787
  try {
1439
1788
  res = await Promise.race([
1440
- this.watchdogWaitTillResult(abortController.signal),
1789
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1441
1790
  this.waitTillState(SpvFromBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 0),
1442
1791
  this.waitTillState(SpvFromBTCSwapState.FRONTED, "eq", abortController.signal).then(() => 1),
1443
1792
  this.waitTillState(SpvFromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 2),