@atomiqlabs/sdk 8.8.3 → 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 (130) 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/events/UnifiedSwapEventListener.d.ts +4 -3
  18. package/dist/events/UnifiedSwapEventListener.js +8 -2
  19. package/dist/http/HttpUtils.d.ts +4 -2
  20. package/dist/http/HttpUtils.js +10 -4
  21. package/dist/http/paramcoders/client/StreamingFetchPromise.d.ts +2 -1
  22. package/dist/http/paramcoders/client/StreamingFetchPromise.js +3 -2
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/intermediaries/IntermediaryDiscovery.d.ts +7 -2
  26. package/dist/intermediaries/IntermediaryDiscovery.js +4 -4
  27. package/dist/intermediaries/apis/IntermediaryAPI.d.ts +171 -14
  28. package/dist/intermediaries/apis/IntermediaryAPI.js +174 -28
  29. package/dist/intermediaries/auth/SignedKeyBasedAuth.d.ts +14 -0
  30. package/dist/intermediaries/auth/SignedKeyBasedAuth.js +68 -0
  31. package/dist/storage/IUnifiedStorage.d.ts +45 -3
  32. package/dist/storage/UnifiedSwapStorage.d.ts +8 -2
  33. package/dist/storage/UnifiedSwapStorage.js +46 -8
  34. package/dist/swapper/Swapper.d.ts +36 -3
  35. package/dist/swapper/Swapper.js +54 -18
  36. package/dist/swapper/SwapperUtils.d.ts +18 -2
  37. package/dist/swapper/SwapperUtils.js +39 -1
  38. package/dist/swaps/ISwap.d.ts +70 -9
  39. package/dist/swaps/ISwap.js +28 -6
  40. package/dist/swaps/ISwapWrapper.d.ts +11 -1
  41. package/dist/swaps/ISwapWrapper.js +23 -3
  42. package/dist/swaps/escrow_swaps/IEscrowSwap.d.ts +1 -1
  43. package/dist/swaps/escrow_swaps/IEscrowSwap.js +4 -2
  44. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.d.ts +2 -1
  45. package/dist/swaps/escrow_swaps/IEscrowSwapWrapper.js +2 -2
  46. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.d.ts +3 -1
  47. package/dist/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.js +3 -2
  48. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.d.ts +47 -31
  49. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.js +201 -67
  50. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.d.ts +3 -1
  51. package/dist/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.js +6 -6
  52. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.d.ts +82 -15
  53. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.js +304 -98
  54. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.d.ts +3 -1
  55. package/dist/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.js +6 -6
  56. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.d.ts +75 -42
  57. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.js +424 -87
  58. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.d.ts +3 -1
  59. package/dist/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.js +7 -7
  60. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.d.ts +54 -11
  61. package/dist/swaps/escrow_swaps/tobtc/IToBTCSwap.js +214 -41
  62. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.d.ts +2 -1
  63. package/dist/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.js +7 -8
  64. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.d.ts +3 -1
  65. package/dist/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.js +5 -5
  66. package/dist/swaps/spv_swaps/SpvFromBTCSwap.d.ts +76 -19
  67. package/dist/swaps/spv_swaps/SpvFromBTCSwap.js +290 -51
  68. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.d.ts +3 -1
  69. package/dist/swaps/spv_swaps/SpvFromBTCWrapper.js +5 -5
  70. package/dist/swaps/trusted/ln/LnForGasSwap.d.ts +53 -12
  71. package/dist/swaps/trusted/ln/LnForGasSwap.js +163 -49
  72. package/dist/swaps/trusted/ln/LnForGasWrapper.js +1 -2
  73. package/dist/swaps/trusted/onchain/OnchainForGasSwap.d.ts +14 -13
  74. package/dist/swaps/trusted/onchain/OnchainForGasSwap.js +30 -47
  75. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.d.ts +3 -1
  76. package/dist/swaps/trusted/onchain/OnchainForGasWrapper.js +4 -4
  77. package/dist/types/SwapExecutionAction.d.ts +141 -34
  78. package/dist/types/SwapExecutionAction.js +104 -0
  79. package/dist/types/SwapExecutionStep.d.ts +144 -0
  80. package/dist/types/SwapExecutionStep.js +87 -0
  81. package/dist/types/TokenAmount.d.ts +6 -0
  82. package/dist/types/TokenAmount.js +26 -1
  83. package/dist/utils/BitcoinUtils.d.ts +2 -0
  84. package/dist/utils/BitcoinUtils.js +34 -1
  85. package/dist/utils/Utils.d.ts +3 -1
  86. package/dist/utils/Utils.js +7 -1
  87. package/package.json +7 -4
  88. package/src/api/ApiEndpoints.ts +427 -0
  89. package/src/api/ApiParser.ts +138 -0
  90. package/src/api/ApiTypes.ts +229 -0
  91. package/src/api/SerializedAction.ts +97 -0
  92. package/src/api/SwapperApi.ts +545 -0
  93. package/src/api/index.ts +5 -0
  94. package/src/events/UnifiedSwapEventListener.ts +11 -3
  95. package/src/http/HttpUtils.ts +10 -4
  96. package/src/http/paramcoders/client/StreamingFetchPromise.ts +4 -2
  97. package/src/index.ts +1 -0
  98. package/src/intermediaries/IntermediaryDiscovery.ts +9 -2
  99. package/src/intermediaries/apis/IntermediaryAPI.ts +314 -30
  100. package/src/intermediaries/auth/SignedKeyBasedAuth.ts +69 -0
  101. package/src/storage/IUnifiedStorage.ts +45 -4
  102. package/src/storage/UnifiedSwapStorage.ts +42 -8
  103. package/src/swapper/Swapper.ts +87 -18
  104. package/src/swapper/SwapperUtils.ts +42 -2
  105. package/src/swaps/ISwap.ts +88 -16
  106. package/src/swaps/ISwapWrapper.ts +28 -3
  107. package/src/swaps/escrow_swaps/IEscrowSwap.ts +5 -3
  108. package/src/swaps/escrow_swaps/IEscrowSwapWrapper.ts +3 -1
  109. package/src/swaps/escrow_swaps/frombtc/IFromBTCLNWrapper.ts +4 -1
  110. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNSwap.ts +264 -67
  111. package/src/swaps/escrow_swaps/frombtc/ln/FromBTCLNWrapper.ts +6 -4
  112. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoSwap.ts +390 -89
  113. package/src/swaps/escrow_swaps/frombtc/ln_auto/FromBTCLNAutoWrapper.ts +6 -4
  114. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCSwap.ts +548 -94
  115. package/src/swaps/escrow_swaps/frombtc/onchain/FromBTCWrapper.ts +7 -5
  116. package/src/swaps/escrow_swaps/tobtc/IToBTCSwap.ts +276 -45
  117. package/src/swaps/escrow_swaps/tobtc/ln/ToBTCLNWrapper.ts +7 -6
  118. package/src/swaps/escrow_swaps/tobtc/onchain/ToBTCWrapper.ts +5 -3
  119. package/src/swaps/spv_swaps/SpvFromBTCSwap.ts +393 -57
  120. package/src/swaps/spv_swaps/SpvFromBTCWrapper.ts +5 -3
  121. package/src/swaps/trusted/ln/LnForGasSwap.ts +211 -47
  122. package/src/swaps/trusted/ln/LnForGasWrapper.ts +1 -2
  123. package/src/swaps/trusted/onchain/OnchainForGasSwap.ts +32 -51
  124. package/src/swaps/trusted/onchain/OnchainForGasWrapper.ts +5 -3
  125. package/src/types/SwapExecutionAction.ts +266 -43
  126. package/src/types/SwapExecutionStep.ts +224 -0
  127. package/src/types/TokenAmount.ts +36 -2
  128. package/src/utils/BitcoinUtils.ts +32 -0
  129. package/src/utils/Utils.ts +10 -1
  130. 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,
@@ -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
 
@@ -892,7 +905,8 @@ export class SpvFromBTCSwap<T extends ChainType>
892
905
  psbt: Transaction,
893
906
  psbtHex: string,
894
907
  psbtBase64: string,
895
- signInputs: number[]
908
+ signInputs: number[],
909
+ feeRate: number
896
910
  }> {
897
911
  const bitcoinWallet: IBitcoinWallet = toBitcoinWallet(_bitcoinWallet, this.wrapper._btcRpc, this.wrapper._options.bitcoinNetwork);
898
912
  if(feeRate!=null) {
@@ -919,7 +933,8 @@ export class SpvFromBTCSwap<T extends ChainType>
919
933
  psbt,
920
934
  psbtHex: serializedPsbt.toString("hex"),
921
935
  psbtBase64: serializedPsbt.toString("base64"),
922
- signInputs
936
+ signInputs,
937
+ feeRate
923
938
  };
924
939
  }
925
940
 
@@ -1000,7 +1015,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1000
1015
  await this._saveAndEmit(SpvFromBTCSwapState.SIGNED);
1001
1016
 
1002
1017
  try {
1003
- await IntermediaryAPI.initSpvFromBTC(
1018
+ await this.wrapper._lpApi.initSpvFromBTC(
1004
1019
  this.chainIdentifier,
1005
1020
  this.url,
1006
1021
  {
@@ -1102,39 +1117,329 @@ export class SpvFromBTCSwap<T extends ChainType>
1102
1117
  }
1103
1118
 
1104
1119
  /**
1105
- * @inheritDoc
1106
- *
1107
- * @param options.bitcoinFeeRate Optional fee rate to use for the created Bitcoin transaction
1108
- * @param options.bitcoinWallet Optional bitcoin wallet address specification to return a funded PSBT,
1109
- * if not provided a raw PSBT is returned instead which necessitates the implementor to manually add
1110
- * inputs to the bitcoin transaction and **set the nSequence field of the 2nd input** (input 1 -
1111
- * indexing from 0) to the value returned in `in1sequence`
1120
+ * @internal
1112
1121
  */
1113
- async txsExecute(options?: {
1122
+ protected async _getExecutionStatus(options?: {
1114
1123
  bitcoinFeeRate?: number,
1115
1124
  bitcoinWallet?: MinimalBitcoinWalletInterface,
1116
- }): Promise<[
1117
- SwapExecutionActionBitcoin<"RAW_PSBT" | "FUNDED_PSBT">
1118
- ]> {
1119
- if(this._state===SpvFromBTCSwapState.CREATED) {
1120
- if (!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
1121
- 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: [
1122
1246
  {
1123
- name: "Payment",
1124
- description: "Send funds to the bitcoin swap address",
1247
+ type: "Payment",
1248
+ side: "source",
1125
1249
  chain: "BITCOIN",
1126
- txs: [
1127
- options?.bitcoinWallet==null
1128
- ? {...await this.getPsbt(), type: "RAW_PSBT"}
1129
- : {...await this.getFundedPsbt(options.bitcoinWallet, options?.bitcoinFeeRate), type: "FUNDED_PSBT"}
1130
- ]
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;
1131
1298
  }
1132
- ];
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
+ }
1320
+ }
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)];
1334
+ }
1335
+
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;
1133
1344
  }
1134
1345
 
1135
- throw new Error("Invalid swap state to obtain execution txns, required CREATED");
1346
+ throw new Error("Invalid swap state for transaction submission!");
1136
1347
  }
1137
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">;
1415
+ }
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
+ }
1138
1443
 
1139
1444
  /**
1140
1445
  * @inheritDoc
@@ -1148,32 +1453,60 @@ export class SpvFromBTCSwap<T extends ChainType>
1148
1453
  * @param options.maxWaitTillAutomaticSettlementSeconds Maximum time to wait for an automatic settlement after
1149
1454
  * the bitcoin transaction is confirmed (defaults to 60 seconds)
1150
1455
  */
1151
- async getCurrentActions(options?: {
1456
+ async getExecutionAction(options?: {
1152
1457
  bitcoinFeeRate?: number,
1153
1458
  bitcoinWallet?: MinimalBitcoinWalletInterface,
1154
1459
  manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1155
1460
  maxWaitTillAutomaticSettlementSeconds?: number
1156
- }): Promise<SwapExecutionAction<T>[]> {
1157
- if(this._state===SpvFromBTCSwapState.CREATED) {
1158
- try {
1159
- return await this.txsExecute(options);
1160
- } catch (e) {}
1161
- }
1162
- if(this._state===SpvFromBTCSwapState.BTC_TX_CONFIRMED) {
1163
- if(
1164
- this.btcTxConfirmedAt==null ||
1165
- options?.maxWaitTillAutomaticSettlementSeconds===0 ||
1166
- (Date.now() - this.btcTxConfirmedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
1167
- ) {
1168
- return [{
1169
- name: "Claim" as const,
1170
- description: "Manually settle (claim) the swap on the destination smart chain",
1171
- chain: this.chainIdentifier,
1172
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
1173
- }];
1174
- }
1175
- }
1176
- 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;
1177
1510
  }
1178
1511
 
1179
1512
 
@@ -1188,6 +1521,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1188
1521
  txId: string,
1189
1522
  confirmations: number,
1190
1523
  targetConfirmations: number,
1524
+ btcTx: BtcTx,
1191
1525
  inputAddresses?: string[]
1192
1526
  } | null> {
1193
1527
  if(this._data?.btcTx?.txid==null) return null;
@@ -1199,6 +1533,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1199
1533
  txId: result.txid,
1200
1534
  confirmations: result.confirmations ?? 0,
1201
1535
  targetConfirmations: this.vaultRequiredConfirmations,
1536
+ btcTx: result,
1202
1537
  inputAddresses: result.inputAddresses
1203
1538
  }
1204
1539
  }
@@ -1379,11 +1714,11 @@ export class SpvFromBTCSwap<T extends ChainType>
1379
1714
  /**
1380
1715
  * Periodically checks the chain to see whether the swap was finished (claimed or refunded)
1381
1716
  *
1382
- * @param abortSignal
1383
1717
  * @param interval How often to check (in seconds), default to 5s
1718
+ * @param abortSignal
1384
1719
  * @internal
1385
1720
  */
1386
- protected async watchdogWaitTillResult(abortSignal?: AbortSignal, interval: number = 5): Promise<
1721
+ protected async watchdogWaitTillResult(interval: number = 5, abortSignal?: AbortSignal): Promise<
1387
1722
  SpvWithdrawalClaimedState | SpvWithdrawalFrontedState | SpvWithdrawalClosedState
1388
1723
  > {
1389
1724
  if(this._data==null) throw new Error("Cannot await the result before the btc transaction is sent!");
@@ -1429,11 +1764,12 @@ export class SpvFromBTCSwap<T extends ChainType>
1429
1764
  * @param maxWaitTimeSeconds Maximum time in seconds to wait for the swap to be settled (by default
1430
1765
  * it waits indefinitely)
1431
1766
  * @param abortSignal Abort signal
1767
+ * @param pollIntervalSeconds How often to poll via the watchdog
1432
1768
  *
1433
1769
  * @returns {boolean} whether the swap was claimed or fronted automatically or not, if the swap was not claimed
1434
1770
  * the user can claim manually through the {@link claim} function
1435
1771
  */
1436
- async waitTillClaimedOrFronted(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
1772
+ async waitTillClaimedOrFronted(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, pollIntervalSeconds?: number): Promise<boolean> {
1437
1773
  if(this._state===SpvFromBTCSwapState.CLAIMED || this._state===SpvFromBTCSwapState.FRONTED) return Promise.resolve(true);
1438
1774
 
1439
1775
  const abortController = extendAbortController(abortSignal);
@@ -1450,7 +1786,7 @@ export class SpvFromBTCSwap<T extends ChainType>
1450
1786
  let res: number | SpvWithdrawalState;
1451
1787
  try {
1452
1788
  res = await Promise.race([
1453
- this.watchdogWaitTillResult(abortController.signal),
1789
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1454
1790
  this.waitTillState(SpvFromBTCSwapState.CLAIMED, "eq", abortController.signal).then(() => 0),
1455
1791
  this.waitTillState(SpvFromBTCSwapState.FRONTED, "eq", abortController.signal).then(() => 1),
1456
1792
  this.waitTillState(SpvFromBTCSwapState.FAILED, "eq", abortController.signal).then(() => 2),
@@ -225,6 +225,7 @@ export class SpvFromBTCWrapper<
225
225
  * @param versionedContracts
226
226
  * @param versionedSynchronizer
227
227
  * @param btcRpc Bitcoin RPC which also supports getting transactions by txoHash
228
+ * @param lpApi
228
229
  * @param options
229
230
  * @param events Instance to use for emitting events
230
231
  */
@@ -248,11 +249,12 @@ export class SpvFromBTCWrapper<
248
249
  }
249
250
  },
250
251
  btcRpc: BitcoinRpcWithAddressIndex<any>,
252
+ lpApi: IntermediaryAPI,
251
253
  options?: AllOptional<SpvFromBTCWrapperOptions>,
252
254
  events?: EventEmitter<{swapState: [ISwap]}>
253
255
  ) {
254
256
  super(
255
- chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens,
257
+ chainIdentifier, unifiedStorage, unifiedChainEvents, chain, prices, tokens, lpApi,
256
258
  {
257
259
  ...options,
258
260
  bitcoinNetwork: options?.bitcoinNetwork ?? TEST_NETWORK,
@@ -834,7 +836,7 @@ export class SpvFromBTCWrapper<
834
836
 
835
837
  try {
836
838
  const resp = await tryWithRetries(async(retryCount: number) => {
837
- return await IntermediaryAPI.prepareSpvFromBTC(
839
+ return await this._lpApi.prepareSpvFromBTC(
838
840
  this.chainIdentifier, lp.url,
839
841
  {
840
842
  address: recipient,
@@ -935,7 +937,7 @@ export class SpvFromBTCWrapper<
935
937
 
936
938
  genesisSmartChainBlockHeight: await throwIfUndefined(
937
939
  finalizedBlockHeightPrefetchPromise,
938
- "Finalize block height promise failed!"
940
+ "Network finalized blockheight pre-fetch failed!"
939
941
  ),
940
942
  contractVersion: version
941
943
  };