mirror of https://github.com/lukechilds/Agama.git
7 changed files with 800 additions and 199 deletions
@ -0,0 +1,500 @@ |
|||||
|
const bitcoinJSForks = require('bitcoinforksjs-lib'); |
||||
|
const bitcoinZcash = require('bitcoinjs-lib-zcash'); |
||||
|
const bitcoinPos = require('bitcoinjs-lib-pos'); |
||||
|
|
||||
|
// not prod ready, only for voting!
|
||||
|
// needs a fix
|
||||
|
|
||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.post('/electrum/createrawtx-multiout', (req, res, next) => { |
||||
|
if (shepherd.checkToken(req.body.token)) { |
||||
|
// TODO: 1) unconf output(s) error message
|
||||
|
// 2) check targets integrity
|
||||
|
const network = req.body.network || shepherd.findNetworkObj(req.body.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
const initTargets = JSON.parse(JSON.stringify(req.body.targets)); |
||||
|
let targets = req.body.targets; |
||||
|
const changeAddress = req.body.change; |
||||
|
const push = req.body.push; |
||||
|
const opreturn = req.body.opreturn; |
||||
|
const btcFee = req.body.btcfee ? Number(req.body.btcfee) : null; |
||||
|
let fee = shepherd.electrumServers[network].txfee; |
||||
|
let wif = req.body.wif; |
||||
|
|
||||
|
if (req.body.gui) { |
||||
|
wif = shepherd.electrumKeys[req.body.coin].priv; |
||||
|
} |
||||
|
|
||||
|
if (req.body.vote) { |
||||
|
wif = shepherd.elections.priv; |
||||
|
} |
||||
|
|
||||
|
if (btcFee) { |
||||
|
fee = 0; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('electrum createrawtx =>', true); |
||||
|
|
||||
|
ecl.connect(); |
||||
|
shepherd.listunspent(ecl, changeAddress, network, true, req.body.verify === 'true' ? true : null) |
||||
|
.then((utxoList) => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
if (utxoList && |
||||
|
utxoList.length && |
||||
|
utxoList[0] && |
||||
|
utxoList[0].txid) { |
||||
|
let utxoListFormatted = []; |
||||
|
let totalInterest = 0; |
||||
|
let totalInterestUTXOCount = 0; |
||||
|
let interestClaimThreshold = 200; |
||||
|
let utxoVerified = true; |
||||
|
|
||||
|
for (let i = 0; i < utxoList.length; i++) { |
||||
|
if (network === 'komodo') { |
||||
|
utxoListFormatted.push({ |
||||
|
txid: utxoList[i].txid, |
||||
|
vout: utxoList[i].vout, |
||||
|
value: Number(utxoList[i].amountSats), |
||||
|
interestSats: Number(utxoList[i].interestSats), |
||||
|
verified: utxoList[i].verified ? utxoList[i].verified : false, |
||||
|
}); |
||||
|
} else { |
||||
|
utxoListFormatted.push({ |
||||
|
txid: utxoList[i].txid, |
||||
|
vout: utxoList[i].vout, |
||||
|
value: Number(utxoList[i].amountSats), |
||||
|
verified: utxoList[i].verified ? utxoList[i].verified : false, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.log('electrum listunspent unformatted ==>', true); |
||||
|
shepherd.log(utxoList, true); |
||||
|
|
||||
|
shepherd.log('electrum listunspent formatted ==>', true); |
||||
|
shepherd.log(utxoListFormatted, true); |
||||
|
|
||||
|
const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted)); |
||||
|
/*let targets = [{ |
||||
|
address: outputAddress, |
||||
|
value: value > _maxSpendBalance ? _maxSpendBalance : value, |
||||
|
}];*/ |
||||
|
shepherd.log('targets =>', true); |
||||
|
shepherd.log(targets, true); |
||||
|
|
||||
|
targets[0].value = targets[0].value + fee; |
||||
|
|
||||
|
shepherd.log(`default fee ${fee}`, true); |
||||
|
shepherd.log(`targets ==>`, true); |
||||
|
shepherd.log(targets, true); |
||||
|
|
||||
|
// default coin selection algo blackjack with fallback to accumulative
|
||||
|
// make a first run, calc approx tx fee
|
||||
|
// if ins and outs are empty reduce max spend by txfee
|
||||
|
const firstRun = shepherd.coinSelect(utxoListFormatted, targets, btcFee ? btcFee : 0); |
||||
|
let inputs = firstRun.inputs; |
||||
|
let outputs = firstRun.outputs; |
||||
|
|
||||
|
if (btcFee) { |
||||
|
shepherd.log(`btc fee per byte ${btcFee}`, true); |
||||
|
fee = firstRun.fee; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('coinselect res =>', true); |
||||
|
shepherd.log('coinselect inputs =>', true); |
||||
|
shepherd.log(inputs, true); |
||||
|
shepherd.log('coinselect outputs =>', true); |
||||
|
shepherd.log(outputs, true); |
||||
|
shepherd.log('coinselect calculated fee =>', true); |
||||
|
shepherd.log(fee, true); |
||||
|
|
||||
|
if (!outputs) { |
||||
|
targets[0].value = targets[0].value - fee; |
||||
|
shepherd.log('second run', true); |
||||
|
shepherd.log('coinselect adjusted targets =>', true); |
||||
|
shepherd.log(targets, true); |
||||
|
|
||||
|
const secondRun = shepherd.coinSelect(utxoListFormatted, targets, 0); |
||||
|
inputs = secondRun.inputs; |
||||
|
outputs = secondRun.outputs; |
||||
|
fee = fee ? fee : secondRun.fee; |
||||
|
|
||||
|
shepherd.log('second run coinselect inputs =>', true); |
||||
|
shepherd.log(inputs, true); |
||||
|
shepherd.log('second run coinselect outputs =>', true); |
||||
|
shepherd.log(outputs, true); |
||||
|
shepherd.log('second run coinselect fee =>', true); |
||||
|
shepherd.log(fee, true); |
||||
|
} |
||||
|
|
||||
|
let _change = 0; |
||||
|
|
||||
|
if (outputs && |
||||
|
outputs.length > 1) { |
||||
|
_change = outputs[outputs.length - 1].value - fee; |
||||
|
} |
||||
|
|
||||
|
if (!btcFee && |
||||
|
_change === 0) { |
||||
|
outputs[0].value = outputs[0].value - fee; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('adjusted outputs'); |
||||
|
shepherd.log(outputs, true); |
||||
|
|
||||
|
shepherd.log('init targets', true); |
||||
|
shepherd.log(initTargets, true); |
||||
|
|
||||
|
if (initTargets[0].value < targets[0].value) { |
||||
|
targets[0].value = initTargets[0].value; |
||||
|
} |
||||
|
|
||||
|
let _targetsSum = 0; |
||||
|
|
||||
|
for (let i = 0; i < targets.length; i++) { |
||||
|
_targetsSum += Number(targets[i].value); |
||||
|
} |
||||
|
|
||||
|
shepherd.log(`total targets sum ${_targetsSum}`); |
||||
|
|
||||
|
/*if (btcFee) { |
||||
|
value = _targetsSum; |
||||
|
} else { |
||||
|
if (_change > 0) { |
||||
|
value = _targetsSum - fee; |
||||
|
} |
||||
|
}*/ |
||||
|
value = _targetsSum; |
||||
|
|
||||
|
shepherd.log('adjusted outputs, value - default fee =>', true); |
||||
|
shepherd.log(outputs, true); |
||||
|
|
||||
|
// check if any outputs are unverified
|
||||
|
if (inputs && |
||||
|
inputs.length) { |
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
if (!inputs[i].verified) { |
||||
|
utxoVerified = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
if (Number(inputs[i].interestSats) > interestClaimThreshold) { |
||||
|
totalInterest += Number(inputs[i].interestSats); |
||||
|
totalInterestUTXOCount++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const _maxSpend = shepherd.maxSpendBalance(utxoListFormatted); |
||||
|
|
||||
|
if (value > _maxSpend) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: `Spend value is too large. Max available amount is ${Number((_maxSpend * 0.00000001.toFixed(8)))}`, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true); |
||||
|
shepherd.log(`value ${value}`, true); |
||||
|
// shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true);
|
||||
|
shepherd.log(`changeto ${changeAddress} amount ${_change} (${_change * 0.00000001})`, true); |
||||
|
|
||||
|
// account for KMD interest
|
||||
|
if (network === 'komodo' && |
||||
|
totalInterest > 0) { |
||||
|
// account for extra vout
|
||||
|
// const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0;
|
||||
|
const _feeOverhead = 0; |
||||
|
|
||||
|
shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true); |
||||
|
shepherd.log(`estimated fee overhead ${_feeOverhead}`, true); |
||||
|
shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true); |
||||
|
|
||||
|
if (_maxSpend - fee === value) { |
||||
|
_change = totalInterest - _change - _feeOverhead; |
||||
|
|
||||
|
if (outputAddress === changeAddress) { |
||||
|
value += _change; |
||||
|
_change = 0; |
||||
|
shepherd.log(`send to self ${outputAddress} = ${changeAddress}`, true); |
||||
|
shepherd.log(`send to self old val ${value}, new val ${value + _change}`, true); |
||||
|
} |
||||
|
} else { |
||||
|
_change = _change + (totalInterest - _feeOverhead); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!inputs && |
||||
|
!outputs) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Can\'t find best fit utxo. Try lower amount.', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
let vinSum = 0; |
||||
|
|
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
vinSum += inputs[i].value; |
||||
|
} |
||||
|
|
||||
|
let voutSum = 0; |
||||
|
|
||||
|
for (let i = 0; i < outputs.length; i++) { |
||||
|
voutSum += outputs[i].value; |
||||
|
} |
||||
|
|
||||
|
const _estimatedFee = vinSum - voutSum; |
||||
|
|
||||
|
shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); |
||||
|
shepherd.log(`vout sum ${voutSum} (${voutSum * 0.00000001})`, true); |
||||
|
shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); |
||||
|
// double check no extra fee is applied
|
||||
|
shepherd.log(`vin - vout - change ${vinSum - value - _change}`); |
||||
|
|
||||
|
if ((vinSum - value - _change) > fee) { |
||||
|
_change += fee; |
||||
|
shepherd.log(`double fee, increase change by ${fee}`); |
||||
|
shepherd.log(`adjusted vin - vout - change ${vinSum - value - _change}`); |
||||
|
} |
||||
|
|
||||
|
// TODO: use individual dust thresholds
|
||||
|
if (_change > 0 && |
||||
|
_change <= 1000) { |
||||
|
shepherd.log(`change is < 1000 sats, donate ${_change} sats to miners`, true); |
||||
|
_change = 0; |
||||
|
} |
||||
|
|
||||
|
outputAddress = outputs; |
||||
|
|
||||
|
if (outputAddress.length > 1) { |
||||
|
outputAddress.pop(); |
||||
|
} |
||||
|
|
||||
|
let _rawtx; |
||||
|
|
||||
|
if (network === 'btg' || |
||||
|
network === 'bch') { |
||||
|
/*_rawtx = shepherd.buildSignedTxForks( |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
wif, |
||||
|
network, |
||||
|
inputs, |
||||
|
_change, |
||||
|
value |
||||
|
);*/ |
||||
|
} else { |
||||
|
_rawtx = shepherd.buildSignedTxMulti( |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
wif, |
||||
|
network, |
||||
|
inputs, |
||||
|
_change, |
||||
|
value, |
||||
|
opreturn |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
if (!push || |
||||
|
push === 'false') { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
changeAdjusted: _change, |
||||
|
totalInterest, |
||||
|
// wif,
|
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionBroadcast(_rawtx) |
||||
|
.then((txid) => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
const _rawObj = { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
changeAdjusted: _change, |
||||
|
totalInterest, |
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}; |
||||
|
|
||||
|
if (txid && |
||||
|
txid.indexOf('bad-txns-inputs-spent') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: _rawObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
if (txid && |
||||
|
txid.length === 64) { |
||||
|
if (txid.indexOf('bad-txns-in-belowout') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: _rawObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: _rawObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} else { |
||||
|
if (txid && |
||||
|
txid.indexOf('bad-txns-in-belowout') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: _rawObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Can\'t broadcast transaction', |
||||
|
raw: _rawObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: utxoList, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'unauthorized access', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// single sig
|
||||
|
shepherd.buildSignedTxMulti = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue, opreturn) => { |
||||
|
let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); |
||||
|
let tx; |
||||
|
|
||||
|
if (shepherd.isZcash(network)) { |
||||
|
tx = new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} else if (shepherd.isPos(network)) { |
||||
|
tx = new bitcoinPos.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} |
||||
|
|
||||
|
shepherd.log('buildSignedTx', true); |
||||
|
// console.log(`buildSignedTx priv key ${wif}`);
|
||||
|
shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); |
||||
|
// console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee);
|
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
tx.addInput(utxo[i].txid, utxo[i].vout); |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < sendTo.length; i++) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.addOutput(sendTo[i].address, Number(sendTo[i].value), shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
tx.addOutput(sendTo[i].address, Number(sendTo[i].value)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (changeValue > 0) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.addOutput(changeAddress, Number(changeValue), shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
tx.addOutput(changeAddress, Number(changeValue)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (opreturn && |
||||
|
opreturn.length) { |
||||
|
for (let i = 0; i < opreturn.length; i++) { |
||||
|
shepherd.log(`opreturn ${i} ${opreturn[i]}`); |
||||
|
const data = Buffer.from(opreturn[i], 'utf8'); |
||||
|
const dataScript = shepherd.bitcoinJS.script.nullData.output.encode(data); |
||||
|
tx.addOutput(dataScript, 1000); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (network === 'komodo' || |
||||
|
network === 'KMD') { |
||||
|
const _locktime = Math.floor(Date.now() / 1000) - 777; |
||||
|
tx.setLockTime(_locktime); |
||||
|
shepherd.log(`kmd tx locktime set to ${_locktime}`, true); |
||||
|
} |
||||
|
|
||||
|
shepherd.log('buildSignedTx unsigned tx data vin', true); |
||||
|
shepherd.log(tx.tx.ins, true); |
||||
|
shepherd.log('buildSignedTx unsigned tx data vout', true); |
||||
|
shepherd.log(tx.tx.outs, true); |
||||
|
shepherd.log('buildSignedTx unsigned tx data', true); |
||||
|
shepherd.log(tx, true); |
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.sign(shepherd.getNetworkData(network), i, key); |
||||
|
} else { |
||||
|
tx.sign(i, key); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const rawtx = tx.build().toHex(); |
||||
|
|
||||
|
shepherd.log('buildSignedTx signed tx hex', true); |
||||
|
shepherd.log(rawtx, true); |
||||
|
|
||||
|
return rawtx; |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,86 @@ |
|||||
|
const bitcoinJSForks = require('bitcoinforksjs-lib'); |
||||
|
const bitcoinZcash = require('bitcoinjs-lib-zcash'); |
||||
|
const bitcoinPos = require('bitcoinjs-lib-pos'); |
||||
|
|
||||
|
module.exports = (shepherd) => { |
||||
|
// utxo split 1 -> 1, multiple outputs
|
||||
|
shepherd.post('/electrum/createrawtx-split', (req, res, next) => { |
||||
|
if (shepherd.checkToken(req.body.token)) { |
||||
|
const wif = req.body.payload.wif; |
||||
|
const utxo = req.body.payload.utxo; |
||||
|
const targets = req.body.payload.targets; |
||||
|
const network = req.body.payload.network; |
||||
|
const change = req.body.payload.change; |
||||
|
const outputAddress = req.body.payload.outputAddress; |
||||
|
const changeAddress = req.body.payload.changeAddress; |
||||
|
|
||||
|
let key = shepherd.isZcash(network) ? bitcoinZcash.ECPair.fromWIF(wif, shepherd.getNetworkData(network)) : shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); |
||||
|
let tx; |
||||
|
|
||||
|
if (shepherd.isZcash(network)) { |
||||
|
tx = new bitcoinZcash.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} else if (shepherd.isPos(network)) { |
||||
|
tx = new bitcoinPos.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
} |
||||
|
|
||||
|
shepherd.log('buildSignedTx', true); |
||||
|
shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); |
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
tx.addInput(utxo[i].txid, utxo[i].vout); |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < targets.length; i++) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.addOutput(outputAddress, Number(targets[i]), shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
tx.addOutput(outputAddress, Number(targets[i])); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Number(change) > 0) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.addOutput(changeAddress, Number(change), shepherd.getNetworkData(network)); |
||||
|
} else { |
||||
|
shepherd.log(`change ${change}`, true); |
||||
|
tx.addOutput(changeAddress, Number(change)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (network === 'komodo' || |
||||
|
network === 'KMD') { |
||||
|
const _locktime = Math.floor(Date.now() / 1000) - 777; |
||||
|
tx.setLockTime(_locktime); |
||||
|
shepherd.log(`kmd tx locktime set to ${_locktime}`, true); |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
if (shepherd.isPos(network)) { |
||||
|
tx.sign(shepherd.getNetworkData(network), i, key); |
||||
|
} else { |
||||
|
tx.sign(i, key); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const rawtx = tx.build().toHex(); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: rawtx, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'unauthorized access', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -1,3 +1,3 @@ |
|||||
version=0.2.0.28b |
version=0.2.0.28c |
||||
type=b-beta |
type=c-beta |
||||
minversion=0.2.0.28 |
minversion=0.2.0.28 |
@ -1 +1 @@ |
|||||
0.2.0.28b-beta |
0.2.0.28c-beta |
Loading…
Reference in new issue