@atomiqlabs/lp-lib 15.0.14 → 16.0.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 (169) hide show
  1. package/LICENSE +201 -201
  2. package/dist/fees/IBtcFeeEstimator.d.ts +3 -3
  3. package/dist/fees/IBtcFeeEstimator.js +2 -2
  4. package/dist/index.d.ts +42 -40
  5. package/dist/index.js +58 -56
  6. package/dist/info/InfoHandler.d.ts +17 -17
  7. package/dist/info/InfoHandler.js +58 -58
  8. package/dist/plugins/IPlugin.d.ts +144 -144
  9. package/dist/plugins/IPlugin.js +34 -34
  10. package/dist/plugins/PluginManager.d.ts +113 -113
  11. package/dist/plugins/PluginManager.js +274 -274
  12. package/dist/prices/BinanceSwapPrice.d.ts +26 -26
  13. package/dist/prices/BinanceSwapPrice.js +92 -92
  14. package/dist/prices/CoinGeckoSwapPrice.d.ts +30 -30
  15. package/dist/prices/CoinGeckoSwapPrice.js +64 -64
  16. package/dist/prices/ISwapPrice.d.ts +43 -43
  17. package/dist/prices/ISwapPrice.js +55 -55
  18. package/dist/prices/OKXSwapPrice.d.ts +26 -26
  19. package/dist/prices/OKXSwapPrice.js +92 -92
  20. package/dist/storage/IIntermediaryStorage.d.ts +18 -18
  21. package/dist/storage/IIntermediaryStorage.js +2 -2
  22. package/dist/storagemanager/IntermediaryStorageManager.d.ts +19 -18
  23. package/dist/storagemanager/IntermediaryStorageManager.js +111 -104
  24. package/dist/storagemanager/StorageManager.d.ts +13 -12
  25. package/dist/storagemanager/StorageManager.js +64 -57
  26. package/dist/swaps/SwapHandler.d.ts +150 -153
  27. package/dist/swaps/SwapHandler.js +154 -157
  28. package/dist/swaps/SwapHandlerSwap.d.ts +79 -79
  29. package/dist/swaps/SwapHandlerSwap.js +78 -78
  30. package/dist/swaps/assertions/AmountAssertions.d.ts +28 -28
  31. package/dist/swaps/assertions/AmountAssertions.js +74 -74
  32. package/dist/swaps/assertions/FromBtcAmountAssertions.d.ts +76 -76
  33. package/dist/swaps/assertions/FromBtcAmountAssertions.js +180 -172
  34. package/dist/swaps/assertions/LightningAssertions.d.ts +44 -44
  35. package/dist/swaps/assertions/LightningAssertions.js +86 -86
  36. package/dist/swaps/assertions/ToBtcAmountAssertions.d.ts +53 -53
  37. package/dist/swaps/assertions/ToBtcAmountAssertions.js +150 -150
  38. package/dist/swaps/escrow/EscrowHandler.d.ts +51 -51
  39. package/dist/swaps/escrow/EscrowHandler.js +158 -158
  40. package/dist/swaps/escrow/EscrowHandlerSwap.d.ts +35 -35
  41. package/dist/swaps/escrow/EscrowHandlerSwap.js +69 -69
  42. package/dist/swaps/escrow/FromBtcBaseSwap.d.ts +14 -14
  43. package/dist/swaps/escrow/FromBtcBaseSwap.js +32 -32
  44. package/dist/swaps/escrow/FromBtcBaseSwapHandler.d.ts +102 -101
  45. package/dist/swaps/escrow/FromBtcBaseSwapHandler.js +210 -207
  46. package/dist/swaps/escrow/ToBtcBaseSwap.d.ts +36 -36
  47. package/dist/swaps/escrow/ToBtcBaseSwap.js +67 -67
  48. package/dist/swaps/escrow/ToBtcBaseSwapHandler.d.ts +53 -53
  49. package/dist/swaps/escrow/ToBtcBaseSwapHandler.js +81 -81
  50. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.d.ts +83 -83
  51. package/dist/swaps/escrow/frombtc_abstract/FromBtcAbs.js +318 -318
  52. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.d.ts +21 -21
  53. package/dist/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.js +50 -50
  54. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.d.ts +107 -107
  55. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.js +673 -673
  56. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +33 -32
  57. package/dist/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.js +91 -88
  58. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +111 -0
  59. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +682 -0
  60. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.d.ts +55 -0
  61. package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.js +120 -0
  62. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.d.ts +169 -171
  63. package/dist/swaps/escrow/tobtc_abstract/ToBtcAbs.js +735 -718
  64. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.d.ts +28 -28
  65. package/dist/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.js +64 -64
  66. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.d.ts +177 -177
  67. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +865 -863
  68. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +24 -24
  69. package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.js +58 -58
  70. package/dist/swaps/spv_vault_swap/SpvVault.d.ts +44 -45
  71. package/dist/swaps/spv_vault_swap/SpvVault.js +145 -145
  72. package/dist/swaps/spv_vault_swap/SpvVaultSwap.d.ts +68 -68
  73. package/dist/swaps/spv_vault_swap/SpvVaultSwap.js +158 -158
  74. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.d.ts +68 -68
  75. package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +530 -528
  76. package/dist/swaps/spv_vault_swap/SpvVaults.d.ts +63 -68
  77. package/dist/swaps/spv_vault_swap/SpvVaults.js +488 -454
  78. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.d.ts +51 -51
  79. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrusted.js +650 -650
  80. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.d.ts +52 -52
  81. package/dist/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.js +118 -118
  82. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.d.ts +76 -76
  83. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.js +492 -493
  84. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +34 -34
  85. package/dist/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.js +81 -81
  86. package/dist/utils/BitcoinUtils.d.ts +4 -4
  87. package/dist/utils/BitcoinUtils.js +61 -61
  88. package/dist/utils/Utils.d.ts +29 -29
  89. package/dist/utils/Utils.js +89 -88
  90. package/dist/utils/paramcoders/IParamReader.d.ts +5 -5
  91. package/dist/utils/paramcoders/IParamReader.js +2 -2
  92. package/dist/utils/paramcoders/IParamWriter.d.ts +4 -4
  93. package/dist/utils/paramcoders/IParamWriter.js +2 -2
  94. package/dist/utils/paramcoders/LegacyParamEncoder.d.ts +10 -10
  95. package/dist/utils/paramcoders/LegacyParamEncoder.js +22 -22
  96. package/dist/utils/paramcoders/ParamDecoder.d.ts +25 -25
  97. package/dist/utils/paramcoders/ParamDecoder.js +222 -222
  98. package/dist/utils/paramcoders/ParamEncoder.d.ts +9 -9
  99. package/dist/utils/paramcoders/ParamEncoder.js +22 -22
  100. package/dist/utils/paramcoders/SchemaVerifier.d.ts +21 -21
  101. package/dist/utils/paramcoders/SchemaVerifier.js +84 -84
  102. package/dist/utils/paramcoders/server/ServerParamDecoder.d.ts +8 -8
  103. package/dist/utils/paramcoders/server/ServerParamDecoder.js +107 -105
  104. package/dist/utils/paramcoders/server/ServerParamEncoder.d.ts +11 -11
  105. package/dist/utils/paramcoders/server/ServerParamEncoder.js +65 -65
  106. package/dist/wallets/IBitcoinWallet.d.ts +74 -67
  107. package/dist/wallets/IBitcoinWallet.js +2 -2
  108. package/dist/wallets/ILightningWallet.d.ts +117 -117
  109. package/dist/wallets/ILightningWallet.js +37 -37
  110. package/dist/wallets/ISpvVaultSigner.d.ts +7 -7
  111. package/dist/wallets/ISpvVaultSigner.js +2 -2
  112. package/package.json +36 -36
  113. package/src/fees/IBtcFeeEstimator.ts +6 -6
  114. package/src/index.ts +53 -51
  115. package/src/info/InfoHandler.ts +100 -100
  116. package/src/plugins/IPlugin.ts +174 -174
  117. package/src/plugins/PluginManager.ts +354 -354
  118. package/src/prices/BinanceSwapPrice.ts +113 -113
  119. package/src/prices/CoinGeckoSwapPrice.ts +87 -87
  120. package/src/prices/ISwapPrice.ts +88 -88
  121. package/src/prices/OKXSwapPrice.ts +113 -113
  122. package/src/storage/IIntermediaryStorage.ts +19 -19
  123. package/src/storagemanager/IntermediaryStorageManager.ts +118 -109
  124. package/src/storagemanager/StorageManager.ts +78 -68
  125. package/src/swaps/SwapHandler.ts +269 -272
  126. package/src/swaps/SwapHandlerSwap.ts +141 -141
  127. package/src/swaps/assertions/AmountAssertions.ts +77 -77
  128. package/src/swaps/assertions/FromBtcAmountAssertions.ts +246 -238
  129. package/src/swaps/assertions/LightningAssertions.ts +103 -103
  130. package/src/swaps/assertions/ToBtcAmountAssertions.ts +203 -203
  131. package/src/swaps/escrow/EscrowHandler.ts +179 -179
  132. package/src/swaps/escrow/EscrowHandlerSwap.ts +86 -86
  133. package/src/swaps/escrow/FromBtcBaseSwap.ts +38 -38
  134. package/src/swaps/escrow/FromBtcBaseSwapHandler.ts +286 -283
  135. package/src/swaps/escrow/ToBtcBaseSwap.ts +85 -85
  136. package/src/swaps/escrow/ToBtcBaseSwapHandler.ts +129 -129
  137. package/src/swaps/escrow/frombtc_abstract/FromBtcAbs.ts +452 -452
  138. package/src/swaps/escrow/frombtc_abstract/FromBtcSwapAbs.ts +61 -61
  139. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnAbs.ts +855 -855
  140. package/src/swaps/escrow/frombtcln_abstract/FromBtcLnSwapAbs.ts +141 -137
  141. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +847 -0
  142. package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAutoSwap.ts +196 -0
  143. package/src/swaps/escrow/tobtc_abstract/ToBtcAbs.ts +909 -890
  144. package/src/swaps/escrow/tobtc_abstract/ToBtcSwapAbs.ts +108 -108
  145. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +1116 -1112
  146. package/src/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.ts +80 -80
  147. package/src/swaps/spv_vault_swap/SpvVault.ts +178 -178
  148. package/src/swaps/spv_vault_swap/SpvVaultSwap.ts +228 -228
  149. package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +673 -671
  150. package/src/swaps/spv_vault_swap/SpvVaults.ts +565 -526
  151. package/src/swaps/trusted/frombtc_trusted/FromBtcTrusted.ts +747 -747
  152. package/src/swaps/trusted/frombtc_trusted/FromBtcTrustedSwap.ts +185 -185
  153. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrusted.ts +589 -591
  154. package/src/swaps/trusted/frombtcln_trusted/FromBtcLnTrustedSwap.ts +121 -121
  155. package/src/utils/BitcoinUtils.ts +59 -59
  156. package/src/utils/Utils.ts +104 -102
  157. package/src/utils/paramcoders/IParamReader.ts +7 -7
  158. package/src/utils/paramcoders/IParamWriter.ts +8 -8
  159. package/src/utils/paramcoders/LegacyParamEncoder.ts +27 -27
  160. package/src/utils/paramcoders/ParamDecoder.ts +218 -218
  161. package/src/utils/paramcoders/ParamEncoder.ts +29 -29
  162. package/src/utils/paramcoders/SchemaVerifier.ts +96 -96
  163. package/src/utils/paramcoders/server/ServerParamDecoder.ts +118 -115
  164. package/src/utils/paramcoders/server/ServerParamEncoder.ts +75 -75
  165. package/src/wallets/IBitcoinWallet.ts +76 -68
  166. package/src/wallets/ILightningWallet.ts +178 -178
  167. package/src/wallets/ISpvVaultSigner.ts +10 -10
  168. package/dist/wallets/ISpvVaultWallet.d.ts +0 -42
  169. package/dist/wallets/ISpvVaultWallet.js +0 -2
@@ -1,454 +1,488 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SpvVaults = exports.VAULT_DUST_AMOUNT = void 0;
4
- const SpvVault_1 = require("./SpvVault");
5
- const Utils_1 = require("../../utils/Utils");
6
- const PluginManager_1 = require("../../plugins/PluginManager");
7
- const AmountAssertions_1 = require("../assertions/AmountAssertions");
8
- const btc_signer_1 = require("@scure/btc-signer");
9
- const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
10
- exports.VAULT_DUST_AMOUNT = 600;
11
- const VAULT_INIT_CONFIRMATIONS = 2;
12
- class SpvVaults {
13
- constructor(vaultStorage, bitcoin, vaultSigner, bitcoinRpc, getChain, config) {
14
- this.logger = {
15
- debug: (msg, ...args) => console.debug("SpvVaults: " + msg, ...args),
16
- info: (msg, ...args) => console.info("SpvVaults: " + msg, ...args),
17
- warn: (msg, ...args) => console.warn("SpvVaults: " + msg, ...args),
18
- error: (msg, ...args) => console.error("SpvVaults: " + msg, ...args)
19
- };
20
- this.vaultStorage = vaultStorage;
21
- this.bitcoin = bitcoin;
22
- this.vaultSigner = vaultSigner;
23
- this.bitcoinRpc = bitcoinRpc;
24
- this.getChain = getChain;
25
- this.config = config;
26
- }
27
- async processDepositEvent(vault, event) {
28
- vault.update(event);
29
- await this.saveVault(vault);
30
- }
31
- async processOpenEvent(vault, event) {
32
- if (vault.state === SpvVault_1.SpvVaultState.BTC_CONFIRMED) {
33
- vault.state = SpvVault_1.SpvVaultState.OPENED;
34
- }
35
- vault.update(event);
36
- await this.saveVault(vault);
37
- }
38
- async processCloseEvent(vault, event) {
39
- if (vault.state === SpvVault_1.SpvVaultState.OPENED) {
40
- vault.state = SpvVault_1.SpvVaultState.CLOSED;
41
- }
42
- vault.update(event);
43
- await this.saveVault(vault);
44
- }
45
- async processClaimEvent(vault, swap, event) {
46
- //Update vault
47
- const foundPendingWithdrawal = vault.pendingWithdrawals.findIndex(val => val.btcTx.txid === event.btcTxId);
48
- if (foundPendingWithdrawal !== -1)
49
- vault.pendingWithdrawals.splice(foundPendingWithdrawal, 1);
50
- vault.update(event);
51
- await this.saveVault(vault);
52
- }
53
- async createVaults(chainId, count, token, confirmations = 2, feeRate) {
54
- const { signer, chainInterface, tokenMultipliers, spvVaultContract } = this.getChain(chainId);
55
- const signerAddress = signer.getAddress();
56
- //Check vaultId of the latest saved vault
57
- let latestVaultId = -1n;
58
- for (let key in this.vaultStorage.data) {
59
- const vault = this.vaultStorage.data[key];
60
- if (vault.chainId !== chainId)
61
- continue;
62
- if (vault.data.getOwner() !== signerAddress)
63
- continue;
64
- if (vault.data.getVaultId() > latestVaultId)
65
- latestVaultId = vault.data.getVaultId();
66
- }
67
- latestVaultId++;
68
- const vaultAddreses = [];
69
- for (let i = 0; i < count; i++) {
70
- const vaultId = latestVaultId + BigInt(i);
71
- const address = await this.vaultSigner.getAddress(chainId, vaultId);
72
- vaultAddreses.push({ vaultId, address });
73
- }
74
- //Construct transaction
75
- const txResult = await this.bitcoin.getSignedMultiTransaction(vaultAddreses.map(val => {
76
- return { address: val.address, amount: exports.VAULT_DUST_AMOUNT };
77
- }), feeRate);
78
- const nativeToken = chainInterface.getNativeCurrencyAddress();
79
- const vaults = await Promise.all(vaultAddreses.map(async (val, index) => {
80
- const vaultData = await spvVaultContract.createVaultData(signerAddress, val.vaultId, txResult.txId + ":" + index, confirmations, [
81
- { token, multiplier: tokenMultipliers?.[token] ?? 1n },
82
- { token: nativeToken, multiplier: tokenMultipliers?.[nativeToken] ?? 1n }
83
- ]);
84
- return new SpvVault_1.SpvVault(chainId, vaultData, val.address);
85
- }));
86
- //Save vaults
87
- if (this.vaultStorage.saveDataArr != null) {
88
- await this.vaultStorage.saveDataArr(vaults.map(val => {
89
- return { id: val.getIdentifier(), object: val };
90
- }));
91
- }
92
- else {
93
- for (let vault of vaults) {
94
- await this.vaultStorage.saveData(vault.getIdentifier(), vault);
95
- }
96
- }
97
- //Send bitcoin tx
98
- await this.bitcoin.sendRawTransaction(txResult.raw);
99
- this.logger.info("createVaults(): Funding " + count + " vaults, bitcoin txId: " + txResult.txId);
100
- return {
101
- vaultsCreated: vaults.map(val => val.data.getVaultId()),
102
- btcTxId: txResult.txId
103
- };
104
- }
105
- async listVaults(chainId, token) {
106
- return Object.keys(this.vaultStorage.data)
107
- .map(key => this.vaultStorage.data[key])
108
- .filter(val => chainId == null ? true : val.chainId === chainId)
109
- .filter(val => val.data.getOwner() === this.getChain(val.chainId)?.signer?.getAddress())
110
- .filter(val => token == null ? true : val.data.getTokenData()[0].token === token);
111
- }
112
- async fundVault(vault, tokenAmounts) {
113
- if (vault.state !== SpvVault_1.SpvVaultState.OPENED)
114
- throw new Error("Vault not opened!");
115
- this.logger.info("fundVault(): Depositing tokens to the vault " + vault.data.getVaultId().toString(10) + ", amounts: " + tokenAmounts.map(val => val.toString(10)).join(", "));
116
- const { signer, spvVaultContract } = this.getChain(vault.chainId);
117
- const txId = await spvVaultContract.deposit(signer, vault.data, tokenAmounts, { waitForConfirmation: true });
118
- this.logger.info("fundVault(): Tokens deposited to vault " + vault.data.getVaultId().toString(10) + ", amounts: " + tokenAmounts.map(val => val.toString(10)).join(", ") + ", txId: " + txId);
119
- return txId;
120
- }
121
- async withdrawFromVault(vault, tokenAmounts, feeRate) {
122
- tokenAmounts.forEach((rawAmount, index) => {
123
- if (vault.balances[index] == null)
124
- throw new Error("Token not found in the vault");
125
- if (vault.balances[index].rawAmount < rawAmount)
126
- throw new Error("Not enough balance in the vault");
127
- });
128
- if (!vault.isReady())
129
- throw new Error("Vault not ready, wait for the latest swap to get at least 1 confirmation!");
130
- const { signer, spvVaultContract } = this.getChain(vault.chainId);
131
- const latestUtxo = vault.getLatestUtxo();
132
- const [txId, voutStr] = latestUtxo.split(":");
133
- const opReturnData = spvVaultContract.toOpReturnData(signer.getAddress(), tokenAmounts);
134
- let opReturnScript;
135
- if (opReturnData.length < 76) {
136
- opReturnScript = Buffer.concat([
137
- Buffer.from([0x6a, opReturnData.length]),
138
- opReturnData
139
- ]);
140
- }
141
- else {
142
- opReturnScript = Buffer.concat([
143
- Buffer.from([0x6a, 0x4c, opReturnData.length]),
144
- opReturnData
145
- ]);
146
- }
147
- let psbt = new btc_signer_1.Transaction({
148
- allowUnknownOutputs: true
149
- });
150
- psbt.addInput({
151
- txid: txId,
152
- index: parseInt(voutStr),
153
- witnessUtxo: {
154
- amount: BigInt(exports.VAULT_DUST_AMOUNT),
155
- script: this.bitcoin.toOutputScript(vault.btcAddress)
156
- }
157
- });
158
- psbt.addOutput({
159
- amount: BigInt(exports.VAULT_DUST_AMOUNT),
160
- script: this.bitcoin.toOutputScript(vault.btcAddress)
161
- });
162
- psbt.addOutput({
163
- amount: 0n,
164
- script: opReturnScript
165
- });
166
- psbt = await this.bitcoin.fundPsbt(psbt, feeRate);
167
- if (psbt.inputsLength < 2)
168
- throw new Error("PSBT needs at least 2 inputs!");
169
- psbt.updateInput(0, { sequence: 0x80000000 });
170
- psbt.updateInput(1, { sequence: 0x80000000 });
171
- psbt = await this.vaultSigner.signPsbt(vault.chainId, vault.data.getVaultId(), psbt, [0]);
172
- const res = await this.bitcoin.signPsbt(psbt);
173
- const parsedTransaction = await this.bitcoinRpc.parseTransaction(res.raw);
174
- const withdrawalData = await spvVaultContract.getWithdrawalData(parsedTransaction);
175
- if (withdrawalData.getSpentVaultUtxo() !== vault.getLatestUtxo()) {
176
- throw new Error("Latest vault UTXO already spent! Please try again later.");
177
- }
178
- withdrawalData.sending = true;
179
- vault.addWithdrawal(withdrawalData);
180
- await this.saveVault(vault);
181
- try {
182
- await this.bitcoin.sendRawTransaction(res.raw);
183
- withdrawalData.sending = false;
184
- }
185
- catch (e) {
186
- withdrawalData.sending = false;
187
- vault.removeWithdrawal(withdrawalData);
188
- await this.saveVault(vault);
189
- throw e;
190
- }
191
- return res.txId;
192
- }
193
- /**
194
- * Call this to check whether some of the previously replaced transactions got re-introduced to the mempool
195
- *
196
- * @param vault
197
- * @param save
198
- */
199
- async checkVaultReplacedTransactions(vault, save) {
200
- const { spvVaultContract } = this.getChain(vault.chainId);
201
- const initialVaultWithdrawalCount = vault.data.getWithdrawalCount();
202
- let latestWithdrawalIndex = initialVaultWithdrawalCount;
203
- const newPendingTxns = [];
204
- const reintroducedTxIds = new Set();
205
- for (let [withdrawalIndex, replacedWithdrawalGroup] of vault.replacedWithdrawals) {
206
- if (withdrawalIndex <= latestWithdrawalIndex)
207
- continue; //Don't check txns that should already be included
208
- for (let replacedWithdrawal of replacedWithdrawalGroup) {
209
- if (reintroducedTxIds.has(replacedWithdrawal.getTxId()))
210
- continue;
211
- const tx = await this.bitcoinRpc.getTransaction(replacedWithdrawal.getTxId());
212
- if (tx == null)
213
- continue;
214
- //Re-introduce transaction to the pending withdrawals list
215
- if (withdrawalIndex > latestWithdrawalIndex) {
216
- const txChain = [replacedWithdrawal];
217
- withdrawalIndex--;
218
- while (withdrawalIndex > latestWithdrawalIndex) {
219
- const tx = await this.bitcoinRpc.getTransaction(txChain[0].getSpentVaultUtxo().split(":")[0]);
220
- if (tx == null)
221
- break;
222
- reintroducedTxIds.add(tx.txid);
223
- txChain.unshift(await spvVaultContract.getWithdrawalData(tx));
224
- withdrawalIndex--;
225
- }
226
- if (withdrawalIndex > latestWithdrawalIndex) {
227
- this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Tried to re-introduce previously replaced TX, but one of txns in the chain not found!`);
228
- continue;
229
- }
230
- newPendingTxns.push(...txChain);
231
- latestWithdrawalIndex += txChain.length;
232
- break; //Don't check other txns at the same withdrawal index
233
- }
234
- else {
235
- this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Tried to re-introduce previously replaced TX, but vault has already processed such withdrawal!`);
236
- }
237
- }
238
- }
239
- if (newPendingTxns.length === 0)
240
- return false;
241
- if (initialVaultWithdrawalCount !== vault.data.getWithdrawalCount()) {
242
- this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Not saving vault after checking replaced transactions, due to withdrawal count changed!`);
243
- return false;
244
- }
245
- const backup = vault.pendingWithdrawals.splice(0, newPendingTxns.length);
246
- const txsToAddOnTop = vault.pendingWithdrawals.splice(0, vault.pendingWithdrawals.length);
247
- try {
248
- newPendingTxns.forEach(val => vault.addWithdrawal(val));
249
- txsToAddOnTop.forEach(val => vault.addWithdrawal(val));
250
- for (let i = 0; i < newPendingTxns.length; i++) {
251
- const withdrawalIndex = initialVaultWithdrawalCount + i + 1;
252
- const arr = vault.replacedWithdrawals.get(withdrawalIndex);
253
- if (arr == null)
254
- continue;
255
- const index = arr.indexOf(newPendingTxns[i]);
256
- if (index === -1) {
257
- this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Cannot remove re-introduced tx ${newPendingTxns[i].getTxId()}, not found in the respective array!`);
258
- continue;
259
- }
260
- arr.splice(index, 1);
261
- if (arr.length === 0)
262
- vault.replacedWithdrawals.delete(withdrawalIndex);
263
- }
264
- this.logger.info(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Re-introduced back ${newPendingTxns.length} txns that were re-added to the mempool!`);
265
- if (save)
266
- await this.saveVault(vault);
267
- return true;
268
- }
269
- catch (e) {
270
- this.logger.error(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Failed to update the vault with new pending txns (rolling back): `, e);
271
- //Rollback the pending withdrawals
272
- vault.pendingWithdrawals.push(...backup, ...txsToAddOnTop);
273
- return false;
274
- }
275
- }
276
- async checkVaults() {
277
- const vaults = Object.keys(this.vaultStorage.data).map(key => this.vaultStorage.data[key]);
278
- const claimWithdrawals = [];
279
- for (let vault of vaults) {
280
- const { signer, spvVaultContract, chainInterface } = this.getChain(vault.chainId);
281
- if (vault.data.getOwner() !== signer.getAddress())
282
- continue;
283
- if (vault.state === SpvVault_1.SpvVaultState.BTC_INITIATED) {
284
- //Check if btc tx confirmed
285
- const txId = vault.initialUtxo.split(":")[0];
286
- const btcTx = await this.bitcoinRpc.getTransaction(txId);
287
- if (btcTx.confirmations >= VAULT_INIT_CONFIRMATIONS) {
288
- //Double-check the state here to prevent race condition
289
- if (vault.state === SpvVault_1.SpvVaultState.BTC_INITIATED) {
290
- vault.state = SpvVault_1.SpvVaultState.BTC_CONFIRMED;
291
- await this.saveVault(vault);
292
- }
293
- this.logger.info("checkVaults(): Vault ID " + vault.data.getVaultId().toString(10) + " confirmed on bitcoin, opening vault on " + vault.chainId);
294
- }
295
- }
296
- if (vault.state === SpvVault_1.SpvVaultState.BTC_CONFIRMED) {
297
- //Check if open txs were sent already
298
- if (vault.scOpenTx != null) {
299
- //Check if confirmed
300
- const status = await chainInterface.getTxStatus(vault.scOpenTx.rawTx);
301
- if (status === "pending")
302
- return;
303
- if (status === "success") {
304
- vault.state = SpvVault_1.SpvVaultState.OPENED;
305
- await this.saveVault(vault);
306
- return;
307
- }
308
- }
309
- const txs = await spvVaultContract.txsOpen(signer.getAddress(), vault.data);
310
- let numTx = 0;
311
- const txIds = await chainInterface.sendAndConfirm(signer, txs, true, undefined, false, async (txId, rawTx) => {
312
- numTx++;
313
- if (numTx === txs.length) {
314
- //Final tx
315
- vault.scOpenTx = { txId, rawTx };
316
- await this.saveVault(vault);
317
- }
318
- });
319
- this.logger.info("checkVaults(): Vault ID " + vault.data.getVaultId().toString(10) + " opened on " + vault.chainId + " txId: " + txIds.join(", "));
320
- vault.state = SpvVault_1.SpvVaultState.OPENED;
321
- await this.saveVault(vault);
322
- }
323
- if (vault.state === SpvVault_1.SpvVaultState.OPENED) {
324
- let changed = await this.checkVaultReplacedTransactions(vault);
325
- //Check if some of the pendingWithdrawals got confirmed
326
- let latestOwnWithdrawalIndex = -1;
327
- let latestConfirmedWithdrawalIndex = -1;
328
- for (let i = vault.pendingWithdrawals.length - 1; i >= 0; i--) {
329
- const pendingWithdrawal = vault.pendingWithdrawals[i];
330
- if (pendingWithdrawal.sending)
331
- continue;
332
- //Check all the pending withdrawals that were not finalized yet
333
- const btcTx = await (0, BitcoinUtils_1.checkTransactionReplaced)(pendingWithdrawal.btcTx.txid, pendingWithdrawal.btcTx.raw, this.bitcoinRpc);
334
- if (btcTx == null) {
335
- //Probable double-spend, remove from pending withdrawals
336
- if (!vault.doubleSpendPendingWithdrawal(pendingWithdrawal)) {
337
- this.logger.warn("checkVaults(): Tried to remove pending withdrawal txId: " + pendingWithdrawal.btcTx.txid + ", but doesn't exist anymore!");
338
- }
339
- else {
340
- this.logger.info("checkVaults(): Successfully removed withdrawal txId: " + pendingWithdrawal.btcTx.txid + ", due to being replaced in the mempool!");
341
- }
342
- changed = true;
343
- }
344
- else {
345
- //Update confirmations count
346
- if (pendingWithdrawal.btcTx.confirmations !== btcTx.confirmations ||
347
- pendingWithdrawal.btcTx.blockhash !== btcTx.blockhash) {
348
- pendingWithdrawal.btcTx.confirmations = btcTx.confirmations;
349
- pendingWithdrawal.btcTx.blockhash = btcTx.blockhash;
350
- changed = true;
351
- }
352
- }
353
- //Check it has enough confirmations
354
- if (pendingWithdrawal.btcTx.confirmations >= vault.data.getConfirmations()) {
355
- latestConfirmedWithdrawalIndex = i;
356
- //Check if the pending withdrawals contain a withdrawal to our own address
357
- if (pendingWithdrawal.isRecipient(signer.getAddress())) {
358
- latestOwnWithdrawalIndex = i;
359
- }
360
- }
361
- }
362
- if (changed) {
363
- await this.saveVault(vault);
364
- }
365
- if (this.config.maxUnclaimedWithdrawals != null && latestConfirmedWithdrawalIndex + 1 >= this.config.maxUnclaimedWithdrawals) {
366
- this.logger.info("checkVaults(): Processing withdrawals by self, because a lot of them are unclaimed!");
367
- claimWithdrawals.push({ vault, withdrawals: vault.pendingWithdrawals.slice(0, latestConfirmedWithdrawalIndex + 1) });
368
- }
369
- else if (latestOwnWithdrawalIndex !== -1) {
370
- claimWithdrawals.push({ vault, withdrawals: vault.pendingWithdrawals.slice(0, latestOwnWithdrawalIndex + 1) });
371
- }
372
- }
373
- }
374
- for (let { vault, withdrawals } of claimWithdrawals) {
375
- if (!await this.claimWithdrawals(vault, withdrawals)) {
376
- this.logger.error("checkVaults(): Cannot process withdrawals " + withdrawals.map(val => val.btcTx.txid).join(", ") + " for vault: " + vault.data.getVaultId());
377
- break;
378
- }
379
- }
380
- }
381
- async claimWithdrawals(vault, withdrawal) {
382
- const { signer, spvVaultContract } = this.getChain(vault.chainId);
383
- try {
384
- const txId = await spvVaultContract.claim(signer, vault.data, withdrawal.map(tx => {
385
- return { tx };
386
- }), undefined, true, { waitForConfirmation: true });
387
- this.logger.info("claimWithdrawal(): Successfully claimed withdrawals, btcTxIds: " + withdrawal.map(val => val.btcTx.txid).join(", ") + " smartChainTxId: " + txId);
388
- return true;
389
- }
390
- catch (e) {
391
- this.logger.error("claimWithdrawal(): Tried to claim but got error: ", e);
392
- return false;
393
- }
394
- }
395
- async getVault(chainId, owner, vaultId) {
396
- return this.vaultStorage.data[chainId + "_" + owner + "_" + vaultId.toString(10)];
397
- }
398
- /**
399
- * Returns a ready-to-use vault for a specific request
400
- *
401
- * @param chainIdentifier
402
- * @param totalSats
403
- * @param token
404
- * @param amount
405
- * @param gasToken
406
- * @param gasTokenAmount
407
- * @protected
408
- */
409
- async findVaultForSwap(chainIdentifier, totalSats, token, amount, gasToken, gasTokenAmount) {
410
- const { signer } = this.getChain(chainIdentifier);
411
- const pluginResponse = await PluginManager_1.PluginManager.onVaultSelection(chainIdentifier, totalSats, { token, amount }, { token: gasToken, amount: gasTokenAmount });
412
- if (pluginResponse != null) {
413
- AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(pluginResponse);
414
- return pluginResponse;
415
- }
416
- const candidates = Object.keys(this.vaultStorage.data)
417
- .map(key => this.vaultStorage.data[key])
418
- .filter(vault => vault.chainId === chainIdentifier && vault.data.getOwner() === signer.getAddress() && vault.isReady())
419
- .filter(vault => {
420
- const token0 = vault.balances[0];
421
- if (token0.token !== token || token0.scaledAmount < amount)
422
- return false;
423
- if (gasToken != null && gasTokenAmount !== 0n) {
424
- const token1 = vault.balances[1];
425
- if (token1.token !== gasToken || token1.scaledAmount < gasTokenAmount)
426
- return false;
427
- }
428
- return true;
429
- });
430
- candidates.sort((a, b) => (0, Utils_1.bigIntSorter)(a.balances[0].scaledAmount, b.balances[0].scaledAmount));
431
- const result = candidates[0];
432
- if (result == null)
433
- throw {
434
- code: 20301,
435
- msg: "No suitable swap vault found, try again later!"
436
- };
437
- return result;
438
- }
439
- saveVault(vault) {
440
- return this.vaultStorage.saveData(vault.getIdentifier(), vault);
441
- }
442
- async startVaultsWatchdog() {
443
- let rerun;
444
- rerun = async () => {
445
- await this.checkVaults().catch(e => console.error(e));
446
- setTimeout(rerun, this.config.vaultsCheckInterval);
447
- };
448
- await rerun();
449
- }
450
- async init() {
451
- const vaults = await this.vaultStorage.loadData(SpvVault_1.SpvVault);
452
- }
453
- }
454
- exports.SpvVaults = SpvVaults;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SpvVaults = exports.VAULT_DUST_AMOUNT = void 0;
4
+ const SpvVault_1 = require("./SpvVault");
5
+ const Utils_1 = require("../../utils/Utils");
6
+ const PluginManager_1 = require("../../plugins/PluginManager");
7
+ const AmountAssertions_1 = require("../assertions/AmountAssertions");
8
+ const btc_signer_1 = require("@scure/btc-signer");
9
+ const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
10
+ exports.VAULT_DUST_AMOUNT = 600;
11
+ const VAULT_INIT_CONFIRMATIONS = 2;
12
+ const MAX_PARALLEL_VAULTS_OPENING = 10;
13
+ class SpvVaults {
14
+ constructor(vaultStorage, bitcoin, vaultSigner, bitcoinRpc, chains, config) {
15
+ this.logger = (0, Utils_1.getLogger)("SpvVaults: ");
16
+ this.vaultStorage = vaultStorage;
17
+ this.bitcoin = bitcoin;
18
+ this.vaultSigner = vaultSigner;
19
+ this.bitcoinRpc = bitcoinRpc;
20
+ this.chains = chains;
21
+ this.config = config;
22
+ for (let chainId in chains.chains) {
23
+ const { chainInterface } = chains.chains[chainId];
24
+ chainInterface.onBeforeTxReplace(async (oldTx, oldTxId, newTx, newTxId) => {
25
+ for (let key in this.vaultStorage.data) {
26
+ const vaultData = this.vaultStorage.data[key];
27
+ if (vaultData.scOpenTxs != null && vaultData.scOpenTxs[oldTxId] != null) {
28
+ vaultData.scOpenTxs[newTxId] = newTx;
29
+ await this.saveVault(vaultData);
30
+ break;
31
+ }
32
+ }
33
+ });
34
+ }
35
+ }
36
+ async processDepositEvent(vault, event) {
37
+ vault.update(event);
38
+ await this.saveVault(vault);
39
+ }
40
+ async processOpenEvent(vault, event) {
41
+ if (vault.state === SpvVault_1.SpvVaultState.BTC_CONFIRMED) {
42
+ vault.state = SpvVault_1.SpvVaultState.OPENED;
43
+ }
44
+ vault.update(event);
45
+ await this.saveVault(vault);
46
+ }
47
+ async processCloseEvent(vault, event) {
48
+ if (vault.state === SpvVault_1.SpvVaultState.OPENED) {
49
+ vault.state = SpvVault_1.SpvVaultState.CLOSED;
50
+ }
51
+ vault.update(event);
52
+ await this.saveVault(vault);
53
+ }
54
+ async processClaimEvent(vault, swap, event) {
55
+ //Update vault
56
+ const foundPendingWithdrawal = vault.pendingWithdrawals.findIndex(val => val.btcTx.txid === event.btcTxId);
57
+ if (foundPendingWithdrawal !== -1)
58
+ vault.pendingWithdrawals.splice(foundPendingWithdrawal, 1);
59
+ vault.update(event);
60
+ await this.saveVault(vault);
61
+ }
62
+ async createVaults(chainId, count, token, confirmations = 2, feeRate) {
63
+ const { signer, chainInterface, tokenMultipliers, spvVaultContract } = this.chains.chains[chainId];
64
+ const signerAddress = signer.getAddress();
65
+ //Check vaultId of the latest saved vault
66
+ let latestVaultId = -1n;
67
+ for (let key in this.vaultStorage.data) {
68
+ const vault = this.vaultStorage.data[key];
69
+ if (vault.chainId !== chainId)
70
+ continue;
71
+ if (vault.data.getOwner() !== signerAddress)
72
+ continue;
73
+ if (vault.data.getVaultId() > latestVaultId)
74
+ latestVaultId = vault.data.getVaultId();
75
+ }
76
+ latestVaultId++;
77
+ const vaultAddreses = [];
78
+ for (let i = 0; i < count; i++) {
79
+ const vaultId = latestVaultId + BigInt(i);
80
+ const address = await this.vaultSigner.getAddress(chainId, vaultId);
81
+ vaultAddreses.push({ vaultId, address });
82
+ }
83
+ const nativeToken = chainInterface.getNativeCurrencyAddress();
84
+ let txId = null;
85
+ let vaults = null;
86
+ await this.bitcoin.execute(async () => {
87
+ //Construct transaction
88
+ const txResult = await this.bitcoin.getSignedMultiTransaction(vaultAddreses.map(val => {
89
+ return { address: val.address, amount: exports.VAULT_DUST_AMOUNT };
90
+ }), feeRate);
91
+ vaults = await Promise.all(vaultAddreses.map(async (val, index) => {
92
+ const vaultData = await spvVaultContract.createVaultData(signerAddress, val.vaultId, txResult.txId + ":" + index, confirmations, [
93
+ { token, multiplier: tokenMultipliers?.[token] ?? 1n },
94
+ { token: nativeToken, multiplier: tokenMultipliers?.[nativeToken] ?? 1n }
95
+ ]);
96
+ return new SpvVault_1.SpvVault(chainId, vaultData, val.address);
97
+ }));
98
+ //Save vaults
99
+ if (this.vaultStorage.saveDataArr != null) {
100
+ await this.vaultStorage.saveDataArr(vaults.map(val => {
101
+ return { id: val.getIdentifier(), object: val };
102
+ }));
103
+ }
104
+ else {
105
+ for (let vault of vaults) {
106
+ await this.vaultStorage.saveData(vault.getIdentifier(), vault);
107
+ }
108
+ }
109
+ //Send bitcoin tx
110
+ await this.bitcoin.sendRawTransaction(txResult.raw);
111
+ txId = txResult.txId;
112
+ });
113
+ this.logger.info("createVaults(): Funding " + count + " vaults, bitcoin txId: " + txId);
114
+ return {
115
+ vaultsCreated: vaults.map(val => val.data.getVaultId()),
116
+ btcTxId: txId
117
+ };
118
+ }
119
+ async listVaults(chainId, token) {
120
+ return Object.keys(this.vaultStorage.data)
121
+ .map(key => this.vaultStorage.data[key])
122
+ .filter(val => chainId == null ? true : val.chainId === chainId)
123
+ .filter(val => val.data.getOwner() === this.chains.chains[val.chainId]?.signer?.getAddress())
124
+ .filter(val => token == null ? true : val.data.getTokenData()[0].token === token);
125
+ }
126
+ async fundVault(vault, tokenAmounts) {
127
+ if (vault.state !== SpvVault_1.SpvVaultState.OPENED)
128
+ throw new Error("Vault not opened!");
129
+ this.logger.info("fundVault(): Depositing tokens to the vault " + vault.data.getVaultId().toString(10) + ", amounts: " + tokenAmounts.map(val => val.toString(10)).join(", "));
130
+ const { signer, spvVaultContract } = this.chains.chains[vault.chainId];
131
+ const txId = await spvVaultContract.deposit(signer, vault.data, tokenAmounts, { waitForConfirmation: true });
132
+ this.logger.info("fundVault(): Tokens deposited to vault " + vault.data.getVaultId().toString(10) + ", amounts: " + tokenAmounts.map(val => val.toString(10)).join(", ") + ", txId: " + txId);
133
+ return txId;
134
+ }
135
+ async withdrawFromVault(vault, tokenAmounts, feeRate) {
136
+ tokenAmounts.forEach((rawAmount, index) => {
137
+ if (vault.balances[index] == null)
138
+ throw new Error("Token not found in the vault");
139
+ if (vault.balances[index].rawAmount < rawAmount)
140
+ throw new Error("Not enough balance in the vault");
141
+ });
142
+ if (!vault.isReady())
143
+ throw new Error("Vault not ready, wait for the latest swap to get at least 1 confirmation!");
144
+ const { signer, spvVaultContract } = this.chains.chains[vault.chainId];
145
+ const latestUtxo = vault.getLatestUtxo();
146
+ const [txId, voutStr] = latestUtxo.split(":");
147
+ const opReturnData = spvVaultContract.toOpReturnData(signer.getAddress(), tokenAmounts);
148
+ let opReturnScript;
149
+ if (opReturnData.length < 76) {
150
+ opReturnScript = Buffer.concat([
151
+ Buffer.from([0x6a, opReturnData.length]),
152
+ opReturnData
153
+ ]);
154
+ }
155
+ else {
156
+ opReturnScript = Buffer.concat([
157
+ Buffer.from([0x6a, 0x4c, opReturnData.length]),
158
+ opReturnData
159
+ ]);
160
+ }
161
+ let psbt = new btc_signer_1.Transaction({
162
+ allowUnknownOutputs: true
163
+ });
164
+ psbt.addInput({
165
+ txid: txId,
166
+ index: parseInt(voutStr),
167
+ witnessUtxo: {
168
+ amount: BigInt(exports.VAULT_DUST_AMOUNT),
169
+ script: this.bitcoin.toOutputScript(vault.btcAddress)
170
+ }
171
+ });
172
+ psbt.addOutput({
173
+ amount: BigInt(exports.VAULT_DUST_AMOUNT),
174
+ script: this.bitcoin.toOutputScript(vault.btcAddress)
175
+ });
176
+ psbt.addOutput({
177
+ amount: 0n,
178
+ script: opReturnScript
179
+ });
180
+ let withdrawalTxId = null;
181
+ await this.bitcoin.execute(async () => {
182
+ psbt = await this.bitcoin.fundPsbt(psbt, feeRate);
183
+ if (psbt.inputsLength < 2)
184
+ throw new Error("PSBT needs at least 2 inputs!");
185
+ psbt.updateInput(0, { sequence: 0x80000000 });
186
+ psbt.updateInput(1, { sequence: 0x80000000 });
187
+ psbt = await this.vaultSigner.signPsbt(vault.chainId, vault.data.getVaultId(), psbt, [0]);
188
+ const res = await this.bitcoin.signPsbt(psbt);
189
+ withdrawalTxId = res.txId;
190
+ const parsedTransaction = await this.bitcoinRpc.parseTransaction(res.raw);
191
+ const withdrawalData = await spvVaultContract.getWithdrawalData(parsedTransaction);
192
+ if (withdrawalData.getSpentVaultUtxo() !== vault.getLatestUtxo()) {
193
+ throw new Error("Latest vault UTXO already spent! Please try again later.");
194
+ }
195
+ withdrawalData.sending = true;
196
+ vault.addWithdrawal(withdrawalData);
197
+ await this.saveVault(vault);
198
+ try {
199
+ await this.bitcoin.sendRawTransaction(res.raw);
200
+ withdrawalData.sending = false;
201
+ }
202
+ catch (e) {
203
+ withdrawalData.sending = false;
204
+ vault.removeWithdrawal(withdrawalData);
205
+ await this.saveVault(vault);
206
+ throw e;
207
+ }
208
+ });
209
+ return withdrawalTxId;
210
+ }
211
+ /**
212
+ * Call this to check whether some of the previously replaced transactions got re-introduced to the mempool
213
+ *
214
+ * @param vault
215
+ * @param save
216
+ */
217
+ async checkVaultReplacedTransactions(vault, save) {
218
+ const { spvVaultContract } = this.chains.chains[vault.chainId];
219
+ const initialVaultWithdrawalCount = vault.data.getWithdrawalCount();
220
+ let latestWithdrawalIndex = initialVaultWithdrawalCount;
221
+ const newPendingTxns = [];
222
+ const reintroducedTxIds = new Set();
223
+ for (let [withdrawalIndex, replacedWithdrawalGroup] of vault.replacedWithdrawals) {
224
+ if (withdrawalIndex <= latestWithdrawalIndex)
225
+ continue; //Don't check txns that should already be included
226
+ for (let replacedWithdrawal of replacedWithdrawalGroup) {
227
+ if (reintroducedTxIds.has(replacedWithdrawal.getTxId()))
228
+ continue;
229
+ const tx = await this.bitcoinRpc.getTransaction(replacedWithdrawal.getTxId());
230
+ if (tx == null)
231
+ continue;
232
+ //Re-introduce transaction to the pending withdrawals list
233
+ if (withdrawalIndex > latestWithdrawalIndex) {
234
+ const txChain = [replacedWithdrawal];
235
+ withdrawalIndex--;
236
+ while (withdrawalIndex > latestWithdrawalIndex) {
237
+ const tx = await this.bitcoinRpc.getTransaction(txChain[0].getSpentVaultUtxo().split(":")[0]);
238
+ if (tx == null)
239
+ break;
240
+ reintroducedTxIds.add(tx.txid);
241
+ txChain.unshift(await spvVaultContract.getWithdrawalData(tx));
242
+ withdrawalIndex--;
243
+ }
244
+ if (withdrawalIndex > latestWithdrawalIndex) {
245
+ this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Tried to re-introduce previously replaced TX, but one of txns in the chain not found!`);
246
+ continue;
247
+ }
248
+ newPendingTxns.push(...txChain);
249
+ latestWithdrawalIndex += txChain.length;
250
+ break; //Don't check other txns at the same withdrawal index
251
+ }
252
+ else {
253
+ this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Tried to re-introduce previously replaced TX, but vault has already processed such withdrawal!`);
254
+ }
255
+ }
256
+ }
257
+ if (newPendingTxns.length === 0)
258
+ return false;
259
+ if (initialVaultWithdrawalCount !== vault.data.getWithdrawalCount()) {
260
+ this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Not saving vault after checking replaced transactions, due to withdrawal count changed!`);
261
+ return false;
262
+ }
263
+ const backup = vault.pendingWithdrawals.splice(0, newPendingTxns.length);
264
+ const txsToAddOnTop = vault.pendingWithdrawals.splice(0, vault.pendingWithdrawals.length);
265
+ try {
266
+ newPendingTxns.forEach(val => vault.addWithdrawal(val));
267
+ txsToAddOnTop.forEach(val => vault.addWithdrawal(val));
268
+ for (let i = 0; i < newPendingTxns.length; i++) {
269
+ const withdrawalIndex = initialVaultWithdrawalCount + i + 1;
270
+ const arr = vault.replacedWithdrawals.get(withdrawalIndex);
271
+ if (arr == null)
272
+ continue;
273
+ const index = arr.indexOf(newPendingTxns[i]);
274
+ if (index === -1) {
275
+ this.logger.warn(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Cannot remove re-introduced tx ${newPendingTxns[i].getTxId()}, not found in the respective array!`);
276
+ continue;
277
+ }
278
+ arr.splice(index, 1);
279
+ if (arr.length === 0)
280
+ vault.replacedWithdrawals.delete(withdrawalIndex);
281
+ }
282
+ this.logger.info(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Re-introduced back ${newPendingTxns.length} txns that were re-added to the mempool!`);
283
+ if (save)
284
+ await this.saveVault(vault);
285
+ return true;
286
+ }
287
+ catch (e) {
288
+ this.logger.error(`checkVaultReplacedTransactions(${vault.getIdentifier()}): Failed to update the vault with new pending txns (rolling back): `, e);
289
+ //Rollback the pending withdrawals
290
+ vault.pendingWithdrawals.push(...backup, ...txsToAddOnTop);
291
+ return false;
292
+ }
293
+ }
294
+ async checkVaults() {
295
+ const vaults = Object.keys(this.vaultStorage.data).map(key => this.vaultStorage.data[key]);
296
+ const claimWithdrawals = [];
297
+ let promises = [];
298
+ for (let vault of vaults) {
299
+ const { signer, spvVaultContract, chainInterface } = this.chains.chains[vault.chainId];
300
+ if (vault.data.getOwner() !== signer.getAddress())
301
+ continue;
302
+ if (vault.state === SpvVault_1.SpvVaultState.BTC_INITIATED) {
303
+ //Check if btc tx confirmed
304
+ const txId = vault.initialUtxo.split(":")[0];
305
+ const btcTx = await this.bitcoinRpc.getTransaction(txId);
306
+ if (btcTx.confirmations >= VAULT_INIT_CONFIRMATIONS) {
307
+ //Double-check the state here to prevent race condition
308
+ if (vault.state === SpvVault_1.SpvVaultState.BTC_INITIATED) {
309
+ vault.state = SpvVault_1.SpvVaultState.BTC_CONFIRMED;
310
+ await this.saveVault(vault);
311
+ }
312
+ this.logger.info("checkVaults(): Vault ID " + vault.data.getVaultId().toString(10) + " confirmed on bitcoin, opening vault on " + vault.chainId);
313
+ }
314
+ }
315
+ if (vault.state === SpvVault_1.SpvVaultState.BTC_CONFIRMED) {
316
+ //Check if open txs were sent already
317
+ if (vault.scOpenTxs != null) {
318
+ //Check if confirmed
319
+ let _continue = false;
320
+ for (let txId in vault.scOpenTxs) {
321
+ const tx = vault.scOpenTxs[txId];
322
+ const status = await chainInterface.getTxStatus(tx);
323
+ if (status === "pending") {
324
+ _continue = true;
325
+ break;
326
+ }
327
+ if (status === "success") {
328
+ vault.state = SpvVault_1.SpvVaultState.OPENED;
329
+ await this.saveVault(vault);
330
+ _continue = true;
331
+ break;
332
+ }
333
+ }
334
+ if (_continue)
335
+ continue;
336
+ }
337
+ const txs = await spvVaultContract.txsOpen(signer.getAddress(), vault.data);
338
+ let numTx = 0;
339
+ promises.push(chainInterface.sendAndConfirm(signer, txs, true, undefined, true, async (txId, rawTx) => {
340
+ numTx++;
341
+ if (numTx === txs.length) {
342
+ //Final tx
343
+ vault.scOpenTxs = { [txId]: rawTx };
344
+ await this.saveVault(vault);
345
+ }
346
+ }).then(txIds => {
347
+ this.logger.info("checkVaults(): Vault ID " + vault.data.getVaultId().toString(10) + " opened on " + vault.chainId + " txId: " + txIds.join(", "));
348
+ vault.state = SpvVault_1.SpvVaultState.OPENED;
349
+ return this.saveVault(vault);
350
+ }));
351
+ if (promises.length >= MAX_PARALLEL_VAULTS_OPENING) {
352
+ await Promise.all(promises);
353
+ promises = [];
354
+ }
355
+ continue;
356
+ }
357
+ if (vault.state === SpvVault_1.SpvVaultState.OPENED) {
358
+ let changed = await this.checkVaultReplacedTransactions(vault);
359
+ //Check if some of the pendingWithdrawals got confirmed
360
+ let latestOwnWithdrawalIndex = -1;
361
+ let latestConfirmedWithdrawalIndex = -1;
362
+ for (let i = vault.pendingWithdrawals.length - 1; i >= 0; i--) {
363
+ const pendingWithdrawal = vault.pendingWithdrawals[i];
364
+ if (pendingWithdrawal.sending)
365
+ continue;
366
+ //Check all the pending withdrawals that were not finalized yet
367
+ const btcTx = await (0, BitcoinUtils_1.checkTransactionReplaced)(pendingWithdrawal.btcTx.txid, pendingWithdrawal.btcTx.raw, this.bitcoinRpc);
368
+ if (btcTx == null) {
369
+ //Probable double-spend, remove from pending withdrawals
370
+ if (!vault.doubleSpendPendingWithdrawal(pendingWithdrawal)) {
371
+ this.logger.warn("checkVaults(): Tried to remove pending withdrawal txId: " + pendingWithdrawal.btcTx.txid + ", but doesn't exist anymore!");
372
+ }
373
+ else {
374
+ this.logger.info("checkVaults(): Successfully removed withdrawal txId: " + pendingWithdrawal.btcTx.txid + ", due to being replaced in the mempool!");
375
+ }
376
+ changed = true;
377
+ }
378
+ else {
379
+ //Update confirmations count
380
+ if (pendingWithdrawal.btcTx.confirmations !== btcTx.confirmations ||
381
+ pendingWithdrawal.btcTx.blockhash !== btcTx.blockhash) {
382
+ pendingWithdrawal.btcTx.confirmations = btcTx.confirmations;
383
+ pendingWithdrawal.btcTx.blockhash = btcTx.blockhash;
384
+ changed = true;
385
+ }
386
+ }
387
+ //Check it has enough confirmations
388
+ if (pendingWithdrawal.btcTx.confirmations >= vault.data.getConfirmations()) {
389
+ latestConfirmedWithdrawalIndex = i;
390
+ //Check if the pending withdrawals contain a withdrawal to our own address
391
+ if (pendingWithdrawal.isRecipient(signer.getAddress())) {
392
+ latestOwnWithdrawalIndex = i;
393
+ }
394
+ }
395
+ }
396
+ if (changed) {
397
+ await this.saveVault(vault);
398
+ }
399
+ if (this.config.maxUnclaimedWithdrawals != null && latestConfirmedWithdrawalIndex + 1 >= this.config.maxUnclaimedWithdrawals) {
400
+ this.logger.info("checkVaults(): Processing withdrawals by self, because a lot of them are unclaimed!");
401
+ claimWithdrawals.push({ vault, withdrawals: vault.pendingWithdrawals.slice(0, latestConfirmedWithdrawalIndex + 1) });
402
+ }
403
+ else if (latestOwnWithdrawalIndex !== -1) {
404
+ claimWithdrawals.push({ vault, withdrawals: vault.pendingWithdrawals.slice(0, latestOwnWithdrawalIndex + 1) });
405
+ }
406
+ }
407
+ }
408
+ for (let { vault, withdrawals } of claimWithdrawals) {
409
+ if (!await this.claimWithdrawals(vault, withdrawals)) {
410
+ this.logger.error("checkVaults(): Cannot process withdrawals " + withdrawals.map(val => val.btcTx.txid).join(", ") + " for vault: " + vault.data.getVaultId());
411
+ break;
412
+ }
413
+ }
414
+ }
415
+ async claimWithdrawals(vault, withdrawal) {
416
+ const { signer, spvVaultContract } = this.chains.chains[vault.chainId];
417
+ try {
418
+ const txId = await spvVaultContract.claim(signer, vault.data, withdrawal.map(tx => {
419
+ return { tx };
420
+ }), undefined, true, { waitForConfirmation: true });
421
+ this.logger.info("claimWithdrawal(): Successfully claimed withdrawals, btcTxIds: " + withdrawal.map(val => val.btcTx.txid).join(", ") + " smartChainTxId: " + txId);
422
+ return true;
423
+ }
424
+ catch (e) {
425
+ this.logger.error("claimWithdrawal(): Tried to claim but got error: ", e);
426
+ return false;
427
+ }
428
+ }
429
+ async getVault(chainId, owner, vaultId) {
430
+ return this.vaultStorage.data[chainId + "_" + owner + "_" + vaultId.toString(10)];
431
+ }
432
+ /**
433
+ * Returns a ready-to-use vault for a specific request
434
+ *
435
+ * @param chainIdentifier
436
+ * @param totalSats
437
+ * @param token
438
+ * @param amount
439
+ * @param gasToken
440
+ * @param gasTokenAmount
441
+ * @protected
442
+ */
443
+ async findVaultForSwap(chainIdentifier, totalSats, token, amount, gasToken, gasTokenAmount) {
444
+ const { signer } = this.chains.chains[chainIdentifier];
445
+ const pluginResponse = await PluginManager_1.PluginManager.onVaultSelection(chainIdentifier, totalSats, { token, amount }, { token: gasToken, amount: gasTokenAmount });
446
+ if (pluginResponse != null) {
447
+ AmountAssertions_1.AmountAssertions.handlePluginErrorResponses(pluginResponse);
448
+ return pluginResponse;
449
+ }
450
+ const candidates = Object.keys(this.vaultStorage.data)
451
+ .map(key => this.vaultStorage.data[key])
452
+ .filter(vault => vault.chainId === chainIdentifier && vault.data.getOwner() === signer.getAddress() && vault.isReady())
453
+ .filter(vault => {
454
+ const token0 = vault.balances[0];
455
+ if (token0.token !== token || token0.scaledAmount < amount)
456
+ return false;
457
+ if (gasToken != null && gasTokenAmount !== 0n) {
458
+ const token1 = vault.balances[1];
459
+ if (token1.token !== gasToken || token1.scaledAmount < gasTokenAmount)
460
+ return false;
461
+ }
462
+ return true;
463
+ });
464
+ candidates.sort((a, b) => (0, Utils_1.bigIntSorter)(a.balances[0].scaledAmount, b.balances[0].scaledAmount));
465
+ const result = candidates[0];
466
+ if (result == null)
467
+ throw {
468
+ code: 20301,
469
+ msg: "No suitable swap vault found, try again later!"
470
+ };
471
+ return result;
472
+ }
473
+ saveVault(vault) {
474
+ return this.vaultStorage.saveData(vault.getIdentifier(), vault);
475
+ }
476
+ async startVaultsWatchdog() {
477
+ let rerun;
478
+ rerun = async () => {
479
+ await this.checkVaults().catch(e => this.logger.error("startVaultsWatchdog(): Error when periodically checking SPV vaults: ", e));
480
+ setTimeout(rerun, this.config.vaultsCheckInterval);
481
+ };
482
+ await rerun();
483
+ }
484
+ async init() {
485
+ const vaults = await this.vaultStorage.loadData(SpvVault_1.SpvVault);
486
+ }
487
+ }
488
+ exports.SpvVaults = SpvVaults;