@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.
- package/.claude/settings.local.json +13 -0
- package/.claude/skills/tl-algo/SKILL.md +255 -0
- package/.gitattributes +2 -0
- package/.github/workflows/publish.yaml +26 -0
- package/4mm.js +163 -0
- package/LICENSE +21 -0
- package/NPMSwapRefactor.zip +0 -0
- package/README.md +217 -0
- package/address.sh +26 -0
- package/algoAPI.js +581 -0
- package/analyzepsbt.js +92 -0
- package/apiEx.js +99 -0
- package/bb_hyperscalper.js +290 -0
- package/bbo_demo.js +111 -0
- package/buyer.js +622 -0
- package/client.js +50 -0
- package/createTxTest.js +26 -0
- package/createWallet.js +75 -0
- package/daytrader.js +531 -0
- package/decodeTest.js +69 -0
- package/fundingManager.js +144 -0
- package/index.js +4 -0
- package/listener.js +27 -0
- package/litecoreTxBuilder.js +1128 -0
- package/mmEx.js +356 -0
- package/networks.js +51 -0
- package/orderbook.js +200 -0
- package/package.json +34 -0
- package/perTradeQueue.js +36 -0
- package/projectsTLNPMTLNPM/package-lock.json +162 -0
- package/projectsTLNPMTLNPM/package.json +5 -0
- package/quick.js +32 -0
- package/quickFut.js +37 -0
- package/quickSell.js +37 -0
- package/relayerClient.js +117 -0
- package/run4mm.js +80 -0
- package/run_bbo_tracker.js +241 -0
- package/seller.js +443 -0
- package/session.js +45 -0
- package/setup-lin-ltc.sh +139 -0
- package/setup-lin.sh +203 -0
- package/setup-win-ltc.bat +108 -0
- package/setup-win.bat +167 -0
- package/spam_screamer_futures.js +222 -0
- package/tradelayer.js/.gitattributes +2 -0
- package/tradelayer.js/README.md +2 -0
- package/tradelayer.js/oldTests/activationTest.js +6 -0
- package/tradelayer.js/oldTests/base58.test.js +23 -0
- package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
- package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
- package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
- package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
- package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
- package/tradelayer.js/oldTests/createPayload.js +0 -0
- package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
- package/tradelayer.js/oldTests/decode.js +205 -0
- package/tradelayer.js/oldTests/decodeTest.js +50 -0
- package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
- package/tradelayer.js/oldTests/encodeDecode.js +340 -0
- package/tradelayer.js/oldTests/expressTest.js +29 -0
- package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
- package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
- package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
- package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
- package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
- package/tradelayer.js/oldTests/indexTest.js +26 -0
- package/tradelayer.js/oldTests/initTokensTest.js +32 -0
- package/tradelayer.js/oldTests/interfaceChild.js +129 -0
- package/tradelayer.js/oldTests/listenerChild.js +112 -0
- package/tradelayer.js/oldTests/opdecode.js +26 -0
- package/tradelayer.js/oldTests/options.js +79 -0
- package/tradelayer.js/oldTests/optxtest.js +116 -0
- package/tradelayer.js/oldTests/optxtest1.js +64 -0
- package/tradelayer.js/oldTests/oracle.test.js +32 -0
- package/tradelayer.js/oldTests/orderbook.test.js +36 -0
- package/tradelayer.js/oldTests/parsing.js +93 -0
- package/tradelayer.js/oldTests/payload.js +13 -0
- package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
- package/tradelayer.js/oldTests/property.test.js +53 -0
- package/tradelayer.js/oldTests/propertyLevel.js +75 -0
- package/tradelayer.js/oldTests/propertyTest.js +32 -0
- package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
- package/tradelayer.js/oldTests/salter.js +14 -0
- package/tradelayer.js/oldTests/tally.js +81 -0
- package/tradelayer.js/oldTests/tally.test.js +48 -0
- package/tradelayer.js/oldTests/tally2.js +124 -0
- package/tradelayer.js/oldTests/tally3.js +142 -0
- package/tradelayer.js/oldTests/tallyDiag.js +38 -0
- package/tradelayer.js/oldTests/testGetRaw.js +40 -0
- package/tradelayer.js/oldTests/testHexConvert.js +47 -0
- package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
- package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
- package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
- package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
- package/tradelayer.js/oldTests/testOPReturn.js +102 -0
- package/tradelayer.js/oldTests/testPayload.js +23 -0
- package/tradelayer.js/oldTests/testRaw.js +50 -0
- package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
- package/tradelayer.js/oldTests/testTxBuild +28 -0
- package/tradelayer.js/oldTests/testTxBuild.js +42 -0
- package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
- package/tradelayer.js/oldTests/txUtilsA.js +515 -0
- package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
- package/tradelayer.js/oldTests/vaults.js +72 -0
- package/tradelayer.js/oldTests/volumeIndex.js +117 -0
- package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
- package/tradelayer.js/output_base64.txt +1 -0
- package/tradelayer.js/package-lock.json +9967 -0
- package/tradelayer.js/package.json +61 -0
- package/tradelayer.js/server/index.js +88 -0
- package/tradelayer.js/server/litecoind.exe +0 -0
- package/tradelayer.js/src/activation.js +303 -0
- package/tradelayer.js/src/adjuster.js +77 -0
- package/tradelayer.js/src/amm.js +400 -0
- package/tradelayer.js/src/base256.js +55 -0
- package/tradelayer.js/src/base94.js +79 -0
- package/tradelayer.js/src/channels.js +1163 -0
- package/tradelayer.js/src/clearing.js +3109 -0
- package/tradelayer.js/src/clearlist.js +364 -0
- package/tradelayer.js/src/client.js +295 -0
- package/tradelayer.js/src/consensus.js +613 -0
- package/tradelayer.js/src/contractRegistry.js +964 -0
- package/tradelayer.js/src/db.js +89 -0
- package/tradelayer.js/src/init.js +24 -0
- package/tradelayer.js/src/insurance.js +347 -0
- package/tradelayer.js/src/interface.js +218 -0
- package/tradelayer.js/src/interfaceExpress.js +178 -0
- package/tradelayer.js/src/iou.js +509 -0
- package/tradelayer.js/src/listener.js +226 -0
- package/tradelayer.js/src/logic.js +1702 -0
- package/tradelayer.js/src/main.js +927 -0
- package/tradelayer.js/src/marginMap.js +2165 -0
- package/tradelayer.js/src/options.js +126 -0
- package/tradelayer.js/src/oracle.js +394 -0
- package/tradelayer.js/src/orderbook.js +4123 -0
- package/tradelayer.js/src/persistence.js +554 -0
- package/tradelayer.js/src/property.js +411 -0
- package/tradelayer.js/src/reOrg.js +41 -0
- package/tradelayer.js/src/scaling.js +145 -0
- package/tradelayer.js/src/tally.js +1275 -0
- package/tradelayer.js/src/tradeHistoryManager.js +552 -0
- package/tradelayer.js/src/txDecoder.js +584 -0
- package/tradelayer.js/src/txEncoder.js +610 -0
- package/tradelayer.js/src/txIndex.js +502 -0
- package/tradelayer.js/src/txUtils.js +1392 -0
- package/tradelayer.js/src/types.js +429 -0
- package/tradelayer.js/src/validity.js +3077 -0
- package/tradelayer.js/src/vaults.js +430 -0
- package/tradelayer.js/src/vesting.js +491 -0
- package/tradelayer.js/src/volumeIndex.js +618 -0
- package/tradelayer.js/src/walletInterface.js +220 -0
- package/tradelayer.js/src/walletListener.js +665 -0
- package/tradelayer.js/tests/256decode.js +82 -0
- package/tradelayer.js/tests/UTXOracle.js +205 -0
- package/tradelayer.js/tests/base94test.js +23 -0
- package/tradelayer.js/tests/cancelTxTest.js +62 -0
- package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
- package/tradelayer.js/tests/decimalTest.js +65 -0
- package/tradelayer.js/tests/decoderTest.js +100 -0
- package/tradelayer.js/tests/deltaCount.js +47 -0
- package/tradelayer.js/tests/deltaCount2.js +60 -0
- package/tradelayer.js/tests/interfaceTest.js +37 -0
- package/tradelayer.js/tests/mainTest.js +53 -0
- package/tradelayer.js/tests/makeActivationTest.js +24 -0
- package/tradelayer.js/tests/maxHeightTest.js +49 -0
- package/tradelayer.js/tests/reverseHash.js +72 -0
- package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
- package/tradelayer.js/tests/tallyTest.js +40 -0
- package/tradelayer.js/tests/testBuybacks.js +46 -0
- package/tradelayer.js/tests/testCodeHash.js +49 -0
- package/tradelayer.js/tests/testConsensusHash.js +91 -0
- package/tradelayer.js/tests/testDecode.js +30 -0
- package/tradelayer.js/tests/testEncodingLengths.js +129 -0
- package/tradelayer.js/tests/testGetTx +32 -0
- package/tradelayer.js/tests/testGetTx.js +32 -0
- package/tradelayer.js/tests/testHexHash.js +32 -0
- package/tradelayer.js/tests/testIndexHash.js +35 -0
- package/tradelayer.js/tests/testInitContracts.js +38 -0
- package/tradelayer.js/tests/testMaxConsensus.js +12 -0
- package/tradelayer.js/tests/testMaxSynth.js +44 -0
- package/tradelayer.js/tests/testMint.js +21 -0
- package/tradelayer.js/tests/testNetwork.js +33 -0
- package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
- package/tradelayer.js/tests/testRebates.js +32 -0
- package/tradelayer.js/tests/testRedeem.js +22 -0
- package/tradelayer.js/tests/testTokenTrade.js +39 -0
- package/tradelayer.js/tests/testTxBuild.js +42 -0
- package/tradelayer.js/tests/testUTXOTrade.js +27 -0
- package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
- package/tradelayer.js/tests/tradeFutures.js +40 -0
- package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
- package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
- package/tradelayer.js/tests/txScanTest.js +134 -0
- package/tradelayer.js/tests/validateTest.js +136 -0
- package/tradelayer.js/tests/vestingTest.js +37 -0
- package/tradelayer.js/utils/activateMainnet.js +59 -0
- package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
- package/tradelayer.js/utils/autocompactdb.js +23 -0
- package/tradelayer.js/utils/base64toHex.js +32 -0
- package/tradelayer.js/utils/broadcastDoge.js +38 -0
- package/tradelayer.js/utils/calcRedeem.js +19 -0
- package/tradelayer.js/utils/checkNetwork.js +27 -0
- package/tradelayer.js/utils/createAddress.js +48 -0
- package/tradelayer.js/utils/createAttestation.js +133 -0
- package/tradelayer.js/utils/createContract.js +118 -0
- package/tradelayer.js/utils/createOracle.js +94 -0
- package/tradelayer.js/utils/createwallet.js +20 -0
- package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
- package/tradelayer.js/utils/crossTokenTrades.js +62 -0
- package/tradelayer.js/utils/dumpPriv.js +29 -0
- package/tradelayer.js/utils/generateChannel.js +34 -0
- package/tradelayer.js/utils/getInfo.js +21 -0
- package/tradelayer.js/utils/hardWipe.js +20 -0
- package/tradelayer.js/utils/hexTo64.js +16 -0
- package/tradelayer.js/utils/importAddress.js +28 -0
- package/tradelayer.js/utils/importpriv.js +20 -0
- package/tradelayer.js/utils/issueOracleContract.js +67 -0
- package/tradelayer.js/utils/issueTokens.js +41 -0
- package/tradelayer.js/utils/listunspent.js +66 -0
- package/tradelayer.js/utils/litecoinClient.js +30 -0
- package/tradelayer.js/utils/loadwallet.js +20 -0
- package/tradelayer.js/utils/publishOracle.js +113 -0
- package/tradelayer.js/utils/sendActivation.js +21 -0
- package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
- package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
- package/tradelayer.js/utils/sendCommit.js +24 -0
- package/tradelayer.js/utils/sendDoge.js +62 -0
- package/tradelayer.js/utils/sendDogeMain.js +67 -0
- package/tradelayer.js/utils/sendDogeTx.js +46 -0
- package/tradelayer.js/utils/sendLTC.js +63 -0
- package/tradelayer.js/utils/sendMainnet.js +62 -0
- package/tradelayer.js/utils/sendTransfer.js +19 -0
- package/tradelayer.js/utils/sendVestTest.js +88 -0
- package/tradelayer.js/utils/sendWithdrawal.js +26 -0
- package/tradelayer.js/utils/simpleStart.js +8 -0
- package/tradelayer.js/utils/startStop.js +27 -0
- package/tradelayer.js/utils/structuredTrades.js +136 -0
- package/tradelayer.js/utils/verifySignature.js +90 -0
- package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
- package/tradelayer.js/utils/walletCache.js +172 -0
- package/tradelayer.js/utils/walletContractInterface.js +48 -0
- package/tradelayer.js/utils/walletFetchTxs.js +66 -0
- package/tradelayer.js/utils/walletUtils.js +97 -0
- package/tradelayer.js/utils/wipeDB.js +55 -0
- package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
- package/txEncoder.js +529 -0
- package/utility.js +28 -0
- package/verifymessage.js +38 -0
- 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
|
+
};
|