@tradelayerprotocol/tradelayer 1.9.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 (249) hide show
  1. package/.claude/settings.local.json +13 -0
  2. package/.claude/skills/tl-algo/SKILL.md +255 -0
  3. package/.gitattributes +2 -0
  4. package/.github/workflows/publish.yaml +26 -0
  5. package/4mm.js +163 -0
  6. package/LICENSE +21 -0
  7. package/NPMSwapRefactor.zip +0 -0
  8. package/README.md +217 -0
  9. package/address.sh +26 -0
  10. package/algoAPI.js +581 -0
  11. package/analyzepsbt.js +92 -0
  12. package/apiEx.js +99 -0
  13. package/bb_hyperscalper.js +290 -0
  14. package/bbo_demo.js +111 -0
  15. package/buyer.js +622 -0
  16. package/client.js +50 -0
  17. package/createTxTest.js +26 -0
  18. package/createWallet.js +75 -0
  19. package/daytrader.js +531 -0
  20. package/decodeTest.js +69 -0
  21. package/fundingManager.js +144 -0
  22. package/index.js +4 -0
  23. package/listener.js +27 -0
  24. package/litecoreTxBuilder.js +1128 -0
  25. package/mmEx.js +356 -0
  26. package/networks.js +51 -0
  27. package/orderbook.js +200 -0
  28. package/package.json +34 -0
  29. package/perTradeQueue.js +36 -0
  30. package/projectsTLNPMTLNPM/package-lock.json +162 -0
  31. package/projectsTLNPMTLNPM/package.json +5 -0
  32. package/quick.js +32 -0
  33. package/quickFut.js +37 -0
  34. package/quickSell.js +37 -0
  35. package/relayerClient.js +117 -0
  36. package/run4mm.js +80 -0
  37. package/run_bbo_tracker.js +241 -0
  38. package/seller.js +443 -0
  39. package/session.js +45 -0
  40. package/setup-lin-ltc.sh +139 -0
  41. package/setup-lin.sh +203 -0
  42. package/setup-win-ltc.bat +108 -0
  43. package/setup-win.bat +167 -0
  44. package/spam_screamer_futures.js +222 -0
  45. package/tradelayer.js/.gitattributes +2 -0
  46. package/tradelayer.js/README.md +2 -0
  47. package/tradelayer.js/oldTests/activationTest.js +6 -0
  48. package/tradelayer.js/oldTests/base58.test.js +23 -0
  49. package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
  50. package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
  51. package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
  52. package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
  53. package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
  54. package/tradelayer.js/oldTests/createPayload.js +0 -0
  55. package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
  56. package/tradelayer.js/oldTests/decode.js +205 -0
  57. package/tradelayer.js/oldTests/decodeTest.js +50 -0
  58. package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
  59. package/tradelayer.js/oldTests/encodeDecode.js +340 -0
  60. package/tradelayer.js/oldTests/expressTest.js +29 -0
  61. package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
  62. package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
  63. package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
  64. package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
  65. package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
  66. package/tradelayer.js/oldTests/indexTest.js +26 -0
  67. package/tradelayer.js/oldTests/initTokensTest.js +32 -0
  68. package/tradelayer.js/oldTests/interfaceChild.js +129 -0
  69. package/tradelayer.js/oldTests/listenerChild.js +112 -0
  70. package/tradelayer.js/oldTests/opdecode.js +26 -0
  71. package/tradelayer.js/oldTests/options.js +79 -0
  72. package/tradelayer.js/oldTests/optxtest.js +116 -0
  73. package/tradelayer.js/oldTests/optxtest1.js +64 -0
  74. package/tradelayer.js/oldTests/oracle.test.js +32 -0
  75. package/tradelayer.js/oldTests/orderbook.test.js +36 -0
  76. package/tradelayer.js/oldTests/parsing.js +93 -0
  77. package/tradelayer.js/oldTests/payload.js +13 -0
  78. package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
  79. package/tradelayer.js/oldTests/property.test.js +53 -0
  80. package/tradelayer.js/oldTests/propertyLevel.js +75 -0
  81. package/tradelayer.js/oldTests/propertyTest.js +32 -0
  82. package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
  83. package/tradelayer.js/oldTests/salter.js +14 -0
  84. package/tradelayer.js/oldTests/tally.js +81 -0
  85. package/tradelayer.js/oldTests/tally.test.js +48 -0
  86. package/tradelayer.js/oldTests/tally2.js +124 -0
  87. package/tradelayer.js/oldTests/tally3.js +142 -0
  88. package/tradelayer.js/oldTests/tallyDiag.js +38 -0
  89. package/tradelayer.js/oldTests/testGetRaw.js +40 -0
  90. package/tradelayer.js/oldTests/testHexConvert.js +47 -0
  91. package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
  92. package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
  93. package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
  94. package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
  95. package/tradelayer.js/oldTests/testOPReturn.js +102 -0
  96. package/tradelayer.js/oldTests/testPayload.js +23 -0
  97. package/tradelayer.js/oldTests/testRaw.js +50 -0
  98. package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
  99. package/tradelayer.js/oldTests/testTxBuild +28 -0
  100. package/tradelayer.js/oldTests/testTxBuild.js +42 -0
  101. package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
  102. package/tradelayer.js/oldTests/txUtilsA.js +515 -0
  103. package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
  104. package/tradelayer.js/oldTests/vaults.js +72 -0
  105. package/tradelayer.js/oldTests/volumeIndex.js +117 -0
  106. package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
  107. package/tradelayer.js/output_base64.txt +1 -0
  108. package/tradelayer.js/package-lock.json +9967 -0
  109. package/tradelayer.js/package.json +61 -0
  110. package/tradelayer.js/server/index.js +88 -0
  111. package/tradelayer.js/server/litecoind.exe +0 -0
  112. package/tradelayer.js/src/activation.js +303 -0
  113. package/tradelayer.js/src/adjuster.js +77 -0
  114. package/tradelayer.js/src/amm.js +400 -0
  115. package/tradelayer.js/src/base256.js +55 -0
  116. package/tradelayer.js/src/base94.js +79 -0
  117. package/tradelayer.js/src/channels.js +1163 -0
  118. package/tradelayer.js/src/clearing.js +3109 -0
  119. package/tradelayer.js/src/clearlist.js +364 -0
  120. package/tradelayer.js/src/client.js +295 -0
  121. package/tradelayer.js/src/consensus.js +613 -0
  122. package/tradelayer.js/src/contractRegistry.js +964 -0
  123. package/tradelayer.js/src/db.js +89 -0
  124. package/tradelayer.js/src/init.js +24 -0
  125. package/tradelayer.js/src/insurance.js +347 -0
  126. package/tradelayer.js/src/interface.js +218 -0
  127. package/tradelayer.js/src/interfaceExpress.js +178 -0
  128. package/tradelayer.js/src/iou.js +509 -0
  129. package/tradelayer.js/src/listener.js +226 -0
  130. package/tradelayer.js/src/logic.js +1702 -0
  131. package/tradelayer.js/src/main.js +927 -0
  132. package/tradelayer.js/src/marginMap.js +2165 -0
  133. package/tradelayer.js/src/options.js +126 -0
  134. package/tradelayer.js/src/oracle.js +394 -0
  135. package/tradelayer.js/src/orderbook.js +4123 -0
  136. package/tradelayer.js/src/persistence.js +554 -0
  137. package/tradelayer.js/src/property.js +411 -0
  138. package/tradelayer.js/src/reOrg.js +41 -0
  139. package/tradelayer.js/src/scaling.js +145 -0
  140. package/tradelayer.js/src/tally.js +1275 -0
  141. package/tradelayer.js/src/tradeHistoryManager.js +552 -0
  142. package/tradelayer.js/src/txDecoder.js +584 -0
  143. package/tradelayer.js/src/txEncoder.js +610 -0
  144. package/tradelayer.js/src/txIndex.js +502 -0
  145. package/tradelayer.js/src/txUtils.js +1392 -0
  146. package/tradelayer.js/src/types.js +429 -0
  147. package/tradelayer.js/src/validity.js +3077 -0
  148. package/tradelayer.js/src/vaults.js +430 -0
  149. package/tradelayer.js/src/vesting.js +491 -0
  150. package/tradelayer.js/src/volumeIndex.js +618 -0
  151. package/tradelayer.js/src/walletInterface.js +220 -0
  152. package/tradelayer.js/src/walletListener.js +665 -0
  153. package/tradelayer.js/tests/256decode.js +82 -0
  154. package/tradelayer.js/tests/UTXOracle.js +205 -0
  155. package/tradelayer.js/tests/base94test.js +23 -0
  156. package/tradelayer.js/tests/cancelTxTest.js +62 -0
  157. package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
  158. package/tradelayer.js/tests/decimalTest.js +65 -0
  159. package/tradelayer.js/tests/decoderTest.js +100 -0
  160. package/tradelayer.js/tests/deltaCount.js +47 -0
  161. package/tradelayer.js/tests/deltaCount2.js +60 -0
  162. package/tradelayer.js/tests/interfaceTest.js +37 -0
  163. package/tradelayer.js/tests/mainTest.js +53 -0
  164. package/tradelayer.js/tests/makeActivationTest.js +24 -0
  165. package/tradelayer.js/tests/maxHeightTest.js +49 -0
  166. package/tradelayer.js/tests/reverseHash.js +72 -0
  167. package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
  168. package/tradelayer.js/tests/tallyTest.js +40 -0
  169. package/tradelayer.js/tests/testBuybacks.js +46 -0
  170. package/tradelayer.js/tests/testCodeHash.js +49 -0
  171. package/tradelayer.js/tests/testConsensusHash.js +91 -0
  172. package/tradelayer.js/tests/testDecode.js +30 -0
  173. package/tradelayer.js/tests/testEncodingLengths.js +129 -0
  174. package/tradelayer.js/tests/testGetTx +32 -0
  175. package/tradelayer.js/tests/testGetTx.js +32 -0
  176. package/tradelayer.js/tests/testHexHash.js +32 -0
  177. package/tradelayer.js/tests/testIndexHash.js +35 -0
  178. package/tradelayer.js/tests/testInitContracts.js +38 -0
  179. package/tradelayer.js/tests/testMaxConsensus.js +12 -0
  180. package/tradelayer.js/tests/testMaxSynth.js +44 -0
  181. package/tradelayer.js/tests/testMint.js +21 -0
  182. package/tradelayer.js/tests/testNetwork.js +33 -0
  183. package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
  184. package/tradelayer.js/tests/testRebates.js +32 -0
  185. package/tradelayer.js/tests/testRedeem.js +22 -0
  186. package/tradelayer.js/tests/testTokenTrade.js +39 -0
  187. package/tradelayer.js/tests/testTxBuild.js +42 -0
  188. package/tradelayer.js/tests/testUTXOTrade.js +27 -0
  189. package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
  190. package/tradelayer.js/tests/tradeFutures.js +40 -0
  191. package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
  192. package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
  193. package/tradelayer.js/tests/txScanTest.js +134 -0
  194. package/tradelayer.js/tests/validateTest.js +136 -0
  195. package/tradelayer.js/tests/vestingTest.js +37 -0
  196. package/tradelayer.js/utils/activateMainnet.js +59 -0
  197. package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
  198. package/tradelayer.js/utils/autocompactdb.js +23 -0
  199. package/tradelayer.js/utils/base64toHex.js +32 -0
  200. package/tradelayer.js/utils/broadcastDoge.js +38 -0
  201. package/tradelayer.js/utils/calcRedeem.js +19 -0
  202. package/tradelayer.js/utils/checkNetwork.js +27 -0
  203. package/tradelayer.js/utils/createAddress.js +48 -0
  204. package/tradelayer.js/utils/createAttestation.js +133 -0
  205. package/tradelayer.js/utils/createContract.js +118 -0
  206. package/tradelayer.js/utils/createOracle.js +94 -0
  207. package/tradelayer.js/utils/createwallet.js +20 -0
  208. package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
  209. package/tradelayer.js/utils/crossTokenTrades.js +62 -0
  210. package/tradelayer.js/utils/dumpPriv.js +29 -0
  211. package/tradelayer.js/utils/generateChannel.js +34 -0
  212. package/tradelayer.js/utils/getInfo.js +21 -0
  213. package/tradelayer.js/utils/hardWipe.js +20 -0
  214. package/tradelayer.js/utils/hexTo64.js +16 -0
  215. package/tradelayer.js/utils/importAddress.js +28 -0
  216. package/tradelayer.js/utils/importpriv.js +20 -0
  217. package/tradelayer.js/utils/issueOracleContract.js +67 -0
  218. package/tradelayer.js/utils/issueTokens.js +41 -0
  219. package/tradelayer.js/utils/listunspent.js +66 -0
  220. package/tradelayer.js/utils/litecoinClient.js +30 -0
  221. package/tradelayer.js/utils/loadwallet.js +20 -0
  222. package/tradelayer.js/utils/publishOracle.js +113 -0
  223. package/tradelayer.js/utils/sendActivation.js +21 -0
  224. package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
  225. package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
  226. package/tradelayer.js/utils/sendCommit.js +24 -0
  227. package/tradelayer.js/utils/sendDoge.js +62 -0
  228. package/tradelayer.js/utils/sendDogeMain.js +67 -0
  229. package/tradelayer.js/utils/sendDogeTx.js +46 -0
  230. package/tradelayer.js/utils/sendLTC.js +63 -0
  231. package/tradelayer.js/utils/sendMainnet.js +62 -0
  232. package/tradelayer.js/utils/sendTransfer.js +19 -0
  233. package/tradelayer.js/utils/sendVestTest.js +88 -0
  234. package/tradelayer.js/utils/sendWithdrawal.js +26 -0
  235. package/tradelayer.js/utils/simpleStart.js +8 -0
  236. package/tradelayer.js/utils/startStop.js +27 -0
  237. package/tradelayer.js/utils/structuredTrades.js +136 -0
  238. package/tradelayer.js/utils/verifySignature.js +90 -0
  239. package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
  240. package/tradelayer.js/utils/walletCache.js +172 -0
  241. package/tradelayer.js/utils/walletContractInterface.js +48 -0
  242. package/tradelayer.js/utils/walletFetchTxs.js +66 -0
  243. package/tradelayer.js/utils/walletUtils.js +97 -0
  244. package/tradelayer.js/utils/wipeDB.js +55 -0
  245. package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
  246. package/txEncoder.js +529 -0
  247. package/utility.js +28 -0
  248. package/verifymessage.js +38 -0
  249. package/ws-transport.js +311 -0
@@ -0,0 +1,1128 @@
1
+ const litecore = require('bitcore-lib-ltc');
2
+ const util = require('util');
3
+ const BigNumber = require('bignumber.js');
4
+ const { payments, Psbt, Transaction } = require('bitcoinjs-lib');
5
+ const { ECPairFactory } = require('ecpair');
6
+ const ecc = require('tiny-secp256k1');
7
+ const ECPair = ECPairFactory(ecc);
8
+ const networks = require('./networks.js');
9
+ const minFeeLtcPerKb = 0.00002;
10
+
11
+ const NETWORKS = {
12
+ LTCTEST: {
13
+ messagePrefix: '\x19Litecoin Testnet Signed Message:\n',
14
+ bech32: 'tltc',
15
+ bip32: {
16
+ public: 0x0436f6e1,
17
+ private: 0x0436ef7d,
18
+ },
19
+ pubKeyHash: 0x6f,
20
+ scriptHash: 0x3a,
21
+ wif: 0xef,
22
+ },
23
+ LTC: {
24
+ messagePrefix: '\x19Litecoin Signed Message:\n',
25
+ bech32: 'ltc',
26
+ bip32: {
27
+ public: 0x019da462,
28
+ private: 0x019d9cfe,
29
+ },
30
+ pubKeyHash: 0x30,
31
+ scriptHash: 0x32,
32
+ wif: 0xb0,
33
+ },
34
+ };
35
+
36
+ const getNetworkConfig = (networkCode) => {
37
+ const network = NETWORKS[networkCode];
38
+ if (!network) {
39
+ throw new Error(`Unsupported network code: ${networkCode}`);
40
+ }
41
+ return network;
42
+ };
43
+
44
+
45
+ const initializePromisifiedMethods = (client) => ({
46
+ walletCreateFundedPsbtAsync: util.promisify(client.cmd.bind(client, 'walletcreatefundedpsbt')),
47
+ createPsbtAsync: util.promisify(client.cmd.bind(client, 'createpsbt')),
48
+ decodeRawTransactionAsync: util.promisify(client.cmd.bind(client, 'decoderawtransaction')),
49
+ createRawTransactionAsync: util.promisify(client.cmd.bind(client, 'createrawtransaction')),
50
+ listUnspentAsync: util.promisify(client.cmd.bind(client, 'listunspent')),
51
+ decoderawtransactionAsync: util.promisify(client.cmd.bind(client, 'decoderawtransaction')),
52
+ dumpprivkeyAsync: util.promisify(client.cmd.bind(client, 'dumpprivkey')),
53
+ sendrawtransactionAsync: util.promisify(client.cmd.bind(client, 'sendrawtransaction')),
54
+ validateAddress: util.promisify(client.cmd.bind(client, 'validateaddress')),
55
+ getBlockCountAsync: util.promisify(client.cmd.bind(client, 'getblockcount')),
56
+ loadWalletAsync: util.promisify(client.cmd.bind(client, 'loadwallet')),
57
+ addMultisigAddressAsync: util.promisify(client.cmd.bind(client, 'addmultisigaddress')),
58
+ signrawtransactionwithwalletAsync: util.promisify(client.cmd.bind(client, 'signrawtransactionwithwallet')),
59
+ signpsbtAsync: util.promisify(client.cmd.bind(client, 'walletprocesspsbt')),
60
+ decodepsbtAsync: util.promisify(client.cmd.bind(client, 'decodepsbt')),
61
+ finalizeAsync: util.promisify(client.cmd.bind(client, 'finalizepsbt')),
62
+ });
63
+
64
+ function formatAmount(amt) {
65
+ // Work in satoshis and back to LTC string
66
+ return (Math.floor(new BigNumber(amt).times(1e8).toNumber()) / 1e8).toFixed(8);
67
+ }
68
+
69
+ // Functions refactored to accept `client` and use its methods
70
+ const buildLitecoinTransaction = async (txConfig, client) => {
71
+ try {
72
+ const {
73
+ buyerKeyPair,
74
+ sellerKeyPair,
75
+ amount,
76
+ payload,
77
+ commitUTXOs,
78
+ network = 'LTCTEST',
79
+ } = txConfig;
80
+
81
+ const { listUnspentAsync, createRawTransactionAsync } = initializePromisifiedMethods(client);
82
+
83
+ const buyerAddress = buyerKeyPair.address;
84
+ const sellerAddress = sellerKeyPair.address;
85
+
86
+ // Fetch unspent UTXOs for the buyer
87
+ const luRes = await listUnspentAsync();
88
+ const _utxos = luRes
89
+ .map((i) => ({ ...i, pubkey: buyerKeyPair.pubkey }))
90
+ .sort((a, b) => b.amount - a.amount);
91
+
92
+
93
+ const utxos = [...commitUTXOs, ..._utxos];
94
+ const minAmount = 0.000056;
95
+ const buyerLtcAmount = minAmount;
96
+ const sellerLtcAmount = Math.max(amount, minAmount);
97
+ const minAmountForAllOuts = buyerLtcAmount + sellerLtcAmount;
98
+ console.log('utxos before get getEnoughInputs2 '+JSON.stringify(utxos))
99
+ const inputsRes = getEnoughInputs2(utxos, minAmountForAllOuts);
100
+ const { finalInputs, fee, amountSum } = inputsRes;
101
+ console.log('final inputs' +JSON.stringify(finalInputs))
102
+ const _inputsSum = finalInputs
103
+ .map(({ amount }) => new BigNumber(amount))
104
+ .reduce((a, b) => a.plus(b), new BigNumber(0));
105
+ const inputsSum = _inputsSum;
106
+
107
+
108
+ const changeBuyerLtcAmount = Math.max(inputsSum - sellerLtcAmount - fee, buyerLtcAmount);
109
+
110
+ const hexPayload = Buffer.from(payload, 'utf8').toString('hex');
111
+ const _insForRawTx = finalInputs.map(({ txid, vout }) => ({ txid, vout }));
112
+ const _outsForRawTx = [
113
+ { [buyerAddress]: formatAmount(changeBuyerLtcAmount) },
114
+ { [sellerAddress]: formatAmount(sellerLtcAmount) },
115
+ { data: hexPayload },
116
+ ];
117
+
118
+ console.log('outs for create raw tx utxo trade '+JSON.stringify(_outsForRawTx))
119
+ const crtRes = await createRawTransactionAsync(_insForRawTx, _outsForRawTx);
120
+ const finalTx = crtRes;
121
+
122
+ const psbtHexConfig = {
123
+ rawtx: finalTx,
124
+ inputs: finalInputs,
125
+ };
126
+
127
+ console.log('psbt config '+JSON.stringify(psbtHexConfig))
128
+
129
+ const psbtHexRes = await buildPsbtViaRpc(psbtHexConfig, client, network);
130
+ if (psbtHexRes.error) throw new Error(`buildPsbt: ${psbtHexRes.error}`);
131
+
132
+ return { data: { rawtx: finalTx, inputs: finalInputs, psbtHex: psbtHexRes.data } };
133
+ } catch (error) {
134
+ console.error('Error building Litecoin transaction:', error);
135
+ return { error: error.message || 'Error building transaction' };
136
+ }
137
+ };
138
+
139
+ async function buildSinglesigCommit({ commitUTXOs, payload, buyerKeyPair }, client) {
140
+ const {
141
+ createrawtransactionAsync,
142
+ signrawtransactionwithwalletAsync,
143
+ sendrawtransactionAsync,
144
+ decoderawtransactionAsync
145
+ } = initializePromisifiedMethods(client);
146
+
147
+ const u = commitUTXOs[0];
148
+ console.log('inside build single '+u.scriptPubKey+' '+!buyerKeyPair?.address)
149
+
150
+ if (u.scriptPubKey?.startsWith('0020')) {
151
+ throw new Error('P2WSH UTXO passed to single-sig builder');
152
+ }
153
+
154
+ if (!buyerKeyPair?.address) {
155
+ throw new Error('buyerKeyPair.address missing');
156
+ }
157
+
158
+ const inputs = [{ txid: u.txid, vout: u.vout }];
159
+ const hexPayload = Buffer.from(payload, 'utf8').toString('hex');
160
+
161
+ const fee = 0.00003;
162
+ const dust = 0.000056;
163
+ const change = Number(u.amount) - fee - dust;
164
+ console.log('change '+change)
165
+ if (!(change > 0)) throw new Error(`insufficient: amount=${u.amount} change=${change}`);
166
+
167
+ const outputs = [
168
+ { [buyerKeyPair.address]: change },
169
+ { data: hexPayload }
170
+ ];
171
+
172
+ const raw = await createrawtransactionAsync(inputs, outputs);
173
+
174
+ console.log('raw '+raw)
175
+ const signed = await signrawtransactionwithwalletAsync(raw);
176
+ console.log('signed '+JSON.stringify(signed))
177
+ if (!signed?.hex) throw new Error('signrawtransactionwithwallet failed');
178
+ if (signed.complete === false) throw new Error('wallet could not fully sign (missing key/utxo?)');
179
+
180
+ const txid = await sendrawtransactionAsync(signed.hex);
181
+
182
+ // Optional sanity:
183
+ // const decoded = await decoderawtransactionAsync(signed.hex);
184
+
185
+ return { txid, signedHex: signed.hex, rawHex: raw };
186
+ }
187
+
188
+
189
+ /**
190
+ * Build a PSBT using the Litecoin Core node via walletcreatefundedpsbt,
191
+ * then inject witnessScripts/amounts for custom P2WSH, and finally export PSBT as hex.
192
+ *
193
+ * @param {Object} buildPsbtOptions - { rawtx, inputs[] }
194
+ * rawtx: hex-encoded "template" transaction (with desired outputs).
195
+ * inputs: array of objects, each at least:
196
+ * { txid, vout, amount, scriptPubKey, witnessScript? }
197
+ * - 'amount' is in LTC (e.g. 0.0000546)
198
+ * - 'witnessScript' is optional if you have a native P2WSH 2-of-2, etc.
199
+ * @param {Object} client - The RPC client connected to your LTC node
200
+ * @param {String} networkCode - e.g. 'LTCTEST' or 'LTC'
201
+ * @returns {Promise<{ data?: string, error?: string }>} PSBT in hex form
202
+ */
203
+ async function buildPsbtViaRpc(buildPsbtOptions, client, networkCode) {
204
+ const {
205
+ createPsbtAsync,
206
+ decodeRawTransactionAsync,
207
+ decodepsbtAsync
208
+ } = initializePromisifiedMethods(client);
209
+
210
+ //try {
211
+ const { rawtx, inputs } = buildPsbtOptions;
212
+ if (!rawtx) throw new Error('Missing rawtx');
213
+
214
+ // 1) Decode the raw TX to gather its outputs (e.g. [ { address: X, amount: Y }, { data: "hex" }, ... ])
215
+ const decoded = await decodeRawTransactionAsync(rawtx);
216
+ if (!decoded || !decoded.vout) {
217
+ throw new Error('Failed to decode raw transaction');
218
+ }
219
+
220
+ // We convert each output into the format needed by walletcreatefundedpsbt:
221
+ // { [address]: amount } or { data: <hex payload> } for OP_RETURN
222
+ const outputsForRpc = [];
223
+ for (const out of decoded.vout) {
224
+ const value = out.value; // LTC
225
+ const spk = out.scriptPubKey;
226
+
227
+ if (spk.type === 'nulldata') {
228
+ // OP_RETURN
229
+ // Usually 'asm' is "OP_RETURN <payload-hex>"
230
+ const parts = spk.asm ? spk.asm.split(' ') : [];
231
+ const opReturnHex = parts[1] || '';
232
+ outputsForRpc.push({ data: opReturnHex });
233
+ } else {
234
+ // Standard address output
235
+ const address = spk.addresses && spk.addresses[0];
236
+ if (!address) {
237
+ throw new Error(`Output script is not recognized as an address: ${spk.asm}`);
238
+ }
239
+ outputsForRpc.push({ [address]: value });
240
+ }
241
+ }
242
+
243
+ // 2) Build the 'inputs' param for walletcreatefundedpsbt
244
+ // Typically: { txid, vout, ...(optionally "sequence", "amount") }
245
+ // If you pass "amount", it's in LTC. This can help the node if it doesn't know the UTXO or if watch-only.
246
+ const inputsForRpc = inputs.map((inp) => ({
247
+ txid: inp.txid,
248
+ vout: inp.vout,
249
+ // optional: sequence, e.g. 0xffffffff
250
+ amount: inp.amount, // walletcreatefundedpsbt expects LTC for watch-only or unknown UTXOs
251
+ }));
252
+
253
+ // 3) Call walletcreatefundedpsbt to form a partial PSBT
254
+ // - We pass 0 for locktime, an empty or custom options object, and bip32derivs = false if you prefer.
255
+ const options = {
256
+ // example: feeRate: 0.00002,
257
+ // example: includeWatching: true,
258
+ "include_unsafe": true
259
+ };
260
+ const bip32derivs = false; // whether to store BIP32 derivation info in the PSBT
261
+
262
+ console.log('outputs and inputs for psbt '+JSON.stringify(inputsForRpc)+' '+JSON.stringify(outputsForRpc))
263
+ const result = await createPsbtAsync(inputsForRpc, outputsForRpc);
264
+ console.log('create psbt result '+JSON.stringify(result))
265
+
266
+ const decode = await decodepsbtAsync(result)
267
+ console.log('decode '+JSON.stringify(decode))
268
+ // 4) Convert the base64 PSBT => bitcoinjs-lib PSBT object
269
+ // Provide the correct LTC network parameters
270
+ const network = getNetworkConfig(networkCode); // your function that returns { bech32, bip32, ... }
271
+ let psbt = Psbt.fromBase64(result, { network });
272
+
273
+ // 5) Inject the correct "value" (amount in satoshis) and optional "witnessScript" for each input
274
+ // Because walletcreatefundedpsbt may not know about custom witnessScripts.
275
+ // Also ensure each input has the correct 'value' in satoshis for PSBT correctness.
276
+ inputs.forEach((inp, i) => {
277
+ const valueSats = new BigNumber(inp.amount).times(1e8).integerValue().toNumber(); // 'witnessUtxo' => { script: Buffer, value: bigInt } for segwit
278
+ const script = Buffer.from(inp.scriptPubKey, 'hex');
279
+
280
+ // Update the input to ensure the correct witnessUtxo
281
+ // (the node might have done this already if it's in your wallet, but let's be certain)
282
+ psbt.updateInput(i, {
283
+ witnessUtxo: {
284
+ script,
285
+ value: valueSats,
286
+ },
287
+ });
288
+
289
+ // If this is a P2WSH with a known 2-of-2 script, attach it:
290
+ if (inp.redeemScript) {
291
+ psbt.updateInput(i, {
292
+ witnessScript: Buffer.from(inp.redeemScript, 'hex'),
293
+ });
294
+ }
295
+ });
296
+
297
+ // 6) Export the final PSBT as hex, so you can share with other signers using bitcoinjs-lib
298
+ const psbtHex = psbt.toHex();
299
+ return { data: psbtHex };
300
+
301
+ //} catch (err) {
302
+ // console.error('buildPsbtViaRpc error:', err.message);
303
+ // return { error: err.message };
304
+ //}
305
+ }
306
+
307
+ /**
308
+ * Build + sign + broadcast a "commit" tx, choosing Singlesig vs PSBT based on UTXO scriptPubKey.
309
+ *
310
+ * REQUIREMENTS:
311
+ * - For singlesig: node wallet must control the input (signrawtransactionwithwallet).
312
+ * - For P2WSH: you must provide a PSBT-based builder+signer (see buildCommitViaPsbtCb).
313
+ *
314
+ * @param {Object} args
315
+ * @param {Array} args.commitUTXOs - array with at least 1 utxo: { txid, vout, amount, scriptPubKey }
316
+ * @param {string} args.payload - utf8 payload to OP_RETURN
317
+ * @param {Object} args.buyerKeyPair - must include .address for change output
318
+ * @param {Object} client - litecoin rpc client
319
+ * @param {Function} buildCommitViaPsbtCb - async (args, client) => { psbtHex } or { data:{psbtHex} }
320
+ * @param {Function} signPsbtCb - async (psbtHex, client) => { signedHex, txid? } (your existing signer)
321
+ */
322
+ async function buildCommitAuto(
323
+ { commitUTXOs, payload, buyerKeyPair },
324
+ client,
325
+ {
326
+ buildCommitViaPsbtCb,
327
+ signPsbtCb
328
+ } = {}
329
+ ) {
330
+ if (!Array.isArray(commitUTXOs) || commitUTXOs.length === 0) {
331
+ throw new Error('buildCommitAuto: commitUTXOs missing/empty');
332
+ }
333
+ if (!payload || typeof payload !== 'string') {
334
+ throw new Error('buildCommitAuto: payload missing');
335
+ }
336
+ if (!buyerKeyPair?.address) {
337
+ throw new Error('buildCommitAuto: buyerKeyPair.address missing');
338
+ }
339
+
340
+ const u = commitUTXOs[0];
341
+ const spk = String(u?.scriptPubKey || '');
342
+
343
+ if (!u?.txid || typeof u.vout !== 'number') {
344
+ throw new Error('buildCommitAuto: commitUTXO missing txid/vout');
345
+ }
346
+ if (!Number.isFinite(Number(u.amount))) {
347
+ throw new Error('buildCommitAuto: commitUTXO missing amount');
348
+ }
349
+ if (!spk) {
350
+ throw new Error('buildCommitAuto: commitUTXO missing scriptPubKey');
351
+ }
352
+
353
+ // P2WSH = 0020{32-byte sha256}
354
+ const isP2WSH = spk.startsWith('0020');
355
+
356
+ if (isP2WSH) {
357
+ if (typeof buildCommitViaPsbtCb !== 'function') {
358
+ throw new Error('buildCommitAuto: P2WSH input detected but buildCommitViaPsbtCb not provided');
359
+ }
360
+ if (typeof signPsbtCb !== 'function') {
361
+ throw new Error('buildCommitAuto: P2WSH input detected but signPsbtCb not provided');
362
+ }
363
+
364
+ // Build PSBT (unsigned settlement/commit style, depending on your pipeline)
365
+ const built = await buildCommitViaPsbtCb({ commitUTXOs, payload, buyerKeyPair }, client);
366
+ const psbtHex = built?.psbtHex ?? built?.data?.psbtHex;
367
+ if (!psbtHex) throw new Error('buildCommitAuto: PSBT builder returned no psbtHex');
368
+
369
+ // Sign PSBT -> signed tx hex (and optionally txid)
370
+ const signed = await signPsbtCb(psbtHex, client);
371
+ if (!signed?.signedHex) throw new Error('buildCommitAuto: PSBT signer returned no signedHex');
372
+
373
+ return {
374
+ txid: signed.txid || null,
375
+ signedHex: signed.signedHex,
376
+ rawHex: null
377
+ };
378
+ }
379
+
380
+ // Otherwise: assume wallet-owned singlesig (P2WPKH 0014.. or P2PKH 76a9.. etc.)
381
+ return await buildSinglesigCommit({ commitUTXOs, payload, buyerKeyPair }, client);
382
+ }
383
+
384
+ /**
385
+ * Singlesig commit builder: create raw, wallet-sign, broadcast.
386
+ * Returns consistent shape: { txid, signedHex, rawHex }
387
+ */
388
+ async function buildSinglesigCommit({ commitUTXOs, payload, buyerKeyPair }, client) {
389
+ const {
390
+ createrawtransactionAsync,
391
+ signrawtransactionwithwalletAsync,
392
+ sendrawtransactionAsync
393
+ } = initializePromisifiedMethods(client);
394
+
395
+ const u = commitUTXOs[0];
396
+
397
+ // Reject P2WSH here too (safety)
398
+ const spk = String(u?.scriptPubKey || '');
399
+ if (spk.startsWith('0020')) {
400
+ throw new Error('buildSinglesigCommit: P2WSH UTXO passed (requires PSBT path)');
401
+ }
402
+ if (!buyerKeyPair?.address) {
403
+ throw new Error('buildSinglesigCommit: buyerKeyPair.address missing');
404
+ }
405
+
406
+ const inputs = [{ txid: u.txid, vout: u.vout }];
407
+ const hexPayload = Buffer.from(payload, 'utf8').toString('hex');
408
+
409
+ // Tune these to your environment
410
+ const fee = 0.00003;
411
+ const dust = 0.000056;
412
+
413
+ const change = Number(u.amount) - fee - dust;
414
+ if (!(change > 0)) {
415
+ throw new Error(`buildSinglesigCommit: insufficient (amount=${u.amount}, change=${change})`);
416
+ }
417
+
418
+ const outputs = [
419
+ { [buyerKeyPair.address]: change },
420
+ { data: hexPayload }
421
+ ];
422
+
423
+ const rawHex = await createrawtransactionAsync(inputs, outputs);
424
+
425
+ const signed = await signrawtransactionwithwalletAsync(rawHex);
426
+ if (!signed?.hex) throw new Error('buildSinglesigCommit: signrawtransactionwithwallet failed');
427
+ if (signed.complete === false) throw new Error('buildSinglesigCommit: wallet could not fully sign');
428
+
429
+ const txid = await sendrawtransactionAsync(signed.hex);
430
+
431
+ return { txid, signedHex: signed.hex, rawHex };
432
+ }
433
+
434
+
435
+
436
+ /*
437
+ const buildPsbt = (buildPsbtOptions, networkCode) => {
438
+ try {
439
+ const { rawtx, inputs } = buildPsbtOptions;
440
+ const tx = Transaction.fromHex(rawtx);
441
+ const network = getNetworkConfig(networkCode);
442
+ console.log('network code '+networkCode+JSON.stringify(network))
443
+ const psbt = new Psbt({ network: network });
444
+
445
+ inputs.forEach((input) => {
446
+ const hash = input.txid;
447
+ const index = input.vout;
448
+ const value = BigInt(input.amount * 100000000);
449
+ const script = Uint8Array.from(Buffer.from(input.scriptPubKey, 'hex'));
450
+ const witnessUtxo = { script, value };
451
+ const inputObj = { hash, index, witnessUtxo };
452
+
453
+ if (input.redeemScript) {
454
+ inputObj.witnessScript = Uint8Array.from(Buffer.from(input.redeemScript, 'hex'));
455
+ }
456
+
457
+ psbt.addInput(inputObj);
458
+ });
459
+
460
+ tx.outs.forEach((output) => {
461
+ psbt.addOutput(output);
462
+ });
463
+
464
+ const psbtHex = psbt.toHex();
465
+ return { data: psbtHex };
466
+ } catch (error) {
467
+ console.error('Error building PSBT:', error.message);
468
+ return { error: error.message };
469
+ }
470
+ };*/
471
+
472
+ const getEnoughInputs2 = (_inputs, amount) => {
473
+ const finalInputs = [];
474
+ let amountSum= 0
475
+ _inputs.forEach((u) => {
476
+ const _amountSum = finalInputs
477
+ .map((r) => new BigNumber(r.amount))
478
+ .reduce((a, b) => a.plus(b), new BigNumber(0));
479
+ amountSum += _amountSum.toNumber();
480
+ const _fee = new BigNumber(0.2 * minFeeLtcPerKb).times(finalInputs.length + 1).toNumber();
481
+ if (amountSum < new BigNumber(amount).plus(_fee).toNumber()) finalInputs.push(u);
482
+ });
483
+ const fee = new BigNumber(0.2 * minFeeLtcPerKb).times(finalInputs.length).toNumber();
484
+ return { finalInputs, fee, amountSum };
485
+ };
486
+
487
+
488
+ const getEnoughInputs = (_inputs, amount) => {
489
+ const finalInputs = [];
490
+ _inputs.forEach(u => {
491
+ const _amountSum = finalInputs.map(r => new BigNumber(r.amount)).reduce((a, b) => a.plus(b), new BigNumber(0)); // Sum inputs using BigNumber
492
+ const amountSum = _amountSum.toNumber(); // Convert back to regular number
493
+ if (amountSum < new BigNumber(amount).toNumber()) finalInputs.push(u); // Adds inputs until the sum reaches 'amount'
494
+ });
495
+ const fee = new BigNumber(0.2 * minFeeLtcPerKb).times(finalInputs.length).toNumber(); // Fee based on selected inputs
496
+ return { finalInputs, fee };
497
+ };
498
+
499
+ /*
500
+ const signPsbtRawTx = (signOptions, client) => {
501
+ try {
502
+ const {wif, network, psbtHex } = signOptions;
503
+ const {signpsbtAsync} = initializePromisifiedMethods(client)
504
+ const psbt = Psbt.fromHex(psbtHex);
505
+ //const psbt64 = Psbt.toBase64(psbt)
506
+ //const signResult = signpsbtAsync(psbt64)
507
+ console.log('sign psbt params '+JSON.stringify(signOptions))
508
+ const networkObj = getNetworkConfig(network);
509
+ const keypair = ECPair.fromWIF(wif, networkObj); // Derive keypair from WIF (Wallet Import Format)
510
+ console.log('network '+JSON.stringify(networkObj))// Create a Psbt instance from the provided hex
511
+ console.log('Signing inputs with keypair:', keypair.publicKey.toString('hex'));
512
+ psbt.data.inputs.forEach((input, i) => {
513
+ console.log(`Input ${i}:`, input);
514
+ });
515
+ console.log('components inside sign Psbt Raw '+JSON.stringify(keypair)+' '+psbt+' '+psbtHex)
516
+ // Sign all inputs using the provided keyPair
517
+ psbt.signAllInputs(keypair);
518
+
519
+ const newPsbtHex = psbt.toHex(); // Get the hex of the PSBT after signing
520
+
521
+ console.log('output '+newPsbtHex)
522
+ try {
523
+ psbt.finalizeAllInputs(); // Finalize the inputs of the PSBT (lock them for signing)
524
+ //psbt.validate();
525
+
526
+ const finalHex = psbt.extractTransaction().toHex(); // Extract the final transaction in hex
527
+ console.log('final hex '+finalHex)
528
+ return { data: { psbtHex: newPsbtHex, isFinished: true, hex: finalHex } };
529
+ } catch (err) {
530
+ console.log('cant finalize a partially signed psbt')
531
+ return { data: { psbtHex: newPsbtHex, isFinished: false } }; // Return hex if finalizing fails
532
+ }
533
+ } catch (error) {
534
+
535
+ return { error: error.message }; // Catch any errors and return the error message
536
+ }
537
+ };*/
538
+ const signPsbtRawTx = async (signOptions, client) => {
539
+ try {
540
+ const { wif, network, psbtHex } = signOptions;
541
+ const { signpsbtAsync } = initializePromisifiedMethods(client);
542
+
543
+ // Convert PSBT to Base64 for RPC
544
+ const psbt = Psbt.fromHex(psbtHex); // Load the PSBT from hex
545
+ const psbt64 = psbt.toBase64(); // Convert PSBT to Base64 (required for RPC)
546
+ console.log('PSBT in Base64:', psbt64);
547
+
548
+ // Use RPC to sign the PSBT
549
+ const signResult = await signpsbtAsync(psbt64);
550
+ console.log('RPC Sign Result:', signResult);
551
+
552
+ // Check if the RPC returned a valid result
553
+ if (!signResult || !signResult.psbt) {
554
+ throw new Error('RPC signing failed or returned invalid result');
555
+ }
556
+
557
+ // Convert the returned PSBT back to a Psbt object
558
+ const signedPsbt = Psbt.fromBase64(signResult.psbt);
559
+ const signedHex = signedPsbt.toHex();
560
+
561
+ console.log('signed hex ' + JSON.stringify(signedHex));
562
+
563
+ // Check if the PSBT is finalized
564
+ if (signResult.complete) {
565
+ const finalHex = signedPsbt.extractTransaction().toHex(); // Extract the final transaction
566
+ console.log('Finalized Transaction Hex:', finalHex);
567
+
568
+ return {
569
+ data: {
570
+ psbtHex: signedHex, // Signed PSBT in hex
571
+ isFinished: true,
572
+ complete: true,
573
+ finalHex: finalHex, // Final transaction hex
574
+ hex: finalHex // Alias for compatibility
575
+ }
576
+ };
577
+ } else {
578
+ console.log('PSBT partially signed, returning for further processing.');
579
+ return {
580
+ data: {
581
+ psbtHex: signedHex, // Partially signed PSBT in hex
582
+ isFinished: false,
583
+ complete: false,
584
+ hex: signedHex // Alias - points to PSBT when not complete
585
+ }
586
+ };
587
+ }
588
+ } catch (error) {
589
+ console.error('Error during RPC PSBT signing:', error.message);
590
+ return { error: error.message };
591
+ }
592
+ };
593
+ // Function to build and sign Token Trade transaction
594
+ const buildTokenTradeTransaction = async (trade, buyerKeyPair, sellerKeyPair, commitUTXOs, payload, client) => {
595
+ try {
596
+ const { signrawtransactionwithwalletAsync } = initializePromisifiedMethods(client);
597
+
598
+ const transaction = new litecore.Transaction();
599
+
600
+ // Add inputs (UTXOs)
601
+ commitUTXOs.forEach(utxo => {
602
+ transaction.from({
603
+ txId: utxo.txid,
604
+ outputIndex: utxo.vout,
605
+ script: utxo.scriptPubKey,
606
+ satoshis: new BigNumber(utxo.amount).times(1e8).integerValue().toNumber()
607
+ });
608
+ });
609
+
610
+ // Add outputs (token trade via OP_RETURN)
611
+ transaction.addData(payload);
612
+
613
+ // Serialize transaction to raw hex
614
+ const rawTxHex = transaction.serialize();
615
+
616
+ // Sign the transaction using the Litecoin wallet
617
+ const signResult = await signrawtransactionwithwalletAsync(rawTxHex);
618
+ if (!signResult || !signResult.hex) {
619
+ throw new Error('Signing transaction failed');
620
+ }
621
+
622
+ return signResult.hex;
623
+ } catch (error) {
624
+ throw new Error(`Token Trade Transaction Build Error: ${error.message}`);
625
+ }
626
+ };
627
+
628
+ // Function to build and sign Futures Transaction
629
+ const buildFuturesTransaction = async (config, client) => {
630
+ console.log('inside build futs ' + JSON.stringify(config));
631
+
632
+ try {
633
+ const {
634
+ createRawTransactionAsync,
635
+ decodeRawTransactionAsync,
636
+ listUnspentAsync
637
+ } = initializePromisifiedMethods(client);
638
+
639
+ const {
640
+ buyerKeyPair,
641
+ sellerKeyPair,
642
+ commitUTXOs,
643
+ payload
644
+ } = config;
645
+
646
+ // Validate required parameters
647
+ if (!Array.isArray(commitUTXOs) || commitUTXOs.length === 0) {
648
+ throw new Error('No commit UTXOs provided');
649
+ }
650
+ if (!payload) {
651
+ throw new Error('No payload provided');
652
+ }
653
+ if (!buyerKeyPair || !buyerKeyPair.address) {
654
+ throw new Error('buyerKeyPair with address is required');
655
+ }
656
+
657
+ // --- Convert payload to hex ---
658
+ const hexPayload = Buffer.from(payload, 'utf8').toString('hex');
659
+ console.log('[FUTURES] payload:', payload, 'hex:', hexPayload);
660
+
661
+ // --- Calculate total from commit UTXOs (multisig) ---
662
+ let commitTotal = new BigNumber(0);
663
+ commitUTXOs.forEach(utxo => {
664
+ commitTotal = commitTotal.plus(utxo.amount || 0);
665
+ });
666
+
667
+ console.log('[FUTURES] Commit UTXO total:', commitTotal.toNumber(), 'LTC');
668
+
669
+ // --- Get buyer's UTXOs to cover fees ---
670
+ console.log('[FUTURES] Looking for buyer UTXOs at:', buyerKeyPair.address);
671
+ const buyerUtxos = await listUnspentAsync(0, 999999, [buyerKeyPair.address]);
672
+
673
+ if (!buyerUtxos || buyerUtxos.length === 0) {
674
+ throw new Error(`No UTXOs available for buyer ${buyerKeyPair.address}`);
675
+ }
676
+
677
+ console.log(`[FUTURES] Found ${buyerUtxos.length} UTXOs for buyer`);
678
+
679
+ // Sort by size and pick the largest
680
+ const sortedBuyerUtxos = buyerUtxos.sort((a, b) =>
681
+ new BigNumber(b.amount || 0).comparedTo(a.amount || 0)
682
+ );
683
+
684
+ // Need at least 0.0001 LTC to cover fees comfortably
685
+ const minRequired = 0.0001;
686
+ const feeUtxo = sortedBuyerUtxos.find(u => (u.amount || 0) >= minRequired);
687
+
688
+ if (!feeUtxo) {
689
+ const maxAvailable = sortedBuyerUtxos[0]?.amount || 0;
690
+ throw new Error(
691
+ `Buyer has insufficient funds. ` +
692
+ `Largest UTXO: ${maxAvailable} LTC, ` +
693
+ `Required: ${minRequired} LTC. ` +
694
+ `Address: ${buyerKeyPair.address}`
695
+ );
696
+ }
697
+
698
+ console.log('[FUTURES] Using fee UTXO:', feeUtxo.txid.slice(0, 8), 'amount:', feeUtxo.amount, 'LTC');
699
+
700
+ // --- Build inputs array: commit UTXOs + buyer's fee UTXO ---
701
+ const inputs = [
702
+ ...commitUTXOs.map(utxo => ({
703
+ txid: utxo.txid,
704
+ vout: utxo.vout
705
+ })),
706
+ {
707
+ txid: feeUtxo.txid,
708
+ vout: feeUtxo.vout
709
+ }
710
+ ];
711
+
712
+ // --- Calculate total input ---
713
+ const totalInput = commitTotal.plus(feeUtxo.amount);
714
+ console.log('[FUTURES] Total input:', totalInput.toNumber(), 'LTC');
715
+
716
+ // --- Calculate outputs ---
717
+ const feeSats = 0.00005; // 5000 sats for 2-input tx
718
+
719
+ // Settlement: return commit amount to buyer (simplified - adjust based on PnL)
720
+ const settlementAmount = commitTotal.toNumber();
721
+
722
+ // Change from fee UTXO back to buyer
723
+ const changeAmount = new BigNumber(feeUtxo.amount).minus(feeSats).toNumber();
724
+
725
+ if (changeAmount <= 0) {
726
+ throw new Error(`Fee UTXO too small. UTXO: ${feeUtxo.amount}, Fee: ${feeSats}`);
727
+ }
728
+
729
+ console.log('[FUTURES] Settlement:', settlementAmount, 'LTC');
730
+ console.log('[FUTURES] Change:', changeAmount, 'LTC');
731
+
732
+ // --- Build outputs array ---
733
+ const outputs = [
734
+ { [buyerKeyPair.address]: settlementAmount + changeAmount }, // Combined output
735
+ { data: hexPayload } // OP_RETURN
736
+ ];
737
+
738
+ console.log('[FUTURES] Inputs:', JSON.stringify(inputs));
739
+ console.log('[FUTURES] Outputs:', JSON.stringify(outputs));
740
+
741
+ // --- Create raw transaction first (for fallback) ---
742
+ const rawTx = await createRawTransactionAsync(inputs, outputs);
743
+ console.log('[FUTURES] Raw tx created');
744
+
745
+ // --- Decode to verify ---
746
+ const decoded = await decodeRawTransactionAsync(rawTx);
747
+ console.log('[FUTURES] Decoded tx:', JSON.stringify(decoded));
748
+
749
+ // --- Build prevTxs array BEFORE creating PSBT ---
750
+ const prevTxs = [
751
+ ...commitUTXOs.map(utxo => ({
752
+ txid: utxo.txid,
753
+ vout: utxo.vout,
754
+ scriptPubKey: utxo.scriptPubKey,
755
+ witnessScript: utxo.redeemScript,
756
+ amount: utxo.amount
757
+ })),
758
+ {
759
+ txid: feeUtxo.txid,
760
+ vout: feeUtxo.vout,
761
+ scriptPubKey: feeUtxo.scriptPubKey,
762
+ amount: feeUtxo.amount
763
+ }
764
+ ];
765
+
766
+ // --- Create PSBT with proper witness data ---
767
+ console.log('[FUTURES] Converting to PSBT format with witness data');
768
+ let psbtHex = rawTx; // Fallback to raw tx
769
+
770
+ try {
771
+ // Use converttopsbt to get base PSBT
772
+ const convertToPsbt = new Promise((resolve, reject) => {
773
+ client.cmd('converttopsbt', rawTx, false, (err, result) => {
774
+ if (err) reject(err);
775
+ else resolve(result);
776
+ });
777
+ });
778
+
779
+ const basePsbtBase64 = await convertToPsbt;
780
+ console.log('[FUTURES] Base PSBT created, adding witness data');
781
+
782
+ // Decode the PSBT to manipulate it using bitcoinjs-lib
783
+ let psbtObj;
784
+ try {
785
+ const { Psbt } = require('bitcoinjs-lib');
786
+ psbtObj = Psbt.fromBase64(basePsbtBase64);
787
+ } catch (libError) {
788
+ console.warn('[FUTURES] bitcoinjs-lib not available:', libError.message);
789
+ throw new Error('Cannot add witness data without bitcoinjs-lib');
790
+ }
791
+
792
+ // Add witnessUtxo for each input
793
+ prevTxs.forEach((prevTx, index) => {
794
+ try {
795
+ const witnessUtxo = {
796
+ script: Buffer.from(prevTx.scriptPubKey, 'hex'),
797
+ value: Math.round(prevTx.amount * 1e8)
798
+ };
799
+
800
+ console.log(`[FUTURES] Adding witness for input ${index}:`, {
801
+ script: prevTx.scriptPubKey.slice(0, 20) + '...',
802
+ value: witnessUtxo.value
803
+ });
804
+
805
+ psbtObj.updateInput(index, { witnessUtxo });
806
+
807
+ // Add witnessScript for P2WSH inputs (multisig)
808
+ if (prevTx.witnessScript) {
809
+ psbtObj.updateInput(index, {
810
+ witnessScript: Buffer.from(prevTx.witnessScript, 'hex')
811
+ });
812
+ console.log(`[FUTURES] Added witnessScript for input ${index}`);
813
+ }
814
+
815
+ } catch (updateError) {
816
+ console.warn(`[FUTURES] Could not update input ${index}:`, updateError.message);
817
+ }
818
+ });
819
+
820
+ psbtHex = psbtObj.toHex(); // Convert to hex, not base64
821
+ console.log('[FUTURES] Successfully created PSBT with witness data (hex)');
822
+ console.log('[FUTURES] PSBT starts with:', psbtHex.slice(0, 20));
823
+
824
+ } catch (psbtError) {
825
+ console.error('[FUTURES] PSBT creation failed:', psbtError.message);
826
+ console.log('[FUTURES] Falling back to raw tx');
827
+ psbtHex = rawTx;
828
+ }
829
+
830
+ // Return transaction with PSBT format (or raw tx as fallback)
831
+ // prevTxs is already constructed above
832
+ return {
833
+ psbtHex: psbtHex, // Proper PSBT hex (or raw tx if conversion failed)
834
+ rawHex: rawTx, // Original raw tx hex
835
+ signedHex: rawTx, // For compatibility with getUTXOFromCommit
836
+ txid: decoded.txid, // Transaction ID of settlement tx
837
+ prevTxs: prevTxs, // UTXO details needed for signing
838
+ commitTxId: commitUTXOs[0].txid, // Seller's commit transaction ID
839
+ isPsbt: psbtHex !== rawTx, // Flag indicating if true PSBT or fallback
840
+ data: { // Nested data object
841
+ psbtHex: psbtHex,
842
+ signedHex: rawTx,
843
+ prevTxs: prevTxs // Include prevTxs in data as well
844
+ }
845
+ };
846
+
847
+ } catch (error) {
848
+ console.error('[FUTURES] Build error:', error);
849
+ throw new Error(`Futures Transaction Build Error: ${error.message}`);
850
+ }
851
+ };
852
+
853
+ const buildSignAndBroadcastCommitTx = async (config, client) => {
854
+ try {
855
+ console.log('[COMMIT] Starting commit tx build and broadcast');
856
+
857
+ const {
858
+ buyerKeyPair,
859
+ sellerKeyPair,
860
+ payload,
861
+ multySigChannelData
862
+ } = config;
863
+
864
+ // Validate
865
+ if (!payload) {
866
+ throw new Error('No payload provided');
867
+ }
868
+ if (!multySigChannelData?.address) {
869
+ throw new Error('No multisig address provided');
870
+ }
871
+ if (!buyerKeyPair?.address) {
872
+ throw new Error('No buyer keypair provided');
873
+ }
874
+
875
+ // Initialize promisified methods
876
+ const {
877
+ listUnspentAsync,
878
+ createRawTransactionAsync,
879
+ decodeRawTransactionAsync,
880
+ dumpprivkeyAsync,
881
+ signrawtransactionwithwalletAsync,
882
+ sendrawtransactionAsync
883
+ } = initializePromisifiedMethods(client);
884
+
885
+ console.log('[COMMIT] Payload:', payload);
886
+ const hexPayload = Buffer.from(payload, 'utf8').toString('hex');
887
+
888
+ // Get buyer's UTXO for funding the commit transaction
889
+ console.log('[COMMIT] Getting buyer UTXOs from:', buyerKeyPair.address);
890
+
891
+ // Add timeout to listunspent
892
+ const listUnspentWithTimeout = Promise.race([
893
+ listUnspentAsync(0, 999999, [buyerKeyPair.address]),
894
+ new Promise((_, reject) =>
895
+ setTimeout(() => reject(new Error('listunspent timeout after 5s')), 5000)
896
+ )
897
+ ]);
898
+
899
+ let utxos;
900
+ try {
901
+ utxos = await listUnspentWithTimeout;
902
+ } catch (timeoutError) {
903
+ console.error('[COMMIT] listunspent failed:', timeoutError.message);
904
+ throw new Error('Could not get buyer UTXOs - wallet may not be loaded or synced');
905
+ }
906
+
907
+ if (!utxos || utxos.length === 0) {
908
+ throw new Error(`No UTXOs found for buyer address ${buyerKeyPair.address}`);
909
+ }
910
+
911
+ console.log('[COMMIT] Found', utxos.length, 'UTXOs for buyer');
912
+
913
+ // Sort and pick largest
914
+ const sortedUTXOs = utxos.sort((a, b) =>
915
+ new BigNumber(b.amount || 0).comparedTo(a.amount || 0)
916
+ );
917
+
918
+ const largestUtxo = sortedUTXOs[0];
919
+ console.log('[COMMIT] Using UTXO:', JSON.stringify({
920
+ txid: largestUtxo.txid,
921
+ vout: largestUtxo.vout,
922
+ amount: largestUtxo.amount
923
+ }));
924
+
925
+ const commitUTXOs = [{
926
+ txid: largestUtxo.txid,
927
+ vout: largestUtxo.vout,
928
+ scriptPubKey: largestUtxo.scriptPubKey,
929
+ amount: largestUtxo.amount
930
+ }];
931
+
932
+ const dust = 0.000056;
933
+ const feeSats = 0.000030;
934
+ const change = new BigNumber(largestUtxo.amount).minus(dust).minus(feeSats).toNumber();
935
+
936
+ if (change <= 0) {
937
+ throw new Error('Insufficient UTXO for dust+fee');
938
+ }
939
+
940
+ console.log('[COMMIT] Amounts - Input:', largestUtxo.amount, 'Dust:', dust, 'Fee:', feeSats, 'Change:', change);
941
+
942
+ // Build inputs
943
+ const inputs = commitUTXOs.map(utxo => ({
944
+ txid: utxo.txid,
945
+ vout: utxo.vout
946
+ }));
947
+
948
+ // Build outputs
949
+ const outputs = [
950
+ { [multySigChannelData.address]: dust },
951
+ { [buyerKeyPair.address]: change },
952
+ { data: hexPayload }
953
+ ];
954
+
955
+ console.log('[COMMIT] Inputs:', JSON.stringify(inputs));
956
+ console.log('[COMMIT] Outputs:', JSON.stringify(outputs));
957
+
958
+ // Create raw transaction
959
+ console.log('[COMMIT] Creating raw transaction...');
960
+ const rawTx = await createRawTransactionAsync(inputs, outputs);
961
+ console.log('[COMMIT] Raw tx created, length:', rawTx.length);
962
+
963
+ // Decode to verify
964
+ const decoded = await decodeRawTransactionAsync(rawTx);
965
+ console.log('[COMMIT] Decoded txid:', decoded.txid);
966
+
967
+ // Get WIF and sign
968
+ console.log('[COMMIT] Getting WIF for address:', buyerKeyPair.address);
969
+ const wif = await dumpprivkeyAsync(buyerKeyPair.address);
970
+
971
+ console.log('[COMMIT] Signing transaction...');
972
+
973
+ console.log('[COMMIT] Signing transaction...');
974
+
975
+ const prevTxs = commitUTXOs.map(utxo => ({
976
+ txid: utxo.txid,
977
+ vout: utxo.vout,
978
+ scriptPubKey: utxo.scriptPubKey,
979
+ amount: utxo.amount
980
+ }));
981
+
982
+ console.log('[COMMIT] Raw tx:', rawTx);
983
+ console.log('[COMMIT] WIF (first 10 chars):', wif.substring(0, 10));
984
+ console.log('[COMMIT] prevTxs:', JSON.stringify(prevTxs, null, 2));
985
+
986
+ console.log('[COMMIT] Signing transaction...');
987
+
988
+ const signResult = await signrawtransactionwithwalletAsync(rawTx);
989
+ console.log('[COMMIT] Signed:', signResult);
990
+
991
+ if (!signResult?.hex) {
992
+ throw new Error('Signing failed - no hex returned');
993
+ }
994
+
995
+ if (!signResult.complete) {
996
+ console.warn('[COMMIT] Transaction not fully signed');
997
+ console.log('[COMMIT] Sign errors:', JSON.stringify(signResult.errors));
998
+ throw new Error('Transaction signing incomplete');
999
+ }
1000
+
1001
+ console.log('[COMMIT] Transaction signed successfully');
1002
+
1003
+ // Broadcast
1004
+ console.log('[COMMIT] Broadcasting transaction...');
1005
+ const txid = await sendrawtransactionAsync(signResult.hex);
1006
+
1007
+
1008
+ if (!txid) {
1009
+ throw new Error('Broadcast failed - no txid returned');
1010
+ }
1011
+
1012
+ console.log('[COMMIT] Broadcast successful, txid:', txid);
1013
+
1014
+ // Find the multisig output
1015
+ const voutArr = decoded.vout || [];
1016
+ const channelOut = voutArr.find(o =>
1017
+ o?.scriptPubKey?.addresses?.[0] === multySigChannelData.address
1018
+ ) || voutArr.find(o =>
1019
+ o?.scriptPubKey?.asm?.includes(multySigChannelData.address)
1020
+ );
1021
+
1022
+ if (!channelOut) {
1023
+ throw new Error('No matching vout for commit UTXO');
1024
+ }
1025
+
1026
+ console.log('[COMMIT] Found channel output at vout:', channelOut.n);
1027
+
1028
+ // Return the commit UTXO data
1029
+ const commitUtxoData = {
1030
+ amount: channelOut.value || dust,
1031
+ vout: channelOut.n || 0,
1032
+ txid: txid,
1033
+ scriptPubKey: multySigChannelData.scriptPubKey,
1034
+ redeemScript: multySigChannelData.redeemScript
1035
+ };
1036
+
1037
+ return {
1038
+ signedHex: signResult.hex,
1039
+ rawHex: rawTx,
1040
+ txid: txid,
1041
+ commitUtxoData: commitUtxoData,
1042
+ broadcast: true
1043
+ };
1044
+
1045
+ } catch (error) {
1046
+ console.error('[COMMIT] Error:', error);
1047
+ throw new Error(`Commit tx failed: ${error.message}`);
1048
+ }
1049
+ };
1050
+
1051
+ const getUTXOFromCommit = async (rawtx, client) => {
1052
+ try {
1053
+ // Validate inputs
1054
+ if (!rawtx) {
1055
+ throw new Error('Raw transaction hex is required');
1056
+ }
1057
+ if (!client) {
1058
+ throw new Error('Client is required for decoding transaction');
1059
+ }
1060
+
1061
+ console.log('[getUTXOFromCommit] Decoding transaction');
1062
+
1063
+ // Try promisified method first, with fallback to direct RPC
1064
+ let decodedTx;
1065
+ try {
1066
+ const { decodeRawTransactionAsync } = initializePromisifiedMethods(client);
1067
+
1068
+ if (!decodeRawTransactionAsync) {
1069
+ throw new Error('decodeRawTransactionAsync not available');
1070
+ }
1071
+
1072
+ decodedTx = await decodeRawTransactionAsync(rawtx);
1073
+
1074
+ } catch (promisifyError) {
1075
+ console.warn('[getUTXOFromCommit] Promisified method failed, trying direct RPC');
1076
+
1077
+ // Fallback to direct RPC call
1078
+ if (!client.cmd || typeof client.cmd !== 'function') {
1079
+ throw new Error('Client does not have cmd method');
1080
+ }
1081
+
1082
+ decodedTx = await new Promise((resolve, reject) => {
1083
+ client.cmd('decoderawtransaction', rawtx, (err, result) => {
1084
+ if (err) reject(new Error(`RPC error: ${err.message}`));
1085
+ else resolve(result);
1086
+ });
1087
+ });
1088
+ }
1089
+
1090
+ if (!decodedTx || !decodedTx.vout) {
1091
+ throw new Error('Failed to decode raw transaction or no outputs found');
1092
+ }
1093
+
1094
+ console.log('[getUTXOFromCommit] Decoded tx has', decodedTx.vout.length, 'outputs, txid:', decodedTx.txid);
1095
+
1096
+ // Just return the first output (index 0) as a basic UTXO
1097
+ // This matches what commitUTXO structure typically is
1098
+ const vout = decodedTx.vout[0];
1099
+
1100
+ if (!vout) {
1101
+ throw new Error('No outputs found in decoded transaction');
1102
+ }
1103
+
1104
+ console.log('[getUTXOFromCommit] Returning output 0 with value', vout.value);
1105
+
1106
+ // Return basic UTXO object matching commitUTXO structure
1107
+ return {
1108
+ txid: decodedTx.txid,
1109
+ vout: vout.n,
1110
+ amount: vout.value,
1111
+ scriptPubKey: vout.scriptPubKey?.hex || '',
1112
+ };
1113
+ } catch (error) {
1114
+ console.error('[getUTXOFromCommit] Error:', error.message);
1115
+ throw new Error(`getUTXOFromCommit Error: ${error.message}`);
1116
+ }
1117
+ };
1118
+
1119
+ module.exports = {
1120
+ buildLitecoinTransaction,
1121
+ buildTokenTradeTransaction,
1122
+ buildFuturesTransaction,
1123
+ getUTXOFromCommit,
1124
+ signPsbtRawTx,
1125
+ buildSinglesigCommit,
1126
+ buildCommitAuto,
1127
+ buildSignAndBroadcastCommitTx
1128
+ };