@atomiqlabs/lp-lib 10.3.11 → 11.0.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 (97) hide show
  1. package/dist/index.d.ts +3 -1
  2. package/dist/index.js +3 -4
  3. package/dist/plugins/IPlugin.d.ts +3 -2
  4. package/dist/plugins/PluginManager.d.ts +3 -2
  5. package/dist/plugins/PluginManager.js +2 -2
  6. package/dist/prices/OKXSwapPrice.d.ts +27 -0
  7. package/dist/prices/OKXSwapPrice.js +106 -0
  8. package/dist/swaps/FromBtcBaseSwap.d.ts +5 -1
  9. package/dist/swaps/FromBtcBaseSwap.js +20 -0
  10. package/dist/swaps/FromBtcBaseSwapHandler.d.ts +1 -0
  11. package/dist/swaps/FromBtcBaseSwapHandler.js +1 -1
  12. package/dist/swaps/FromBtcLnBaseSwapHandler.d.ts +8 -6
  13. package/dist/swaps/FromBtcLnBaseSwapHandler.js +7 -5
  14. package/dist/swaps/SwapHandler.d.ts +1 -4
  15. package/dist/swaps/SwapHandler.js +1 -2
  16. package/dist/swaps/SwapHandlerSwap.d.ts +4 -0
  17. package/dist/swaps/SwapHandlerSwap.js +9 -1
  18. package/dist/swaps/ToBtcBaseSwap.d.ts +3 -1
  19. package/dist/swaps/ToBtcBaseSwap.js +8 -2
  20. package/dist/swaps/ToBtcBaseSwapHandler.d.ts +1 -0
  21. package/dist/swaps/ToBtcBaseSwapHandler.js +1 -1
  22. package/dist/swaps/frombtc_abstract/FromBtcAbs.d.ts +3 -5
  23. package/dist/swaps/frombtc_abstract/FromBtcAbs.js +18 -25
  24. package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.d.ts +1 -4
  25. package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.js +3 -16
  26. package/dist/swaps/frombtc_trusted/FromBtcTrusted.d.ts +6 -9
  27. package/dist/swaps/frombtc_trusted/FromBtcTrusted.js +238 -137
  28. package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.d.ts +9 -6
  29. package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.js +15 -10
  30. package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.d.ts +2 -2
  31. package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.js +42 -62
  32. package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +1 -6
  33. package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.js +2 -14
  34. package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.d.ts +3 -5
  35. package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.js +90 -87
  36. package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +1 -2
  37. package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.js +5 -8
  38. package/dist/swaps/tobtc_abstract/ToBtcAbs.d.ts +5 -125
  39. package/dist/swaps/tobtc_abstract/ToBtcAbs.js +41 -334
  40. package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.d.ts +1 -4
  41. package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.js +2 -11
  42. package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.d.ts +5 -55
  43. package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.js +152 -398
  44. package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +1 -6
  45. package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.js +2 -15
  46. package/dist/utils/Utils.d.ts +0 -10
  47. package/dist/utils/Utils.js +1 -34
  48. package/dist/wallets/IBitcoinWallet.d.ts +62 -0
  49. package/dist/wallets/IBitcoinWallet.js +2 -0
  50. package/dist/wallets/ILightningWallet.d.ts +118 -0
  51. package/dist/wallets/ILightningWallet.js +37 -0
  52. package/package.json +4 -9
  53. package/src/index.ts +4 -5
  54. package/src/plugins/IPlugin.ts +4 -2
  55. package/src/plugins/PluginManager.ts +6 -3
  56. package/src/prices/OKXSwapPrice.ts +114 -0
  57. package/src/swaps/FromBtcBaseSwap.ts +24 -1
  58. package/src/swaps/FromBtcBaseSwapHandler.ts +6 -2
  59. package/src/swaps/FromBtcLnBaseSwapHandler.ts +22 -6
  60. package/src/swaps/SwapHandler.ts +1 -8
  61. package/src/swaps/SwapHandlerSwap.ts +14 -1
  62. package/src/swaps/ToBtcBaseSwap.ts +12 -3
  63. package/src/swaps/ToBtcBaseSwapHandler.ts +6 -2
  64. package/src/swaps/frombtc_abstract/FromBtcAbs.ts +24 -28
  65. package/src/swaps/frombtc_abstract/FromBtcSwapAbs.ts +3 -18
  66. package/src/swaps/frombtc_trusted/FromBtcTrusted.ts +260 -159
  67. package/src/swaps/frombtc_trusted/FromBtcTrustedSwap.ts +22 -15
  68. package/src/swaps/frombtcln_abstract/FromBtcLnAbs.ts +69 -79
  69. package/src/swaps/frombtcln_abstract/FromBtcLnSwapAbs.ts +3 -20
  70. package/src/swaps/frombtcln_trusted/FromBtcLnTrusted.ts +108 -103
  71. package/src/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.ts +6 -9
  72. package/src/swaps/tobtc_abstract/ToBtcAbs.ts +52 -410
  73. package/src/swaps/tobtc_abstract/ToBtcSwapAbs.ts +3 -18
  74. package/src/swaps/tobtcln_abstract/ToBtcLnAbs.ts +157 -434
  75. package/src/swaps/tobtcln_abstract/ToBtcLnSwapAbs.ts +3 -20
  76. package/src/utils/Utils.ts +0 -31
  77. package/src/wallets/IBitcoinWallet.ts +66 -0
  78. package/src/wallets/ILightningWallet.ts +179 -0
  79. package/dist/fees/OneDollarFeeEstimator.d.ts +0 -16
  80. package/dist/fees/OneDollarFeeEstimator.js +0 -71
  81. package/dist/utils/coinselect2/accumulative.d.ts +0 -6
  82. package/dist/utils/coinselect2/accumulative.js +0 -44
  83. package/dist/utils/coinselect2/blackjack.d.ts +0 -6
  84. package/dist/utils/coinselect2/blackjack.js +0 -41
  85. package/dist/utils/coinselect2/index.d.ts +0 -16
  86. package/dist/utils/coinselect2/index.js +0 -40
  87. package/dist/utils/coinselect2/utils.d.ts +0 -64
  88. package/dist/utils/coinselect2/utils.js +0 -121
  89. package/src/fees/OneDollarFeeEstimator.ts +0 -95
  90. package/src/utils/coinselect2/accumulative.js +0 -32
  91. package/src/utils/coinselect2/accumulative.ts +0 -58
  92. package/src/utils/coinselect2/blackjack.js +0 -29
  93. package/src/utils/coinselect2/blackjack.ts +0 -54
  94. package/src/utils/coinselect2/index.js +0 -16
  95. package/src/utils/coinselect2/index.ts +0 -50
  96. package/src/utils/coinselect2/utils.js +0 -110
  97. package/src/utils/coinselect2/utils.ts +0 -183
@@ -1,7 +1,5 @@
1
1
  import {Express, Request, Response} from "express";
2
2
  import * as BN from "bn.js";
3
- import * as bitcoin from "bitcoinjs-lib";
4
- import * as lncli from "ln-service";
5
3
  import {ToBtcSwapAbs, ToBtcSwapState} from "./ToBtcSwapAbs";
6
4
  import {MultichainData, SwapHandlerType} from "../SwapHandler";
7
5
  import {ISwapPrice} from "../ISwapPrice";
@@ -16,13 +14,9 @@ import {
16
14
  BitcoinRpc,
17
15
  BtcBlock
18
16
  } from "@atomiqlabs/base";
19
- import {AuthenticatedLnd} from "lightning";
20
17
  import {expressHandlerWrapper, HEX_REGEX, isDefinedRuntimeError} from "../../utils/Utils";
21
18
  import {PluginManager} from "../../plugins/PluginManager";
22
19
  import {IIntermediaryStorage} from "../../storage/IIntermediaryStorage";
23
- import {IBtcFeeEstimator} from "../../fees/IBtcFeeEstimator";
24
- import {coinSelect} from "../../utils/coinselect2";
25
- import {CoinselectTxInput, CoinselectTxOutput, utils} from "../../utils/coinselect2/utils";
26
20
  import {randomBytes} from "crypto";
27
21
  import {FieldTypeEnum, verifySchema} from "../../utils/paramcoders/SchemaVerifier";
28
22
  import {serverParamDecoder} from "../../utils/paramcoders/server/ServerParamDecoder";
@@ -30,36 +24,22 @@ import {IParamReader} from "../../utils/paramcoders/IParamReader";
30
24
  import {ServerParamEncoder} from "../../utils/paramcoders/server/ServerParamEncoder";
31
25
  import {ToBtcBaseConfig, ToBtcBaseSwapHandler} from "../ToBtcBaseSwapHandler";
32
26
  import {PromiseQueue} from "promise-queue-ts";
27
+ import {IBitcoinWallet} from "../../wallets/IBitcoinWallet";
33
28
 
34
29
  const OUTPUT_SCRIPT_MAX_LENGTH = 200;
35
30
 
36
- type SpendableUtxo = {
37
- address: string,
38
- address_format: string,
39
- confirmation_count: number,
40
- output_script: string,
41
- tokens: number,
42
- transaction_id: string,
43
- transaction_vout: number
44
- };
45
-
46
31
  export type ToBtcConfig = ToBtcBaseConfig & {
47
32
  sendSafetyFactor: BN,
48
33
 
49
- bitcoinNetwork: bitcoin.networks.Network,
50
-
51
34
  minChainCltv: BN,
52
35
 
53
- networkFeeMultiplierPPM: BN,
36
+ networkFeeMultiplier: number,
54
37
  minConfirmations: number,
55
38
  maxConfirmations: number,
56
39
  maxConfTarget: number,
57
40
  minConfTarget: number,
58
41
 
59
- txCheckInterval: number,
60
-
61
- feeEstimator?: IBtcFeeEstimator,
62
- onchainReservedPerChannel?: number
42
+ txCheckInterval: number
63
43
  };
64
44
 
65
45
  export type ToBtcRequestType = {
@@ -77,28 +57,11 @@ export type ToBtcRequestType = {
77
57
  * Handler for to BTC swaps, utilizing PTLCs (proof-time locked contracts) using btc relay (on-chain bitcoin SPV)
78
58
  */
79
59
  export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState> {
80
- protected readonly CONFIRMATIONS_REQUIRED = 1;
81
- protected readonly ADDRESS_FORMAT_MAP = {
82
- "p2wpkh": "p2wpkh",
83
- "np2wpkh": "p2sh-p2wpkh",
84
- "p2tr" : "p2tr"
85
- };
86
- protected readonly LND_CHANGE_OUTPUT_TYPE = "p2tr";
87
- protected readonly UTXO_CACHE_TIMEOUT = 5*1000;
88
- protected readonly CHANNEL_COUNT_CACHE_TIMEOUT = 30*1000;
89
-
90
60
  readonly type = SwapHandlerType.TO_BTC;
91
61
 
92
62
  activeSubscriptions: {[txId: string]: ToBtcSwapAbs} = {};
93
- cachedUtxos: {
94
- utxos: (CoinselectTxInput & {confirmations: number})[],
95
- timestamp: number
96
- };
97
- cachedChannelCount: {
98
- count: number,
99
- timestamp: number
100
- };
101
63
  bitcoinRpc: BitcoinRpc<BtcBlock>;
64
+ bitcoin: IBitcoinWallet;
102
65
  sendBtcQueue: PromiseQueue = new PromiseQueue();
103
66
 
104
67
  readonly config: ToBtcConfig;
@@ -107,15 +70,15 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
107
70
  storageDirectory: IIntermediaryStorage<ToBtcSwapAbs>,
108
71
  path: string,
109
72
  chainData: MultichainData,
110
- lnd: AuthenticatedLnd,
73
+ bitcoin: IBitcoinWallet,
111
74
  swapPricing: ISwapPrice,
112
75
  bitcoinRpc: BitcoinRpc<BtcBlock>,
113
76
  config: ToBtcConfig
114
77
  ) {
115
- super(storageDirectory, path, chainData, lnd, swapPricing);
78
+ super(storageDirectory, path, chainData, swapPricing);
116
79
  this.bitcoinRpc = bitcoinRpc;
80
+ this.bitcoin = bitcoin;
117
81
  this.config = config;
118
- this.config.onchainReservedPerChannel = this.config.onchainReservedPerChannel || 40000;
119
82
  }
120
83
 
121
84
  /**
@@ -125,217 +88,18 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
125
88
  * @param address
126
89
  * @param nonce
127
90
  * @param amount
128
- * @param bitcoinNetwork
129
91
  */
130
- private getHash(chainIdentifier: string, address: string, nonce: BN, amount: BN, bitcoinNetwork: bitcoin.networks.Network): Buffer {
131
- const parsedOutputScript = bitcoin.address.toOutputScript(address, bitcoinNetwork);
92
+ private getHash(chainIdentifier: string, address: string, nonce: BN, amount: BN): Buffer {
93
+ const parsedOutputScript = this.bitcoin.toOutputScript(address);
132
94
  const {swapContract} = this.getChain(chainIdentifier);
133
95
  return swapContract.getHashForOnchain(parsedOutputScript, amount, nonce);
134
96
  }
135
97
 
136
- /**
137
- * Returns spendable UTXOs, these are either confirmed UTXOs, or unconfirmed ones that are either whitelisted,
138
- * or created by our transactions (and therefore only we could doublespend)
139
- *
140
- * @private
141
- */
142
- protected async getSpendableUtxos(): Promise<SpendableUtxo[]> {
143
- const resBlockheight = await lncli.getHeight({
144
- lnd: this.LND
145
- });
146
-
147
- const blockheight: number = resBlockheight.current_block_height;
148
-
149
- const resChainTxns = await lncli.getChainTransactions({
150
- lnd: this.LND,
151
- after: blockheight-this.CONFIRMATIONS_REQUIRED
152
- });
153
-
154
- const selfUTXOs: Set<string> = PluginManager.getWhitelistedTxIds();
155
-
156
- const transactions = resChainTxns.transactions;
157
- for(let tx of transactions) {
158
- if(tx.is_outgoing) {
159
- selfUTXOs.add(tx.id);
160
- }
161
- }
162
-
163
- const resUtxos = await lncli.getUtxos({
164
- lnd: this.LND
165
- });
166
-
167
- return resUtxos.utxos.filter(utxo => utxo.confirmation_count>=this.CONFIRMATIONS_REQUIRED || selfUTXOs.has(utxo.transaction_id));
168
- }
169
-
170
- /**
171
- * Returns utxo pool to be used by the coinselection algorithm
172
- *
173
- * @private
174
- */
175
- protected async getUtxoPool(useCached: boolean = false): Promise<(CoinselectTxInput & {confirmations: number})[]> {
176
- if(!useCached || this.cachedUtxos==null || this.cachedUtxos.timestamp<Date.now()-this.UTXO_CACHE_TIMEOUT) {
177
- const utxos = await this.getSpendableUtxos();
178
-
179
- let totalSpendable = 0;
180
- const utxoPool = utxos.map(utxo => {
181
- totalSpendable += utxo.tokens;
182
- return {
183
- vout: utxo.transaction_vout,
184
- txId: utxo.transaction_id,
185
- value: utxo.tokens,
186
- type: this.ADDRESS_FORMAT_MAP[utxo.address_format],
187
- outputScript: Buffer.from(utxo.output_script, "hex"),
188
- address: utxo.address,
189
- confirmations: utxo.confirmation_count
190
- };
191
- });
192
-
193
- this.cachedUtxos = {
194
- utxos: utxoPool,
195
- timestamp: Date.now()
196
- };
197
-
198
- this.logger.info("getUtxoPool(): total spendable value: "+totalSpendable+" num utxos: "+utxoPool.length);
199
- }
200
-
201
- return this.cachedUtxos.utxos;
202
- }
203
-
204
- /**
205
- * Checks whether a coinselect result leaves enough funds to cover potential lightning anchor transaction fees
206
- *
207
- * @param utxoPool
208
- * @param obj
209
- * @param satsPerVbyte
210
- * @param useCached Whether to use a cached channel count
211
- * @param initialOutputLength
212
- * @private
213
- * @returns true if alright, false if the coinselection doesn't leave enough funds for anchor fees
214
- */
215
- protected async isLeavingEnoughForLightningAnchors(
216
- utxoPool: CoinselectTxInput[],
217
- obj: {inputs?: CoinselectTxInput[], outputs?: CoinselectTxOutput[]},
218
- satsPerVbyte: BN,
219
- useCached: boolean = false,
220
- initialOutputLength: number = 1
221
- ): Promise<boolean> {
222
- if(obj.inputs==null || obj.outputs==null) return false;
223
- const spentInputs = new Set<string>();
224
- obj.inputs.forEach(txIn => {
225
- spentInputs.add(txIn.txId+":"+txIn.vout);
226
- });
227
-
228
- let leavesValue: BN = new BN(0);
229
- utxoPool.forEach(val => {
230
- const utxoEconomicalValue: BN = new BN(val.value).sub(satsPerVbyte.mul(new BN(utils.inputBytes(val).length)));
231
- if (
232
- //Utxo not spent
233
- !spentInputs.has(val.txId + ":" + val.vout) &&
234
- //Only economical utxos at current fees
235
- !utxoEconomicalValue.isNeg()
236
- ) {
237
- leavesValue = leavesValue.add(utxoEconomicalValue);
238
- }
239
- });
240
- if(obj.outputs.length>initialOutputLength) {
241
- const changeUtxo = obj.outputs[obj.outputs.length-1];
242
- leavesValue = leavesValue.add(
243
- new BN(changeUtxo.value).sub(satsPerVbyte.mul(new BN(utils.inputBytes(changeUtxo).length)))
244
- );
245
- }
246
-
247
- if(!useCached || this.cachedChannelCount==null || this.cachedChannelCount.timestamp<Date.now()-this.CHANNEL_COUNT_CACHE_TIMEOUT) {
248
- const {channels} = await lncli.getChannels({lnd: this.LND});
249
- this.cachedChannelCount = {
250
- count: channels.length,
251
- timestamp: Date.now()
252
- }
253
- }
254
-
255
- return leavesValue.gt(new BN(this.config.onchainReservedPerChannel).mul(new BN(this.cachedChannelCount.count)));
256
- }
257
-
258
- /**
259
- * Gets the change address from the underlying LND instance
260
- *
261
- * @private
262
- */
263
- protected getChangeAddress(): Promise<string> {
264
- return new Promise((resolve, reject) => {
265
- this.LND.wallet.nextAddr({
266
- type: 4,
267
- change: true
268
- }, (err, res) => {
269
- if(err!=null) {
270
- reject([503, 'UnexpectedErrGettingNextAddr', {err}]);
271
- return;
272
- }
273
- resolve(res.addr);
274
- });
275
- });
276
- }
277
-
278
- /**
279
- * Computes bitcoin on-chain network fee, takes channel reserve & network fee multiplier into consideration
280
- *
281
- * @param targetAddress Bitcoin address to send the funds to
282
- * @param targetAmount Amount of funds to send to the address
283
- * @param estimate Whether the chain fee should be just estimated and therefore cached utxo set could be used
284
- * @param multiplierPPM Multiplier for the sats/vB returned from the fee estimator in PPM (parts per million)
285
- * @private
286
- * @returns Fee estimate & inputs/outputs to use when constructing transaction, or null in case of not enough funds
287
- */
288
- private async getChainFee(targetAddress: string, targetAmount: number, estimate: boolean = false, multiplierPPM?: BN): Promise<{
289
- satsPerVbyte: BN,
290
- networkFee: BN,
291
- inputs: CoinselectTxInput[],
292
- outputs: CoinselectTxOutput[]
293
- } | null> {
294
- let feeRate: number | null = this.config.feeEstimator==null
295
- ? await lncli.getChainFeeRate({lnd: this.LND})
296
- .then(res => res.tokens_per_vbyte)
297
- .catch(e => this.logger.error("getChainFee(): LND getChainFeeRate error", e))
298
- : await this.config.feeEstimator.estimateFee();
299
-
300
- if(feeRate==null) return null;
301
-
302
- let satsPerVbyte = new BN(Math.ceil(feeRate));
303
- if(multiplierPPM!=null) satsPerVbyte = satsPerVbyte.mul(multiplierPPM).div(new BN(1000000));
304
-
305
- const utxoPool: CoinselectTxInput[] = await this.getUtxoPool(estimate);
306
-
307
- let obj = coinSelect(utxoPool, [{
308
- address: targetAddress,
309
- value: targetAmount,
310
- script: bitcoin.address.toOutputScript(targetAddress, this.config.bitcoinNetwork)
311
- }], satsPerVbyte.toNumber(), this.LND_CHANGE_OUTPUT_TYPE);
312
-
313
- if(obj.inputs==null || obj.outputs==null) return null;
314
-
315
- if(!await this.isLeavingEnoughForLightningAnchors(utxoPool, obj, satsPerVbyte, estimate)) return null;
316
-
317
- this.logger.info("getChainFee(): fee estimated,"+
318
- " target: "+targetAddress+
319
- " amount: "+targetAmount.toString(10)+
320
- " fee: "+obj.fee+
321
- " sats/vB: "+satsPerVbyte+
322
- " inputs: "+obj.inputs.length+
323
- " outputs: "+obj.outputs.length+
324
- " multiplier: "+(multiplierPPM==null ? 1 : multiplierPPM.toNumber()/1000000));
325
-
326
- return {
327
- networkFee: new BN(obj.fee),
328
- satsPerVbyte,
329
- outputs: obj.outputs,
330
- inputs: obj.inputs
331
- };
332
- }
333
-
334
98
  /**
335
99
  * Tries to claim the swap after our transaction was confirmed
336
100
  *
337
101
  * @param tx
338
- * @param payment
102
+ * @param swap
339
103
  * @param vout
340
104
  */
341
105
  private async tryClaimSwap(tx: {blockhash: string, confirmations: number, txid: string, hex: string}, swap: ToBtcSwapAbs, vout: number): Promise<boolean> {
@@ -365,10 +129,8 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
365
129
  protected async processPastSwap(swap: ToBtcSwapAbs) {
366
130
  const {swapContract, signer} = this.getChain(swap.chainIdentifier);
367
131
 
368
- const timestamp = new BN(Math.floor(Date.now()/1000)).sub(new BN(this.config.maxSkew));
369
-
370
- if(swap.state===ToBtcSwapState.SAVED && swap.signatureExpiry!=null) {
371
- const isSignatureExpired = swap.signatureExpiry.lt(timestamp);
132
+ if(swap.state===ToBtcSwapState.SAVED) {
133
+ const isSignatureExpired = swapContract.isInitAuthorizationExpired(swap.data, swap);
372
134
  if(isSignatureExpired) {
373
135
  const isCommitted = await swapContract.isCommited(swap.data);
374
136
  if(!isCommitted) {
@@ -384,8 +146,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
384
146
  }
385
147
 
386
148
  if(swap.state===ToBtcSwapState.NON_PAYABLE || swap.state===ToBtcSwapState.SAVED) {
387
- const isSwapExpired = swap.data.getExpiry().lt(timestamp);
388
- if(isSwapExpired) {
149
+ if(swapContract.isExpired(signer.getAddress(), swap.data)) {
389
150
  this.swapLogger.info(swap, "processPastSwap(state=NON_PAYABLE|SAVED): swap expired, cancelling, address: "+swap.address);
390
151
  await this.removeSwapData(swap, ToBtcSwapState.CANCELED);
391
152
  return;
@@ -450,7 +211,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
450
211
  this.swapLogger.debug(swap, "processBtcTx(): address: "+swap.address+" amount: "+swap.amount.toString(10)+" btcTx: "+tx);
451
212
 
452
213
  //Search for required transaction output (vout)
453
- const outputScript = bitcoin.address.toOutputScript(swap.address, this.config.bitcoinNetwork);
214
+ const outputScript = this.bitcoin.toOutputScript(swap.address);
454
215
  const vout = tx.outs.find(e => new BN(e.value).eq(swap.amount) && Buffer.from(e.scriptPubKey.hex, "hex").equals(outputScript));
455
216
  if(vout==null) {
456
217
  this.swapLogger.warn(swap, "processBtcTx(): cannot find correct vout,"+
@@ -477,7 +238,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
477
238
  const swap: ToBtcSwapAbs = this.activeSubscriptions[txId];
478
239
  //TODO: RBF the transaction if it's already taking too long to confirm
479
240
  try {
480
- let tx: BtcTx = await this.bitcoinRpc.getTransaction(txId);
241
+ let tx: BtcTx = await this.bitcoin.getWalletTransaction(txId);
481
242
  if(tx==null) continue;
482
243
 
483
244
  if(await this.processBtcTx(swap, tx)) {
@@ -549,133 +310,12 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
549
310
  code: 90003,
550
311
  msg: "Fee changed too much!",
551
312
  data: {
552
- quotedFee: actualSatsPerVbyte.toString(10),
553
- actualFee: quotedSatsPerVbyte.toString(10)
313
+ quotedFee: quotedSatsPerVbyte.toString(10),
314
+ actualFee: actualSatsPerVbyte.toString(10)
554
315
  }
555
316
  };
556
317
  }
557
318
 
558
- /**
559
- * Runs sanity check on the calculated fee for the transaction
560
- *
561
- * @param psbt
562
- * @param tx
563
- * @param maxAllowedSatsPerVbyte
564
- * @param actualSatsPerVbyte
565
- * @private
566
- * @throws {Error} Will throw an error if the fee sanity check doesn't pass
567
- */
568
- protected checkPsbtFee(
569
- psbt: bitcoin.Psbt,
570
- tx: bitcoin.Transaction,
571
- maxAllowedSatsPerVbyte: BN,
572
- actualSatsPerVbyte: BN
573
- ): BN {
574
- const txFee = new BN(psbt.getFee());
575
-
576
- //Sanity check on sats/vB
577
- const maxAllowedFee = new BN(tx.virtualSize())
578
- //Considering the extra output was not added, because was detrminetal
579
- .add(new BN(utils.outputBytes({type: this.LND_CHANGE_OUTPUT_TYPE})))
580
- //Multiply by maximum allowed feerate
581
- .mul(maxAllowedSatsPerVbyte)
582
- //Possibility that extra output was not added due to it being lower than dust
583
- .add(new BN(utils.dustThreshold({type: this.LND_CHANGE_OUTPUT_TYPE})));
584
-
585
- if(txFee.gt(maxAllowedFee)) throw new Error("Generated tx fee too high: "+JSON.stringify({
586
- maxAllowedFee: maxAllowedFee.toString(10),
587
- actualFee: txFee.toString(10),
588
- psbtHex: psbt.toHex(),
589
- maxAllowedSatsPerVbyte: maxAllowedSatsPerVbyte.toString(10),
590
- actualSatsPerVbyte: actualSatsPerVbyte.toString(10)
591
- }));
592
-
593
- return txFee;
594
- }
595
-
596
- /**
597
- * Create PSBT for swap payout from coinselection result
598
- *
599
- * @param address
600
- * @param amount
601
- * @param escrowNonce
602
- * @param coinselectResult
603
- * @private
604
- */
605
- private async getPsbt(
606
- address: string,
607
- amount: BN,
608
- escrowNonce: BN,
609
- coinselectResult: {inputs: CoinselectTxInput[], outputs: CoinselectTxOutput[]}
610
- ): Promise<bitcoin.Psbt> {
611
- let psbt = new bitcoin.Psbt();
612
-
613
- //Apply nonce
614
- const nonceBuffer = Buffer.from(escrowNonce.toArray("be", 8));
615
-
616
- const locktimeBN = new BN(nonceBuffer.slice(0, 5), "be");
617
- let locktime = locktimeBN.toNumber() + 500000000;
618
- psbt.setLocktime(locktime);
619
-
620
- const sequenceBN = new BN(nonceBuffer.slice(5, 8), "be");
621
- const sequence = 0xFE000000 + sequenceBN.toNumber();
622
- psbt.addInputs(coinselectResult.inputs.map(input => {
623
- return {
624
- hash: input.txId,
625
- index: input.vout,
626
- witnessUtxo: {
627
- script: input.outputScript,
628
- value: input.value
629
- },
630
- sighashType: 0x01,
631
- sequence
632
- };
633
- }));
634
-
635
- psbt.addOutput({
636
- script: bitcoin.address.toOutputScript(address, this.config.bitcoinNetwork),
637
- value: amount.toNumber()
638
- });
639
-
640
- //Add change output
641
- if(coinselectResult.outputs.length>1) psbt.addOutput({
642
- script: bitcoin.address.toOutputScript(await this.getChangeAddress(), this.config.bitcoinNetwork),
643
- value: coinselectResult.outputs[1].value
644
- });
645
-
646
- return psbt;
647
- }
648
-
649
- /**
650
- * Signs provided PSBT and also returns a raw signed transaction
651
- *
652
- * @param psbt
653
- * @private
654
- */
655
- protected async signPsbt(psbt: bitcoin.Psbt): Promise<{psbt: bitcoin.Psbt, rawTx: string}> {
656
- const signedPsbt = await lncli.signPsbt({
657
- lnd: this.LND,
658
- psbt: psbt.toHex()
659
- });
660
- return {
661
- psbt: bitcoin.Psbt.fromHex(signedPsbt.psbt),
662
- rawTx: signedPsbt.transaction
663
- };
664
- }
665
-
666
- /**
667
- * Sends raw bitcoin transaction
668
- *
669
- * @param rawTx
670
- * @private
671
- */
672
- protected async sendRawTransaction(rawTx: string): Promise<void> {
673
- await lncli.broadcastChainTransaction({
674
- lnd: this.LND,
675
- transaction: rawTx
676
- });
677
- }
678
-
679
319
  /**
680
320
  * Sends a bitcoin transaction to payout BTC for a swap
681
321
  *
@@ -691,37 +331,32 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
691
331
  this.checkExpiresTooSoon(swap);
692
332
  if(swap.metadata!=null) swap.metadata.times.payCLTVChecked = Date.now();
693
333
 
694
- const coinselectResult = await this.getChainFee(swap.address, swap.amount.toNumber());
695
- if(coinselectResult==null) throw {
696
- code: 90002,
697
- msg: "Failed to run coinselect algorithm (not enough funds?)"
698
- }
334
+ const satsPerVbyte = await this.bitcoin.getFeeRate();
335
+ this.checkCalculatedTxFee(swap.satsPerVbyte, new BN(satsPerVbyte));
699
336
  if(swap.metadata!=null) swap.metadata.times.payChainFee = Date.now();
700
337
 
701
- this.checkCalculatedTxFee(swap.satsPerVbyte, coinselectResult.satsPerVbyte);
702
-
703
- //Construct payment PSBT
704
- let unsignedPsbt = await this.getPsbt(swap.address, swap.amount, swap.data.getEscrowNonce(), coinselectResult);
705
- this.swapLogger.debug(swap, "sendBitcoinPayment(): generated psbt: "+unsignedPsbt.toHex());
706
-
707
- //Sign the PSBT
708
- const {psbt, rawTx} = await this.signPsbt(unsignedPsbt);
338
+ const signResult = await this.bitcoin.getSignedTransaction(
339
+ swap.address,
340
+ swap.amount.toNumber(),
341
+ satsPerVbyte,
342
+ swap.data.getEscrowNonce(),
343
+ swap.satsPerVbyte.toNumber()
344
+ );
345
+ if(signResult==null) throw {
346
+ code: 90002,
347
+ msg: "Failed to create signed transaction (not enough funds?)"
348
+ }
709
349
  if(swap.metadata!=null) swap.metadata.times.paySignPSBT = Date.now();
710
- this.swapLogger.debug(swap, "sendBitcoinPayment(): signed raw transaction: "+rawTx);
711
350
 
712
- const tx = bitcoin.Transaction.fromHex(rawTx);
713
- const txFee = this.checkPsbtFee(psbt, tx, swap.satsPerVbyte, coinselectResult.satsPerVbyte);
714
-
715
- swap.txId = tx.getId();
716
- swap.setRealNetworkFee(txFee);
351
+ this.swapLogger.debug(swap, "sendBitcoinPayment(): signed raw transaction: "+signResult.raw);
352
+ swap.txId = signResult.tx.getId();
353
+ swap.setRealNetworkFee(new BN(signResult.networkFee));
717
354
  await swap.setState(ToBtcSwapState.BTC_SENDING);
718
355
  await this.storageManager.saveData(swap.getHash(), swap.getSequence(), swap);
719
356
 
720
- await this.sendRawTransaction(rawTx);
357
+ await this.bitcoin.sendRawTransaction(signResult.raw);
721
358
  if(swap.metadata!=null) swap.metadata.times.payTxSent = Date.now();
722
- this.swapLogger.info(swap, "sendBitcoinPayment(): btc transaction generated, signed & broadcasted, txId: "+tx.getId()+" address: "+swap.address);
723
- //Invalidate the UTXO cache
724
- this.cachedUtxos = null;
359
+ this.swapLogger.info(swap, "sendBitcoinPayment(): btc transaction generated, signed & broadcasted, txId: "+signResult.tx.getId()+" address: "+swap.address);
725
360
 
726
361
  await swap.setState(ToBtcSwapState.BTC_SENT);
727
362
  await this.storageManager.saveData(swap.getHash(), swap.getSequence(), swap);
@@ -736,7 +371,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
736
371
  private async processInitialized(swap: ToBtcSwapAbs) {
737
372
  if(swap.state===ToBtcSwapState.BTC_SENDING) {
738
373
  //Bitcoin transaction was signed (maybe also sent)
739
- const tx = await this.bitcoinRpc.getTransaction(swap.txId);
374
+ const tx = await this.bitcoin.getWalletTransaction(swap.txId);
740
375
 
741
376
  const isTxSent = tx!=null;
742
377
  if(!isTxSent) {
@@ -913,7 +548,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
913
548
  let parsedOutputScript: Buffer;
914
549
 
915
550
  try {
916
- parsedOutputScript = bitcoin.address.toOutputScript(address, this.config.bitcoinNetwork);
551
+ parsedOutputScript = this.bitcoin.toOutputScript(address);
917
552
  } catch (e) {
918
553
  throw {
919
554
  code: 20031,
@@ -934,7 +569,8 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
934
569
  * @throws {DefinedRuntimeError} will throw an error if the swap is expired
935
570
  */
936
571
  protected checkExpired(swap: ToBtcSwapAbs) {
937
- const isExpired = swap.data.getExpiry().lt(new BN(Math.floor(Date.now()/1000)).sub(new BN(this.config.maxSkew)));
572
+ const {swapContract, signer} = this.getChain(swap.chainIdentifier);
573
+ const isExpired = swapContract.isExpired(signer.getAddress(), swap.data);
938
574
  if(isExpired) throw {
939
575
  _httpStatus: 200,
940
576
  code: 20010,
@@ -950,7 +586,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
950
586
  * @throws {DefinedRuntimeError} will throw an error if there are not enough BTC funds
951
587
  */
952
588
  private async checkAndGetNetworkFee(address: string, amount: BN): Promise<{ networkFee: BN, satsPerVbyte: BN }> {
953
- let chainFeeResp = await this.getChainFee(address, amount.toNumber(), true, this.config.networkFeeMultiplierPPM);
589
+ let chainFeeResp = await this.bitcoin.estimateFee(address, amount.toNumber(), null, this.config.networkFeeMultiplier);
954
590
 
955
591
  const hasEnoughFunds = chainFeeResp!=null;
956
592
  if(!hasEnoughFunds) throw {
@@ -958,7 +594,10 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
958
594
  msg: "Not enough liquidity"
959
595
  };
960
596
 
961
- return chainFeeResp;
597
+ return {
598
+ networkFee: new BN(chainFeeResp.networkFee),
599
+ satsPerVbyte: new BN(chainFeeResp.satsPerVbyte)
600
+ };
962
601
  }
963
602
 
964
603
  startRestServer(restServer: Express) {
@@ -1047,7 +686,7 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
1047
686
  }, abortController.signal, pricePrefetchPromise);
1048
687
  metadata.times.priceCalculated = Date.now();
1049
688
 
1050
- const paymentHash = this.getHash(chainIdentifier, parsedBody.address, parsedBody.nonce, amountBD, this.config.bitcoinNetwork).toString("hex");
689
+ const paymentHash = this.getHash(chainIdentifier, parsedBody.address, parsedBody.nonce, amountBD).toString("hex");
1051
690
 
1052
691
  //Add grace period another time, so the user has 1 hour to commit
1053
692
  const expirySeconds = this.getExpiryFromCLTV(parsedBody.confirmationTarget, parsedBody.confirmations).add(new BN(this.config.gracePeriod));
@@ -1087,11 +726,14 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
1087
726
  networkFeeInToken,
1088
727
  networkFeeData.satsPerVbyte,
1089
728
  parsedBody.nonce,
1090
- parsedBody.confirmationTarget,
1091
- new BN(sigData.timeout)
729
+ parsedBody.confirmationTarget
1092
730
  );
1093
731
  createdSwap.data = payObject;
1094
732
  createdSwap.metadata = metadata;
733
+ createdSwap.prefix = sigData.prefix;
734
+ createdSwap.timeout = sigData.timeout;
735
+ createdSwap.signature = sigData.signature
736
+ createdSwap.feeRate = sigData.feeRate;
1095
737
 
1096
738
  await PluginManager.swapCreate(createdSwap);
1097
739
  await this.storageManager.saveData(paymentHash, sequence, createdSwap);
@@ -1147,8 +789,6 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
1147
789
  msg: "Payment not found"
1148
790
  };
1149
791
 
1150
- const {swapContract, signer} = this.getChain(payment.chainIdentifier);
1151
-
1152
792
  this.checkExpired(payment);
1153
793
 
1154
794
  if (payment.state === ToBtcSwapState.COMMITED) throw {
@@ -1166,6 +806,8 @@ export class ToBtcAbs extends ToBtcBaseSwapHandler<ToBtcSwapAbs, ToBtcSwapState>
1166
806
  }
1167
807
  };
1168
808
 
809
+ const {swapContract, signer} = this.getChain(payment.chainIdentifier);
810
+
1169
811
  if (payment.state === ToBtcSwapState.NON_PAYABLE) {
1170
812
  const isCommited = await swapContract.isCommited(payment.data);
1171
813
  if (!isCommited) throw {