beignet 0.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 (329) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc +40 -0
  3. package/.prettierrc +9 -0
  4. package/LICENSE +21 -0
  5. package/README.md +166 -0
  6. package/dist/electrum/constants.js +31 -0
  7. package/dist/electrum/constants.js.map +1 -0
  8. package/dist/electrum/index.js +552 -0
  9. package/dist/electrum/index.js.map +1 -0
  10. package/dist/index.js +23 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/shapes/electrum.js +14 -0
  13. package/dist/shapes/electrum.js.map +1 -0
  14. package/dist/shapes/index.js +19 -0
  15. package/dist/shapes/index.js.map +1 -0
  16. package/dist/shapes/wallet.js +115 -0
  17. package/dist/shapes/wallet.js.map +1 -0
  18. package/dist/transaction/index.js +692 -0
  19. package/dist/transaction/index.js.map +1 -0
  20. package/dist/types/electrum/constants.d.ts +7 -0
  21. package/dist/types/electrum/index.d.ts +85 -0
  22. package/dist/types/electrum.js +9 -0
  23. package/dist/types/electrum.js.map +1 -0
  24. package/dist/types/index.d.ts +6 -0
  25. package/dist/types/index.js +20 -0
  26. package/dist/types/index.js.map +1 -0
  27. package/dist/types/shapes/electrum.d.ts +5 -0
  28. package/dist/types/shapes/index.d.ts +2 -0
  29. package/dist/types/shapes/wallet.d.ts +10 -0
  30. package/dist/types/transaction/index.d.ts +92 -0
  31. package/dist/types/transaction.js +12 -0
  32. package/dist/types/transaction.js.map +1 -0
  33. package/dist/types/types/electrum.d.ts +113 -0
  34. package/dist/types/types/index.d.ts +3 -0
  35. package/dist/types/types/transaction.d.ts +51 -0
  36. package/dist/types/types/wallet.d.ts +400 -0
  37. package/dist/types/utils/conversion.d.ts +10 -0
  38. package/dist/types/utils/derivation-path.d.ts +19 -0
  39. package/dist/types/utils/electrum.d.ts +10 -0
  40. package/dist/types/utils/exchange-rate.d.ts +30 -0
  41. package/dist/types/utils/helpers.d.ts +23 -0
  42. package/dist/types/utils/index.d.ts +7 -0
  43. package/dist/types/utils/result.d.ts +16 -0
  44. package/dist/types/utils/transaction.d.ts +21 -0
  45. package/dist/types/utils/wallet.d.ts +47 -0
  46. package/dist/types/wallet/constants.d.ts +12 -0
  47. package/dist/types/wallet/index.d.ts +214 -0
  48. package/dist/types/wallet.js +36 -0
  49. package/dist/types/wallet.js.map +1 -0
  50. package/dist/utils/conversion.js +52 -0
  51. package/dist/utils/conversion.js.map +1 -0
  52. package/dist/utils/derivation-path.js +104 -0
  53. package/dist/utils/derivation-path.js.map +1 -0
  54. package/dist/utils/electrum.js +149 -0
  55. package/dist/utils/electrum.js.map +1 -0
  56. package/dist/utils/exchange-rate.js +49 -0
  57. package/dist/utils/exchange-rate.js.map +1 -0
  58. package/dist/utils/helpers.js +137 -0
  59. package/dist/utils/helpers.js.map +1 -0
  60. package/dist/utils/index.js +24 -0
  61. package/dist/utils/index.js.map +1 -0
  62. package/dist/utils/result.js +36 -0
  63. package/dist/utils/result.js.map +1 -0
  64. package/dist/utils/transaction.js +327 -0
  65. package/dist/utils/transaction.js.map +1 -0
  66. package/dist/utils/wallet.js +227 -0
  67. package/dist/utils/wallet.js.map +1 -0
  68. package/dist/wallet/constants.js +15 -0
  69. package/dist/wallet/constants.js.map +1 -0
  70. package/dist/wallet/index.js +2113 -0
  71. package/dist/wallet/index.js.map +1 -0
  72. package/docs/html/.nojekyll +1 -0
  73. package/docs/html/assets/highlight.css +22 -0
  74. package/docs/html/assets/main.js +58 -0
  75. package/docs/html/assets/search.js +1 -0
  76. package/docs/html/assets/style.css +1367 -0
  77. package/docs/html/classes/Electrum.html +812 -0
  78. package/docs/html/classes/Transaction.html +687 -0
  79. package/docs/html/classes/Wallet.html +2030 -0
  80. package/docs/html/enums/EAddressType.html +227 -0
  81. package/docs/html/enums/EAvailableNetworks.html +255 -0
  82. package/docs/html/enums/EBoostType.html +220 -0
  83. package/docs/html/enums/EFeeId.html +241 -0
  84. package/docs/html/enums/EPaymentType.html +220 -0
  85. package/docs/html/enums/EProtocol.html +220 -0
  86. package/docs/html/enums/EUnit.html +227 -0
  87. package/docs/html/functions/availableNetworks.html +200 -0
  88. package/docs/html/functions/constructByteCountParam.html +210 -0
  89. package/docs/html/functions/decodeOpReturnMessage.html +202 -0
  90. package/docs/html/functions/decodeRawTransaction.html +210 -0
  91. package/docs/html/functions/err.html +214 -0
  92. package/docs/html/functions/formatKeyDerivationPath.html +219 -0
  93. package/docs/html/functions/formatPeerData.html +207 -0
  94. package/docs/html/functions/generateMnemonic.html +223 -0
  95. package/docs/html/functions/generateWalletId.html +202 -0
  96. package/docs/html/functions/getAddressFromScriptPubKey.html +209 -0
  97. package/docs/html/functions/getAddressTypeFromPath.html +206 -0
  98. package/docs/html/functions/getByteCount.html +206 -0
  99. package/docs/html/functions/getDataFallback.html +207 -0
  100. package/docs/html/functions/getDefaultPort.html +209 -0
  101. package/docs/html/functions/getDefaultWalletData.html +200 -0
  102. package/docs/html/functions/getDefaultWalletDataKeys.html +200 -0
  103. package/docs/html/functions/getExchangeRates.html +200 -0
  104. package/docs/html/functions/getHighestUsedIndexFromTxHashes.html +216 -0
  105. package/docs/html/functions/getKeyDerivationPath.html +211 -0
  106. package/docs/html/functions/getKeyDerivationPathObject.html +216 -0
  107. package/docs/html/functions/getKeyDerivationPathString.html +220 -0
  108. package/docs/html/functions/getKeyValue.html +206 -0
  109. package/docs/html/functions/getPeers.html +211 -0
  110. package/docs/html/functions/getProtocolForPort.html +209 -0
  111. package/docs/html/functions/getScriptHash.html +210 -0
  112. package/docs/html/functions/getSeed.html +209 -0
  113. package/docs/html/functions/getSeedHash.html +206 -0
  114. package/docs/html/functions/getSha256.html +206 -0
  115. package/docs/html/functions/getStorageKeyValues.html +209 -0
  116. package/docs/html/functions/getTxFee.html +210 -0
  117. package/docs/html/functions/getWalletDataStorageKey.html +206 -0
  118. package/docs/html/functions/isValidBech32mEncodedString.html +211 -0
  119. package/docs/html/functions/objectKeys-1.html +211 -0
  120. package/docs/html/functions/objectsMatch.html +210 -0
  121. package/docs/html/functions/ok.html +214 -0
  122. package/docs/html/functions/parseOnChainPaymentRequest.html +204 -0
  123. package/docs/html/functions/reduceValue.html +215 -0
  124. package/docs/html/functions/removeDustOutputs.html +206 -0
  125. package/docs/html/functions/setReplaceByFee.html +210 -0
  126. package/docs/html/functions/shuffleArray.html +211 -0
  127. package/docs/html/functions/validateAddress.html +216 -0
  128. package/docs/html/functions/validateMnemonic.html +206 -0
  129. package/docs/html/functions/validateTransaction.html +206 -0
  130. package/docs/html/index.html +360 -0
  131. package/docs/html/interfaces/IAddInput.html +231 -0
  132. package/docs/html/interfaces/IAddress.html +247 -0
  133. package/docs/html/interfaces/IAddressData.html +231 -0
  134. package/docs/html/interfaces/IAddressType.html +199 -0
  135. package/docs/html/interfaces/IAddressTypeData.html +252 -0
  136. package/docs/html/interfaces/IAddresses.html +199 -0
  137. package/docs/html/interfaces/IBoostedTransaction.html +238 -0
  138. package/docs/html/interfaces/IBoostedTransactions.html +199 -0
  139. package/docs/html/interfaces/ICreateTransaction.html +224 -0
  140. package/docs/html/interfaces/ICustomGetAddress.html +231 -0
  141. package/docs/html/interfaces/ICustomGetScriptHash.html +224 -0
  142. package/docs/html/interfaces/IElectrumGetAddressBalanceRes.html +235 -0
  143. package/docs/html/interfaces/IExchangeRates.html +210 -0
  144. package/docs/html/interfaces/IFormattedPeerData.html +245 -0
  145. package/docs/html/interfaces/IFormattedTransaction.html +343 -0
  146. package/docs/html/interfaces/IFormattedTransactions.html +199 -0
  147. package/docs/html/interfaces/IGenerateAddresses.html +252 -0
  148. package/docs/html/interfaces/IGenerateAddressesResponse.html +224 -0
  149. package/docs/html/interfaces/IGetAddress.html +231 -0
  150. package/docs/html/interfaces/IGetAddressBalanceRes.html +226 -0
  151. package/docs/html/interfaces/IGetAddressByPath.html +224 -0
  152. package/docs/html/interfaces/IGetAddressHistoryResponse.html +269 -0
  153. package/docs/html/interfaces/IGetAddressResponse.html +231 -0
  154. package/docs/html/interfaces/IGetAddressScriptHashesHistoryResponse.html +245 -0
  155. package/docs/html/interfaces/IGetDerivationPath.html +259 -0
  156. package/docs/html/interfaces/IGetFeeEstimatesResponse.html +238 -0
  157. package/docs/html/interfaces/IGetHeaderResponse.html +245 -0
  158. package/docs/html/interfaces/IGetNextAvailableAddressResponse.html +238 -0
  159. package/docs/html/interfaces/IGetTransactions.html +245 -0
  160. package/docs/html/interfaces/IGetTransactionsFromInputs.html +245 -0
  161. package/docs/html/interfaces/IGetUtxosResponse.html +224 -0
  162. package/docs/html/interfaces/IHeader.html +231 -0
  163. package/docs/html/interfaces/IIndexes.html +238 -0
  164. package/docs/html/interfaces/IKeyDerivationPath.html +247 -0
  165. package/docs/html/interfaces/IKeyDerivationPathData.html +224 -0
  166. package/docs/html/interfaces/INewBlock.html +224 -0
  167. package/docs/html/interfaces/IOnchainFees.html +245 -0
  168. package/docs/html/interfaces/IOutput.html +231 -0
  169. package/docs/html/interfaces/IPeerData.html +231 -0
  170. package/docs/html/interfaces/IRbfData.html +252 -0
  171. package/docs/html/interfaces/ISend.html +224 -0
  172. package/docs/html/interfaces/ISendTransaction.html +322 -0
  173. package/docs/html/interfaces/ISendTx.html +231 -0
  174. package/docs/html/interfaces/ISetupTransaction.html +238 -0
  175. package/docs/html/interfaces/ISubscribeToAddress.html +247 -0
  176. package/docs/html/interfaces/ISubscribeToHeader.html +245 -0
  177. package/docs/html/interfaces/ITargets.html +238 -0
  178. package/docs/html/interfaces/ITransaction.html +281 -0
  179. package/docs/html/interfaces/ITxHash.html +217 -0
  180. package/docs/html/interfaces/ITxHashes.html +235 -0
  181. package/docs/html/interfaces/IUtxo.html +275 -0
  182. package/docs/html/interfaces/IVin.html +252 -0
  183. package/docs/html/interfaces/IVout.html +246 -0
  184. package/docs/html/interfaces/IWallet.html +350 -0
  185. package/docs/html/interfaces/IWalletData.html +343 -0
  186. package/docs/html/types/ElectrumConnectionPubSub.html +233 -0
  187. package/docs/html/types/ElectrumConnectionSubscription.html +204 -0
  188. package/docs/html/types/InputData.html +203 -0
  189. package/docs/html/types/ObjectKeys.html +198 -0
  190. package/docs/html/types/Result.html +201 -0
  191. package/docs/html/types/TAddressIndexInfo.html +204 -0
  192. package/docs/html/types/TAddressLabel.html +193 -0
  193. package/docs/html/types/TAddressType.html +193 -0
  194. package/docs/html/types/TAddressTypeContent.html +198 -0
  195. package/docs/html/types/TAddressTypes.html +193 -0
  196. package/docs/html/types/TAvailableNetworks.html +193 -0
  197. package/docs/html/types/TConnectToElectrumRes.html +193 -0
  198. package/docs/html/types/TDecodeRawTx.html +214 -0
  199. package/docs/html/types/TElectrumNetworks.html +193 -0
  200. package/docs/html/types/TGetAddressHistory.html +200 -0
  201. package/docs/html/types/TGetByteCountInput.html +193 -0
  202. package/docs/html/types/TGetByteCountInputs.html +193 -0
  203. package/docs/html/types/TGetByteCountOutput.html +193 -0
  204. package/docs/html/types/TGetByteCountOutputs.html +193 -0
  205. package/docs/html/types/TGetData.html +211 -0
  206. package/docs/html/types/TGetTotalFeeObj.html +204 -0
  207. package/docs/html/types/TKeyDerivationAccount.html +193 -0
  208. package/docs/html/types/TKeyDerivationChange.html +193 -0
  209. package/docs/html/types/TKeyDerivationCoinType.html +193 -0
  210. package/docs/html/types/TKeyDerivationIndex.html +193 -0
  211. package/docs/html/types/TKeyDerivationPurpose.html +193 -0
  212. package/docs/html/types/TMessageDataMap.html +210 -0
  213. package/docs/html/types/TMessageKeys.html +193 -0
  214. package/docs/html/types/TOnMessage.html +213 -0
  215. package/docs/html/types/TProcessUnconfirmedTransactions.html +202 -0
  216. package/docs/html/types/TProtocol.html +193 -0
  217. package/docs/html/types/TServer.html +204 -0
  218. package/docs/html/types/TSetData.html +213 -0
  219. package/docs/html/types/TSetupTransactionResponse.html +193 -0
  220. package/docs/html/types/TStorage.html +200 -0
  221. package/docs/html/types/TSubscribedReceive.html +193 -0
  222. package/docs/html/types/TTransactionMessage.html +198 -0
  223. package/docs/html/types/TTxResponse.html +206 -0
  224. package/docs/html/types/TTxResult.html +200 -0
  225. package/docs/html/types/TUnspentAddressScriptHashData.html +198 -0
  226. package/docs/html/types/TWalletDataKeys.html +193 -0
  227. package/docs/html/variables/defaultElectrumPorts.html +193 -0
  228. package/docs/html/variables/electrumConnection.html +199 -0
  229. package/docs/html/variables/mostUsedExchangeTickers.html +241 -0
  230. package/docs/markdown/.nojekyll +1 -0
  231. package/docs/markdown/README.md +1875 -0
  232. package/docs/markdown/classes/Electrum.md +718 -0
  233. package/docs/markdown/classes/Transaction.md +536 -0
  234. package/docs/markdown/classes/Wallet.md +2189 -0
  235. package/docs/markdown/enums/EAddressType.md +41 -0
  236. package/docs/markdown/enums/EAvailableNetworks.md +85 -0
  237. package/docs/markdown/enums/EBoostType.md +30 -0
  238. package/docs/markdown/enums/EFeeId.md +63 -0
  239. package/docs/markdown/enums/EPaymentType.md +30 -0
  240. package/docs/markdown/enums/EProtocol.md +30 -0
  241. package/docs/markdown/enums/EUnit.md +41 -0
  242. package/docs/markdown/interfaces/IAddInput.md +41 -0
  243. package/docs/markdown/interfaces/IAddress.md +69 -0
  244. package/docs/markdown/interfaces/IAddressData.md +41 -0
  245. package/docs/markdown/interfaces/IAddressType.md +7 -0
  246. package/docs/markdown/interfaces/IAddressTypeData.md +74 -0
  247. package/docs/markdown/interfaces/IAddresses.md +7 -0
  248. package/docs/markdown/interfaces/IBoostedTransaction.md +52 -0
  249. package/docs/markdown/interfaces/IBoostedTransactions.md +7 -0
  250. package/docs/markdown/interfaces/ICreateTransaction.md +30 -0
  251. package/docs/markdown/interfaces/ICustomGetAddress.md +41 -0
  252. package/docs/markdown/interfaces/ICustomGetScriptHash.md +30 -0
  253. package/docs/markdown/interfaces/IElectrumGetAddressBalanceRes.md +55 -0
  254. package/docs/markdown/interfaces/IExchangeRates.md +7 -0
  255. package/docs/markdown/interfaces/IFormattedPeerData.md +63 -0
  256. package/docs/markdown/interfaces/IFormattedTransaction.md +217 -0
  257. package/docs/markdown/interfaces/IFormattedTransactions.md +7 -0
  258. package/docs/markdown/interfaces/IGenerateAddresses.md +74 -0
  259. package/docs/markdown/interfaces/IGenerateAddressesResponse.md +30 -0
  260. package/docs/markdown/interfaces/IGetAddress.md +41 -0
  261. package/docs/markdown/interfaces/IGetAddressBalanceRes.md +36 -0
  262. package/docs/markdown/interfaces/IGetAddressByPath.md +30 -0
  263. package/docs/markdown/interfaces/IGetAddressHistoryResponse.md +121 -0
  264. package/docs/markdown/interfaces/IGetAddressResponse.md +41 -0
  265. package/docs/markdown/interfaces/IGetAddressScriptHashesHistoryResponse.md +63 -0
  266. package/docs/markdown/interfaces/IGetDerivationPath.md +100 -0
  267. package/docs/markdown/interfaces/IGetFeeEstimatesResponse.md +52 -0
  268. package/docs/markdown/interfaces/IGetHeaderResponse.md +63 -0
  269. package/docs/markdown/interfaces/IGetNextAvailableAddressResponse.md +52 -0
  270. package/docs/markdown/interfaces/IGetTransactions.md +63 -0
  271. package/docs/markdown/interfaces/IGetTransactionsFromInputs.md +63 -0
  272. package/docs/markdown/interfaces/IGetUtxosResponse.md +30 -0
  273. package/docs/markdown/interfaces/IHeader.md +41 -0
  274. package/docs/markdown/interfaces/IIndexes.md +52 -0
  275. package/docs/markdown/interfaces/IKeyDerivationPath.md +69 -0
  276. package/docs/markdown/interfaces/IKeyDerivationPathData.md +30 -0
  277. package/docs/markdown/interfaces/INewBlock.md +30 -0
  278. package/docs/markdown/interfaces/IOnchainFees.md +63 -0
  279. package/docs/markdown/interfaces/IOutput.md +41 -0
  280. package/docs/markdown/interfaces/IPeerData.md +41 -0
  281. package/docs/markdown/interfaces/IRbfData.md +74 -0
  282. package/docs/markdown/interfaces/ISend.md +30 -0
  283. package/docs/markdown/interfaces/ISendTransaction.md +184 -0
  284. package/docs/markdown/interfaces/ISendTx.md +41 -0
  285. package/docs/markdown/interfaces/ISetupTransaction.md +52 -0
  286. package/docs/markdown/interfaces/ISubscribeToAddress.md +60 -0
  287. package/docs/markdown/interfaces/ISubscribeToHeader.md +59 -0
  288. package/docs/markdown/interfaces/ITargets.md +52 -0
  289. package/docs/markdown/interfaces/ITransaction.md +88 -0
  290. package/docs/markdown/interfaces/ITxHash.md +19 -0
  291. package/docs/markdown/interfaces/ITxHashes.md +55 -0
  292. package/docs/markdown/interfaces/IUtxo.md +112 -0
  293. package/docs/markdown/interfaces/IVin.md +70 -0
  294. package/docs/markdown/interfaces/IVout.md +52 -0
  295. package/docs/markdown/interfaces/IWallet.md +209 -0
  296. package/docs/markdown/interfaces/IWalletData.md +217 -0
  297. package/example/helpers.ts +76 -0
  298. package/example/index.ts +77 -0
  299. package/package.json +69 -0
  300. package/src/electrum/constants.ts +28 -0
  301. package/src/electrum/index.ts +838 -0
  302. package/src/index.ts +6 -0
  303. package/src/shapes/electrum.ts +13 -0
  304. package/src/shapes/index.ts +2 -0
  305. package/src/shapes/wallet.ts +133 -0
  306. package/src/transaction/index.ts +1078 -0
  307. package/src/types/electrum.ts +139 -0
  308. package/src/types/index.ts +3 -0
  309. package/src/types/transaction.ts +58 -0
  310. package/src/types/wallet.ts +503 -0
  311. package/src/utils/conversion.ts +88 -0
  312. package/src/utils/derivation-path.ts +171 -0
  313. package/src/utils/electrum.ts +188 -0
  314. package/src/utils/exchange-rate.ts +70 -0
  315. package/src/utils/helpers.ts +191 -0
  316. package/src/utils/index.ts +7 -0
  317. package/src/utils/result.ts +79 -0
  318. package/src/utils/transaction.ts +417 -0
  319. package/src/utils/wallet.ts +351 -0
  320. package/src/wallet/constants.ts +24 -0
  321. package/src/wallet/index.ts +3304 -0
  322. package/tests/constants.ts +9 -0
  323. package/tests/derivation.test.ts +146 -0
  324. package/tests/electrum.test.ts +137 -0
  325. package/tests/storage.test.ts +222 -0
  326. package/tests/transaction.test.ts +187 -0
  327. package/tests/wallet.test.ts +307 -0
  328. package/tsconfig.json +32 -0
  329. package/typedoc.json +5 -0
@@ -0,0 +1,1078 @@
1
+ import {
2
+ EAddressType,
3
+ EBoostType,
4
+ EFeeId,
5
+ IAddresses,
6
+ IOutput,
7
+ ISendTransaction,
8
+ IUtxo,
9
+ TGetTotalFeeObj
10
+ } from '../types';
11
+ import { getDefaultSendTransaction } from '../shapes';
12
+ import { Wallet } from '../wallet';
13
+ import { Result, ok, err, validateTransaction } from '../utils';
14
+ import { reduceValue, shuffleArray } from '../utils';
15
+ import { TRANSACTION_DEFAULTS } from '../wallet/constants';
16
+ import {
17
+ constructByteCountParam,
18
+ getByteCount,
19
+ removeDustOutputs,
20
+ setReplaceByFee
21
+ } from '../utils';
22
+ import {
23
+ IAddInput,
24
+ ICreateTransaction,
25
+ ISetupTransaction,
26
+ ITargets,
27
+ TSetupTransactionResponse
28
+ } from '../types';
29
+ import { networks, Psbt } from 'bitcoinjs-lib';
30
+ import { BIP32Interface } from 'bip32';
31
+ import ecc from '@bitcoinerlab/secp256k1';
32
+ import * as bitcoin from 'bitcoinjs-lib';
33
+ import { getAddressInfo } from 'bitcoin-address-validation';
34
+
35
+ bitcoin.initEccLib(ecc);
36
+
37
+ export class Transaction {
38
+ private _data: ISendTransaction;
39
+ private readonly _wallet: Wallet;
40
+
41
+ constructor({ wallet }: { wallet: Wallet }) {
42
+ this._wallet = wallet;
43
+ this._data = getDefaultSendTransaction();
44
+ }
45
+
46
+ public get data(): ISendTransaction {
47
+ return this._data;
48
+ }
49
+
50
+ /**
51
+ * Sets up a transaction for a given wallet by gathering inputs, setting the next available change address as an output and sets up the baseline fee structure.
52
+ * This function will not override previously set transaction data. To do that you'll need to call resetSendTransaction.
53
+ * @param {string[]} [inputTxHashes]
54
+ * @param {IUtxo[]} [utxos]
55
+ * @param {boolean} [rbf]
56
+ * @param {number} [satsPerByte]
57
+ * @returns {Promise<Result<Partial<ISendTransaction>>>}
58
+ */
59
+ public async setupTransaction({
60
+ inputTxHashes,
61
+ utxos,
62
+ rbf = false,
63
+ satsPerByte = 1
64
+ }: ISetupTransaction = {}): Promise<TSetupTransactionResponse> {
65
+ try {
66
+ const addressType = this._wallet.data.addressType;
67
+
68
+ const currentWallet = this._wallet.data;
69
+
70
+ const transaction = currentWallet.transaction;
71
+
72
+ // Gather required inputs.
73
+ let inputs: IUtxo[] = [];
74
+ if (inputTxHashes) {
75
+ // If specified, filter for the desired tx_hash and push the utxo as an input.
76
+ inputs = currentWallet.utxos.filter((utxo) => {
77
+ return inputTxHashes.includes(utxo.tx_hash);
78
+ });
79
+ } else if (utxos) {
80
+ inputs = utxos;
81
+ } else {
82
+ inputs = currentWallet.utxos;
83
+ }
84
+
85
+ if (!inputs.length) {
86
+ // If inputs were previously selected, leave them.
87
+ if (transaction.inputs.length > 0) {
88
+ inputs = transaction.inputs;
89
+ } else {
90
+ // Otherwise, lets use our available utxo's.
91
+ //inputs = currentWallet.utxos;
92
+ inputs = this.removeBlackListedUtxos(currentWallet.utxos);
93
+ }
94
+ }
95
+
96
+ if (!inputs.length) {
97
+ return err('No inputs specified.');
98
+ }
99
+
100
+ const currentChangeAddresses = currentWallet.changeAddresses;
101
+
102
+ const addressTypeKeys = Object.values(EAddressType);
103
+ let changeAddresses: IAddresses = {};
104
+ addressTypeKeys.forEach((key) => {
105
+ changeAddresses = {
106
+ ...changeAddresses,
107
+ ...currentChangeAddresses[key]
108
+ };
109
+ });
110
+ const changeAddressesArr = Object.values(changeAddresses).map(
111
+ ({ address }) => address
112
+ );
113
+
114
+ const changeAddressIndexContent =
115
+ currentWallet.changeAddressIndex[addressType];
116
+ // Set the current change address.
117
+ const changeAddress = changeAddressIndexContent.address;
118
+
119
+ // The following code is no longer needed in beignet, but keeping until 1:1 migration with Bitkit is complete.
120
+ // if (!changeAddress || changeAddressIndexContent.index < 0) {
121
+ // // It's possible we haven't set the change address index yet. Generate one on the fly.
122
+ // const generateAddressResponse = await this._wallet.generateAddresses({
123
+ // addressAmount: 0,
124
+ // changeAddressAmount: 1,
125
+ // addressType
126
+ // });
127
+ // if (generateAddressResponse.isErr()) {
128
+ // return err(generateAddressResponse.error.message);
129
+ // }
130
+ // changeAddress =
131
+ // generateAddressResponse.value.changeAddresses[0].address;
132
+ // }
133
+ if (!changeAddress) {
134
+ return err('Unable to successfully generate a change address.');
135
+ }
136
+
137
+ // Set the minimum fee.
138
+ const fee = this.getTotalFee({
139
+ satsPerByte,
140
+ message: ''
141
+ });
142
+
143
+ const lightningInvoice = currentWallet.transaction?.lightningInvoice;
144
+
145
+ let outputs = currentWallet.transaction.outputs || [];
146
+ if (!lightningInvoice) {
147
+ //Remove any potential change address that may have been included from a previous tx attempt.
148
+ outputs = outputs.filter((output) => {
149
+ if (output.address && !changeAddressesArr.includes(output.address)) {
150
+ return output;
151
+ }
152
+ });
153
+ }
154
+
155
+ const payload = {
156
+ inputs,
157
+ changeAddress,
158
+ fee,
159
+ outputs,
160
+ rbf,
161
+ satsPerByte
162
+ };
163
+
164
+ this._data = {
165
+ ...this._data,
166
+ ...payload
167
+ };
168
+
169
+ // Save the transaction data.
170
+ await this._wallet.saveWalletData('transaction', this._data);
171
+
172
+ return ok(payload);
173
+ } catch (e) {
174
+ return err(e);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * This completely resets the send transaction state.
180
+ * @returns {Promise<Result<string>>}
181
+ */
182
+ async resetSendTransaction(): Promise<Result<string>> {
183
+ this._data = getDefaultSendTransaction();
184
+ await this._wallet.saveWalletData('transaction', this._data);
185
+ return ok('Transaction reset.');
186
+ }
187
+
188
+ /**
189
+ * Removes blacklisted UTXO's from the UTXO array.
190
+ * @param utxos
191
+ */
192
+ removeBlackListedUtxos(utxos?: IUtxo[]): IUtxo[] {
193
+ if (!utxos) {
194
+ utxos = this._wallet.data.utxos;
195
+ }
196
+ const blacklistedUtxos = this._wallet.data.blacklistedUtxos;
197
+ return utxos.filter((utxo) => {
198
+ return !blacklistedUtxos.find(
199
+ (blacklistedUtxo) =>
200
+ utxo.address === blacklistedUtxo.address &&
201
+ utxo.height === blacklistedUtxo.height &&
202
+ utxo.tx_hash === blacklistedUtxo.tx_hash &&
203
+ utxo.tx_pos === blacklistedUtxo.tx_pos
204
+ );
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Attempt to estimate the current fee for a given transaction and its UTXO's
210
+ * @param {number} [satsPerByte]
211
+ * @param {string} [message]
212
+ * @param {Partial<ISendTransaction>} [transaction]
213
+ * @param {boolean} [fundingLightning]
214
+ * @returns {number}
215
+ */
216
+ getTotalFee = ({
217
+ satsPerByte,
218
+ message = '',
219
+ transaction = this.data,
220
+ fundingLightning = false
221
+ }: {
222
+ satsPerByte: number;
223
+ message?: string;
224
+ transaction?: Partial<ISendTransaction>;
225
+ fundingLightning?: boolean;
226
+ }): number => {
227
+ const baseTransactionSize = TRANSACTION_DEFAULTS.recommendedBaseFee;
228
+ try {
229
+ const inputs = transaction.inputs || [];
230
+ const outputs = transaction.outputs || [];
231
+ const changeAddress = transaction.changeAddress;
232
+
233
+ //Group all input & output addresses into their respective array.
234
+ const inputAddresses = inputs.map((input) => input.address);
235
+ const outputAddresses = outputs.map((output) => output.address);
236
+
237
+ //No need for a change address when draining the wallet
238
+ if (changeAddress && !transaction.max) {
239
+ outputAddresses.push(changeAddress);
240
+ }
241
+
242
+ //Determine the address type of each address and construct the object for fee calculation
243
+ const inputParam = constructByteCountParam(inputAddresses);
244
+ const outputParam = constructByteCountParam(outputAddresses);
245
+ //Increase P2WPKH output address by one for lightning funding calculation.
246
+ if (fundingLightning) {
247
+ outputParam.P2WPKH = (outputParam.P2WPKH || 0) + 1;
248
+ }
249
+
250
+ const transactionByteCount = getByteCount(
251
+ inputParam,
252
+ outputParam,
253
+ message
254
+ );
255
+ return transactionByteCount * satsPerByte;
256
+ } catch {
257
+ return baseTransactionSize * satsPerByte;
258
+ }
259
+ };
260
+
261
+ /**
262
+ * Attempt to estimate the current fee for a given transaction and its UTXO's
263
+ * @param {number} [satsPerByte]
264
+ * @param {string} [message]
265
+ * @param {Partial<ISendTransaction>} [transaction]
266
+ * @param {boolean} [fundingLightning]
267
+ * @returns {Result<TGetTotalFeeObj>}
268
+ */
269
+ getTotalFeeObj = ({
270
+ satsPerByte = this._wallet.feeEstimates.normal,
271
+ message = '',
272
+ transaction = this.data,
273
+ fundingLightning = false
274
+ }: {
275
+ satsPerByte?: number;
276
+ message?: string;
277
+ transaction?: Partial<ISendTransaction>;
278
+ fundingLightning?: boolean;
279
+ } = {}): Result<TGetTotalFeeObj> => {
280
+ try {
281
+ if (!transaction.inputs?.length) {
282
+ this.setupTransaction({});
283
+ transaction = this.data;
284
+ }
285
+ const inputs = transaction.inputs || [];
286
+ const outputs = transaction.outputs || [];
287
+ const changeAddress = transaction.changeAddress;
288
+
289
+ //Group all input & output addresses into their respective array.
290
+ const inputAddresses = inputs.map((input) => input.address);
291
+ const outputAddresses = outputs.map((output) => output.address);
292
+
293
+ // Always assume we're sending to at least one output for a proper base calculation.
294
+ let increaseAddressCount = 0;
295
+ if (!outputAddresses.length) {
296
+ increaseAddressCount++;
297
+ }
298
+ //No need for a change address when draining the wallet
299
+ if (changeAddress && !transaction.max) {
300
+ outputAddresses.push(changeAddress);
301
+ }
302
+
303
+ //Determine the address type of each address and construct the object for fee calculation
304
+ const inputParam = constructByteCountParam(inputAddresses);
305
+ const outputParam = constructByteCountParam(outputAddresses, [
306
+ { addrType: this._wallet.data.addressType, count: increaseAddressCount }
307
+ ]);
308
+ //Increase P2WPKH output address by one for lightning funding calculation.
309
+ if (fundingLightning) {
310
+ outputParam.P2WPKH = (outputParam.P2WPKH || 0) + 1;
311
+ }
312
+
313
+ const transactionByteCount = getByteCount(
314
+ inputParam,
315
+ outputParam,
316
+ message
317
+ );
318
+ const maxSatPerByte = this.getMaxSatsPerByte({
319
+ transactionByteCount,
320
+ balance: this._wallet.getBalance()
321
+ });
322
+ if (maxSatPerByte < satsPerByte) {
323
+ return ok({
324
+ totalFee: transactionByteCount * maxSatPerByte,
325
+ transactionByteCount: maxSatPerByte ? transactionByteCount : 0,
326
+ satsPerByte: maxSatPerByte,
327
+ maxSatPerByte
328
+ });
329
+ }
330
+ return ok({
331
+ totalFee: transactionByteCount * satsPerByte,
332
+ transactionByteCount,
333
+ satsPerByte,
334
+ maxSatPerByte
335
+ });
336
+ } catch (e) {
337
+ return err(e);
338
+ }
339
+ };
340
+
341
+ /**
342
+ * Returns the maximum sats per byte that can be used for a given transaction.
343
+ * @param {number} transactionByteCount
344
+ * @param {number} [balance]
345
+ * @returns {number}
346
+ */
347
+ getMaxSatsPerByte = ({
348
+ transactionByteCount,
349
+ balance = this._wallet.getBalance()
350
+ }: {
351
+ transactionByteCount: number;
352
+ balance?: number;
353
+ }): number => {
354
+ return Math.floor(balance / (2 * transactionByteCount));
355
+ };
356
+
357
+ /**
358
+ * Creates complete signed transaction using the transaction data store
359
+ * @param {ISendTransaction} [transactionData]
360
+ * @param {boolean} [shuffleOutputs]
361
+ * @returns {Promise<Result<{id: string, hex: string}>>}
362
+ */
363
+ createTransaction = async ({
364
+ transactionData = this.data,
365
+ shuffleOutputs = true
366
+ }: ICreateTransaction = {}): Promise<Result<{ id: string; hex: string }>> => {
367
+ //Remove any outputs that are below the dust limit and apply them to the fee.
368
+ removeDustOutputs(transactionData.outputs);
369
+
370
+ const inputValue = this.getTransactionInputValue({
371
+ inputs: transactionData.inputs
372
+ });
373
+ const outputValue = this.getTransactionOutputValue({
374
+ outputs: transactionData.outputs
375
+ });
376
+ if (inputValue === 0) {
377
+ const message = 'No inputs to spend.';
378
+ return err(message);
379
+ }
380
+ const fee = inputValue - outputValue;
381
+
382
+ //Refuse tx if the fee is greater than the amount we're attempting to send.
383
+ if (fee > inputValue) {
384
+ const message = 'Fee is larger than the intended payment.';
385
+ return err(message);
386
+ }
387
+
388
+ const validateRes = validateTransaction(transactionData);
389
+ if (validateRes.isErr()) return err(validateRes.error.message);
390
+
391
+ try {
392
+ const bip32InterfaceRes = await this._wallet.getBip32Interface();
393
+ if (bip32InterfaceRes.isErr()) {
394
+ return err(bip32InterfaceRes.error.message);
395
+ }
396
+
397
+ //Create PSBT before signing inputs
398
+ const psbtRes = await this.createPsbtFromTransactionData({
399
+ transactionData,
400
+ bip32Interface: bip32InterfaceRes.value,
401
+ shuffleTargets: shuffleOutputs
402
+ });
403
+
404
+ if (psbtRes.isErr()) {
405
+ return err(psbtRes.error);
406
+ }
407
+
408
+ const psbt = psbtRes.value;
409
+
410
+ const signedPsbtRes = await this.signPsbt({
411
+ psbt,
412
+ bip32Interface: bip32InterfaceRes.value
413
+ });
414
+
415
+ if (signedPsbtRes.isErr()) {
416
+ return err(signedPsbtRes.error);
417
+ }
418
+
419
+ const tx = signedPsbtRes.value.extractTransaction();
420
+ const id = tx.getId();
421
+ const hex = tx.toHex();
422
+ return ok({ id, hex });
423
+ } catch (e) {
424
+ return err(e);
425
+ }
426
+ };
427
+
428
+ /**
429
+ * Returns total value of all utxos.
430
+ * @param {IUtxo[]} [inputs]
431
+ */
432
+ getTransactionInputValue = ({ inputs }: { inputs?: IUtxo[] }): number => {
433
+ try {
434
+ if (!inputs) {
435
+ const transaction = this.data;
436
+ inputs = transaction.inputs;
437
+ }
438
+ if (inputs) {
439
+ const response = reduceValue({ arr: inputs, value: 'value' });
440
+ if (response.isOk()) {
441
+ return response.value;
442
+ }
443
+ }
444
+ return 0;
445
+ } catch (e) {
446
+ return 0;
447
+ }
448
+ };
449
+
450
+ /**
451
+ * Loops through inputs and signs them
452
+ * @param {Psbt} psbt
453
+ * @param {BIP32Interface} bip32Interface
454
+ * @returns {Promise<Result<Psbt>>}
455
+ */
456
+ signPsbt = async ({
457
+ psbt,
458
+ bip32Interface
459
+ }: {
460
+ psbt: Psbt;
461
+ bip32Interface: BIP32Interface;
462
+ }): Promise<Result<Psbt>> => {
463
+ const transactionDataRes = this.data;
464
+
465
+ const { inputs } = transactionDataRes;
466
+ for (const [index, input] of inputs.entries()) {
467
+ try {
468
+ const keyPair = bip32Interface.derivePath(input.path);
469
+ psbt.signInput(index, keyPair);
470
+ } catch (e) {
471
+ return err(e);
472
+ }
473
+ }
474
+
475
+ psbt.finalizeAllInputs();
476
+
477
+ return ok(psbt);
478
+ };
479
+
480
+ /**
481
+ * Returns a PSBT that includes unsigned funding inputs.
482
+ * @param {ISendTransaction} transactionData
483
+ * @param {BIP32Interface} bip32Interface
484
+ * @param shuffleTargets
485
+ * @returns {Promise<Result<Psbt>>}
486
+ */
487
+ createPsbtFromTransactionData = async ({
488
+ transactionData,
489
+ bip32Interface,
490
+ shuffleTargets = true
491
+ }: {
492
+ transactionData: ISendTransaction;
493
+ bip32Interface?: BIP32Interface;
494
+ shuffleTargets?: boolean;
495
+ }): Promise<Result<Psbt>> => {
496
+ const { inputs, outputs, fee, rbf } = transactionData;
497
+ let { changeAddress, message } = transactionData;
498
+
499
+ //Get balance of current inputs.
500
+ const balance = this.getTransactionInputValue({
501
+ inputs
502
+ });
503
+
504
+ //Get value of current outputs.
505
+ const outputValue = this.getTransactionOutputValue({
506
+ outputs
507
+ });
508
+
509
+ const network = networks[this._wallet.network];
510
+
511
+ //Collect all outputs.
512
+ let targets: ITargets[] = outputs.concat();
513
+
514
+ //Change address and amount to send back to wallet.
515
+ if (changeAddress) {
516
+ const changeAddressValue = balance - (outputValue + fee);
517
+ // Ensure we're not creating unspendable dust.
518
+ // If we have less than 2x the recommended base fee, just contribute it to the fee in this transaction.
519
+ if (changeAddressValue > TRANSACTION_DEFAULTS.dustLimit) {
520
+ targets.push({
521
+ address: changeAddress,
522
+ value: changeAddressValue,
523
+ index: targets.length
524
+ });
525
+ }
526
+ // Looks like we don't need a change address.
527
+ // Double check we don't have any spare sats hanging around.
528
+ } else if (outputValue + fee < balance) {
529
+ // If we have spare sats hanging around and the difference is greater than the dust limit, generate a changeAddress to send them to.
530
+ const diffValue = balance - (outputValue + fee);
531
+ if (diffValue > TRANSACTION_DEFAULTS.dustLimit) {
532
+ const changeAddressRes = await this._wallet.getChangeAddress();
533
+ if (changeAddressRes.isErr()) {
534
+ return err(changeAddressRes.error.message);
535
+ }
536
+ changeAddress = changeAddressRes.value.address;
537
+ targets.push({
538
+ address: changeAddress,
539
+ value: diffValue,
540
+ index: targets.length
541
+ });
542
+ }
543
+ }
544
+
545
+ //Embed any OP_RETURN messages.
546
+ if (message.trim() !== '') {
547
+ const messageLength = message.length;
548
+ const lengthMin = 5;
549
+ //This is a patch for the following: https://github.com/coreyphillips/moonshine/issues/52
550
+ if (messageLength > 0 && messageLength < lengthMin) {
551
+ message += ' '.repeat(lengthMin - messageLength);
552
+ }
553
+ const data = Buffer.from(message, 'utf8');
554
+ const embed = bitcoin.payments.embed({
555
+ data: [data],
556
+ network
557
+ });
558
+ targets.push({ script: embed.output!, value: 0, index: targets.length });
559
+ }
560
+
561
+ if (!bip32Interface) {
562
+ const bip32InterfaceRes = await this._wallet.getBip32Interface();
563
+ if (bip32InterfaceRes.isErr()) {
564
+ return err(bip32InterfaceRes.error.message);
565
+ }
566
+ bip32Interface = bip32InterfaceRes.value;
567
+ }
568
+
569
+ const root = bip32Interface;
570
+ const psbt = new bitcoin.Psbt({ network });
571
+
572
+ //Add Inputs from inputs array
573
+ try {
574
+ for (const input of inputs) {
575
+ const path = input.path;
576
+ const keyPair: BIP32Interface = root.derivePath(path);
577
+ await this.addInput({
578
+ psbt,
579
+ keyPair,
580
+ input
581
+ });
582
+ }
583
+ } catch (e) {
584
+ return err(e);
585
+ }
586
+
587
+ //Set RBF if supported and prompted via rbf in Settings.
588
+ setReplaceByFee({ psbt, setRbf: !!rbf });
589
+
590
+ // Shuffle targets if not run from unit test and add outputs.
591
+ if (shuffleTargets) {
592
+ targets = shuffleArray(targets);
593
+ }
594
+
595
+ targets.forEach((target) => {
596
+ //Check if OP_RETURN
597
+ let isOpReturn = false;
598
+ try {
599
+ isOpReturn = !!target.script;
600
+ } catch (e) {}
601
+ if (isOpReturn) {
602
+ if (target.script) {
603
+ psbt.addOutput({
604
+ script: target.script,
605
+ value: target.value ?? 0
606
+ });
607
+ }
608
+ } else {
609
+ if (target.address && target.value) {
610
+ psbt.addOutput({
611
+ address: target.address,
612
+ value: target.value
613
+ });
614
+ }
615
+ }
616
+ });
617
+
618
+ return ok(psbt);
619
+ };
620
+
621
+ addInput = async ({
622
+ psbt,
623
+ keyPair,
624
+ input
625
+ }: IAddInput): Promise<Result<string>> => {
626
+ try {
627
+ const network = networks[this._wallet.network];
628
+ const { type } = getAddressInfo(input.address);
629
+
630
+ if (!input.value) {
631
+ return err('No input provided.');
632
+ }
633
+
634
+ if (type === 'p2wpkh') {
635
+ const p2wpkh = bitcoin.payments.p2wpkh({
636
+ pubkey: keyPair.publicKey,
637
+ network
638
+ });
639
+ if (!p2wpkh?.output) {
640
+ return err('p2wpkh.output is undefined.');
641
+ }
642
+ psbt.addInput({
643
+ hash: input.tx_hash,
644
+ index: input.tx_pos,
645
+ witnessUtxo: {
646
+ script: p2wpkh.output,
647
+ value: input.value
648
+ }
649
+ });
650
+ }
651
+
652
+ if (type === 'p2sh') {
653
+ const p2wpkh = bitcoin.payments.p2wpkh({
654
+ pubkey: keyPair.publicKey,
655
+ network
656
+ });
657
+ const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network });
658
+ if (!p2sh?.output) {
659
+ return err('p2sh.output is undefined.');
660
+ }
661
+ if (!p2sh?.redeem) {
662
+ return err('p2sh.redeem.output is undefined.');
663
+ }
664
+ psbt.addInput({
665
+ hash: input.tx_hash,
666
+ index: input.tx_pos,
667
+ witnessUtxo: {
668
+ script: p2sh.output,
669
+ value: input.value
670
+ },
671
+ redeemScript: p2sh.redeem.output
672
+ });
673
+ }
674
+
675
+ if (type === 'p2pkh') {
676
+ const transaction = await this._wallet.electrum.getTransactions({
677
+ txHashes: [{ tx_hash: input.tx_hash }]
678
+ });
679
+ if (transaction.isErr()) {
680
+ return err(transaction.error.message);
681
+ }
682
+ const hex = transaction.value.data[0].result.hex;
683
+ const nonWitnessUtxo = Buffer.from(hex, 'hex');
684
+ psbt.addInput({
685
+ hash: input.tx_hash,
686
+ index: input.tx_pos,
687
+ nonWitnessUtxo
688
+ });
689
+ }
690
+ return ok('Success');
691
+ } catch {
692
+ return err('Unable to add input.');
693
+ }
694
+ };
695
+
696
+ /**
697
+ * Adds an output at the specified index to the current transaction.
698
+ * @param {string} address
699
+ * @param {number} value
700
+ * @param {number} [index]
701
+ * @returns {IOutput}
702
+ */
703
+ addOutput = async ({
704
+ address,
705
+ value,
706
+ index = 0
707
+ }: IOutput): Promise<Result<string>> => {
708
+ if (!this.data.inputs?.length) {
709
+ const setupRes = await this.setupTransaction();
710
+ if (setupRes.isErr()) return err(setupRes.error.message);
711
+ }
712
+ return this.updateSendTransaction({
713
+ transaction: {
714
+ outputs: [{ address, value, index }]
715
+ }
716
+ });
717
+ };
718
+
719
+ /**
720
+ * Returns total value of all outputs. Excludes any value that would be sent to the change address.
721
+ * @param {IOutput[]} [outputs]
722
+ * @returns {number}
723
+ */
724
+ getTransactionOutputValue = ({
725
+ outputs
726
+ }: {
727
+ outputs?: IOutput[];
728
+ } = {}): number => {
729
+ try {
730
+ if (!outputs) {
731
+ const transaction = this.data;
732
+ outputs = transaction.outputs;
733
+ }
734
+ const response = reduceValue({ arr: outputs, value: 'value' });
735
+ if (response.isOk()) {
736
+ return response.value;
737
+ }
738
+ return 0;
739
+ } catch {
740
+ return 0;
741
+ }
742
+ };
743
+
744
+ /**
745
+ * This updates the transaction state used for sending.
746
+ * @param {Partial<ISendTransaction>} transaction
747
+ * @return {Promise<Result<string>>}
748
+ */
749
+ updateSendTransaction = ({
750
+ transaction
751
+ }: {
752
+ transaction: Partial<ISendTransaction>;
753
+ }): Result<string> => {
754
+ try {
755
+ //Add output if specified
756
+ if (transaction.outputs) {
757
+ const currentTransaction = this._wallet.transaction.data;
758
+ const outputs = currentTransaction.outputs.concat();
759
+ transaction.outputs.forEach((output) => {
760
+ outputs[output.index] = output;
761
+ });
762
+ transaction.outputs = outputs;
763
+ }
764
+
765
+ this._data = {
766
+ ...this._data,
767
+ ...transaction
768
+ };
769
+
770
+ this._wallet.saveWalletData('transaction', this.data).then();
771
+
772
+ return ok('Transaction updated');
773
+ } catch (e) {
774
+ return err(e);
775
+ }
776
+ };
777
+
778
+ /**
779
+ * Updates the fee for the current transaction by the specified amount.
780
+ * @param {number} [satsPerByte]
781
+ * @param {EFeeId} [selectedFeeId]
782
+ * @param {number} [index]
783
+ * @param {ISendTransaction} [transaction]
784
+ * @returns {Result<{ fee: number }>}
785
+ */
786
+ public updateFee({
787
+ satsPerByte,
788
+ selectedFeeId = EFeeId.custom,
789
+ index = 0,
790
+ transaction = this.data
791
+ }: {
792
+ satsPerByte: number;
793
+ selectedFeeId?: EFeeId;
794
+ index?: number;
795
+ transaction?: ISendTransaction;
796
+ }): Result<{ fee: number }> {
797
+ const inputTotal = this.getTransactionInputValue({
798
+ inputs: transaction.inputs
799
+ });
800
+
801
+ const { max, message, outputs } = transaction;
802
+ let address = '';
803
+ if (outputs.length > index) {
804
+ address = outputs[index]?.address ?? '';
805
+ }
806
+
807
+ const newFee = this.getTotalFee({ satsPerByte, message });
808
+
809
+ //Return if the new fee exceeds half of the user's balance
810
+ if (newFee >= inputTotal / 2) {
811
+ return err(
812
+ 'Unable to increase the fee any further. Otherwise, it will exceed half the current balance.'
813
+ );
814
+ }
815
+
816
+ const totalTransactionValue = this.getTransactionOutputValue({
817
+ outputs
818
+ });
819
+ const newTotalAmount = totalTransactionValue + newFee;
820
+ const _transaction: Partial<ISendTransaction> = {
821
+ satsPerByte,
822
+ fee: newFee,
823
+ selectedFeeId
824
+ };
825
+
826
+ if (max) {
827
+ // Update the tx value with the new fee to continue sending the max amount.
828
+ _transaction.outputs = [{ address, value: inputTotal - newFee, index }];
829
+ }
830
+
831
+ // Check that the user has enough funds
832
+ if (max || newTotalAmount <= inputTotal) {
833
+ this.updateSendTransaction({
834
+ transaction: _transaction
835
+ });
836
+ return ok({ fee: newFee });
837
+ }
838
+
839
+ return err(
840
+ 'New total amount exceeds the available balance. Unable to update the transaction fee.'
841
+ );
842
+ }
843
+
844
+ /**
845
+ * Toggles the max amount to the provided output index.
846
+ * @param {string} [address] If left undefined, the current receiving address will be provided.
847
+ * @param {ISendTransaction} [transaction]
848
+ * @param {number} [index]
849
+ * @param satsPerByte
850
+ * @param rbf
851
+ */
852
+ sendMax = async ({
853
+ address,
854
+ transaction,
855
+ index = 0,
856
+ satsPerByte,
857
+ rbf = false
858
+ }: {
859
+ address?: string;
860
+ transaction?: ISendTransaction;
861
+ index?: number;
862
+ satsPerByte?: number;
863
+ rbf?: boolean;
864
+ } = {}): Promise<Result<string>> => {
865
+ try {
866
+ if (!transaction) {
867
+ transaction = this.data;
868
+ }
869
+ if (!transaction.inputs?.length) {
870
+ const setupRes = await this.setupTransaction({ rbf });
871
+ if (setupRes.isErr()) return err(setupRes.error.message);
872
+ }
873
+ const outputs = transaction.outputs ?? [];
874
+ // No address specified, attempt to assign the address currently specified in the current output index.
875
+ if (!address) {
876
+ address = outputs[index]?.address ?? '';
877
+ }
878
+
879
+ const maxAmountResponse = this.getMaxSendAmount({
880
+ satsPerByte: satsPerByte ?? 1,
881
+ selectedFeeId: transaction.selectedFeeId,
882
+ transaction
883
+ });
884
+ if (maxAmountResponse.isErr()) {
885
+ return err(maxAmountResponse.error);
886
+ }
887
+ const { amount, fee } = maxAmountResponse.value;
888
+
889
+ if (!transaction.max) {
890
+ this.updateSendTransaction({
891
+ transaction: {
892
+ max: true,
893
+ outputs: [{ address, value: amount, index }],
894
+ fee
895
+ }
896
+ });
897
+ } else {
898
+ this.updateSendTransaction({
899
+ transaction: {
900
+ max: false
901
+ }
902
+ });
903
+ }
904
+
905
+ return ok('Successfully setup max send transaction.');
906
+ } catch (e) {
907
+ return err(e);
908
+ }
909
+ };
910
+
911
+ /**
912
+ * Calculates the max amount able to send for onchain/lightning
913
+ * @param {ISendTransaction} [transaction]
914
+ * @param {number} [index]
915
+ */
916
+ //TODO: Double check the transactionSpeed and customFeeRate are being used correctly.
917
+ estimateTransactionCosts = ({
918
+ transaction,
919
+ customFeeRate
920
+ }: {
921
+ transaction?: ISendTransaction;
922
+ customFeeRate?: number;
923
+ }): Result<{ amount: number; fee: number; satsPerByte: number }> => {
924
+ try {
925
+ if (!transaction) {
926
+ transaction = this.data;
927
+ }
928
+
929
+ const currentWallet = this._wallet.data;
930
+ const onchainBalance = currentWallet.balance;
931
+
932
+ const inputValue = this.getTransactionInputValue({
933
+ inputs: transaction.inputs
934
+ });
935
+ const amount = onchainBalance > inputValue ? onchainBalance : inputValue;
936
+
937
+ let utxos: IUtxo[] = [];
938
+ //Ensure we add the larger utxo set for a more accurate fee.
939
+ if (transaction.inputs.length > currentWallet?.utxos.length) {
940
+ utxos = transaction.inputs;
941
+ } else {
942
+ utxos = currentWallet?.utxos ?? [];
943
+ }
944
+ const fees = this._wallet.feeEstimates;
945
+ const selectedFeeId = this._wallet.selectedFeeId;
946
+ const satsPerByte = customFeeRate ?? fees[selectedFeeId] ?? 1;
947
+ const fee = this.getTotalFee({
948
+ satsPerByte,
949
+ message: transaction.message,
950
+ transaction: {
951
+ ...transaction,
952
+ max: true,
953
+ inputs: utxos,
954
+ selectedFeeId,
955
+ satsPerByte
956
+ }
957
+ });
958
+
959
+ const maxAmount = {
960
+ amount: amount - fee,
961
+ fee,
962
+ satsPerByte
963
+ };
964
+
965
+ if (amount <= fee) {
966
+ return err('Balance is too low to spend.');
967
+ }
968
+
969
+ return ok(maxAmount);
970
+ } catch (e) {
971
+ return err(e);
972
+ }
973
+ };
974
+
975
+ /**
976
+ * Calculates the max amount able to send for the provided/current onchain transaction.
977
+ * @param {number} satsPerByte
978
+ * @param {EFeeId} [selectedFeeId]
979
+ * @param {ISendTransaction} [transaction]
980
+ * @returns {Result<{ amount: number; fee?: number }>}
981
+ */
982
+ getMaxSendAmount({
983
+ satsPerByte,
984
+ selectedFeeId = EFeeId.none,
985
+ transaction
986
+ }: {
987
+ satsPerByte: number;
988
+ selectedFeeId?: EFeeId;
989
+ transaction?: ISendTransaction;
990
+ }): Result<{ amount: number; fee?: number }> {
991
+ try {
992
+ if (!transaction) {
993
+ transaction = this.data;
994
+ }
995
+
996
+ const currentWallet = this._wallet.data;
997
+ const onchainBalance = currentWallet.balance;
998
+
999
+ const inputValue = this.getTransactionInputValue({
1000
+ inputs: transaction.inputs
1001
+ });
1002
+ const amount = onchainBalance > inputValue ? onchainBalance : inputValue;
1003
+
1004
+ let utxos: IUtxo[] = [];
1005
+ //Ensure we add the larger utxo set for a more accurate fee.
1006
+ if (transaction.inputs.length > currentWallet?.utxos.length) {
1007
+ utxos = transaction.inputs;
1008
+ } else {
1009
+ utxos = currentWallet?.utxos ?? [];
1010
+ }
1011
+
1012
+ const fee = this.getTotalFee({
1013
+ satsPerByte,
1014
+ message: transaction.message,
1015
+ transaction: {
1016
+ ...transaction,
1017
+ max: true,
1018
+ inputs: utxos,
1019
+ selectedFeeId,
1020
+ satsPerByte
1021
+ }
1022
+ });
1023
+
1024
+ const maxAmount = {
1025
+ amount: amount - fee,
1026
+ fee
1027
+ };
1028
+
1029
+ if (amount <= fee) {
1030
+ return err('Balance is too low to spend.');
1031
+ }
1032
+
1033
+ return ok(maxAmount);
1034
+ } catch (e) {
1035
+ return err(e);
1036
+ }
1037
+ }
1038
+
1039
+ /**
1040
+ * Sets up a CPFP transaction.
1041
+ * @param {string} [txid]
1042
+ * @param {number} [satsPerByte]
1043
+ */
1044
+ async setupCpfp({
1045
+ txid,
1046
+ satsPerByte
1047
+ }: {
1048
+ txid?: string; // txid of utxo to include in the CPFP tx. Undefined will gather all utxo's.
1049
+ satsPerByte?: number;
1050
+ }): Promise<Result<ISendTransaction>> {
1051
+ const response = await this.setupTransaction({
1052
+ inputTxHashes: txid ? [txid] : undefined,
1053
+ rbf: true
1054
+ });
1055
+
1056
+ const receiveAddress = await this._wallet.getReceiveAddress({});
1057
+ if (receiveAddress.isErr()) {
1058
+ return err(receiveAddress.error.message);
1059
+ }
1060
+
1061
+ this._data = {
1062
+ ...this._data,
1063
+ ...response,
1064
+ satsPerByte: satsPerByte ?? this.data?.satsPerByte ?? 1,
1065
+ boostType: EBoostType.cpfp
1066
+ };
1067
+
1068
+ // Construct the tx to send funds back to ourselves using the assigned inputs, receive address and fee.
1069
+ const sendMaxResponse = await this.sendMax({
1070
+ address: receiveAddress.value
1071
+ });
1072
+ if (sendMaxResponse.isErr()) {
1073
+ return err(sendMaxResponse.error.message);
1074
+ }
1075
+
1076
+ return ok(this.data);
1077
+ }
1078
+ }