@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
@@ -40,7 +40,16 @@ import {
40
40
  serializePriceInfoType
41
41
  } from "../../../../types/PriceInfoType";
42
42
  import {sha256} from "@noble/hashes/sha2";
43
- import {SwapExecutionAction} from "../../../../types/SwapExecutionAction";
43
+ import {
44
+ SwapExecutionActionSendToAddress,
45
+ SwapExecutionActionSignSmartChainTx,
46
+ SwapExecutionActionWait
47
+ } from "../../../../types/SwapExecutionAction";
48
+ import {
49
+ SwapExecutionStepPayment,
50
+ SwapExecutionStepSettlement
51
+ } from "../../../../types/SwapExecutionStep";
52
+ import {SwapStateInfo} from "../../../../types/SwapStateInfo";
44
53
 
45
54
  /**
46
55
  * State enum for FromBTCLNAuto swaps
@@ -742,9 +751,10 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
742
751
  *
743
752
  * @throws {Error} If an invalid secret preimage is provided
744
753
  */
745
- setSecretPreimage(secret: string) {
754
+ async setSecretPreimage(secret: string) {
746
755
  if(!this.isValidSecretPreimage(secret)) throw new Error("Invalid secret preimage provided, hash doesn't match!");
747
756
  this.secret = secret;
757
+ await this._broadcastSecret().catch(e => this.logger.error("setSecretPreimage(): Failed to broadcast swap secret: ", e));
748
758
  }
749
759
 
750
760
  /**
@@ -828,28 +838,244 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
828
838
  }
829
839
 
830
840
  /**
831
- * @inheritDoc
841
+ * @internal
832
842
  */
833
- async txsExecute() {
834
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
835
- if (!await this._verifyQuoteValid()) throw new Error("Quote already expired or close to expiry!");
836
- return [
843
+ protected async _getExecutionStatus(options?: {
844
+ maxWaitTillAutomaticSettlementSeconds?: number,
845
+ secret?: string
846
+ }) {
847
+ if(options?.secret!=null) await this.setSecretPreimage(options.secret);
848
+
849
+ const state = this._state;
850
+ const now = Date.now();
851
+
852
+ let lightningPaymentStatus: SwapExecutionStepPayment<"LIGHTNING">["status"] = "inactive";
853
+ let destinationSettlementStatus: SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">["status"] = "inactive";
854
+ let buildCurrentAction: (actionOptions?: {
855
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"]
856
+ }) => Promise<
857
+ SwapExecutionActionSendToAddress<true> |
858
+ SwapExecutionActionWait<"LP" | "SETTLEMENT"> |
859
+ SwapExecutionActionSignSmartChainTx<T> |
860
+ undefined
861
+ > = async () => undefined;
862
+
863
+ switch(state) {
864
+ case FromBTCLNAutoSwapState.PR_CREATED: {
865
+ const quoteValid = await this._verifyQuoteValid();
866
+ lightningPaymentStatus = quoteValid ? "awaiting" : "soft_expired";
867
+ if(quoteValid && this.pr!=null && this.pr.toLowerCase().startsWith("ln")) {
868
+ buildCurrentAction = this._buildLightningPaymentAction.bind(this);
869
+ }
870
+ break;
871
+ }
872
+ case FromBTCLNAutoSwapState.QUOTE_SOFT_EXPIRED:
873
+ lightningPaymentStatus = "soft_expired";
874
+ break;
875
+ case FromBTCLNAutoSwapState.QUOTE_EXPIRED:
876
+ lightningPaymentStatus = "expired";
877
+ break;
878
+ case FromBTCLNAutoSwapState.PR_PAID:
879
+ lightningPaymentStatus = "received";
880
+ destinationSettlementStatus = "waiting_lp";
881
+ buildCurrentAction = this._buildWaitLpAction.bind(this);
882
+ break;
883
+ case FromBTCLNAutoSwapState.CLAIM_COMMITED:
884
+ lightningPaymentStatus = "received";
885
+ if(
886
+ this._commitedAt==null ||
887
+ options?.maxWaitTillAutomaticSettlementSeconds===0 ||
888
+ (now - this._commitedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
889
+ ) {
890
+ destinationSettlementStatus = "awaiting_manual";
891
+ if(this.hasSecretPreimage()) {
892
+ //TODO: Maybe add an action that would prompt the user to reveal the pre-image
893
+ buildCurrentAction = this._buildClaimSmartChainTxAction.bind(this);
894
+ }
895
+ } else {
896
+ destinationSettlementStatus = "awaiting_automatic";
897
+ //TODO: Maybe add an action that would prompt the user to reveal the pre-image
898
+ buildCurrentAction = this._buildWaitSettlementAction.bind(this, options?.maxWaitTillAutomaticSettlementSeconds);
899
+ }
900
+ break;
901
+ case FromBTCLNAutoSwapState.CLAIM_CLAIMED:
902
+ lightningPaymentStatus = "confirmed";
903
+ destinationSettlementStatus = "settled";
904
+ break;
905
+ case FromBTCLNAutoSwapState.EXPIRED:
906
+ case FromBTCLNAutoSwapState.FAILED:
907
+ lightningPaymentStatus = "expired";
908
+ destinationSettlementStatus = "expired";
909
+ break;
910
+ }
911
+
912
+ return {
913
+ steps: [
837
914
  {
838
- name: "Payment" as const,
839
- description: "Initiates the swap by paying up the lightning network invoice",
840
- chain: "LIGHTNING" as const,
841
- txs: [
842
- {
843
- type: "BOLT11_PAYMENT_REQUEST" as const,
844
- address: this.getAddress(),
845
- hyperlink: this.getHyperlink()
846
- }
847
- ]
915
+ type: "Payment",
916
+ side: "source",
917
+ chain: "LIGHTNING",
918
+ title: "Lightning payment",
919
+ description: "Pay the Lightning network invoice to initiate the swap",
920
+ status: lightningPaymentStatus,
921
+ initTxId: this.getInputTxId(),
922
+ settleTxId: lightningPaymentStatus==="confirmed" ? this.getInputTxId() : undefined
923
+ },
924
+ {
925
+ type: "Settlement",
926
+ side: "destination",
927
+ chain: this.chainIdentifier,
928
+ title: "Destination settlement",
929
+ description: `Wait for the LP to initiate on the ${this.chainIdentifier} side, then wait for automatic settlement, or settle manually if it takes too long`,
930
+ status: destinationSettlementStatus,
931
+ initTxId: this._commitTxId,
932
+ settleTxId: this._claimTxId
848
933
  }
849
- ];
934
+ ] as [
935
+ SwapExecutionStepPayment<"LIGHTNING">,
936
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
937
+ ],
938
+ buildCurrentAction,
939
+ state
940
+ };
941
+ }
942
+
943
+ /**
944
+ * @internal
945
+ */
946
+ private async _buildLightningPaymentAction(): Promise<SwapExecutionActionSendToAddress<true>> {
947
+ return {
948
+ type: "SendToAddress",
949
+ name: "Deposit on Lightning",
950
+ description: "Pay the lightning network invoice to initiate the swap",
951
+ chain: "LIGHTNING",
952
+ txs: [{
953
+ type: "BOLT11_PAYMENT_REQUEST",
954
+ address: this.getAddress(),
955
+ hyperlink: this.getHyperlink(),
956
+ amount: this.getInput()
957
+ }],
958
+ waitForTransactions: async (
959
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
960
+ ) => {
961
+ const abortController = extendAbortController(
962
+ abortSignal, maxWaitTimeSeconds, "Timed out waiting for lightning payment"
963
+ );
964
+ const success = await this._waitForLpPaymentReceived(pollIntervalSeconds, abortController.signal);
965
+ if(!success) throw new Error("Quote expired while waiting for Lightning payment");
966
+
967
+ return this.getInputTxId()!;
968
+ }
969
+ } as SwapExecutionActionSendToAddress<true>;
970
+ }
971
+
972
+ /**
973
+ * @internal
974
+ */
975
+ private async _buildWaitLpAction(): Promise<SwapExecutionActionWait<"LP">> {
976
+ return {
977
+ type: "Wait",
978
+ name: "Awaiting LP payout",
979
+ description: "Wait for the LP to create the swap HTLC on the destination smart chain",
980
+ pollTimeSeconds: 5,
981
+ expectedTimeSeconds: 10,
982
+ wait: async (
983
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
984
+ ) => {
985
+ const abortController = extendAbortController(
986
+ abortSignal, maxWaitTimeSeconds, "Timed out waiting for LP payout"
987
+ );
988
+ await this.waitTillCommited(pollIntervalSeconds, abortController.signal);
989
+ }
990
+ } as SwapExecutionActionWait<"LP">;
991
+ }
992
+
993
+ /**
994
+ * @internal
995
+ */
996
+ private async _buildWaitSettlementAction(
997
+ maxWaitTillAutomaticSettlementSeconds?: number
998
+ ): Promise<SwapExecutionActionWait<"SETTLEMENT">> {
999
+ return {
1000
+ type: "Wait",
1001
+ name: "Automatic settlement",
1002
+ description: "Wait for automatic settlement by the watchtower",
1003
+ pollTimeSeconds: 5,
1004
+ expectedTimeSeconds: 10,
1005
+ wait: async (
1006
+ maxWaitTimeSeconds?: number, pollIntervalSeconds?: number, abortSignal?: AbortSignal
1007
+ ) => {
1008
+ await this.waitTillClaimed(
1009
+ maxWaitTimeSeconds ?? maxWaitTillAutomaticSettlementSeconds ?? 60,
1010
+ abortSignal,
1011
+ undefined,
1012
+ pollIntervalSeconds
1013
+ );
1014
+ }
1015
+ } as SwapExecutionActionWait<"SETTLEMENT">;
1016
+ }
1017
+
1018
+ /**
1019
+ * @inheritDoc
1020
+ * @internal
1021
+ */
1022
+ async _submitExecutionTransactions(
1023
+ txs: (T["SignedTXType"] | string)[],
1024
+ abortSignal?: AbortSignal,
1025
+ requiredStates?: FromBTCLNAutoSwapState[],
1026
+ idempotent?: boolean
1027
+ ): Promise<string[]> {
1028
+ const parsedTxs: T["SignedTXType"][] = [];
1029
+ for(let tx of txs) {
1030
+ parsedTxs.push(typeof(tx)==="string" ? await this.wrapper._chain.deserializeSignedTx(tx) : tx);
1031
+ }
1032
+
1033
+ if(idempotent) {
1034
+ // Handle idempotent calls
1035
+ if(this.wrapper._chain.getTxId!=null) {
1036
+ const txIds = await Promise.all(parsedTxs.map(tx => this.wrapper._chain.getTxId!(tx)));
1037
+ const foundTxId = txIds.find(txId => this._claimTxId===txId);
1038
+ if(foundTxId!=null) return txIds;
1039
+ }
1040
+ }
1041
+
1042
+ if(requiredStates!=null && !requiredStates.includes(this._state)) throw new Error("Swap state has changed before transactions were submitted!");
1043
+
1044
+ if(this._state===FromBTCLNAutoSwapState.CLAIM_COMMITED) {
1045
+ const txIds = await this.wrapper._chain.sendSignedAndConfirm(parsedTxs, true, abortSignal, false);
1046
+ await this.waitTillClaimed(undefined, abortSignal);
1047
+ return txIds;
850
1048
  }
851
1049
 
852
- throw new Error("Invalid swap state to obtain execution txns, required PR_CREATED");
1050
+ throw new Error("Invalid swap state for transaction submission!");
1051
+ }
1052
+
1053
+ /**
1054
+ * @internal
1055
+ */
1056
+ private async _buildClaimSmartChainTxAction(actionOptions?: {
1057
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1058
+ secret?: string
1059
+ }): Promise<SwapExecutionActionSignSmartChainTx<T>> {
1060
+ const signerAddress =
1061
+ await this.wrapper._getSignerAddress(actionOptions?.manualSettlementSmartChainSigner);
1062
+
1063
+ return {
1064
+ type: "SignSmartChainTransaction",
1065
+ name: "Settle manually",
1066
+ description: "Manually settle (claim) the swap on the destination smart chain",
1067
+ chain: this.chainIdentifier,
1068
+ txs: await this.prepareTransactions(this.txsClaim(actionOptions?.manualSettlementSmartChainSigner, actionOptions?.secret)),
1069
+ submitTransactions: async (txs: (T["SignedTXType"] | string)[], abortSignal?: AbortSignal, idempotent?: boolean) => {
1070
+ return this._submitExecutionTransactions(
1071
+ txs,
1072
+ abortSignal,
1073
+ [FromBTCLNAutoSwapState.CLAIM_COMMITED],
1074
+ idempotent
1075
+ );
1076
+ },
1077
+ requiredSigner: signerAddress ?? this._getInitiator()
1078
+ } as SwapExecutionActionSignSmartChainTx<T>;
853
1079
  }
854
1080
 
855
1081
  /**
@@ -860,32 +1086,59 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
860
1086
  * @param options.secret A swap secret to broadcast to watchtowers, generally only needed if the swap
861
1087
  * was recovered from on-chain data, or the pre-image was generated outside the SDK
862
1088
  */
863
- async getCurrentActions(options?: {
1089
+ async getExecutionAction(options?: {
864
1090
  manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
865
1091
  maxWaitTillAutomaticSettlementSeconds?: number,
866
1092
  secret?: string
867
- }): Promise<SwapExecutionAction<T>[]> {
868
- if(options?.secret!=null) this.setSecretPreimage(options.secret);
869
- if (this._state === FromBTCLNAutoSwapState.PR_CREATED) {
870
- try {
871
- return await this.txsExecute();
872
- } catch (e) {}
873
- }
874
- if(this.isClaimable()) {
875
- if(
876
- this._commitedAt==null ||
877
- options?.maxWaitTillAutomaticSettlementSeconds===0 ||
878
- (Date.now() - this._commitedAt) > (options?.maxWaitTillAutomaticSettlementSeconds ?? 60)*1000
879
- ) {
880
- return [{
881
- name: "Claim" as const,
882
- description: "Manually settle (claim) the swap on the destination smart chain",
883
- chain: this.chainIdentifier,
884
- txs: await this.txsClaim(options?.manualSettlementSmartChainSigner)
885
- }];
886
- }
887
- }
888
- return [];
1093
+ }): Promise<
1094
+ SwapExecutionActionSendToAddress<true> |
1095
+ SwapExecutionActionWait<"LP" | "SETTLEMENT"> |
1096
+ SwapExecutionActionSignSmartChainTx<T> |
1097
+ undefined
1098
+ > {
1099
+ const executionStatus = await this._getExecutionStatus(options);
1100
+ return executionStatus.buildCurrentAction(options);
1101
+ }
1102
+
1103
+ /**
1104
+ * @inheritDoc
1105
+ */
1106
+ // TODO: Figure how we gonna trigger an LNURL-withdraw with the execution actions
1107
+ async getExecutionStatus(options?: {
1108
+ skipBuildingAction?: boolean,
1109
+ manualSettlementSmartChainSigner?: string | T["Signer"] | T["NativeSigner"],
1110
+ maxWaitTillAutomaticSettlementSeconds?: number,
1111
+ secret?: string
1112
+ }): Promise<{
1113
+ steps: [
1114
+ SwapExecutionStepPayment<"LIGHTNING">,
1115
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1116
+ ],
1117
+ currentAction:
1118
+ SwapExecutionActionSendToAddress<true> |
1119
+ SwapExecutionActionWait<"LP" | "SETTLEMENT"> |
1120
+ SwapExecutionActionSignSmartChainTx<T> |
1121
+ undefined,
1122
+ stateInfo: SwapStateInfo<FromBTCLNAutoSwapState>
1123
+ }> {
1124
+ const executionStatus = await this._getExecutionStatus(options);
1125
+ return {
1126
+ steps: executionStatus.steps,
1127
+ currentAction: options?.skipBuildingAction ? undefined : await executionStatus.buildCurrentAction(options),
1128
+ stateInfo: this._getStateInfo(executionStatus.state)
1129
+ };
1130
+ }
1131
+
1132
+ /**
1133
+ * @inheritDoc
1134
+ */
1135
+ async getExecutionSteps(options?: {
1136
+ maxWaitTillAutomaticSettlementSeconds?: number
1137
+ }): Promise<[
1138
+ SwapExecutionStepPayment<"LIGHTNING">,
1139
+ SwapExecutionStepSettlement<T["ChainId"], "awaiting_automatic" | "awaiting_manual">
1140
+ ]> {
1141
+ return (await this._getExecutionStatus(options)).steps;
889
1142
  }
890
1143
 
891
1144
 
@@ -914,7 +1167,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
914
1167
  if(paymentHash==null)
915
1168
  throw new Error("Failed to check LP payment received, payment hash not known (probably recovered swap?)");
916
1169
 
917
- const resp = await IntermediaryAPI.getInvoiceStatus(this.url, paymentHash.toString("hex"));
1170
+ const resp = await this.wrapper._lpApi.getInvoiceStatus(this.url, paymentHash.toString("hex"));
918
1171
  switch(resp.code) {
919
1172
  case InvoiceStatusResponseCodes.PAID:
920
1173
  const data = new (this.wrapper._swapDataDeserializer(this._contractVersion))(resp.data.data);
@@ -954,33 +1207,6 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
954
1207
  return false;
955
1208
  }
956
1209
 
957
- /**
958
- * Checks the data returned by the intermediary in the payment auth request
959
- *
960
- * @param data Parsed swap data as returned by the intermediary
961
- *
962
- * @throws {IntermediaryError} If the returned are not valid
963
- * @throws {Error} If the swap is already committed on-chain
964
- *
965
- * @private
966
- */
967
- private async checkIntermediaryReturnedData(data: T["Data"]): Promise<void> {
968
- if (!data.isPayOut()) throw new IntermediaryError("Invalid not pay out");
969
- if (data.getType() !== ChainSwapType.HTLC) throw new IntermediaryError("Invalid swap type");
970
- if (!data.isOfferer(this.getSwapData().getOfferer())) throw new IntermediaryError("Invalid offerer used");
971
- if (!data.isClaimer(this._getInitiator())) throw new IntermediaryError("Invalid claimer used");
972
- if (!data.isToken(this.getSwapData().getToken())) throw new IntermediaryError("Invalid token used");
973
- if (data.getSecurityDeposit() !== this.getSwapData().getSecurityDeposit()) throw new IntermediaryError("Invalid security deposit!");
974
- if (data.getClaimerBounty() !== this.getSwapData().getClaimerBounty()) throw new IntermediaryError("Invalid security deposit!");
975
- if (data.getAmount() < this.getSwapData().getAmount()) throw new IntermediaryError("Invalid amount received!");
976
- if (data.getClaimHash() !== this.getSwapData().getClaimHash()) throw new IntermediaryError("Invalid payment hash used!");
977
- if (!data.isDepositToken(this.getSwapData().getDepositToken())) throw new IntermediaryError("Invalid deposit token used!");
978
- if (data.hasSuccessAction()) throw new IntermediaryError("Invalid has success action");
979
-
980
- if (await this.wrapper._contract(this._contractVersion).isExpired(this._getInitiator(), data)) throw new IntermediaryError("Not enough time to claim!");
981
- if (this.wrapper._getHtlcTimeout(data) <= (Date.now()/1000)) throw new IntermediaryError("HTLC expires too soon!");
982
- }
983
-
984
1210
  /**
985
1211
  * Waits till a lightning network payment is received by the intermediary, and the intermediary
986
1212
  * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
@@ -990,16 +1216,17 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
990
1216
  * If this swap is using an LNURL-withdraw link as input, it automatically posts the
991
1217
  * generated invoice to the LNURL service to pay it.
992
1218
  *
993
- * @param onPaymentReceived Callback as for when the LP reports having received the ln payment
1219
+ * @remarks For internal use, rather use {@link waitForPayment} which properly waits till the LP also
1220
+ * offers a swap HTLC.
1221
+ *
994
1222
  * @param abortSignal Abort signal to stop waiting for payment
995
1223
  * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
1224
+ *
1225
+ * @internal
996
1226
  */
997
- async waitForPayment(onPaymentReceived?: (txId: string) => void, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
1227
+ async _waitForLpPaymentReceived(checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
998
1228
  checkIntervalSeconds ??= 5;
999
- if(this._state===FromBTCLNAutoSwapState.PR_PAID) {
1000
- await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1001
- }
1002
- if(this._state>=FromBTCLNAutoSwapState.CLAIM_COMMITED) return true;
1229
+ if(this._state>=FromBTCLNAutoSwapState.PR_PAID) return true;
1003
1230
  if(
1004
1231
  this._state!==FromBTCLNAutoSwapState.PR_CREATED
1005
1232
  ) throw new Error("Must be in PR_CREATED state!");
@@ -1046,7 +1273,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1046
1273
  if(this.url!=null) promises.push((async () => {
1047
1274
  let resp: InvoiceStatusResponse = {code: InvoiceStatusResponseCodes.PENDING, msg: ""};
1048
1275
  while(!abortController.signal.aborted && resp.code===InvoiceStatusResponseCodes.PENDING) {
1049
- resp = await IntermediaryAPI.getInvoiceStatus(this.url!, paymentHash.toString("hex"));
1276
+ resp = await this.wrapper._lpApi.getInvoiceStatus(this.url!, paymentHash.toString("hex"));
1050
1277
  if(resp.code===InvoiceStatusResponseCodes.PENDING)
1051
1278
  await timeoutPromise(checkIntervalSeconds*1000, abortController.signal);
1052
1279
  }
@@ -1069,9 +1296,64 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1069
1296
  abortController.abort();
1070
1297
 
1071
1298
  if(!paymentResult) return false;
1072
- if(onPaymentReceived!=null) onPaymentReceived(this.getInputTxId()!);
1073
1299
  }
1074
1300
 
1301
+ return this._state>=FromBTCLNAutoSwapState.PR_PAID;
1302
+ }
1303
+
1304
+
1305
+ /**
1306
+ * Checks the data returned by the intermediary in the payment auth request
1307
+ *
1308
+ * @param data Parsed swap data as returned by the intermediary
1309
+ *
1310
+ * @throws {IntermediaryError} If the returned are not valid
1311
+ * @throws {Error} If the swap is already committed on-chain
1312
+ *
1313
+ * @private
1314
+ */
1315
+ private async checkIntermediaryReturnedData(data: T["Data"]): Promise<void> {
1316
+ if (!data.isPayOut()) throw new IntermediaryError("Invalid not pay out");
1317
+ if (data.getType() !== ChainSwapType.HTLC) throw new IntermediaryError("Invalid swap type");
1318
+ if (!data.isOfferer(this.getSwapData().getOfferer())) throw new IntermediaryError("Invalid offerer used");
1319
+ if (!data.isClaimer(this._getInitiator())) throw new IntermediaryError("Invalid claimer used");
1320
+ if (!data.isToken(this.getSwapData().getToken())) throw new IntermediaryError("Invalid token used");
1321
+ if (data.getSecurityDeposit() !== this.getSwapData().getSecurityDeposit()) throw new IntermediaryError("Invalid security deposit!");
1322
+ if (data.getClaimerBounty() !== this.getSwapData().getClaimerBounty()) throw new IntermediaryError("Invalid security deposit!");
1323
+ if (data.getAmount() < this.getSwapData().getAmount()) throw new IntermediaryError("Invalid amount received!");
1324
+ if (data.getClaimHash() !== this.getSwapData().getClaimHash()) throw new IntermediaryError("Invalid payment hash used!");
1325
+ if (!data.isDepositToken(this.getSwapData().getDepositToken())) throw new IntermediaryError("Invalid deposit token used!");
1326
+ if (data.hasSuccessAction()) throw new IntermediaryError("Invalid has success action");
1327
+
1328
+ if (await this.wrapper._contract(this._contractVersion).isExpired(this._getInitiator(), data)) throw new IntermediaryError("Not enough time to claim!");
1329
+ if (this.wrapper._getHtlcTimeout(data) <= (Date.now()/1000)) throw new IntermediaryError("HTLC expires too soon!");
1330
+ }
1331
+
1332
+ /**
1333
+ * Waits till a lightning network payment is received by the intermediary, and the intermediary
1334
+ * initiates the swap HTLC on the smart chain side. After the HTLC is initiated you can wait
1335
+ * for an automatic settlement by the watchtowers with the {@link waitTillClaimed} function,
1336
+ * or settle manually using the {@link claim} or {@link txsClaim} functions.
1337
+ *
1338
+ * If this swap is using an LNURL-withdraw link as input, it automatically posts the
1339
+ * generated invoice to the LNURL service to pay it.
1340
+ *
1341
+ * @param onPaymentReceived Callback as for when the LP reports having received the ln payment
1342
+ * @param abortSignal Abort signal to stop waiting for payment
1343
+ * @param checkIntervalSeconds How often to poll the intermediary for answer (default 5 seconds)
1344
+ */
1345
+ async waitForPayment(onPaymentReceived?: (txId: string) => void, checkIntervalSeconds?: number, abortSignal?: AbortSignal): Promise<boolean> {
1346
+ checkIntervalSeconds ??= 5;
1347
+ if(this._state===FromBTCLNAutoSwapState.PR_PAID) {
1348
+ await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1349
+ }
1350
+ if(this._state>=FromBTCLNAutoSwapState.CLAIM_COMMITED) return true;
1351
+
1352
+ const success = await this._waitForLpPaymentReceived(checkIntervalSeconds, abortSignal);
1353
+ if(!success) return false;
1354
+
1355
+ if(onPaymentReceived!=null) onPaymentReceived(this.getInputTxId()!);
1356
+
1075
1357
  if((this._state as FromBTCLNAutoSwapState)===FromBTCLNAutoSwapState.PR_PAID) {
1076
1358
  await this.waitTillCommited(checkIntervalSeconds, abortSignal);
1077
1359
  }
@@ -1096,7 +1378,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1096
1378
  if(this._state!==FromBTCLNAutoSwapState.PR_PAID) throw new Error("Invalid state");
1097
1379
 
1098
1380
  const abortController = extendAbortController(abortSignal);
1099
- let result: number | boolean;
1381
+ let result: SwapCommitState | number | null;
1100
1382
  try {
1101
1383
  result = await Promise.race([
1102
1384
  this.watchdogWaitTillCommited(checkIntervalSeconds, abortController.signal),
@@ -1108,7 +1390,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1108
1390
  throw e;
1109
1391
  }
1110
1392
 
1111
- if(result===false) {
1393
+ if(result===null) {
1112
1394
  this.logger.debug("waitTillCommited(): Resolved from watchdog - HTLC expired");
1113
1395
  if(
1114
1396
  this._state===FromBTCLNAutoSwapState.PR_PAID
@@ -1121,12 +1403,15 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1121
1403
  if(
1122
1404
  this._state===FromBTCLNAutoSwapState.PR_PAID
1123
1405
  ) {
1406
+ if(typeof(result)==="object" && (result as any).getInitTxId!=null && this._commitTxId==null)
1407
+ this._commitTxId = await (result as any).getInitTxId();
1124
1408
  this._commitedAt ??= Date.now();
1125
1409
  await this._saveAndEmit(FromBTCLNAutoSwapState.CLAIM_COMMITED);
1126
1410
  }
1127
1411
 
1128
- if(result===0) this.logger.debug("waitTillCommited(): Resolved from state changed");
1129
- if(result===true) {
1412
+ if(result===0) {
1413
+ this.logger.debug("waitTillCommited(): Resolved from state changed");
1414
+ } else if(result!=null) {
1130
1415
  this.logger.debug("waitTillCommited(): Resolved from watchdog - commited");
1131
1416
  if(this.secret!=null) await this._broadcastSecret().catch(e => {
1132
1417
  this.logger.error("waitTillCommited(): Error broadcasting swap secret: ", e);
@@ -1209,12 +1494,13 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1209
1494
  * @param abortSignal AbortSignal
1210
1495
  * @param secret A swap secret to broadcast to watchtowers, generally only needed if the swap
1211
1496
  * was recovered from on-chain data, or the pre-image was generated outside the SDK
1497
+ * @param pollIntervalSeconds How often to poll via the watchdog
1212
1498
  *
1213
1499
  * @throws {Error} If swap is in invalid state (must be {@link FromBTCLNAutoSwapState.CLAIM_COMMITED})
1214
1500
  * @throws {Error} If the LP refunded sooner than we were able to claim
1215
1501
  * @returns {boolean} whether the swap was claimed in time or not
1216
1502
  */
1217
- async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, secret?: string): Promise<boolean> {
1503
+ async waitTillClaimed(maxWaitTimeSeconds?: number, abortSignal?: AbortSignal, secret?: string, pollIntervalSeconds?: number): Promise<boolean> {
1218
1504
  if(this._state===FromBTCLNAutoSwapState.CLAIM_CLAIMED) return Promise.resolve(true);
1219
1505
  if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED) throw new Error("Invalid state (not CLAIM_COMMITED)");
1220
1506
 
@@ -1238,7 +1524,7 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1238
1524
  let res: 0 | 1 | SwapCommitState;
1239
1525
  try {
1240
1526
  res = await Promise.race([
1241
- this.watchdogWaitTillResult(undefined, abortController.signal),
1527
+ this.watchdogWaitTillResult(pollIntervalSeconds, abortController.signal),
1242
1528
  this.waitTillState(FromBTCLNAutoSwapState.CLAIM_CLAIMED, "eq", abortController.signal).then(() => 0 as const),
1243
1529
  this.waitTillState(FromBTCLNAutoSwapState.EXPIRED, "eq", abortController.signal).then(() => 1 as const),
1244
1530
  ]);
@@ -1476,28 +1762,43 @@ export class FromBTCLNAutoSwap<T extends ChainType = ChainType>
1476
1762
  async _forciblySetOnchainState(commitStatus: SwapCommitState): Promise<boolean> {
1477
1763
  switch(commitStatus?.type) {
1478
1764
  case SwapCommitStateType.PAID:
1765
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1479
1766
  if(this._claimTxId==null) this._claimTxId = await commitStatus.getClaimTxId();
1480
1767
  if(this.secret==null || this.pr==null) this._setSwapSecret(await commitStatus.getClaimResult());
1481
1768
  this._state = FromBTCLNAutoSwapState.CLAIM_CLAIMED;
1482
1769
  return true;
1483
1770
  case SwapCommitStateType.NOT_COMMITED:
1484
- if(this._refundTxId==null && commitStatus.getRefundTxId!=null) this._refundTxId = await commitStatus.getRefundTxId();
1771
+ let changed: boolean = false;
1772
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1773
+ this._commitTxId = await (commitStatus as any).getInitTxId();
1774
+ changed = true;
1775
+ }
1776
+ if(this._refundTxId==null && commitStatus.getRefundTxId!=null) {
1777
+ this._refundTxId = await commitStatus.getRefundTxId();
1778
+ changed = true;
1779
+ }
1485
1780
  if(this._refundTxId!=null) {
1486
1781
  this._state = FromBTCLNAutoSwapState.FAILED;
1487
- return true;
1782
+ changed = true;
1488
1783
  }
1489
- break;
1784
+ return changed;
1490
1785
  case SwapCommitStateType.EXPIRED:
1786
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) this._commitTxId = await (commitStatus as any).getInitTxId();
1491
1787
  if(this._refundTxId==null && commitStatus.getRefundTxId!=null) this._refundTxId = await commitStatus.getRefundTxId();
1492
1788
  this._state = this._refundTxId==null ? FromBTCLNAutoSwapState.QUOTE_EXPIRED : FromBTCLNAutoSwapState.FAILED;
1493
1789
  return true;
1494
1790
  case SwapCommitStateType.COMMITED:
1791
+ let save: boolean = false;
1792
+ if(this._commitTxId==null && (commitStatus as any).getInitTxId!=null) {
1793
+ this._commitTxId = await (commitStatus as any).getInitTxId();
1794
+ save = true;
1795
+ }
1495
1796
  if(this._state!==FromBTCLNAutoSwapState.CLAIM_COMMITED && this._state!==FromBTCLNAutoSwapState.EXPIRED) {
1496
1797
  this._commitedAt ??= Date.now();
1497
1798
  this._state = FromBTCLNAutoSwapState.CLAIM_COMMITED;
1498
- return true;
1799
+ save = true;
1499
1800
  }
1500
- break;
1801
+ return save;
1501
1802
  }
1502
1803
  return false;
1503
1804
  }