@atomiqlabs/lp-lib 17.1.2 → 17.3.1

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 (57) hide show
  1. package/dist/info/InfoHandler.js +2 -1
  2. package/dist/plugins/IPlugin.d.ts +17 -3
  3. package/dist/plugins/IPlugin.js +9 -8
  4. package/dist/plugins/PluginManager.d.ts +5 -3
  5. package/dist/plugins/PluginManager.js +30 -0
  6. package/dist/swaps/SwapHandler.d.ts +1 -0
  7. package/dist/swaps/SwapHandlerSwap.d.ts +1 -0
  8. package/dist/swaps/assertions/FromBtcAmountAssertions.js +2 -2
  9. package/dist/swaps/assertions/ToBtcAmountAssertions.js +2 -2
  10. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +7 -0
  11. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.d.ts +1 -0
  12. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.js +3 -0
  13. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +31 -11
  14. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +1 -0
  15. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.js +3 -0
  16. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +14 -2
  17. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.d.ts +1 -0
  18. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.js +3 -0
  19. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +25 -2
  20. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.d.ts +1 -0
  21. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.js +3 -0
  22. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +13 -1
  23. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +1 -0
  24. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.js +3 -0
  25. package/dist/swaps/spv_vault_swap/SpvVaultSwap.d.ts +1 -0
  26. package/dist/swaps/spv_vault_swap/SpvVaultSwap.js +3 -0
  27. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +14 -2
  28. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +47 -32
  29. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.d.ts +1 -0
  30. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.js +3 -0
  31. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +14 -0
  32. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +1 -0
  33. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.js +3 -0
  34. package/package.json +2 -2
  35. package/src/info/InfoHandler.ts +6 -3
  36. package/src/plugins/IPlugin.ts +36 -14
  37. package/src/plugins/PluginManager.ts +41 -6
  38. package/src/swaps/SwapHandler.ts +2 -1
  39. package/src/swaps/SwapHandlerSwap.ts +2 -0
  40. package/src/swaps/assertions/FromBtcAmountAssertions.ts +2 -2
  41. package/src/swaps/assertions/ToBtcAmountAssertions.ts +2 -2
  42. package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +11 -0
  43. package/src/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.ts +4 -0
  44. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +23 -1
  45. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.ts +4 -0
  46. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +15 -1
  47. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.ts +4 -0
  48. package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +29 -3
  49. package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +4 -0
  50. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +19 -2
  51. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.ts +4 -0
  52. package/src/swaps/spv_vault_swap/SpvVaultSwap.ts +4 -0
  53. package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +17 -2
  54. package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +53 -33
  55. package/src/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.ts +4 -0
  56. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +20 -1
  57. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.ts +4 -0
@@ -7,6 +7,7 @@ const PluginManager_1 = require("../../../plugins/PluginManager");
7
7
  const Utils_1 = require("../../../utils/Utils");
8
8
  const SchemaVerifier_1 = require("../../../utils/paramcoders/SchemaVerifier");
9
9
  const FromBtcAmountAssertions_1 = require("../../assertions/FromBtcAmountAssertions");
10
+ const IPlugin_1 = require("../../../plugins/IPlugin");
10
11
  class FromBtcTrusted extends SwapHandler_1.SwapHandler {
11
12
  constructor(storageDirectory, path, chains, bitcoin, swapPricing, bitcoinRpc, config) {
12
13
  var _a;
@@ -30,42 +31,45 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
30
31
  })));
31
32
  }
32
33
  async refundSwap(swap) {
33
- if (swap.refundAddress == null) {
34
- if (swap.state !== FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDABLE) {
35
- await swap.setState(FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDABLE);
36
- await this.storageManager.saveData(swap.getIdentifierHash(), swap.getSequence(), swap);
37
- }
38
- return;
34
+ if (swap.state !== FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDABLE) {
35
+ await swap.setState(FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDABLE);
36
+ await this.storageManager.saveData(swap.getIdentifierHash(), swap.getSequence(), swap);
39
37
  }
40
- let unlock = swap.lock(30 * 1000);
38
+ if (swap.refundAddress == null)
39
+ return;
40
+ let unlock = swap.lock(Infinity);
41
41
  if (unlock == null)
42
42
  return;
43
- const feeRate = await this.bitcoin.getFeeRate();
44
- const ourOutput = swap.btcTx.outs[swap.vout];
45
- const resp = await this.bitcoin.drainAll(swap.refundAddress, [{
46
- type: this.bitcoin.getAddressType(),
47
- confirmations: swap.btcTx.confirmations,
48
- outputScript: Buffer.from(ourOutput.scriptPubKey.hex, "hex"),
49
- value: ourOutput.value,
50
- txId: swap.btcTx.txid,
51
- vout: swap.vout
52
- }], feeRate);
53
- if (resp == null) {
54
- this.swapLogger.error(swap, "refundSwap(): cannot refund swap because of dust limit, txId: " + swap.txId);
43
+ try {
44
+ const feeRate = await this.bitcoin.getFeeRate();
45
+ const ourOutput = swap.btcTx.outs[swap.vout];
46
+ const resp = await this.bitcoin.drainAll(swap.refundAddress, [{
47
+ type: this.bitcoin.getAddressType(),
48
+ confirmations: swap.btcTx.confirmations,
49
+ outputScript: Buffer.from(ourOutput.scriptPubKey.hex, "hex"),
50
+ value: ourOutput.value,
51
+ txId: swap.btcTx.txid,
52
+ vout: swap.vout
53
+ }], feeRate);
54
+ if (resp == null) {
55
+ this.swapLogger.error(swap, "refundSwap(): cannot refund swap because of dust limit, txId: " + swap.txId);
56
+ unlock();
57
+ return;
58
+ }
59
+ if (swap.metadata != null)
60
+ swap.metadata.times.refundSignPSBT = Date.now();
61
+ this.swapLogger.debug(swap, "refundSwap(): signed raw transaction: " + resp.raw);
62
+ const refundTxId = resp.txId;
63
+ swap.refundTxId = refundTxId;
64
+ //Send the refund TX
65
+ await this.bitcoin.sendRawTransaction(resp.raw);
66
+ this.swapLogger.debug(swap, "refundSwap(): sent refund transaction: " + refundTxId);
67
+ this.refundedSwaps.set(swap.getIdentifierHash(), refundTxId);
68
+ await this.removeSwapData(swap, FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDED);
69
+ }
70
+ finally {
55
71
  unlock();
56
- return;
57
72
  }
58
- if (swap.metadata != null)
59
- swap.metadata.times.refundSignPSBT = Date.now();
60
- this.swapLogger.debug(swap, "refundSwap(): signed raw transaction: " + resp.raw);
61
- const refundTxId = resp.txId;
62
- swap.refundTxId = refundTxId;
63
- //Send the refund TX
64
- await this.bitcoin.sendRawTransaction(resp.raw);
65
- this.swapLogger.debug(swap, "refundSwap(): sent refund transaction: " + refundTxId);
66
- this.refundedSwaps.set(swap.getIdentifierHash(), refundTxId);
67
- await this.removeSwapData(swap, FromBtcTrustedSwap_1.FromBtcTrustedSwapState.REFUNDED);
68
- unlock();
69
73
  }
70
74
  async burn(swap) {
71
75
  const ourOutput = swap.btcTx.outs[swap.vout];
@@ -241,10 +245,21 @@ class FromBtcTrusted extends SwapHandler_1.SwapHandler {
241
245
  }
242
246
  if (swap.state !== FromBtcTrustedSwap_1.FromBtcTrustedSwapState.BTC_CONFIRMED)
243
247
  return;
248
+ const txns = await chainInterface.txsTransfer(signer.getAddress(), swap.token, swap.adjustedOutput, swap.dstAddress);
244
249
  let unlock = swap.lock(30 * 1000);
245
250
  if (unlock == null)
246
251
  return;
247
- const txns = await chainInterface.txsTransfer(signer.getAddress(), swap.token, swap.adjustedOutput, swap.dstAddress);
252
+ const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreFromBtcExecute(SwapHandler_1.SwapHandlerType.FROM_BTC_TRUSTED, swap);
253
+ if ((0, IPlugin_1.isQuoteThrow)(pluginCheckResult)) {
254
+ this.swapLogger.error(swap, "processPastSwap(): Error, got negative response from plugin: ", pluginCheckResult);
255
+ await this.refundSwap(swap);
256
+ unlock();
257
+ return;
258
+ }
259
+ if (swap.state !== FromBtcTrustedSwap_1.FromBtcTrustedSwapState.BTC_CONFIRMED) {
260
+ unlock();
261
+ return;
262
+ }
248
263
  await chainInterface.sendAndConfirm(signer, txns, true, null, false, async (txId, rawTx) => {
249
264
  swap.txIds = { init: txId };
250
265
  swap.scRawTx = rawTx;
@@ -49,4 +49,5 @@ export declare class FromBtcTrustedSwap extends SwapHandlerSwap<FromBtcTrustedSw
49
49
  isInitiated(): boolean;
50
50
  isSuccess(): boolean;
51
51
  getIdentifierHash(): string;
52
+ getDestinationAddress(): string;
52
53
  }
@@ -114,5 +114,8 @@ class FromBtcTrustedSwap extends SwapHandlerSwap_1.SwapHandlerSwap {
114
114
  getIdentifierHash() {
115
115
  return (0, crypto_1.createHash)("sha256").update(this.btcAddress).digest().toString("hex");
116
116
  }
117
+ getDestinationAddress() {
118
+ return this.dstAddress;
119
+ }
117
120
  }
118
121
  exports.FromBtcTrustedSwap = FromBtcTrustedSwap;
@@ -9,6 +9,7 @@ const SchemaVerifier_1 = require("../../../utils/paramcoders/SchemaVerifier");
9
9
  const PluginManager_1 = require("../../../plugins/PluginManager");
10
10
  const FromBtcAmountAssertions_1 = require("../../assertions/FromBtcAmountAssertions");
11
11
  const LightningAssertions_1 = require("../../assertions/LightningAssertions");
12
+ const IPlugin_1 = require("../../../plugins/IPlugin");
12
13
  /**
13
14
  * Swap handler handling from BTCLN swaps using submarine swaps
14
15
  */
@@ -183,6 +184,19 @@ class FromBtcLnTrusted extends SwapHandler_1.SwapHandler {
183
184
  let unlock = invoiceData.lock(Infinity);
184
185
  if (unlock == null)
185
186
  return;
187
+ const pluginCheckResult = await PluginManager_1.PluginManager.onHandlePreFromBtcExecute(SwapHandler_1.SwapHandlerType.FROM_BTCLN_TRUSTED, invoiceData);
188
+ if ((0, IPlugin_1.isQuoteThrow)(pluginCheckResult)) {
189
+ await this.cancelSwapAndInvoice(invoiceData);
190
+ unlock();
191
+ throw {
192
+ code: 29999,
193
+ msg: pluginCheckResult.message
194
+ };
195
+ }
196
+ if (invoiceData.state !== FromBtcLnTrustedSwap_1.FromBtcLnTrustedSwapState.RECEIVED) {
197
+ unlock();
198
+ return;
199
+ }
186
200
  const result = await chainInterface.sendAndConfirm(signer, txns, true, null, false, async (txId, rawTx) => {
187
201
  invoiceData.txIds = { init: txId };
188
202
  invoiceData.scRawTx = rawTx;
@@ -31,4 +31,5 @@ export declare class FromBtcLnTrustedSwap extends SwapHandlerSwap<FromBtcLnTrust
31
31
  isInitiated(): boolean;
32
32
  isSuccess(): boolean;
33
33
  getIdentifierHash(): string;
34
+ getDestinationAddress(): string;
34
35
  }
@@ -77,5 +77,8 @@ class FromBtcLnTrustedSwap extends SwapHandlerSwap_1.SwapHandlerSwap {
77
77
  getIdentifierHash() {
78
78
  return (0, crypto_1.createHash)("sha256").update(Buffer.from(this.secret, "hex")).digest().toString("hex");
79
79
  }
80
+ getDestinationAddress() {
81
+ return this.dstAddress;
82
+ }
80
83
  }
81
84
  exports.FromBtcLnTrustedSwap = FromBtcLnTrustedSwap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/lp-lib",
3
- "version": "17.1.2",
3
+ "version": "17.3.1",
4
4
  "description": "Main functionality implementation for atomiq LP node",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  "author": "adambor",
24
24
  "license": "ISC",
25
25
  "dependencies": {
26
- "@atomiqlabs/base": "^13.0.4",
26
+ "@atomiqlabs/base": "^13.5.0",
27
27
  "@atomiqlabs/server-base": "^3.0.0",
28
28
  "@scure/btc-signer": "1.6.0",
29
29
  "express": "4.21.1",
@@ -16,7 +16,8 @@ type InfoHandlerResponse = {
16
16
  chains: {
17
17
  [chainIdentifier: string]: {
18
18
  address: string,
19
- signature: string
19
+ signature: string,
20
+ contractVersion: string
20
21
  }
21
22
  }
22
23
  }
@@ -74,14 +75,16 @@ export class InfoHandler {
74
75
  const chains: {
75
76
  [chainIdentifier: string]: {
76
77
  address: string,
77
- signature: string
78
+ signature: string,
79
+ contractVersion: string
78
80
  }
79
81
  } = {};
80
82
  for(let chainIdentifier in this.chainData.chains) {
81
83
  const singleChain = this.chainData.chains[chainIdentifier];
82
84
  chains[chainIdentifier] = {
83
85
  address: singleChain.signer.getAddress(),
84
- signature: await singleChain.swapContract.getDataSignature(singleChain.signer, envelopeBuffer)
86
+ signature: await singleChain.swapContract.getDataSignature(singleChain.signer, envelopeBuffer),
87
+ contractVersion: singleChain.contractVersion ?? "v1"
85
88
  };
86
89
  }
87
90
 
@@ -1,11 +1,12 @@
1
1
  import {BitcoinRpc} from "@atomiqlabs/base";
2
2
  import {
3
- FromBtcLnRequestType,
4
- FromBtcRequestType, FromBtcTrustedRequestType,
3
+ FromBtcLnAbs, FromBtcLnAutoSwap,
4
+ FromBtcLnRequestType, FromBtcLnSwapAbs, FromBtcLnTrustedSwap,
5
+ FromBtcRequestType, FromBtcSwapAbs, FromBtcTrustedRequestType, FromBtcTrustedSwap,
5
6
  ISwapPrice, MultichainData, RequestData, SpvVaultPostQuote, SpvVaultSwap, SpvVaultSwapRequestType,
6
7
  SwapHandler, SwapHandlerType,
7
- ToBtcLnRequestType,
8
- ToBtcRequestType
8
+ ToBtcLnRequestType, ToBtcLnSwapAbs,
9
+ ToBtcRequestType, ToBtcSwapAbs
9
10
  } from "..";
10
11
  import {SwapHandlerSwap} from "../swaps/SwapHandlerSwap";
11
12
  import {Command} from "@atomiqlabs/server-base";
@@ -20,7 +21,7 @@ export type QuoteThrow = {
20
21
  }
21
22
 
22
23
  export function isQuoteThrow(obj: any): obj is QuoteThrow {
23
- return obj.type==="throw" && typeof(obj.message)==="string";
24
+ return obj!=null && obj.type==="throw" && typeof(obj.message)==="string";
24
25
  }
25
26
 
26
27
  export type QuoteSetFees = {
@@ -32,7 +33,8 @@ export type QuoteSetFees = {
32
33
  };
33
34
 
34
35
  export function isQuoteSetFees(obj: any): obj is QuoteSetFees {
35
- return obj.type==="fees" &&
36
+ return obj!=null &&
37
+ obj.type==="fees" &&
36
38
  (obj.baseFee==null || typeof(obj.baseFee) === "bigint") &&
37
39
  (obj.feePPM==null || typeof(obj.feePPM) === "bigint") &&
38
40
  (obj.securityDepositApyPPM==null || typeof(obj.securityDepositApyPPM) === "bigint") &&
@@ -45,7 +47,7 @@ export type QuoteAmountTooLow = {
45
47
  }
46
48
 
47
49
  export function isQuoteAmountTooLow(obj: any): obj is QuoteAmountTooLow {
48
- return obj.type==="low" && typeof(obj.data)==="object" && typeof(obj.data.min)==="bigint" && typeof(obj.data.max)==="bigint";
50
+ return obj!=null && obj.type==="low" && obj.data!=null && typeof(obj.data)==="object" && typeof(obj.data.min)==="bigint" && typeof(obj.data.max)==="bigint";
49
51
  }
50
52
 
51
53
  export type QuoteAmountTooHigh = {
@@ -54,7 +56,7 @@ export type QuoteAmountTooHigh = {
54
56
  }
55
57
 
56
58
  export function isQuoteAmountTooHigh(obj: any): obj is QuoteAmountTooHigh {
57
- return obj.type==="high" && typeof(obj.data)==="object" && typeof(obj.data.min)==="bigint" && typeof(obj.data.max)==="bigint";
59
+ return obj!=null && obj.type==="high" && obj.data!=null && typeof(obj.data)==="object" && typeof(obj.data.min)==="bigint" && typeof(obj.data.max)==="bigint";
58
60
  }
59
61
 
60
62
  export type PluginQuote = {
@@ -64,9 +66,9 @@ export type PluginQuote = {
64
66
  };
65
67
 
66
68
  export function isPluginQuote(obj: any): obj is PluginQuote {
67
- return obj.type==="success" &&
68
- typeof(obj.amount)==="object" && typeof(obj.amount.input)==="boolean" && typeof(obj.amount.amount)==="bigint" &&
69
- typeof(obj.swapFee)==="object" && typeof(obj.swapFee.inInputTokens)==="bigint" && typeof(obj.swapFee.inOutputTokens)==="bigint";
69
+ return obj!=null && obj.type==="success" &&
70
+ obj.amount!=null && typeof(obj.amount)==="object" && typeof(obj.amount.input)==="boolean" && typeof(obj.amount.amount)==="bigint" &&
71
+ obj.swapFee!=null && typeof(obj.swapFee)==="object" && typeof(obj.swapFee.inInputTokens)==="bigint" && typeof(obj.swapFee.inOutputTokens)==="bigint";
70
72
  }
71
73
 
72
74
  export type ToBtcPluginQuote = PluginQuote & {
@@ -74,7 +76,7 @@ export type ToBtcPluginQuote = PluginQuote & {
74
76
  }
75
77
 
76
78
  export function isToBtcPluginQuote(obj: any): obj is ToBtcPluginQuote {
77
- return typeof(obj.networkFee)==="object" && typeof(obj.networkFee.inInputTokens)==="bigint" && typeof(obj.networkFee.inOutputTokens)==="bigint" &&
79
+ return obj!=null && obj.networkFee!=null && typeof(obj.networkFee)==="object" && typeof(obj.networkFee.inInputTokens)==="bigint" && typeof(obj.networkFee.inOutputTokens)==="bigint" &&
78
80
  isPluginQuote(obj);
79
81
  }
80
82
 
@@ -94,8 +96,8 @@ export interface IPlugin {
94
96
 
95
97
  swapPricing: ISwapPrice,
96
98
  tokens: {
97
- [ticker: string]: {
98
- [chainId: string]: {
99
+ [chainId: string]: {
100
+ [ticker: string]: {
99
101
  address: string,
100
102
  decimals: number
101
103
  }
@@ -133,6 +135,22 @@ export interface IPlugin {
133
135
  fees: {baseFeeInBtc: bigint, feePPM: bigint},
134
136
  gasTokenAmount?: {input: false, amount: bigint, token: string, pricePrefetch?: Promise<bigint>}
135
137
  ): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh | PluginQuote>;
138
+ /**
139
+ * Triggered when the quote is about to get executed, this means:
140
+ * - FROM_BTCLN - lightning network payment received and the LP is about to sign an authorization allowing the user to settle
141
+ * - FROM_BTC - triggered on quote request, before the final authorization is given to the user
142
+ * - FROM_BTCLN_TRUSTED - triggered when the LP receives the funds on the source chain and before destination funds are sent
143
+ * - FROM_BTC_TRUSTED - triggered when the LP receives the funds on the source chain and before destination funds are sent
144
+ * - FROM_BTC_SPV - triggered before LP broadcasts the co-signed swap PSBT
145
+ * - FROM_BTCLN_AUTO - lightning network payment received and the LP is about to offer an HTLC to the user
146
+ *
147
+ * @param swapType
148
+ * @param swap
149
+ */
150
+ onHandlePreFromBtcExecute?(
151
+ swapType: SwapHandlerType.FROM_BTCLN | SwapHandlerType.FROM_BTC | SwapHandlerType.FROM_BTCLN_TRUSTED | SwapHandlerType.FROM_BTC_TRUSTED | SwapHandlerType.FROM_BTC_SPV | SwapHandlerType.FROM_BTCLN_AUTO,
152
+ swap: FromBtcLnSwapAbs | FromBtcSwapAbs | FromBtcLnTrustedSwap | FromBtcTrustedSwap | SpvVaultSwap | FromBtcLnAutoSwap
153
+ ): Promise<QuoteThrow | null>;
136
154
 
137
155
  onHandlePreToBtcQuote?(
138
156
  swapType: SwapHandlerType.TO_BTCLN | SwapHandlerType.TO_BTC,
@@ -150,6 +168,10 @@ export interface IPlugin {
150
168
  constraints: {minInBtc: bigint, maxInBtc: bigint},
151
169
  fees: {baseFeeInBtc: bigint, feePPM: bigint, networkFeeGetter: (amount: bigint) => Promise<bigint>}
152
170
  ): Promise<QuoteThrow | QuoteSetFees | QuoteAmountTooLow | QuoteAmountTooHigh | ToBtcPluginQuote>;
171
+ onHandlePreToBtcExecute?(
172
+ swapType: SwapHandlerType.TO_BTCLN | SwapHandlerType.TO_BTC,
173
+ swap: ToBtcLnSwapAbs | ToBtcSwapAbs
174
+ ): Promise<QuoteThrow | null>;
153
175
 
154
176
  onHandlePostedFromBtcQuote?(
155
177
  swapType: SwapHandlerType.FROM_BTC_SPV,
@@ -8,12 +8,13 @@ import {
8
8
  QuoteThrow, ToBtcPluginQuote
9
9
  } from "./IPlugin";
10
10
  import {
11
- FromBtcLnRequestType,
12
- FromBtcRequestType, FromBtcTrustedRequestType,
11
+ FromBtcLnAutoSwap,
12
+ FromBtcLnRequestType, FromBtcLnSwapAbs, FromBtcLnTrustedSwap,
13
+ FromBtcRequestType, FromBtcSwapAbs, FromBtcTrustedRequestType, FromBtcTrustedSwap,
13
14
  ISwapPrice, MultichainData, RequestData, SpvVaultPostQuote, SpvVaultSwap, SpvVaultSwapRequestType,
14
15
  SwapHandler, SwapHandlerType,
15
- ToBtcLnRequestType,
16
- ToBtcRequestType
16
+ ToBtcLnRequestType, ToBtcLnSwapAbs,
17
+ ToBtcRequestType, ToBtcSwapAbs
17
18
  } from "..";
18
19
  import {SwapHandlerSwap} from "../swaps/SwapHandlerSwap";
19
20
  import * as fs from "fs";
@@ -73,8 +74,8 @@ export class PluginManager {
73
74
 
74
75
  swapPricing: ISwapPrice,
75
76
  tokens: {
76
- [ticker: string]: {
77
- [chainId: string]: {
77
+ [chainId: string]: {
78
+ [ticker: string]: {
78
79
  address: string,
79
80
  decimals: number
80
81
  }
@@ -224,6 +225,23 @@ export class PluginManager {
224
225
  return null;
225
226
  }
226
227
 
228
+ static async onHandlePreFromBtcExecute(
229
+ swapType: SwapHandlerType.FROM_BTCLN | SwapHandlerType.FROM_BTC | SwapHandlerType.FROM_BTCLN_TRUSTED | SwapHandlerType.FROM_BTC_TRUSTED | SwapHandlerType.FROM_BTC_SPV | SwapHandlerType.FROM_BTCLN_AUTO,
230
+ swap: FromBtcLnSwapAbs | FromBtcSwapAbs | FromBtcLnTrustedSwap | FromBtcTrustedSwap | SpvVaultSwap | FromBtcLnAutoSwap
231
+ ): Promise<QuoteThrow | null> {
232
+ for(let plugin of PluginManager.plugins.values()) {
233
+ try {
234
+ if(plugin.onHandlePreFromBtcExecute!=null) {
235
+ const result = await plugin.onHandlePreFromBtcExecute(swapType, swap);
236
+ if(result!=null && isQuoteThrow(result)) return result;
237
+ }
238
+ } catch (e) {
239
+ pluginLogger.error(plugin, "onHandlePreFromBtcExecute(): plugin error", e);
240
+ }
241
+ }
242
+ return null;
243
+ }
244
+
227
245
  static async onHandlePostToBtcQuote<T extends {networkFee: bigint}>(
228
246
  swapType: SwapHandlerType.TO_BTCLN | SwapHandlerType.TO_BTC,
229
247
  request: RequestData<ToBtcLnRequestType | ToBtcRequestType>,
@@ -291,6 +309,23 @@ export class PluginManager {
291
309
  return null;
292
310
  }
293
311
 
312
+ static async onHandlePreToBtcExecute(
313
+ swapType: SwapHandlerType.TO_BTCLN | SwapHandlerType.TO_BTC,
314
+ swap: ToBtcLnSwapAbs | ToBtcSwapAbs
315
+ ): Promise<QuoteThrow | null> {
316
+ for(let plugin of PluginManager.plugins.values()) {
317
+ try {
318
+ if(plugin.onHandlePreToBtcExecute!=null) {
319
+ const result = await plugin.onHandlePreToBtcExecute(swapType, swap);
320
+ if(result!=null && isQuoteThrow(result)) return result;
321
+ }
322
+ } catch (e) {
323
+ pluginLogger.error(plugin, "onHandlePreToBtcExecute(): plugin error", e);
324
+ }
325
+ }
326
+ return null;
327
+ }
328
+
294
329
  static async onHandlePostedFromBtcQuote(
295
330
  swapType: SwapHandlerType.FROM_BTC_SPV,
296
331
  request: RequestData<SpvVaultPostQuote>,
@@ -62,7 +62,8 @@ export type ChainData<T extends ChainType = ChainType> = {
62
62
  allowedTokens: string[],
63
63
  tokenMultipliers?: {[tokenAddress: string]: bigint},
64
64
  allowedDepositTokens?: string[],
65
- btcRelay?: T["BtcRelay"]
65
+ btcRelay?: T["BtcRelay"],
66
+ contractVersion?: string
66
67
  }
67
68
 
68
69
  export type RequestData<T> = {
@@ -139,4 +139,6 @@ export abstract class SwapHandlerSwap<S = any> extends Lockable implements Stora
139
139
  */
140
140
  abstract getSwapFee(): {inInputToken: bigint, inOutputToken: bigint};
141
141
 
142
+ abstract getDestinationAddress(): string;
143
+
142
144
  }
@@ -55,8 +55,8 @@ export class FromBtcAmountAssertions extends AmountAssertions {
55
55
  AmountAssertions.handlePluginErrorResponses(res);
56
56
  if(isQuoteSetFees(res)) {
57
57
  return {
58
- baseFee: res.baseFee || this.config.baseFee,
59
- feePPM: res.feePPM || this.config.feePPM,
58
+ baseFee: res.baseFee ?? this.config.baseFee,
59
+ feePPM: res.feePPM ?? this.config.feePPM,
60
60
  securityDepositApyPPM: res.securityDepositApyPPM,
61
61
  securityDepositBaseMultiplierPPM: res.securityDepositBaseMultiplierPPM
62
62
  }
@@ -33,8 +33,8 @@ export class ToBtcAmountAssertions extends AmountAssertions {
33
33
  AmountAssertions.handlePluginErrorResponses(res);
34
34
  if(isQuoteSetFees(res)) {
35
35
  return {
36
- baseFee: res.baseFee || this.config.baseFee,
37
- feePPM: res.feePPM || this.config.feePPM
36
+ baseFee: res.baseFee ?? this.config.baseFee,
37
+ feePPM: res.feePPM ?? this.config.feePPM
38
38
  }
39
39
  }
40
40
  }
@@ -20,6 +20,8 @@ import {IParamReader} from "../../../utils/paramcoders/IParamReader";
20
20
  import {ServerParamEncoder} from "../../../utils/paramcoders/server/ServerParamEncoder";
21
21
  import {FromBtcBaseConfig, FromBtcBaseSwapHandler} from "../FromBtcBaseSwapHandler";
22
22
  import {IBitcoinWallet} from "../../../wallets/IBitcoinWallet";
23
+ import {isQuoteThrow} from "../../../plugins/IPlugin";
24
+ import {FromBtcLnSwapState} from "../frombtcln_abstract/FromBtcLnSwapAbs";
23
25
 
24
26
  export type FromBtcConfig = FromBtcBaseConfig & {
25
27
  confirmations: number,
@@ -409,6 +411,15 @@ export class FromBtcAbs extends FromBtcBaseSwapHandler<FromBtcSwapAbs, FromBtcSw
409
411
  createdSwap.signature = sigData.signature;
410
412
  createdSwap.feeRate = sigData.feeRate;
411
413
 
414
+ const pluginCheckResult = await PluginManager.onHandlePreFromBtcExecute(
415
+ SwapHandlerType.FROM_BTC,
416
+ createdSwap
417
+ );
418
+ if(isQuoteThrow(pluginCheckResult)) throw {
419
+ code: 29999,
420
+ msg: pluginCheckResult.message
421
+ };
422
+
412
423
  await PluginManager.swapCreate(createdSwap);
413
424
  await this.saveSwapData(createdSwap);
414
425
 
@@ -58,4 +58,8 @@ export class FromBtcSwapAbs<T extends SwapData = SwapData> extends FromBtcBaseSw
58
58
  return this.amount;
59
59
  }
60
60
 
61
+ getDestinationAddress(): string {
62
+ return this.data.getClaimer();
63
+ }
64
+
61
65
  }
@@ -27,6 +27,8 @@ import {
27
27
  LightningNetworkInvoice
28
28
  } from "../../../wallets/ILightningWallet";
29
29
  import {LightningAssertions} from "../../assertions/LightningAssertions";
30
+ import {isQuoteThrow} from "../../../plugins/IPlugin";
31
+ import {FromBtcLnAutoSwapState} from "../frombtcln_autoinit/FromBtcLnAutoSwap";
30
32
 
31
33
  export type FromBtcLnConfig = FromBtcBaseConfig & {
32
34
  invoiceTimeoutSeconds?: number,
@@ -374,6 +376,20 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
374
376
  //No need to check abortController anymore since all pending promises are resolved by now
375
377
  if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcSwapSigned = Date.now();
376
378
 
379
+ const pluginCheckResult = await PluginManager.onHandlePreFromBtcExecute(
380
+ SwapHandlerType.FROM_BTCLN,
381
+ invoiceData
382
+ );
383
+ if(isQuoteThrow(pluginCheckResult)) {
384
+ const error = {
385
+ code: 29999,
386
+ msg: pluginCheckResult.message
387
+ };
388
+ if(invoiceData.metadata!=null) invoiceData.metadata.htlcReceiveError = error;
389
+ if(invoiceData.state===FromBtcLnSwapState.CREATED) await this.cancelSwapAndInvoice(invoiceData);
390
+ throw error;
391
+ }
392
+
377
393
  //Important to prevent race condition and issuing 2 signed init messages at the same time
378
394
  if(invoiceData.state===FromBtcLnSwapState.CREATED) {
379
395
  //Re-check right before signing
@@ -836,7 +852,13 @@ export class FromBtcLnAbs extends FromBtcBaseSwapHandler<FromBtcLnSwapAbs, FromB
836
852
  msg: "Invoice already committed"
837
853
  };
838
854
 
839
- res.status(200).json({
855
+ if (swap.state === FromBtcLnSwapState.CLAIMED) throw {
856
+ _httpStatus: 200,
857
+ code: 10005,
858
+ msg: "Invoice already committed & claimed"
859
+ };
860
+
861
+ if (swap.state === FromBtcLnSwapState.RECEIVED) res.status(200).json({
840
862
  code: 10000,
841
863
  msg: "Success",
842
864
  data: {
@@ -138,4 +138,8 @@ export class FromBtcLnSwapAbs<T extends SwapData = SwapData> extends FromBtcBase
138
138
  return this.state===FromBtcLnSwapState.SETTLED;
139
139
  }
140
140
 
141
+ getDestinationAddress(): string {
142
+ return this.claimer
143
+ }
144
+
141
145
  }
@@ -19,6 +19,7 @@ import {
19
19
  LightningNetworkInvoice
20
20
  } from "../../../wallets/ILightningWallet";
21
21
  import {LightningAssertions} from "../../assertions/LightningAssertions";
22
+ import {isQuoteThrow} from "../../../plugins/IPlugin";
22
23
 
23
24
  export type FromBtcLnAutoConfig = FromBtcBaseConfig & {
24
25
  invoiceTimeoutSeconds?: number,
@@ -420,6 +421,20 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
420
421
  signature: invoiceData.signature
421
422
  }, true);
422
423
 
424
+ const pluginCheckResult = await PluginManager.onHandlePreFromBtcExecute(
425
+ SwapHandlerType.FROM_BTCLN_AUTO,
426
+ invoiceData
427
+ );
428
+ if(isQuoteThrow(pluginCheckResult)) {
429
+ const error = {
430
+ code: 29999,
431
+ msg: pluginCheckResult.message
432
+ };
433
+ if(invoiceData.metadata!=null) invoiceData.metadata.htlcOfferError = error;
434
+ if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) await this.cancelSwapAndInvoice(invoiceData);
435
+ throw error;
436
+ }
437
+
423
438
  if(invoiceData.state===FromBtcLnAutoSwapState.RECEIVED) {
424
439
  //Re-check the current HTLC count
425
440
  try {
@@ -821,7 +836,6 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
821
836
  };
822
837
 
823
838
  if (
824
- swap.state === FromBtcLnAutoSwapState.RECEIVED ||
825
839
  swap.state === FromBtcLnAutoSwapState.TXS_SENT ||
826
840
  swap.state === FromBtcLnAutoSwapState.COMMITED
827
841
  ) {
@@ -193,4 +193,8 @@ export class FromBtcLnAutoSwap<T extends SwapData = SwapData> extends FromBtcBas
193
193
  return this.state===FromBtcLnAutoSwapState.SETTLED;
194
194
  }
195
195
 
196
+ getDestinationAddress(): string {
197
+ return this.claimer
198
+ }
199
+
196
200
  }
@@ -23,6 +23,8 @@ import {ServerParamEncoder} from "../../../utils/paramcoders/server/ServerParamE
23
23
  import {ToBtcBaseConfig, ToBtcBaseSwapHandler} from "../ToBtcBaseSwapHandler";
24
24
  import {IBitcoinWallet} from "../../../wallets/IBitcoinWallet";
25
25
  import {checkTransactionReplaced} from "../../../utils/BitcoinUtils";
26
+ import {isQuoteThrow} from "../../../plugins/IPlugin";
27
+ import {FromBtcLnAutoSwapState} from "../frombtcln_autoinit/FromBtcLnAutoSwap";
26
28
 
27
29
  const OUTPUT_SCRIPT_MAX_LENGTH = 200;
28
30
 
@@ -435,9 +437,33 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
435
437
  }
436
438
 
437
439
  if(swap.state===ToBtcSwapState.COMMITED) {
438
- const unlock: () => boolean = swap.lock(60);
440
+ const unlock: () => boolean = swap.lock(Infinity);
439
441
  if(unlock==null) return;
440
442
 
443
+ const pluginCheckResult = await PluginManager.onHandlePreToBtcExecute(
444
+ SwapHandlerType.TO_BTC,
445
+ swap
446
+ );
447
+ if(isQuoteThrow(pluginCheckResult)) {
448
+ const error = {
449
+ code: 29999,
450
+ msg: pluginCheckResult.message
451
+ };
452
+ this.swapLogger.error(swap, "processInitialized(state=COMMITED): setting state to NON_PAYABLE due to send bitcoin payment error", error);
453
+ if(swap.metadata!=null) swap.metadata.payError = error;
454
+ unlock();
455
+ if(swap.state===ToBtcSwapState.COMMITED) {
456
+ await swap.setState(ToBtcSwapState.NON_PAYABLE);
457
+ await this.saveSwapData(swap);
458
+ }
459
+ return;
460
+ }
461
+
462
+ if(swap.state!==ToBtcSwapState.COMMITED) {
463
+ unlock();
464
+ return;
465
+ }
466
+
441
467
  this.swapLogger.debug(swap, "processInitialized(state=COMMITED): sending bitcoin transaction, address: "+swap.address);
442
468
 
443
469
  try {
@@ -453,9 +479,9 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
453
479
  this.swapLogger.error(swap, "processInitialized(state=COMMITED): send bitcoin payment error", e);
454
480
  throw e;
455
481
  }
482
+ } finally {
483
+ unlock();
456
484
  }
457
-
458
- unlock();
459
485
  }
460
486
 
461
487
  if(swap.state===ToBtcSwapState.NON_PAYABLE) return;