@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,1392 @@
|
|
|
1
|
+
const litecore = require('bitcore-lib-ltc');
|
|
2
|
+
const Encode = require('./txEncoder.js');
|
|
3
|
+
const BigNumber = require('bignumber.js');
|
|
4
|
+
const Consensus = require('./consensus.js');
|
|
5
|
+
const clientPromise = require('./client').getInstance(); // Import the ClientWrapper instance
|
|
6
|
+
const COIN = 100000000;
|
|
7
|
+
const STANDARD_FEE = 2000; // Standard fee in LTC
|
|
8
|
+
const DUST_THRESHOLD = 54600;
|
|
9
|
+
|
|
10
|
+
const TxUtils = {
|
|
11
|
+
async init() {
|
|
12
|
+
this.client = await clientPromise;
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
async getRawTransaction(txid) {
|
|
16
|
+
if(!this.client){
|
|
17
|
+
console.log('awaiting client in get raw tx')
|
|
18
|
+
await init()
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const doc = await this.client.getRawTransaction(txid, true);
|
|
22
|
+
//console.log(doc)
|
|
23
|
+
return doc
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error(`Error fetching transaction for txid ${txid}:`, error);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
async getTransaction(txid) {
|
|
30
|
+
if(!this.client){
|
|
31
|
+
console.log('awaiting client in get raw tx')
|
|
32
|
+
await init()
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const doc = await this.client.getTransaction(txid);
|
|
36
|
+
//console.log(doc)
|
|
37
|
+
return doc
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`Error fetching transaction for txid ${txid}:`, error);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
getAddressTypeUniversal(address) {
|
|
44
|
+
console.log('address in get type '+address)
|
|
45
|
+
// Bitcoin
|
|
46
|
+
if (address.startsWith('1')) return 'p2pkh'; // BTC legacy
|
|
47
|
+
if (address.startsWith('3')) return 'p2sh'; // BTC P2SH
|
|
48
|
+
if (address.startsWith('bc1q')) return 'p2wpkh'; // BTC bech32 segwit
|
|
49
|
+
if (address.startsWith('bc1p')) return 'p2tr'; // BTC taproot
|
|
50
|
+
|
|
51
|
+
// Litecoin
|
|
52
|
+
if (address.startsWith('L') || address.startsWith('M')) return 'p2pkh'; // LTC legacy (mainnet/testnet)
|
|
53
|
+
if (address.startsWith('3')) return 'p2sh'; // LTC also uses '3' for P2SH
|
|
54
|
+
if (address.startsWith('ltc1q')) return 'p2wpkh'; // LTC bech32 segwit
|
|
55
|
+
if (address.startsWith('ltc1p')) return 'p2tr'; // LTC taproot
|
|
56
|
+
|
|
57
|
+
// Testnet Bitcoin
|
|
58
|
+
if (address.startsWith('m') || address.startsWith('n')) return 'p2pkh'; // BTC testnet legacy
|
|
59
|
+
if (address.startsWith('2')) return 'p2sh'; // BTC testnet P2SH
|
|
60
|
+
if (address.startsWith('tb1q')) return 'p2wpkh'; // BTC testnet bech32 segwit
|
|
61
|
+
if (address.startsWith('tb1p')) return 'p2tr'; // BTC testnet taproot
|
|
62
|
+
|
|
63
|
+
// Testnet Litecoin (rare, for completeness)
|
|
64
|
+
if (address.startsWith('tltc1q')) return 'p2wpkh'; // LTC testnet bech32 segwit
|
|
65
|
+
if (address.startsWith('tltc1p')) return 'p2tr'; // LTC testnet taproot
|
|
66
|
+
|
|
67
|
+
return 'unknown';
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
async extractPubkeyByType(vin, scriptType) {
|
|
71
|
+
// P2PKH (legacy)
|
|
72
|
+
if (scriptType === 'p2pkh') {
|
|
73
|
+
if (vin.scriptSig && vin.scriptSig.asm) {
|
|
74
|
+
const asm = vin.scriptSig.asm.split(' ');
|
|
75
|
+
const pubkeyHex = asm[asm.length - 1];
|
|
76
|
+
if (/^[0-9a-fA-F]{66}$/.test(pubkeyHex) || /^[0-9a-fA-F]{130}$/.test(pubkeyHex)) {
|
|
77
|
+
return pubkeyHex;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// P2WPKH (native segwit)
|
|
83
|
+
if (scriptType === 'p2wpkh') {
|
|
84
|
+
console.log('witness '+JSON.stringify(vin.txinwitness))
|
|
85
|
+
if (vin.txinwitness && vin.txinwitness.length > 1) {
|
|
86
|
+
const pubkeyHex = vin.txinwitness[1];
|
|
87
|
+
if (/^[0-9a-fA-F]{66}$/.test(pubkeyHex) || /^[0-9a-fA-F]{130}$/.test(pubkeyHex)) {
|
|
88
|
+
return pubkeyHex;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// P2SH (multisig)
|
|
94
|
+
if (scriptType === 'p2sh') {
|
|
95
|
+
// P2SH can be anything, but for multisig, pubkeys are in redeemScript
|
|
96
|
+
// Look for scriptSig.asm, extract all pubkeys (33 or 65 bytes each)
|
|
97
|
+
if (vin.scriptSig && vin.scriptSig.asm) {
|
|
98
|
+
const asm = vin.scriptSig.asm.split(' ');
|
|
99
|
+
// Multisig format: <sig> <sig> ... <redeemScript>
|
|
100
|
+
// RedeemScript comes last; pubkeys are inside the redeemScript (need to parse it)
|
|
101
|
+
// Here, just return all valid-length hexes (skip signatures)
|
|
102
|
+
const possiblePubkeys = asm.filter(x =>
|
|
103
|
+
/^[0-9a-fA-F]{66}$/.test(x) || /^[0-9a-fA-F]{130}$/.test(x)
|
|
104
|
+
);
|
|
105
|
+
return possiblePubkeys.length > 0 ? possiblePubkeys : null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// P2TR (taproot, not usually relevant unless key path spent)
|
|
110
|
+
if (scriptType === 'p2tr') {
|
|
111
|
+
// Taproot doesn't always reveal a pubkey in the script (key path spend only reveals signature)
|
|
112
|
+
// If script path, pubkeys may be present in the control block/witness
|
|
113
|
+
return null; // (Implement as needed)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Unknown type, fallback
|
|
117
|
+
return null;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async validateAddressWrapper(address) {
|
|
122
|
+
if(!this.client){
|
|
123
|
+
console.log('awaiting client in get raw tx')
|
|
124
|
+
await init()
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
return await this.client.validateAddress(address);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error(`Error validating address ${address}:`, error);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
async addOPReturn(txBlob, payload) {
|
|
134
|
+
return new litecore.Transaction(txBlob).addData(payload);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
isRBF(tx){
|
|
138
|
+
return tx.vin.some(input => input.sequence < 0xfffffffe);
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async getBlockHeight(blockhash) {
|
|
143
|
+
if(!this.client){
|
|
144
|
+
console.log('awaiting client in get raw tx')
|
|
145
|
+
await init()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const block = await this.client.getBlock(blockhash);
|
|
150
|
+
return block.height;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`Error fetching block height for blockhash ${blockhash}:`, error);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async getBlockCount() {
|
|
157
|
+
if(!this.client){
|
|
158
|
+
console.log('awaiting client in get raw tx')
|
|
159
|
+
await init()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
return await this.client.getBlockCount();
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error(`Error fetching block count:`, error);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async getSender(txId) {
|
|
171
|
+
let tx
|
|
172
|
+
try{
|
|
173
|
+
tx = await this.client.getRawTransaction(txId)
|
|
174
|
+
}catch(err){
|
|
175
|
+
console.log('err getting tx for sender'+err)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!tx || !tx.vin || tx.vin.length === 0) {
|
|
179
|
+
return new Error(`Invalid transaction data for ${txId}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const vin = tx.vin[0]; // Assuming we're only interested in the first input
|
|
183
|
+
if (!vin.txid) {
|
|
184
|
+
return new Error(`No previous transaction reference in input for ${vin.txid}`);
|
|
185
|
+
}
|
|
186
|
+
//console.log('get sender tx id '+vin.txid)
|
|
187
|
+
|
|
188
|
+
const parentTx = await this.client.getRawTransaction(vin.txid)
|
|
189
|
+
if (!parentTx || !parentTx.vout || parentTx.vout.length <= vin.vout) {
|
|
190
|
+
return new Error(`Invalid parent transaction data for ${vin.txid}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const output = parentTx.vout[vin.vout];
|
|
194
|
+
if (!output || !output.scriptPubKey || !output.scriptPubKey.addresses) {
|
|
195
|
+
return new Error(`No output found for vin ${vin.vout} in transaction ${vin.txid}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const senderAddress = output.scriptPubKey.addresses[0]; // Assuming single address
|
|
199
|
+
const amount = output.value; // Amount in LTC
|
|
200
|
+
//console.log(senderAddress,amount)
|
|
201
|
+
return { senderAddress, amount };
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
async getReference(txId) {
|
|
206
|
+
let tx
|
|
207
|
+
try {
|
|
208
|
+
tx = await this.client.getRawTransaction(txId, true);
|
|
209
|
+
if (!tx || !tx.vout) {
|
|
210
|
+
return new Error(`Invalid transaction data for ${txId}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let referenceOutput = null;
|
|
214
|
+
|
|
215
|
+
// Iterate over outputs to find the last non-OP_RETURN output
|
|
216
|
+
for (let i = tx.vout.length - 1; i >= 0; i--) {
|
|
217
|
+
const output = tx.vout[i];
|
|
218
|
+
if (output.scriptPubKey.type !== 'nulldata') { // 'nulldata' type is typically used for OP_RETURN
|
|
219
|
+
referenceOutput = output;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (referenceOutput) {
|
|
225
|
+
const address = referenceOutput.scriptPubKey.addresses[0]; // Assuming single address
|
|
226
|
+
const satoshis = Math.round(referenceOutput.value * COIN); // Convert LTC to satoshis
|
|
227
|
+
//console.log(satoshis)
|
|
228
|
+
return { address, satoshis };
|
|
229
|
+
} else {
|
|
230
|
+
return new Error("Reference output not found");
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error(`Error in getReference for transaction ${txId}:`, error);
|
|
234
|
+
return error;
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async loadWallet() {
|
|
240
|
+
if(!this.client){
|
|
241
|
+
console.log('awaiting client in get raw tx')
|
|
242
|
+
await init()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
return await this.client.loadWallet('wallet.dat');
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('Error loading wallet:', error);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
async listUnspent(minConf, maxConf, addresses) {
|
|
253
|
+
if(!this.client){
|
|
254
|
+
console.log('awaiting client in get raw tx')
|
|
255
|
+
await init()
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
return await this.client.listUnspent(minConf, maxConf, addresses);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error(`Error listing UTXOs for addresses ${addresses}:`, error);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
async signRawTransaction(rawTx) {
|
|
266
|
+
if(!this.client){
|
|
267
|
+
console.log('awaiting client in get raw tx')
|
|
268
|
+
this.client = await clientPromise;
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
return await this.client.signrawtransactionwithwallet(rawTx);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error(`Error signing transaction:`, error);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
async sendRawTransaction(serializedTx) {
|
|
280
|
+
if(!this.client){
|
|
281
|
+
console.log('awaiting client in get raw tx')
|
|
282
|
+
this.client = await clientPromise;
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
return await this.client.sendrawtransaction(serializedTx);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error(`Error sending transaction:`, error);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// Add other functions here, replacing direct calls to client methods with the appropriate wrapped methods
|
|
294
|
+
// Example:
|
|
295
|
+
async getTransactionOutputs(txId) {
|
|
296
|
+
if(!this.client){
|
|
297
|
+
console.log('awaiting client in get raw tx')
|
|
298
|
+
this.client = await clientPromise;
|
|
299
|
+
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const tx = await this.client.getRawTransaction(txId);
|
|
304
|
+
return tx.vout.map(output => ({
|
|
305
|
+
address: output.scriptPubKey.addresses ? output.scriptPubKey.addresses[0] : null,
|
|
306
|
+
satoshis: Math.round(output.value * COIN),
|
|
307
|
+
vout: output.n
|
|
308
|
+
})).filter(output => output.address); // Filter out outputs without addresses (OP_RETURN)
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error(`Error getting outputs for tx ${txId}:`, error);
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
async getReferenceAddresses(txId) {
|
|
315
|
+
if(!this.client){
|
|
316
|
+
console.log('awaiting client in get raw tx')
|
|
317
|
+
this.client = await clientPromise;
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const tx = await this.client.getRawTransaction(txId, true); // Fetch the raw transaction data
|
|
323
|
+
if (!tx || !tx.vout) {
|
|
324
|
+
throw new Error(`Invalid transaction data for ${txId}`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const referenceAddresses = [];
|
|
328
|
+
for (let i = 0; i < tx.vout.length; i++) {
|
|
329
|
+
const output = tx.vout[i];
|
|
330
|
+
|
|
331
|
+
if (output.scriptPubKey.type === 'nulldata' && i > 0) {
|
|
332
|
+
const prevOutput = tx.vout[i - 1];
|
|
333
|
+
referenceAddresses.push(prevOutput.scriptPubKey.addresses[0]);
|
|
334
|
+
} else if (output.value < (2 * DUST_THRESHOLD) / COIN) {
|
|
335
|
+
referenceAddresses.push(output.scriptPubKey.addresses[0]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return referenceAddresses.length > 0 ? referenceAddresses : new Error("No reference outputs found");
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`Error in getReferenceAddresses for transaction ${txId}:`, error);
|
|
342
|
+
return error;
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
async listUnspent(minconf, maxconf, addresses) {
|
|
347
|
+
if(!this.client){
|
|
348
|
+
console.log('awaiting client in get raw tx')
|
|
349
|
+
this.client = await clientPromise;
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
return await this.client.listUnspent(minconf, maxconf, addresses);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error(`Error listing UTXOs:`, error);
|
|
357
|
+
return error;
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
async decoderawtransaction(hexString) {
|
|
362
|
+
if(!this.client){
|
|
363
|
+
console.log('awaiting client in get raw tx')
|
|
364
|
+
this.client = await clientPromise;
|
|
365
|
+
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
return await this.client.decoderawtransaction(hexString);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.error(`Error decoding raw transaction:`, error);
|
|
372
|
+
return error;
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
async signrawtransactionwithwallet(rawTx) {
|
|
377
|
+
if(!this.client){
|
|
378
|
+
console.log('awaiting client in get raw tx')
|
|
379
|
+
this.client = await clientPromise;
|
|
380
|
+
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
return await this.client.signrawtransactionwithwallet(rawTx);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error(`Error signing raw transaction with wallet:`, error);
|
|
387
|
+
return error;
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
async getPayload(rawTx) {
|
|
392
|
+
if (!rawTx || !rawTx.vout) {
|
|
393
|
+
console.error("Invalid transaction data or missing 'vout' property.");
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
for (const output of rawTx.vout) {
|
|
398
|
+
if (output.scriptPubKey.type === 'nulldata') {
|
|
399
|
+
const payloadData = output.scriptPubKey.asm;
|
|
400
|
+
console.log("Extracted payload: ", payloadData);
|
|
401
|
+
return payloadData;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
console.log("No payload found in transaction.");
|
|
406
|
+
return null;
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
async getAdditionalInputs(txId) {
|
|
410
|
+
if(!this.client){
|
|
411
|
+
console.log('awaitingthis.clientin get raw tx')
|
|
412
|
+
this.client = await clientPromise;
|
|
413
|
+
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const tx = await this.client.getRawTransaction(txId, true);
|
|
417
|
+
if (!tx || !tx.vin || tx.vin.length <= 1) {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
let additionalInputs = [];
|
|
422
|
+
for (let i = 1; i < tx.vin.length; i++) {
|
|
423
|
+
const input = tx.vin[i];
|
|
424
|
+
const parentTx = await client.getRawTransaction(input.txid, true);
|
|
425
|
+
const output = parentTx.vout[input.vout];
|
|
426
|
+
|
|
427
|
+
const address = output.scriptPubKey.addresses[0];
|
|
428
|
+
const amount = output.value;
|
|
429
|
+
|
|
430
|
+
additionalInputs.push({ address, amount });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return additionalInputs;
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error(`Error in getAdditionalInputs for transaction ${txId}:`, error);
|
|
436
|
+
return error;
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
async setSender(address, requiredAmount) {
|
|
441
|
+
try {
|
|
442
|
+
if(!this.client){
|
|
443
|
+
console.log('awaitingthis.clientin get raw tx')
|
|
444
|
+
this.client = await clientPromise;
|
|
445
|
+
|
|
446
|
+
}
|
|
447
|
+
let utxos = await this.client.listUnspent(0, 9999999, [address]);
|
|
448
|
+
utxos.sort((a, b) => b.amount - a.amount);
|
|
449
|
+
|
|
450
|
+
let selectedUtxos = [];
|
|
451
|
+
let totalAmount = 0;
|
|
452
|
+
|
|
453
|
+
for (let utxo of utxos) {
|
|
454
|
+
selectedUtxos.push(utxo);
|
|
455
|
+
totalAmount += utxo.amount;
|
|
456
|
+
if (totalAmount >= requiredAmount) {
|
|
457
|
+
return selectedUtxos;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let allUtxos = await client.listUnspent(0, 9999999);
|
|
462
|
+
allUtxos = allUtxos.filter(utxo => !selectedUtxos.includes(utxo)).sort((a, b) => b.amount - a.amount);
|
|
463
|
+
|
|
464
|
+
for (let utxo of allUtxos) {
|
|
465
|
+
if (utxo.address !== address) {
|
|
466
|
+
selectedUtxos.push(utxo);
|
|
467
|
+
totalAmount += utxo.amount;
|
|
468
|
+
if (totalAmount >= requiredAmount) break;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (totalAmount < requiredAmount) {
|
|
473
|
+
throw new Error('Insufficient funds: Total UTXOs amount is less than the required amount');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return selectedUtxos;
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error('Error in setSender:', error);
|
|
479
|
+
return error;
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
async createRawTransaction(inputs, outputs, locktime = 0, replaceable = false) {
|
|
484
|
+
const transaction = new litecore.Transaction();
|
|
485
|
+
|
|
486
|
+
for (const input of inputs) {
|
|
487
|
+
const tx = await client.getRawTransaction(input.txid, true);
|
|
488
|
+
const utxo = tx.vout[input.vout];
|
|
489
|
+
transaction.from({
|
|
490
|
+
txId: input.txid,
|
|
491
|
+
outputIndex: input.vout,
|
|
492
|
+
script: utxo.scriptPubKey.hex,
|
|
493
|
+
satoshis: Math.round(utxo.value * COIN)
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
outputs.forEach(output => {
|
|
498
|
+
if (output.address) {
|
|
499
|
+
transaction.to(output.address, output.amount * COIN);
|
|
500
|
+
} else if (output.data) {
|
|
501
|
+
const script = litecore.Script.buildDataOut(output.data, 'hex');
|
|
502
|
+
transaction.addOutput(new litecore.Transaction.Output({ script: script, satoshis: 0 }));
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
if (locktime > 0) {
|
|
507
|
+
transaction.lockUntilDate(locktime);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return transaction;
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
addPayload(payload, rawTx) {
|
|
514
|
+
const transaction = new litecore.Transaction(rawTx);
|
|
515
|
+
const script = litecore.Script.buildDataOut('tl' + payload, 'hex');
|
|
516
|
+
transaction.addOutput(new litecore.Transaction.Output({ script: script, satoshis: 0 }));
|
|
517
|
+
return transaction.toString();
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
async setChange(senderAddress, amount, rawTx) {
|
|
521
|
+
const transaction = new litecore.Transaction(rawTx);
|
|
522
|
+
console.log("Transaction inputs:", transaction.inputs);
|
|
523
|
+
console.log("Transaction outputs:", transaction.outputs);
|
|
524
|
+
|
|
525
|
+
const inputAmount = transaction.inputs.reduce((sum, input) => {
|
|
526
|
+
console.log("Current input:", input);
|
|
527
|
+
return sum + (input.output ? input.output.satoshis : 0);
|
|
528
|
+
}, 0);
|
|
529
|
+
|
|
530
|
+
const outputAmount = transaction.outputs.reduce((sum, output) => {
|
|
531
|
+
console.log("Current output:", output);
|
|
532
|
+
return sum + output.satoshis;
|
|
533
|
+
}, 0);
|
|
534
|
+
|
|
535
|
+
const changeAmount = inputAmount - outputAmount - (STANDARD_FEE * 1e8);
|
|
536
|
+
console.log("Calculated change amount (in satoshis):", changeAmount);
|
|
537
|
+
|
|
538
|
+
if (changeAmount > DUST_THRESHOLD * 1e8) {
|
|
539
|
+
transaction.change(senderAddress);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return transaction.serialize();
|
|
543
|
+
},
|
|
544
|
+
|
|
545
|
+
signTransaction(rawTx, privateKey) {
|
|
546
|
+
const transaction = new litecore.Transaction(rawTx);
|
|
547
|
+
const privateKeyObj = new litecore.PrivateKey(privateKey);
|
|
548
|
+
transaction.sign(privateKeyObj);
|
|
549
|
+
return transaction.toString();
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
async beginRawTransaction(txid, vout) {
|
|
553
|
+
try {
|
|
554
|
+
const inputs = [{ txid: txid, vout: vout }];
|
|
555
|
+
const outputs = {};
|
|
556
|
+
const rawTx = await this.createRawTransaction(inputs, [outputs]);
|
|
557
|
+
return rawTx;
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.error(`Error in createRawTransaction:`, error);
|
|
560
|
+
return error;
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
async addInputs(utxos, rawTx) {
|
|
565
|
+
try {
|
|
566
|
+
let decodedTx = await this.client.decoderawtransaction(rawTx);
|
|
567
|
+
utxos.forEach(utxo => {
|
|
568
|
+
decodedTx.vin.push({ txid: utxo.txid, vout: utxo.vout });
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
return await this.client.createRawTransaction(decodedTx.vin, decodedTx.vout);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
console.error('Error in addInputs:', error);
|
|
574
|
+
return error;
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
async constructInitialTradeTokenTx(params, senderChannel) {
|
|
579
|
+
try {
|
|
580
|
+
const utxos = await this.client.listUnspent(0, 9999999, [senderChannel]);
|
|
581
|
+
if (utxos.length === 0) throw new Error('No UTXOs found for the sender channel address');
|
|
582
|
+
|
|
583
|
+
const selectedUtxo = utxos[0];
|
|
584
|
+
params.channelUtxo = { txid: selectedUtxo.txid, vout: selectedUtxo.vout };
|
|
585
|
+
|
|
586
|
+
let payload = "tl3" + Encode.encodeTradeTokenForUTXO({ ...params, referenceAddress: senderChannel });
|
|
587
|
+
let rawTx = await this.client.createRawTransaction([{ txid: params.channelUtxo.txid, vout: params.channelUtxo.vout }], []);
|
|
588
|
+
|
|
589
|
+
rawTx = this.addPayload(payload, rawTx);
|
|
590
|
+
rawTx = await this.setChange(params.sellerChangeAddress, params.sellerChangeAmount, rawTx);
|
|
591
|
+
|
|
592
|
+
let signedTx = await this.client.signrawtransactionwithwallet(rawTx);
|
|
593
|
+
return signedTx;
|
|
594
|
+
} catch (error) {
|
|
595
|
+
console.error('Error in constructInitialTradeTokenTx:', error);
|
|
596
|
+
return error;
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
|
|
600
|
+
async tradeUTXO(params, senderChannel, senderLTC) {
|
|
601
|
+
try {
|
|
602
|
+
const minConf = 0;
|
|
603
|
+
const maxConf = 9999999;
|
|
604
|
+
|
|
605
|
+
console.log('Fetching UTXOs for:', senderChannel, senderLTC);
|
|
606
|
+
|
|
607
|
+
const utxosSender = await this.client.listUnspent(minConf, maxConf, [senderChannel]);
|
|
608
|
+
const utxosBuyer = await this.client.listUnspent(minConf, maxConf, [senderLTC]);
|
|
609
|
+
|
|
610
|
+
if (utxosSender.length === 0 || utxosBuyer.length === 0) {
|
|
611
|
+
throw new Error('No UTXOs found for one or both addresses');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const selectedUtxoSender = utxosSender[0];
|
|
615
|
+
const selectedUtxoBuyer = utxosBuyer[0];
|
|
616
|
+
|
|
617
|
+
console.log('Selected UTXOs:', selectedUtxoSender, selectedUtxoBuyer);
|
|
618
|
+
|
|
619
|
+
let payload = "tl3" + Encode.encodeTradeTokenForUTXO({
|
|
620
|
+
...params,
|
|
621
|
+
referenceAddress: senderChannel,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
let transaction = new litecore.Transaction()
|
|
625
|
+
.from([selectedUtxoSender, selectedUtxoBuyer])
|
|
626
|
+
.addData(payload)
|
|
627
|
+
.fee(STANDARD_FEE);
|
|
628
|
+
|
|
629
|
+
console.log('Calculating output values...');
|
|
630
|
+
|
|
631
|
+
let totalInput = (selectedUtxoSender.amount + selectedUtxoBuyer.amount) * COIN;
|
|
632
|
+
let requiredOutput = Math.floor(params.satsExpected);
|
|
633
|
+
let remainingSats = totalInput - STANDARD_FEE - requiredOutput;
|
|
634
|
+
|
|
635
|
+
if (remainingSats < 0) {
|
|
636
|
+
throw new Error('Insufficient funds after subtracting the required output and fee.');
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
transaction.to(senderChannel, requiredOutput);
|
|
640
|
+
|
|
641
|
+
let changeSender = Math.floor(selectedUtxoSender.amount * COIN) - (STANDARD_FEE / 2);
|
|
642
|
+
let changeBuyer = Math.floor(selectedUtxoBuyer.amount * COIN) - (requiredOutput + (STANDARD_FEE / 2));
|
|
643
|
+
|
|
644
|
+
transaction.to(senderChannel, changeSender);
|
|
645
|
+
transaction.to(senderLTC, changeBuyer);
|
|
646
|
+
|
|
647
|
+
console.log('Signing the transaction...');
|
|
648
|
+
|
|
649
|
+
let privateKey1 = await client.dumpprivkey(senderChannel);
|
|
650
|
+
let privateKey2 = await client.dumpprivkey(senderLTC);
|
|
651
|
+
|
|
652
|
+
transaction.sign(privateKey1);
|
|
653
|
+
transaction.sign(privateKey2);
|
|
654
|
+
|
|
655
|
+
console.log('Serializing the transaction...');
|
|
656
|
+
|
|
657
|
+
const serializedTx = transaction.serialize();
|
|
658
|
+
const txid = await client.sendrawtransaction(serializedTx);
|
|
659
|
+
console.log('Trade transaction sent:', txid);
|
|
660
|
+
|
|
661
|
+
return txid;
|
|
662
|
+
} catch (error) {
|
|
663
|
+
console.error('Error in tradeUTXO:', error);
|
|
664
|
+
throw error;
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
|
|
668
|
+
async finalizeTradeTokenTx(initialRawTx, additionalParams) {
|
|
669
|
+
try {
|
|
670
|
+
let rawTx = await this.addInputs(additionalParams.additionalUtxos, initialRawTx);
|
|
671
|
+
rawTx = await this.setChange(additionalParams.buyerChangeAddress, additionalParams.buyerChangeAmount, rawTx);
|
|
672
|
+
rawTx = await this.setChange(additionalParams.referenceAddress, additionalParams.referenceAmount, rawTx);
|
|
673
|
+
|
|
674
|
+
let signedTx = await this.client.signrawtransactionwithwallet(rawTx);
|
|
675
|
+
return signedTx;
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.error('Error in finalizeTradeTokenTx:', error);
|
|
678
|
+
return error;
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
|
|
682
|
+
async parseAndCoSignMultisigTransaction(rawTx, expectedUTXOValue, coSignerAddress, coSignerPrivateKey, network) {
|
|
683
|
+
try {
|
|
684
|
+
const decodedTx = await this.client.decoderawtransaction(rawTx, network);
|
|
685
|
+
let paymentOutputIndex = decodedTx.vout.findIndex(output => output.scriptPubKey.type === 'nulldata');
|
|
686
|
+
|
|
687
|
+
if (paymentOutputIndex === -1 || paymentOutputIndex === 0) {
|
|
688
|
+
return new Error('No OP_RETURN output found or no outputs before OP_RETURN');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
let paymentOutput = decodedTx.vout[paymentOutputIndex - 1];
|
|
692
|
+
|
|
693
|
+
if (!paymentOutput || paymentOutput.value < expectedUTXOValue) {
|
|
694
|
+
return new Error('Transaction does not meet the expected UTXO value criteria');
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const additionalUTXOs = await this.getAdditionalUTXOs(coSignerAddress, expectedUTXOValue - paymentOutput.value, network);
|
|
698
|
+
rawTx = await this.addInputsToTransaction(rawTx, additionalUTXOs, network);
|
|
699
|
+
|
|
700
|
+
const coSignedTx = await this.coSignTransaction(rawTx, coSignerPrivateKey, network);
|
|
701
|
+
return coSignedTx;
|
|
702
|
+
} catch (error) {
|
|
703
|
+
console.error('Error in parseAndCoSignMultisigTransaction:', error);
|
|
704
|
+
return error;
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
|
|
708
|
+
async issuePropertyTransaction(fromAddress, initialAmount, ticker, whitelists, managed, backupAddress, nft) {
|
|
709
|
+
try {
|
|
710
|
+
const privateKey = await this.client.dumpprivkey(fromAddress);
|
|
711
|
+
const minAmountSatoshis = STANDARD_FEE;
|
|
712
|
+
const utxo = await this.findSuitableUTXO(fromAddress, minAmountSatoshis);
|
|
713
|
+
|
|
714
|
+
let transaction = new litecore.Transaction().from(utxo).fee(STANDARD_FEE);
|
|
715
|
+
transaction.change(fromAddress);
|
|
716
|
+
|
|
717
|
+
let payload = 'tl1' + Encode.encodeTokenIssue({
|
|
718
|
+
initialAmount: initialAmount,
|
|
719
|
+
ticker: ticker,
|
|
720
|
+
whitelists: whitelists,
|
|
721
|
+
managed: managed,
|
|
722
|
+
backupAddress: backupAddress,
|
|
723
|
+
nft: nft
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
console.log('Preparing payload for property issuance:', payload);
|
|
727
|
+
transaction.addData(payload);
|
|
728
|
+
|
|
729
|
+
transaction.sign(privateKey);
|
|
730
|
+
const serializedTx = transaction.serialize();
|
|
731
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
732
|
+
console.log('Property issuance transaction sent:', txid);
|
|
733
|
+
return txid;
|
|
734
|
+
} catch (error) {
|
|
735
|
+
console.error('Error in issuePropertyTransaction:', error);
|
|
736
|
+
throw error;
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
|
|
740
|
+
async tokenTradeTransaction(fromAddress, propertyIdOffered, propertyIdDesired, amountOffered, amountExpected) {
|
|
741
|
+
try {
|
|
742
|
+
const privateKey = await this.client.dumpprivkey(fromAddress);
|
|
743
|
+
const minAmountSatoshis = STANDARD_FEE;
|
|
744
|
+
const utxo = await this.findSuitableUTXO(fromAddress, minAmountSatoshis);
|
|
745
|
+
|
|
746
|
+
let transaction = new litecore.Transaction().from(utxo).fee(STANDARD_FEE);
|
|
747
|
+
transaction.change(fromAddress);
|
|
748
|
+
|
|
749
|
+
let payload = 'tl5' + Encode.encodeOnChainTokenForToken({
|
|
750
|
+
propertyIdOffered: propertyIdOffered,
|
|
751
|
+
propertyIdDesired: propertyIdDesired,
|
|
752
|
+
amountOffered: amountOffered,
|
|
753
|
+
amountExpected: amountExpected
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
console.log('Preparing payload for token trade:', payload);
|
|
757
|
+
transaction.addData(payload);
|
|
758
|
+
|
|
759
|
+
transaction.sign(privateKey);
|
|
760
|
+
const serializedTx = transaction.serialize();
|
|
761
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
762
|
+
console.log('Token trade transaction sent:', txid);
|
|
763
|
+
return txid;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
console.error('Error in tokenTradeTransaction:', error);
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
|
|
770
|
+
async sendTransaction(fromAddress, toAddress, propertyId, amount, sendAll) {
|
|
771
|
+
try {
|
|
772
|
+
const privateKey = await this.client.dumpprivkey(fromAddress);
|
|
773
|
+
if (sendAll == null) sendAll = 0;
|
|
774
|
+
|
|
775
|
+
const minAmountSatoshis = STANDARD_FEE;
|
|
776
|
+
const utxo = await this.findSuitableUTXO(fromAddress, minAmountSatoshis);
|
|
777
|
+
|
|
778
|
+
let transaction = new litecore.Transaction().from(utxo).fee(STANDARD_FEE);
|
|
779
|
+
//transaction.change(fromAddress);
|
|
780
|
+
|
|
781
|
+
let payload = Encode.encodeSend({
|
|
782
|
+
sendAll: sendAll,
|
|
783
|
+
address: toAddress,
|
|
784
|
+
propertyId: propertyId,
|
|
785
|
+
amount: amount
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
console.log('Preparing payload:', payload);
|
|
789
|
+
transaction.addData(payload);
|
|
790
|
+
|
|
791
|
+
transaction.sign(privateKey);
|
|
792
|
+
const serializedTx = transaction.serialize();
|
|
793
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
794
|
+
console.log('Send transaction sent:', txid);
|
|
795
|
+
|
|
796
|
+
return txid;
|
|
797
|
+
} catch (error) {
|
|
798
|
+
console.error('Error in sendTransaction:', error);
|
|
799
|
+
return error;
|
|
800
|
+
}
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
async activationTransaction(adminAddress, txTypeToActivate) {
|
|
804
|
+
try {
|
|
805
|
+
const codeHash = await Consensus.hashFiles()
|
|
806
|
+
let activationPayload = Encode.encodeActivateTradeLayer({
|
|
807
|
+
txTypeToActivate: txTypeToActivate,
|
|
808
|
+
codeHash: codeHash
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
const utxos = await this.client.listUnspent(1, 9999999, [adminAddress]);
|
|
812
|
+
console.log(utxos);
|
|
813
|
+
if (utxos.length === 0) throw new Error('No UTXOs available for the admin address.');
|
|
814
|
+
|
|
815
|
+
const minAmountSatoshis = STANDARD_FEE;
|
|
816
|
+
const utxo = await this.findSuitableUTXO(adminAddress, minAmountSatoshis);
|
|
817
|
+
|
|
818
|
+
let transaction = new litecore.Transaction().from(utxo)
|
|
819
|
+
.addData(activationPayload)
|
|
820
|
+
.change(adminAddress)
|
|
821
|
+
.fee(STANDARD_FEE);
|
|
822
|
+
|
|
823
|
+
const privateKey = await this.client.dumpprivkey(adminAddress);
|
|
824
|
+
transaction.sign(privateKey);
|
|
825
|
+
|
|
826
|
+
const serializedTx = transaction.uncheckedSerialize();
|
|
827
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
828
|
+
|
|
829
|
+
console.log(`Activation transaction sent successfully. TXID: ${txid}`);
|
|
830
|
+
return txid;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
console.error('Error in activationTransaction:', error);
|
|
833
|
+
throw error;
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
async createContractSeriesTransaction(thisAddress, contractParams) {
|
|
839
|
+
try {
|
|
840
|
+
var txNumber = 16;
|
|
841
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
842
|
+
payload += Encode.encodeCreateFutureContractSeries(contractParams);
|
|
843
|
+
|
|
844
|
+
const utxos = await this.client.listUnspent(1, 9999999, [thisAddress]);
|
|
845
|
+
console.log(utxos);
|
|
846
|
+
if (utxos.length === 0) throw new Error('No UTXOs available for the address');
|
|
847
|
+
|
|
848
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
849
|
+
const rawTx = new litecore.Transaction()
|
|
850
|
+
.from(utxo)
|
|
851
|
+
.addData(payload)
|
|
852
|
+
.change(thisAddress)
|
|
853
|
+
.fee(STANDARD_FEE);
|
|
854
|
+
|
|
855
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
856
|
+
rawTx.sign(privateKey);
|
|
857
|
+
|
|
858
|
+
const serializedTx = rawTx.serialize();
|
|
859
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
860
|
+
|
|
861
|
+
console.log(`Create contract transaction sent successfully. TXID: ${txid}`);
|
|
862
|
+
return txid;
|
|
863
|
+
} catch (error) {
|
|
864
|
+
console.error('Error in createContractSeriesTransaction:', error);
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
|
|
869
|
+
async commitTransaction(fromAddress, toAddress, propertyId, amount, privateKey) {
|
|
870
|
+
try {
|
|
871
|
+
let transaction = new litecore.Transaction();
|
|
872
|
+
|
|
873
|
+
const utxo = await this.findSuitableUTXO(fromAddress, STANDARD_FEE);
|
|
874
|
+
transaction.from(utxo).fee(STANDARD_FEE).change(fromAddress);
|
|
875
|
+
|
|
876
|
+
const payload = 'tl4' + Encode.encodeTradeCommitment({ toAddress, propertyId, amount });
|
|
877
|
+
transaction.addData(payload);
|
|
878
|
+
|
|
879
|
+
transaction.sign(privateKey);
|
|
880
|
+
return transaction;
|
|
881
|
+
} catch (error) {
|
|
882
|
+
console.error('Error in commitTransaction:', error);
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
},
|
|
886
|
+
|
|
887
|
+
async createGeneralTransaction(thisAddress, contractParams, txNumber) {
|
|
888
|
+
try {
|
|
889
|
+
var payload = Encode.encodeCreateFutureContractSeries(contractParams);
|
|
890
|
+
|
|
891
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
892
|
+
const rawTx = new litecore.Transaction()
|
|
893
|
+
.from(utxo)
|
|
894
|
+
.addData(payload)
|
|
895
|
+
.change(thisAddress)
|
|
896
|
+
.fee(STANDARD_FEE);
|
|
897
|
+
|
|
898
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
899
|
+
rawTx.sign(privateKey);
|
|
900
|
+
|
|
901
|
+
const serializedTx = rawTx.serialize();
|
|
902
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
903
|
+
|
|
904
|
+
console.log(`General transaction sent successfully. TXID: ${txid}`);
|
|
905
|
+
return txid;
|
|
906
|
+
} catch (error) {
|
|
907
|
+
console.error('Error in createGeneralTransaction:', error);
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
|
|
912
|
+
async createAttestTransaction(thisAddress, contractParams, txNumber) {
|
|
913
|
+
try {
|
|
914
|
+
// 1. ------ Build Payload (marker + type36 + encoded fields) ------
|
|
915
|
+
const payload = Encode.encodeIssueOrRevokeAttestation(contractParams);
|
|
916
|
+
const payloadBuffer = Buffer.from(payload, "utf8");
|
|
917
|
+
|
|
918
|
+
// 2. ------ Find UTXO for fees ------
|
|
919
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
920
|
+
if (!utxo) {
|
|
921
|
+
throw new Error(`No suitable UTXO found for ${thisAddress}`);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
const inputSats = Math.round(utxo.satoshis);
|
|
925
|
+
const feeSats = 2000;
|
|
926
|
+
const change = inputSats - feeSats;
|
|
927
|
+
console.log('input sats and fee sats '+inputSats+' '+feeSats)
|
|
928
|
+
|
|
929
|
+
// 3. ------ Build the transaction ------
|
|
930
|
+
let tx = new litecore.Transaction().from(utxo);
|
|
931
|
+
|
|
932
|
+
// 3a. Add OP_RETURN payload
|
|
933
|
+
tx = tx.addOutput(
|
|
934
|
+
new litecore.Transaction.Output({
|
|
935
|
+
script: litecore.Script.buildDataOut(payloadBuffer),
|
|
936
|
+
satoshis: 0,
|
|
937
|
+
})
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
// 3b. Add change only if > dust (546 sats)
|
|
941
|
+
if (change > 546) {
|
|
942
|
+
tx = tx.to(thisAddress, change);
|
|
943
|
+
}
|
|
944
|
+
// else → OP_RETURN-only valid TL TX
|
|
945
|
+
|
|
946
|
+
// 4. ------ Apply fee ------
|
|
947
|
+
tx = tx.fee(feeSats);
|
|
948
|
+
|
|
949
|
+
// 5. ------ Sign ------
|
|
950
|
+
const priv = await this.client.dumpprivkey(thisAddress);
|
|
951
|
+
tx = tx.sign(priv);
|
|
952
|
+
|
|
953
|
+
// 6. ------ Final serialize + broadcast ------
|
|
954
|
+
const raw = tx.serialize();
|
|
955
|
+
const txid = await this.client.sendrawtransaction(raw);
|
|
956
|
+
|
|
957
|
+
console.log(`General TX (type-${txNumber}) sent: ${txid}`);
|
|
958
|
+
return txid;
|
|
959
|
+
|
|
960
|
+
} catch (err) {
|
|
961
|
+
console.error("Error in createGeneralTransaction:", err);
|
|
962
|
+
throw err;
|
|
963
|
+
}
|
|
964
|
+
},
|
|
965
|
+
|
|
966
|
+
async createOracleTransaction(thisAddress, contractParams) {
|
|
967
|
+
try {
|
|
968
|
+
var txNumber = 13;
|
|
969
|
+
payload += Encode.encodeCreateOracle(contractParams);
|
|
970
|
+
|
|
971
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
972
|
+
console.log('chosen utxo ' + JSON.stringify(utxo));
|
|
973
|
+
const rawTx = new litecore.Transaction()
|
|
974
|
+
.from(utxo)
|
|
975
|
+
.addData(payload)
|
|
976
|
+
.change(thisAddress)
|
|
977
|
+
.fee(STANDARD_FEE);
|
|
978
|
+
|
|
979
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
980
|
+
rawTx.sign(privateKey);
|
|
981
|
+
|
|
982
|
+
const serializedTx = rawTx.uncheckedSerialize();
|
|
983
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
984
|
+
|
|
985
|
+
console.log(`Create Oracle transaction sent successfully. TXID: ${txid}`);
|
|
986
|
+
return txid;
|
|
987
|
+
} catch (error) {
|
|
988
|
+
console.error('Error in createOracleTransaction:', error);
|
|
989
|
+
throw error;
|
|
990
|
+
}
|
|
991
|
+
},
|
|
992
|
+
|
|
993
|
+
async publishDataTransaction(thisAddress, contractParams) {
|
|
994
|
+
try {
|
|
995
|
+
var txNumber = 14;
|
|
996
|
+
payload += Encode.encodePublishOracleData(contractParams);
|
|
997
|
+
|
|
998
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
999
|
+
const rawTx = new litecore.Transaction()
|
|
1000
|
+
.from(utxo)
|
|
1001
|
+
.addData(payload)
|
|
1002
|
+
.change(thisAddress)
|
|
1003
|
+
.fee(STANDARD_FEE);
|
|
1004
|
+
|
|
1005
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1006
|
+
rawTx.sign(privateKey);
|
|
1007
|
+
|
|
1008
|
+
const serializedTx = rawTx.serialize();
|
|
1009
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1010
|
+
|
|
1011
|
+
console.log(`Oracle publish transaction sent successfully. TXID: ${txid}`);
|
|
1012
|
+
return txid;
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
console.error('Error in publishDataTransaction:', error);
|
|
1015
|
+
throw error;
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
|
|
1019
|
+
async createContractOnChainTradeTransaction(thisAddress, contractParams) {
|
|
1020
|
+
//try {
|
|
1021
|
+
var txNumber = 18;
|
|
1022
|
+
var payload = Encode.encodeTradeContractOnchain(contractParams);
|
|
1023
|
+
console.log('payload in contract tx '+payload)
|
|
1024
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1025
|
+
console.log('utxo '+JSON.stringify(utxo))
|
|
1026
|
+
const rawTx = new litecore.Transaction()
|
|
1027
|
+
.from(utxo)
|
|
1028
|
+
.addData(payload)
|
|
1029
|
+
.change(thisAddress)
|
|
1030
|
+
.fee(2000);
|
|
1031
|
+
|
|
1032
|
+
console.log('raw tx '+JSON.stringify(rawTx))
|
|
1033
|
+
|
|
1034
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1035
|
+
rawTx.sign(privateKey);
|
|
1036
|
+
|
|
1037
|
+
const serializedTx = rawTx.serialize();
|
|
1038
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1039
|
+
|
|
1040
|
+
console.log(`Contract on-chain trade transaction sent successfully. TXID: ${txid}`);
|
|
1041
|
+
return txid;
|
|
1042
|
+
//} catch (error) {
|
|
1043
|
+
// console.error('Error in createContractOnChainTradeTransaction:', error);
|
|
1044
|
+
// throw error;
|
|
1045
|
+
//}
|
|
1046
|
+
},
|
|
1047
|
+
|
|
1048
|
+
async createCancelTransaction(thisAddress, cancelParams) {
|
|
1049
|
+
try {
|
|
1050
|
+
var txNumber = 6;
|
|
1051
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1052
|
+
payload += Encode.encodeCancelOrder(cancelParams);
|
|
1053
|
+
|
|
1054
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1055
|
+
const rawTx = new litecore.Transaction()
|
|
1056
|
+
.from(utxo)
|
|
1057
|
+
.addData(payload)
|
|
1058
|
+
.change(thisAddress)
|
|
1059
|
+
.fee(STANDARD_FEE);
|
|
1060
|
+
|
|
1061
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1062
|
+
rawTx.sign(privateKey);
|
|
1063
|
+
|
|
1064
|
+
const serializedTx = rawTx.serialize();
|
|
1065
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1066
|
+
|
|
1067
|
+
console.log(`Cancel transaction sent successfully. TXID: ${txid}`);
|
|
1068
|
+
return txid;
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
console.error('Error in createCancelTransaction:', error);
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
|
|
1075
|
+
async createCommitTransaction(thisAddress, commitParams) {
|
|
1076
|
+
try {
|
|
1077
|
+
var txNumber = 4;
|
|
1078
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1079
|
+
payload += Encode.encodeCommit(commitParams);
|
|
1080
|
+
|
|
1081
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1082
|
+
const rawTx = new litecore.Transaction()
|
|
1083
|
+
.from(utxo)
|
|
1084
|
+
.addData(payload)
|
|
1085
|
+
.change(thisAddress)
|
|
1086
|
+
.fee(STANDARD_FEE);
|
|
1087
|
+
|
|
1088
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1089
|
+
rawTx.sign(privateKey);
|
|
1090
|
+
|
|
1091
|
+
const serializedTx = rawTx.serialize();
|
|
1092
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1093
|
+
|
|
1094
|
+
console.log(`Commit transaction sent successfully. TXID: ${txid}`);
|
|
1095
|
+
return txid;
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
console.error('Error in createCommitTransaction:', error);
|
|
1098
|
+
throw error;
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
|
|
1102
|
+
async createWithdrawalTransaction(thisAddress, withdrawalParams) {
|
|
1103
|
+
try {
|
|
1104
|
+
var txNumber = 21;
|
|
1105
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1106
|
+
payload += Encode.encodeWithdrawal(withdrawalParams);
|
|
1107
|
+
|
|
1108
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1109
|
+
const rawTx = new litecore.Transaction()
|
|
1110
|
+
.from(utxo)
|
|
1111
|
+
.addData(payload)
|
|
1112
|
+
.change(thisAddress)
|
|
1113
|
+
.fee(STANDARD_FEE);
|
|
1114
|
+
|
|
1115
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1116
|
+
rawTx.sign(privateKey);
|
|
1117
|
+
|
|
1118
|
+
const serializedTx = rawTx.serialize();
|
|
1119
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1120
|
+
|
|
1121
|
+
console.log(`Withdrawal transaction sent successfully. TXID: ${txid}`);
|
|
1122
|
+
return txid;
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
console.error('Error in createWithdrawalTransaction:', error);
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
|
|
1129
|
+
async createChannelContractTradeTransaction(thisAddress, params) {
|
|
1130
|
+
try {
|
|
1131
|
+
var txNumber = 19;
|
|
1132
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1133
|
+
payload += Encode.encodeTradeContractChannel(params);
|
|
1134
|
+
|
|
1135
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1136
|
+
const rawTx = new litecore.Transaction()
|
|
1137
|
+
.from(utxo)
|
|
1138
|
+
.addData(payload)
|
|
1139
|
+
.change(thisAddress)
|
|
1140
|
+
.fee(STANDARD_FEE);
|
|
1141
|
+
|
|
1142
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1143
|
+
rawTx.sign(privateKey);
|
|
1144
|
+
|
|
1145
|
+
const serializedTx = rawTx.serialize();
|
|
1146
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1147
|
+
|
|
1148
|
+
console.log(`Channel Contract Trade transaction sent successfully. TXID: ${txid}`);
|
|
1149
|
+
return txid;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
console.error('Error in createChannelContractTradeTransaction:', error);
|
|
1152
|
+
throw error;
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
|
|
1156
|
+
async createChannelTokenTradeTransaction(thisAddress, params) {
|
|
1157
|
+
try {
|
|
1158
|
+
var txNumber = 20;
|
|
1159
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1160
|
+
payload += Encode.encodeTradeTokensChannel(params);
|
|
1161
|
+
|
|
1162
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1163
|
+
const rawTx = new litecore.Transaction()
|
|
1164
|
+
.from(utxo)
|
|
1165
|
+
.addData(payload)
|
|
1166
|
+
.change(thisAddress)
|
|
1167
|
+
.fee(STANDARD_FEE);
|
|
1168
|
+
|
|
1169
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1170
|
+
rawTx.sign(privateKey);
|
|
1171
|
+
|
|
1172
|
+
const serializedTx = rawTx.serialize();
|
|
1173
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1174
|
+
|
|
1175
|
+
console.log(`Channel Token Trade transaction sent successfully. TXID: ${txid}`);
|
|
1176
|
+
return txid;
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
console.error('Error in createChannelTokenTradeTransaction:', error);
|
|
1179
|
+
throw error;
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
|
|
1183
|
+
async createTransferTransaction(thisAddress, params) {
|
|
1184
|
+
try {
|
|
1185
|
+
var txNumber = 22;
|
|
1186
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1187
|
+
payload += Encode.encodeTransfer(params);
|
|
1188
|
+
|
|
1189
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1190
|
+
const rawTx = new litecore.Transaction()
|
|
1191
|
+
.from(utxo)
|
|
1192
|
+
.addData(payload)
|
|
1193
|
+
.change(thisAddress)
|
|
1194
|
+
.fee(STANDARD_FEE);
|
|
1195
|
+
|
|
1196
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1197
|
+
rawTx.sign(privateKey);
|
|
1198
|
+
|
|
1199
|
+
const serializedTx = rawTx.serialize();
|
|
1200
|
+
const txid = await this.client.sendrawtransaction(serializedTx);
|
|
1201
|
+
|
|
1202
|
+
console.log(`Transfer transaction sent successfully. TXID: ${txid}`);
|
|
1203
|
+
return txid;
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
console.error('Error in createTransferTransaction:', error);
|
|
1206
|
+
throw error;
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
|
|
1210
|
+
async createMintTransaction(thisAddress, params) {
|
|
1211
|
+
try {
|
|
1212
|
+
var txNumber = 24;
|
|
1213
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1214
|
+
payload += Encode.encodeMintSynthetic(params);
|
|
1215
|
+
|
|
1216
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1217
|
+
const rawTx = new litecore.Transaction()
|
|
1218
|
+
.from(utxo)
|
|
1219
|
+
.addData(payload)
|
|
1220
|
+
.change(thisAddress)
|
|
1221
|
+
.fee(STANDARD_FEE);
|
|
1222
|
+
|
|
1223
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1224
|
+
rawTx.sign(privateKey);
|
|
1225
|
+
|
|
1226
|
+
const serializedTx = rawTx.serialize();
|
|
1227
|
+
const txid = await client.sendrawtransaction(serializedTx);
|
|
1228
|
+
|
|
1229
|
+
console.log(`Mint transaction sent successfully. TXID: ${txid}`);
|
|
1230
|
+
return txid;
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
console.error('Error in createMintTransaction:', error);
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
|
|
1237
|
+
async createRedeemTransaction(thisAddress, params) {
|
|
1238
|
+
try {
|
|
1239
|
+
var txNumber = 25;
|
|
1240
|
+
var payload = 'tl' + txNumber.toString(36);
|
|
1241
|
+
payload += Encode.encodeRedeemSynthetic(params);
|
|
1242
|
+
|
|
1243
|
+
const utxo = await this.findSuitableUTXO(thisAddress, STANDARD_FEE);
|
|
1244
|
+
const rawTx = new litecore.Transaction()
|
|
1245
|
+
.from(utxo)
|
|
1246
|
+
.addData(payload)
|
|
1247
|
+
.change(thisAddress)
|
|
1248
|
+
.fee(STANDARD_FEE);
|
|
1249
|
+
|
|
1250
|
+
const privateKey = await this.client.dumpprivkey(thisAddress);
|
|
1251
|
+
rawTx.sign(privateKey);
|
|
1252
|
+
|
|
1253
|
+
const serializedTx = rawTx.serialize();
|
|
1254
|
+
const txid = await client.sendrawtransaction(serializedTx);
|
|
1255
|
+
|
|
1256
|
+
console.log(`Redeem transaction sent successfully. TXID: ${txid}`);
|
|
1257
|
+
return txid;
|
|
1258
|
+
} catch (error) {
|
|
1259
|
+
console.error('Error in createRedeemTransaction:', error);
|
|
1260
|
+
throw error;
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
|
|
1264
|
+
createMultisig(pubKey1, pubKey2, coin = 'ltc', isTestnet, address = '') {
|
|
1265
|
+
const kind = TxUtils.inferChannelAddrType(address);
|
|
1266
|
+
console.log('coin/net '+coin+' '+isTestnet)
|
|
1267
|
+
coin = coin.toLowerCase();
|
|
1268
|
+
// --- Setup network params ---
|
|
1269
|
+
let params
|
|
1270
|
+
if (coin === 'btc') {
|
|
1271
|
+
params = isTestnet
|
|
1272
|
+
? require('bitcoinjs-lib').networks.testnet
|
|
1273
|
+
: require('bitcoinjs-lib').networks.bitcoin;
|
|
1274
|
+
} else if (coin === 'ltc') {
|
|
1275
|
+
params = isTestnet
|
|
1276
|
+
? {
|
|
1277
|
+
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
1278
|
+
bech32: 'tltc',
|
|
1279
|
+
bip32: { public: 0x043587cf, private: 0x04358394 },
|
|
1280
|
+
pubKeyHash: 0x6f,
|
|
1281
|
+
scriptHash: 0x3a,
|
|
1282
|
+
wif: 0xef,
|
|
1283
|
+
}
|
|
1284
|
+
: {
|
|
1285
|
+
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
1286
|
+
bech32: 'ltc',
|
|
1287
|
+
bip32: { public: 0x019da462, private: 0x019d9cfe },
|
|
1288
|
+
pubKeyHash: 0x30,
|
|
1289
|
+
scriptHash: 0x32,
|
|
1290
|
+
wif: 0xb0,
|
|
1291
|
+
};
|
|
1292
|
+
} else {
|
|
1293
|
+
throw new Error("Unsupported coin type: " + coin);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// --- Bech32 native SegWit multisig ---
|
|
1297
|
+
if (kind === 'p2wsh') {
|
|
1298
|
+
const lib = require('bitcoinjs-lib');
|
|
1299
|
+
const pubkeys = [
|
|
1300
|
+
Buffer.from(pubKey1, 'hex'),
|
|
1301
|
+
Buffer.from(pubKey2, 'hex')
|
|
1302
|
+
];
|
|
1303
|
+
const p2ms = lib.payments.p2ms({ m: 2, pubkeys, network: params });
|
|
1304
|
+
const p2wsh = lib.payments.p2wsh({ redeem: p2ms, network: params });
|
|
1305
|
+
return p2wsh.address;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// --- P2SH legacy multisig ---
|
|
1309
|
+
else if (kind === 'p2sh-main' || kind === 'p2sh-test') {
|
|
1310
|
+
if (coin === 'btc') {
|
|
1311
|
+
const bitcore = require('bitcore-lib');
|
|
1312
|
+
const netParam = isTestnet ? bitcore.Networks.testnet : bitcore.Networks.mainnet;
|
|
1313
|
+
const publicKeys = [
|
|
1314
|
+
new bitcore.PublicKey(pubKey1),
|
|
1315
|
+
new bitcore.PublicKey(pubKey2)
|
|
1316
|
+
];
|
|
1317
|
+
const multisig = new bitcore.Address(publicKeys, 2, netParam);
|
|
1318
|
+
return multisig.toString(); // 3... or 2...
|
|
1319
|
+
} else if (coin === 'ltc') {
|
|
1320
|
+
const litecore = require('bitcore-lib-ltc');
|
|
1321
|
+
const netParam = isTestnet ? litecore.Networks.testnet : litecore.Networks.livenet;
|
|
1322
|
+
const publicKeys = [
|
|
1323
|
+
new litecore.PublicKey(pubKey1),
|
|
1324
|
+
new litecore.PublicKey(pubKey2)
|
|
1325
|
+
];
|
|
1326
|
+
const multisig = new litecore.Address(publicKeys, 2, netParam);
|
|
1327
|
+
return multisig.toString(); // M..., Q...
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// --- Unknown address type ---
|
|
1332
|
+
else {
|
|
1333
|
+
throw new Error("Unknown or unsupported address type for channel: " + address);
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
|
|
1337
|
+
// Infer channel address *format* so you can derive the same type when rebuilding multisig.
|
|
1338
|
+
// Returns: 'p2wsh' | 'p2sh-main' | 'p2sh-test' | 'unknown'
|
|
1339
|
+
inferChannelAddrType(addr) {
|
|
1340
|
+
console.log('inside addr type', addr);
|
|
1341
|
+
const a = String(addr || '').trim();
|
|
1342
|
+
const lower = a.toLowerCase();
|
|
1343
|
+
|
|
1344
|
+
// Bech32 / Bech32m (SegWit) for BTC + LTC (we treat all bech32 channels as P2WSH for your multisig)
|
|
1345
|
+
// BTC mainnet: bc1..., BTC testnet: tb1..., LTC mainnet: ltc1..., LTC testnet: tltc1...
|
|
1346
|
+
if (
|
|
1347
|
+
lower.startsWith('bc1') || lower.startsWith('tb1') ||
|
|
1348
|
+
lower.startsWith('ltc1') || lower.startsWith('tltc1')
|
|
1349
|
+
) {
|
|
1350
|
+
return 'p2wsh';
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// Base58 P2SH (legacy multisig)
|
|
1354
|
+
// BTC mainnet: 3..., LTC mainnet: M... (many libs also accept 3... for LTC)
|
|
1355
|
+
if (/^[3M]/.test(a)) return 'p2sh-main';
|
|
1356
|
+
|
|
1357
|
+
// BTC testnet: 2..., LTC testnet (older style): Q...
|
|
1358
|
+
if (/^[2Q]/.test(a)) return 'p2sh-test';
|
|
1359
|
+
|
|
1360
|
+
return 'unknown';
|
|
1361
|
+
},
|
|
1362
|
+
|
|
1363
|
+
async findSuitableUTXO(address, minAmount) {
|
|
1364
|
+
const utxos = await this.client.listUnspent(0, 9999999, [address]);
|
|
1365
|
+
const suitableUtxo = utxos.find(utxo => (utxo.amount >=0.00002))//* COIN >= minAmount) && (utxo.amount * COIN >= DUST_THRESHOLD));
|
|
1366
|
+
if (!suitableUtxo) {
|
|
1367
|
+
throw new Error('No suitable UTXO found.');
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
return {
|
|
1371
|
+
txId: suitableUtxo.txid,
|
|
1372
|
+
outputIndex: suitableUtxo.vout,
|
|
1373
|
+
address: suitableUtxo.address,
|
|
1374
|
+
script: suitableUtxo.scriptPubKey,
|
|
1375
|
+
satoshis: Math.round(suitableUtxo.amount * 1e8) // Convert LTC to satoshis
|
|
1376
|
+
};
|
|
1377
|
+
},
|
|
1378
|
+
|
|
1379
|
+
decodeTransactionType(encodedPayload) {
|
|
1380
|
+
const txType = parseInt(encodedPayload.substring(0, 2), 16);
|
|
1381
|
+
return txType;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
// Ensure init is called before using any other methods
|
|
1388
|
+
(async () => {
|
|
1389
|
+
await TxUtils.init();
|
|
1390
|
+
})();
|
|
1391
|
+
|
|
1392
|
+
module.exports = TxUtils;
|