Minor changes and Bug fixes
- deduce BTC fee from users - If timeout rejected tx are confirmed, refund the asset - Blockchain bond and Bob's fund withdraw now sends equivalent BTC to users (via BTC blockchain) - Fixed minor bugs - Changed toFixed (fn to limit the decimal) to toStandardDecimal (set_global.js)
This commit is contained in:
parent
821de1a6d7
commit
e4adaf7ed7
@ -288,6 +288,8 @@ CREATE TABLE DirectConvert(
|
|||||||
CREATE TABLE RefundTransact(
|
CREATE TABLE RefundTransact(
|
||||||
id INT NOT NULL AUTO_INCREMENT,
|
id INT NOT NULL AUTO_INCREMENT,
|
||||||
floID CHAR(34) NOT NULL,
|
floID CHAR(34) NOT NULL,
|
||||||
|
asset_type TINYINT NOT NULL,
|
||||||
|
asset VARCHAR(32) NOT NULL,
|
||||||
amount DECIMAL(16, 8),
|
amount DECIMAL(16, 8),
|
||||||
in_txid VARCHAR(128),
|
in_txid VARCHAR(128),
|
||||||
out_txid VARCHAR(128),
|
out_txid VARCHAR(128),
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
(function (EXPORTS) { //btcOperator v1.0.12
|
(function (EXPORTS) { //btcOperator v1.0.13
|
||||||
/* BTC Crypto and API Operator */
|
/* BTC Crypto and API Operator */
|
||||||
const btcOperator = EXPORTS;
|
const btcOperator = EXPORTS;
|
||||||
|
|
||||||
@ -307,8 +307,8 @@
|
|||||||
parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null);
|
parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null);
|
||||||
if (invalids.length)
|
if (invalids.length)
|
||||||
throw "Invalid receivers:" + invalids;
|
throw "Invalid receivers:" + invalids;
|
||||||
if (parameters.change_addr && !validateAddress(parameters.change_addr))
|
if (parameters.change_address && !validateAddress(parameters.change_address))
|
||||||
throw "Invalid change_address:" + parameters.change_addr;
|
throw "Invalid change_address:" + parameters.change_address;
|
||||||
//fee and amounts
|
//fee and amounts
|
||||||
if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc)
|
if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc)
|
||||||
throw "Invalid fee:" + parameters.fee;
|
throw "Invalid fee:" + parameters.fee;
|
||||||
@ -323,16 +323,29 @@
|
|||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr) {
|
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
|
let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
|
||||||
const tx = coinjs.transaction();
|
const tx = coinjs.transaction();
|
||||||
let output_size = addOutputs(tx, receivers, amounts, change_addr);
|
let output_size = addOutputs(tx, receivers, amounts, change_address);
|
||||||
addInputs(tx, senders, redeemScripts, total_amount, fee, output_size).then(result => {
|
addInputs(tx, senders, redeemScripts, total_amount, fee, output_size).then(result => {
|
||||||
if (result.change_amount > 0)
|
if (result.change_amount > 0) //add change amount if any
|
||||||
tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi
|
tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi
|
||||||
else
|
if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver
|
||||||
tx.outs.pop(); //remove the change output if no change_amount
|
let fee_remaining = parseInt(result.fee * SATOSHI_IN_BTC);
|
||||||
|
for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) {
|
||||||
|
if (fee_remaining < tx.outs[i].value) {
|
||||||
|
tx.outs[i].value -= fee_remaining;
|
||||||
|
fee_remaining = 0;
|
||||||
|
} else {
|
||||||
|
fee_remaining -= tx.outs[i].value;
|
||||||
|
tx.outs[i].value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fee_remaining > 0)
|
||||||
|
return reject("Send amount is less than fee");
|
||||||
|
}
|
||||||
|
tx.outs = tx.outs.filter(o => o.value !== 0); //remove all output with value 0
|
||||||
result.output_size = output_size;
|
result.output_size = output_size;
|
||||||
result.output_amount = total_amount;
|
result.output_amount = total_amount;
|
||||||
result.total_size = BASE_TX_SIZE + output_size + result.input_size;
|
result.total_size = BASE_TX_SIZE + output_size + result.input_size;
|
||||||
@ -342,10 +355,10 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size) {
|
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (fee !== null) {
|
if (fee !== null) {
|
||||||
addUTXOs(tx, senders, redeemScripts, total_amount + fee, false).then(result => {
|
addUTXOs(tx, senders, redeemScripts, fee_from_receiver ? total_amount : total_amount + fee, false).then(result => {
|
||||||
result.fee = fee;
|
result.fee = fee;
|
||||||
resolve(result);
|
resolve(result);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
@ -353,7 +366,10 @@
|
|||||||
get_fee_rate().then(fee_rate => {
|
get_fee_rate().then(fee_rate => {
|
||||||
let net_fee = BASE_TX_SIZE * fee_rate;
|
let net_fee = BASE_TX_SIZE * fee_rate;
|
||||||
net_fee += (output_size * fee_rate);
|
net_fee += (output_size * fee_rate);
|
||||||
addUTXOs(tx, senders, redeemScripts, total_amount + net_fee, fee_rate).then(result => {
|
(fee_from_receiver ?
|
||||||
|
addUTXOs(tx, senders, redeemScripts, total_amount, false) :
|
||||||
|
addUTXOs(tx, senders, redeemScripts, total_amount + net_fee, fee_rate)
|
||||||
|
).then(result => {
|
||||||
result.fee = parseFloat((net_fee + (result.input_size * fee_rate)).toFixed(8));
|
result.fee = parseFloat((net_fee + (result.input_size * fee_rate)).toFixed(8));
|
||||||
result.fee_rate = fee_rate;
|
result.fee_rate = fee_rate;
|
||||||
resolve(result);
|
resolve(result);
|
||||||
@ -416,14 +432,14 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOutputs(tx, receivers, amounts, change_addr) {
|
function addOutputs(tx, receivers, amounts, change_address) {
|
||||||
let size = 0;
|
let size = 0;
|
||||||
for (let i in receivers) {
|
for (let i in receivers) {
|
||||||
tx.addoutput(receivers[i], amounts[i]);
|
tx.addoutput(receivers[i], amounts[i]);
|
||||||
size += _sizePerOutput(receivers[i]);
|
size += _sizePerOutput(receivers[i]);
|
||||||
}
|
}
|
||||||
tx.addoutput(change_addr, 0);
|
tx.addoutput(change_address, 0);
|
||||||
size += _sizePerOutput(change_addr);
|
size += _sizePerOutput(change_address);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,9 +480,9 @@
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee, change_addr = null) {
|
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
createSignedTx(senders, privkeys, receivers, amounts, fee, change_addr).then(result => {
|
createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => {
|
||||||
debugger;
|
debugger;
|
||||||
broadcastTx(result.transaction.serialize())
|
broadcastTx(result.transaction.serialize())
|
||||||
.then(txid => resolve(txid))
|
.then(txid => resolve(txid))
|
||||||
@ -475,7 +491,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, change_addr = null) {
|
const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
({
|
({
|
||||||
@ -489,7 +505,7 @@
|
|||||||
receivers,
|
receivers,
|
||||||
amounts,
|
amounts,
|
||||||
fee,
|
fee,
|
||||||
change_addr
|
change_address: options.change_address
|
||||||
}));
|
}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(e)
|
return reject(e)
|
||||||
@ -504,7 +520,7 @@
|
|||||||
if (redeemScripts.includes(null)) //TODO: segwit
|
if (redeemScripts.includes(null)) //TODO: segwit
|
||||||
return reject("Unable to get redeem-script");
|
return reject("Unable to get redeem-script");
|
||||||
//create transaction
|
//create transaction
|
||||||
createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr || senders[0]).then(result => {
|
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
|
||||||
let tx = result.transaction;
|
let tx = result.transaction;
|
||||||
console.debug("Unsigned:", tx.serialize());
|
console.debug("Unsigned:", tx.serialize());
|
||||||
new Set(wif_keys).forEach(key => console.debug("Signing key:", key, tx.sign(key, 1 /*sighashtype*/))); //Sign the tx using private key WIF
|
new Set(wif_keys).forEach(key => console.debug("Signing key:", key, tx.sign(key, 1 /*sighashtype*/))); //Sign the tx using private key WIF
|
||||||
@ -514,7 +530,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
btcOperator.createTx = function (senders, receivers, amounts, fee = null, change_addr = null) {
|
btcOperator.createTx = function (senders, receivers, amounts, fee = null, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
({
|
({
|
||||||
@ -526,7 +542,7 @@
|
|||||||
receivers,
|
receivers,
|
||||||
amounts,
|
amounts,
|
||||||
fee,
|
fee,
|
||||||
change_addr
|
change_address: options.change_address
|
||||||
}));
|
}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(e)
|
return reject(e)
|
||||||
@ -535,7 +551,7 @@
|
|||||||
if (redeemScripts.includes(null)) //TODO: segwit
|
if (redeemScripts.includes(null)) //TODO: segwit
|
||||||
return reject("Unable to get redeem-script");
|
return reject("Unable to get redeem-script");
|
||||||
//create transaction
|
//create transaction
|
||||||
createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr || senders[0]).then(result => {
|
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
|
||||||
result.tx_hex = result.transaction.serialize();
|
result.tx_hex = result.transaction.serialize();
|
||||||
delete result.transaction;
|
delete result.transaction;
|
||||||
resolve(result);
|
resolve(result);
|
||||||
@ -543,7 +559,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null) {
|
btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
//validate tx parameters
|
//validate tx parameters
|
||||||
if (validateAddress(sender) !== "multisig")
|
if (validateAddress(sender) !== "multisig")
|
||||||
@ -561,13 +577,14 @@
|
|||||||
} = validateTxParameters({
|
} = validateTxParameters({
|
||||||
receivers,
|
receivers,
|
||||||
amounts,
|
amounts,
|
||||||
fee
|
fee,
|
||||||
|
change_address: options.change_address
|
||||||
}));
|
}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(e)
|
return reject(e)
|
||||||
}
|
}
|
||||||
//create transaction
|
//create transaction
|
||||||
createTransaction([sender], [redeemScript], receivers, amounts, fee, sender).then(result => {
|
createTransaction([sender], [redeemScript], receivers, amounts, fee, options.change_address || sender, options.fee_from_receiver).then(result => {
|
||||||
result.tx_hex = result.transaction.serialize();
|
result.tx_hex = result.transaction.serialize();
|
||||||
delete result.transaction;
|
delete result.transaction;
|
||||||
resolve(result);
|
resolve(result);
|
||||||
|
|||||||
@ -152,7 +152,7 @@ function retryVaultWithdrawal() {
|
|||||||
} else if (r.asset_type == pCode.ASSET_TYPE_TOKEN)
|
} else if (r.asset_type == pCode.ASSET_TYPE_TOKEN)
|
||||||
blockchain.withdrawAsset.retry(r.floID, r.asset, r.amount, r.id)
|
blockchain.withdrawAsset.retry(r.floID, r.asset, r.amount, r.id)
|
||||||
})
|
})
|
||||||
}).catch(error => reject(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmVaultWithdraw() {
|
function confirmVaultWithdraw() {
|
||||||
@ -210,7 +210,16 @@ verifyTx.BTC = function (sender, txid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function verifyConvert() {
|
function verifyConvert() {
|
||||||
DB.query("UPDATE DirectConvert SET r_status=? WHERE r_status=? AND locktime<?", [pCode.STATUS_REJECTED, pCode.STATUS_PENDING, new Date(Date.now() - REQUEST_TIMEOUT)]).then(result => {
|
//Set all timeout convert request to refund mode (thus, asset will be refund if tx gets confirmed later)
|
||||||
|
let req_timeout = new Date(Date.now() - REQUEST_TIMEOUT),
|
||||||
|
to_refund_sql = "INSERT INTO RefundTransact (floID, in_txid, asset_type, asset, r_status)" +
|
||||||
|
" SELECT floID, in_txid, ? AS asset_type, ? AS asset, r_status" +
|
||||||
|
" WHERE r_status=? AND locktime<? AND mode=?";
|
||||||
|
let txQueries = [];
|
||||||
|
txQueries.push([to_refund_sql, [pCode.ASSET_TYPE_TOKEN, floGlobals.currency, pCode.STATUS_PENDING, req_timeout, pCode.CONVERT_MODE_GET]]);
|
||||||
|
txQueries.push([to_refund_sql, [pCode.ASSET_TYPE_COIN, "BTC", pCode.STATUS_PENDING, req_timeout, pCode.CONVERT_MODE_PUT]]);
|
||||||
|
txQueries.push(["UPDATE DirectConvert SET r_status=? WHERE r_status=? AND locktime<?", [pCode.STATUS_REJECTED, pCode.STATUS_PENDING, req_timeout]]);
|
||||||
|
DB.transaction(txQueries).then(result => {
|
||||||
DB.query("SELECT id, floID, mode, in_txid, amount, quantity FROM DirectConvert WHERE r_status=? AND coin=?", [pCode.STATUS_PENDING, "BTC"]).then(results => {
|
DB.query("SELECT id, floID, mode, in_txid, amount, quantity FROM DirectConvert WHERE r_status=? AND coin=?", [pCode.STATUS_PENDING, "BTC"]).then(results => {
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
if (r.mode == pCode.CONVERT_MODE_GET) {
|
if (r.mode == pCode.CONVERT_MODE_GET) {
|
||||||
@ -242,7 +251,7 @@ function verifyConvert() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
}).catch(error => reject(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryConvert() {
|
function retryConvert() {
|
||||||
@ -345,11 +354,34 @@ function confirmConvertFundWithdraw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function verifyRefund() {
|
function verifyRefund() {
|
||||||
DB.query("SELECT id, floID, in_txid FROM RefundTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
DB.query("SELECT id, floID, asset_type, asset, in_txid FROM RefundTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
verifyTx.token(r.floID, r.in_txid, true)
|
if (r.ASSET_TYPE_COIN) {
|
||||||
.then(({ amount }) => blockchain.refundTransact.init(r.floID, amount, r.id))
|
if (r.asset == "FLO")
|
||||||
.catch(error => {
|
verifyTx.FLO(r.floID, r.in_txid)
|
||||||
|
.then(amount => blockchain.refundTransact.init(r.floID, r.asset, amount, r.id))
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
if (error[0])
|
||||||
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_REJECTED, r.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
});
|
||||||
|
else if (r.asset == "BTC")
|
||||||
|
verifyTx.BTC(r.floID, r.in_txid)
|
||||||
|
.then(amount => blockchain.refundTransact.init(r.floID, r.asset, amount, r.id))
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
if (error[0])
|
||||||
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_REJECTED, r.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
});
|
||||||
|
} else if (r.ASSET_TYPE_TOKEN)
|
||||||
|
verifyTx.token(r.floID, r.in_txid).then(({ token, amount }) => {
|
||||||
|
if (token !== r.asset)
|
||||||
|
throw ([true, "Transaction token mismatched"]);
|
||||||
|
else
|
||||||
|
blockchain.refundTransact.init(r.floID, token, amount, r.id);
|
||||||
|
}).catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
if (error[0])
|
if (error[0])
|
||||||
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_REJECTED, r.id])
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_REJECTED, r.id])
|
||||||
@ -360,36 +392,54 @@ function verifyRefund() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function retryRefund() {
|
function retryRefund() {
|
||||||
DB.query("SELECT id, floID, amount FROM RefundTransact WHERE r_status=?", [pCode.STATUS_PROCESSING]).then(results => {
|
DB.query("SELECT id, floID, asset, amount FROM RefundTransact WHERE r_status=?", [pCode.STATUS_PROCESSING]).then(results => {
|
||||||
results.forEach(r => blockchain.refundTransact.retry(r.floID, r.amount, r.id))
|
results.forEach(r => blockchain.refundTransact.retry(r.floID, r.asset, r.amount, r.id))
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmRefund() {
|
function confirmRefund() {
|
||||||
DB.query("SELECT * FROM RefundTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
DB.query("SELECT * FROM RefundTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
||||||
results.forEach(r => {
|
results.forEach(r => { //TODO
|
||||||
floTokenAPI.getTx(r.out_txid).then(tx => {
|
if (r.ASSET_TYPE_COIN) {
|
||||||
if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
|
if (r.asset == "FLO")
|
||||||
return;
|
floBlockchainAPI.getTx(r.out_txid).then(tx => {
|
||||||
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
|
if (!tx.blockheight || !tx.confirmations) //Still not confirmed
|
||||||
.then(result => console.info(`Refunded ${r.amount} to ${r.floID}`))
|
return;
|
||||||
.catch(error => console.error(error));
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
|
||||||
}).catch(error => console.error(error));
|
.then(result => console.info(`Refunded ${r.amount} ${r.asset} to ${r.floID}`))
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
else if (r.asset == "BTC")
|
||||||
|
btcOperator.getTx(r.out_txid).then(tx => {
|
||||||
|
if (!tx.blockhash || !tx.confirmations) //Still not confirmed
|
||||||
|
return;
|
||||||
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
|
||||||
|
.then(result => console.info(`Refunded ${r.amount} ${r.asset} to ${r.floID}`))
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
} else if (r.ASSET_TYPE_TOKEN)
|
||||||
|
floTokenAPI.getTx(r.out_txid).then(tx => {
|
||||||
|
if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
|
||||||
|
return;
|
||||||
|
DB.query("UPDATE RefundTransact SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
|
||||||
|
.then(result => console.info(`Refunded ${r.amount} ${r.asset} to ${r.floID}`))
|
||||||
|
.catch(error => console.error(error));
|
||||||
|
}).catch(error => console.error(error));
|
||||||
})
|
})
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryBondClosing() {
|
function retryBondClosing() {
|
||||||
DB.query("SELECT id, floID, amount FROM CloseBondTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
DB.query("SELECT id, floID, amount, btc_net, usd_net FROM CloseBondTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
||||||
results.forEach(r => blockchain.bondTransact.retry(r.floID, r.amount, r.id))
|
results.forEach(r => blockchain.bondTransact.retry(r.floID, r.amount, r.btc_net, r.usd_net, r.id))
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmBondClosing() {
|
function confirmBondClosing() {
|
||||||
DB.query("SELECT * FROM CloseBondTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
DB.query("SELECT * FROM CloseBondTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
floTokenAPI.getTx(r.txid).then(tx => {
|
btcOperator.getTx(r.txid).then(tx => {
|
||||||
if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
|
if (!tx.blockhash || !tx.confirmations) //Still not confirmed
|
||||||
return;
|
return;
|
||||||
let closeBondString = bond_util.stringify.end(r.bond_id, r.end_date, r.btc_net, r.usd_net, r.amount, r.ref_sign, r.txid);
|
let closeBondString = bond_util.stringify.end(r.bond_id, r.end_date, r.btc_net, r.usd_net, r.amount, r.ref_sign, r.txid);
|
||||||
floBlockchainAPI.writeData(global.myFloID, closeBondString, global.myPrivKey, bond_util.config.adminID).then(txid => {
|
floBlockchainAPI.writeData(global.myFloID, closeBondString, global.myPrivKey, bond_util.config.adminID).then(txid => {
|
||||||
@ -403,16 +453,16 @@ function confirmBondClosing() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function retryFundClosing() {
|
function retryFundClosing() {
|
||||||
DB.query("SELECT id, floID, amount FROM CloseFundTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
DB.query("SELECT id, floID, amount, btc_net, usd_net FROM CloseFundTransact WHERE r_status=?", [pCode.STATUS_PENDING]).then(results => {
|
||||||
results.forEach(r => blockchain.fundTransact.retry(r.floID, r.amount, r.id))
|
results.forEach(r => blockchain.fundTransact.retry(r.floID, r.amount, r.btc_net, r.usd_net, r.id))
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmFundClosing() {
|
function confirmFundClosing() {
|
||||||
DB.query("SELECT * FROM CloseFundTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
DB.query("SELECT * FROM CloseFundTransact WHERE r_status=?", [pCode.STATUS_CONFIRMATION]).then(results => {
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
floTokenAPI.getTx(r.txid).then(tx => {
|
btcOperator.getTx(r.txid).then(tx => {
|
||||||
if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
|
if (!tx.blockhash || !tx.confirmations) //Still not confirmed
|
||||||
return;
|
return;
|
||||||
let closeFundString = fund_util.stringify.end(r.fund_id, r.floID, r.end_date, r.btc_net, r.usd_net, r.amount, r.ref_sign, r.txid);
|
let closeFundString = fund_util.stringify.end(r.fund_id, r.floID, r.end_date, r.btc_net, r.usd_net, r.amount, r.ref_sign, r.txid);
|
||||||
floBlockchainAPI.writeData(global.myFloID, closeFundString, global.myPrivKey, fund_util.config.adminID).then(txid => {
|
floBlockchainAPI.writeData(global.myFloID, closeFundString, global.myPrivKey, fund_util.config.adminID).then(txid => {
|
||||||
|
|||||||
@ -79,7 +79,7 @@ function sendTx(floID, asset, quantity, sinkID, sinkKey, message) {
|
|||||||
case "BTC":
|
case "BTC":
|
||||||
let btc_sinkID = btcOperator.convert.legacy2bech(sinkID),
|
let btc_sinkID = btcOperator.convert.legacy2bech(sinkID),
|
||||||
btc_receiver = btcOperator.convert.legacy2bech(floID);
|
btc_receiver = btcOperator.convert.legacy2bech(floID);
|
||||||
return btcOperator.sendTx(btc_sinkID, sinkKey, btc_receiver, quantity, null);
|
return btcOperator.sendTx(btc_sinkID, sinkKey, btc_receiver, quantity, null, { fee_from_receiver: true });
|
||||||
default:
|
default:
|
||||||
return floTokenAPI.sendToken(sinkKey, quantity, floID, message, asset);
|
return floTokenAPI.sendToken(sinkKey, quantity, floID, message, asset);
|
||||||
}
|
}
|
||||||
@ -95,6 +95,7 @@ const updateSyntax = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function sendAsset(floID, asset, quantity, type, id) {
|
function sendAsset(floID, asset, quantity, type, id) {
|
||||||
|
quantity = global.toStandardDecimal(quantity);
|
||||||
getSinkID(quantity, asset).then(sinkID => {
|
getSinkID(quantity, asset).then(sinkID => {
|
||||||
let callback = (sinkKey) => {
|
let callback = (sinkKey) => {
|
||||||
//Send asset to user via API
|
//Send asset to user via API
|
||||||
@ -118,7 +119,7 @@ function sendAsset(floID, asset, quantity, type, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function withdrawAsset_init(floID, asset, amount) {
|
function withdrawAsset_init(floID, asset, amount) {
|
||||||
amount = parseFloat(amount.toFixed(8));
|
amount = global.toStandardDecimal(amount);
|
||||||
let asset_type = ["FLO", "BTC"].includes(asset) ? pCode.ASSET_TYPE_COIN : pCode.ASSET_TYPE_TOKEN;
|
let asset_type = ["FLO", "BTC"].includes(asset) ? pCode.ASSET_TYPE_COIN : pCode.ASSET_TYPE_TOKEN;
|
||||||
DB.query("INSERT INTO VaultTransactions (floID, mode, asset_type, asset, amount, r_status) VALUES (?)", [[floID, pCode.VAULT_MODE_WITHDRAW, asset_type, asset, amount, pCode.STATUS_PENDING]])
|
DB.query("INSERT INTO VaultTransactions (floID, mode, asset_type, asset, amount, r_status) VALUES (?)", [[floID, pCode.VAULT_MODE_WITHDRAW, asset_type, asset, amount, pCode.STATUS_PENDING]])
|
||||||
.then(result => sendAsset(floID, asset, amount, TYPE_VAULT, result.insertId))
|
.then(result => sendAsset(floID, asset, amount, TYPE_VAULT, result.insertId))
|
||||||
@ -132,7 +133,7 @@ function withdrawAsset_retry(floID, asset, amount, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertToCoin_init(floID, coin, currency_amount, rate, id) {
|
function convertToCoin_init(floID, coin, currency_amount, rate, id) {
|
||||||
let coin_quantity = parseFloat((currency_amount / rate).toFixed(8));
|
let coin_quantity = global.toStandardDecimal(currency_amount / rate);
|
||||||
DB.query("UPDATE DirectConvert SET quantity=?, r_status=?, rate=?, locktime=DEFAULT WHERE id=?", [coin_quantity, pCode.STATUS_PROCESSING, rate, id])
|
DB.query("UPDATE DirectConvert SET quantity=?, r_status=?, rate=?, locktime=DEFAULT WHERE id=?", [coin_quantity, pCode.STATUS_PROCESSING, rate, id])
|
||||||
.then(result => sendAsset(floID, coin, coin_quantity, TYPE_CONVERT, id))
|
.then(result => sendAsset(floID, coin, coin_quantity, TYPE_CONVERT, id))
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
@ -145,7 +146,7 @@ function convertToCoin_retry(floID, coin, coin_quantity, id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function convertFromCoin_init(floID, coin_quantity, rate, id) {
|
function convertFromCoin_init(floID, coin_quantity, rate, id) {
|
||||||
let currency_amount = parseFloat((coin_quantity * rate).toFixed(8));
|
let currency_amount = global.toStandardDecimal(coin_quantity * rate);
|
||||||
DB.query("UPDATE DirectConvert SET amount=?, r_status=?, rate=?, locktime=DEFAULT WHERE id=?", [currency_amount, pCode.STATUS_PROCESSING, rate, id])
|
DB.query("UPDATE DirectConvert SET amount=?, r_status=?, rate=?, locktime=DEFAULT WHERE id=?", [currency_amount, pCode.STATUS_PROCESSING, rate, id])
|
||||||
.then(result => sendAsset(floID, floGlobals.currency, currency_amount, TYPE_CONVERT, id))
|
.then(result => sendAsset(floID, floGlobals.currency, currency_amount, TYPE_CONVERT, id))
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
@ -163,29 +164,28 @@ function convertFundWithdraw_retry(asset, amount, id) {
|
|||||||
else sendAsset(floGlobals.adminID, asset, amount, TYPE_CONVERT_POOL, id);
|
else sendAsset(floGlobals.adminID, asset, amount, TYPE_CONVERT_POOL, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bondTransact_retry(floID, amount, id) {
|
function bondTransact_retry(floID, amount, btc_rate, usd_rate, id) {
|
||||||
if (id in callbackCollection[TYPE_BOND])
|
if (id in callbackCollection[TYPE_BOND])
|
||||||
console.debug("A callback is already pending for this Bond closing");
|
console.debug("A callback is already pending for this Bond closing");
|
||||||
else sendAsset(floID, floGlobals.currency, amount, TYPE_BOND, id);
|
else sendAsset(floID, "BTC", amount / (btc_rate * usd_rate), TYPE_BOND, id);
|
||||||
}
|
}
|
||||||
|
function fundTransact_retry(floID, amount, btc_rate, usd_rate, id) {
|
||||||
function fundTransact_retry(floID, amount, id) {
|
|
||||||
if (id in callbackCollection[TYPE_FUND])
|
if (id in callbackCollection[TYPE_FUND])
|
||||||
console.debug("A callback is already pending for this Fund investment closing");
|
console.debug("A callback is already pending for this Fund investment closing");
|
||||||
else sendAsset(floID, floGlobals.currency, amount, TYPE_FUND, id);
|
else sendAsset(floID, "BTC", amount / (btc_rate * usd_rate), TYPE_FUND, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refundTransact_init(floID, amount, id) {
|
function refundTransact_init(floID, asset, amount, id) {
|
||||||
amount = parseFloat(amount.toFixed(8));
|
amount = global.toStandardDecimal(amount);
|
||||||
DB.query("UPDATE RefundTransact SET amount=?, r_status=?, locktime=DEFAULT WHERE id=?", [amount, pCode.STATUS_PROCESSING, id])
|
DB.query("UPDATE RefundTransact SET amount=?, r_status=?, locktime=DEFAULT WHERE id=?", [amount, pCode.STATUS_PROCESSING, id])
|
||||||
.then(result => sendAsset(floID, floGlobals.currency, amount, TYPE_REFUND, id))
|
.then(result => sendAsset(floID, asset, amount, TYPE_REFUND, id))
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
function refundTransact_retry(floID, amount, id) {
|
function refundTransact_retry(floID, asset, amount, id) {
|
||||||
if (id in callbackCollection[TYPE_REFUND])
|
if (id in callbackCollection[TYPE_REFUND])
|
||||||
console.debug("A callback is already pending for this Refund");
|
console.debug("A callback is already pending for this Refund");
|
||||||
else sendAsset(floID, floGlobals.currency, amount, TYPE_REFUND, id);
|
else sendAsset(floID, asset, amount, TYPE_REFUND, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ function startCouplingForAsset(asset, updatePrice = false) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
price.getRates(asset, updatePrice).then(cur_rate => {
|
price.getRates(asset, updatePrice).then(cur_rate => {
|
||||||
cur_rate = cur_rate.toFixed(8);
|
cur_rate = global.toStandardDecimal(cur_rate);
|
||||||
couplingInstance[asset] = true; //set instance as running
|
couplingInstance[asset] = true; //set instance as running
|
||||||
recursiveCoupling(asset, cur_rate, updatePrice);
|
recursiveCoupling(asset, cur_rate, updatePrice);
|
||||||
}).catch(error => console.error(error));
|
}).catch(error => console.error(error));
|
||||||
|
|||||||
@ -71,7 +71,7 @@ getBalance.floID_token = (floID, token) => new Promise((resolve, reject) => {
|
|||||||
DB.query("SELECT quantity AS balance FROM UserBalance WHERE floID=? AND token=?", [floID, token]).then(result => resolve({
|
DB.query("SELECT quantity AS balance FROM UserBalance WHERE floID=? AND token=?", [floID, token]).then(result => resolve({
|
||||||
floID,
|
floID,
|
||||||
token,
|
token,
|
||||||
balance: result.length ? result[0].balance.toFixed(8) : 0
|
balance: result.length ? global.toStandardDecimal(result[0].balance) : 0
|
||||||
})).catch(error => reject(error))
|
})).catch(error => reject(error))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ getBalance.floID = (floID) => new Promise((resolve, reject) => {
|
|||||||
balance: {}
|
balance: {}
|
||||||
};
|
};
|
||||||
for (let row of result)
|
for (let row of result)
|
||||||
response.balance[row.token] = row.balance.toFixed(8);
|
response.balance[row.token] = global.toStandardDecimal(row.balance);
|
||||||
resolve(response);
|
resolve(response);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
});
|
});
|
||||||
@ -94,7 +94,7 @@ getBalance.token = (token) => new Promise((resolve, reject) => {
|
|||||||
balance: {}
|
balance: {}
|
||||||
};
|
};
|
||||||
for (let row of result)
|
for (let row of result)
|
||||||
response.balance[row.floID] = row.balance.toFixed(8);
|
response.balance[row.floID] = global.toStandardDecimal(row.balance);
|
||||||
resolve(response);
|
resolve(response);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
});
|
});
|
||||||
|
|||||||
10
src/price.js
10
src/price.js
@ -22,11 +22,11 @@ const updateLastTime = asset => lastTime[asset] = Date.now();
|
|||||||
|
|
||||||
//store FLO price in DB every 1 hr
|
//store FLO price in DB every 1 hr
|
||||||
function storeHistory(asset, rate) {
|
function storeHistory(asset, rate) {
|
||||||
DB.query("INSERT INTO PriceHistory (asset, rate) VALUE (?)", [[asset, rate.toFixed(8)]])
|
DB.query("INSERT INTO PriceHistory (asset, rate) VALUE (?)", [[asset, global.toStandardDecimal(rate)]])
|
||||||
.then(_ => null).catch(error => console.error(error))
|
.then(_ => null).catch(error => console.error(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
storeHistory.start = function() {
|
storeHistory.start = function () {
|
||||||
storeHistory.stop();
|
storeHistory.stop();
|
||||||
storeHistory.instance = setInterval(() => {
|
storeHistory.instance = setInterval(() => {
|
||||||
for (let asset in currentRate)
|
for (let asset in currentRate)
|
||||||
@ -34,7 +34,7 @@ storeHistory.start = function() {
|
|||||||
}, REC_HISTORY_INTERVAL);
|
}, REC_HISTORY_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
storeHistory.stop = function() {
|
storeHistory.stop = function () {
|
||||||
if (storeHistory.instance !== undefined) {
|
if (storeHistory.instance !== undefined) {
|
||||||
clearInterval(storeHistory.instance);
|
clearInterval(storeHistory.instance);
|
||||||
delete storeHistory.instance;
|
delete storeHistory.instance;
|
||||||
@ -157,8 +157,8 @@ function checkForRatedSellers(asset) {
|
|||||||
DB.query("SELECT COUNT(*) as value FROM SellOrder WHERE floID IN (" +
|
DB.query("SELECT COUNT(*) as value FROM SellOrder WHERE floID IN (" +
|
||||||
" SELECT UserTag.floID FROM UserTag INNER JOIN TagList ON UserTag.tag = TagList.tag" +
|
" SELECT UserTag.floID FROM UserTag INNER JOIN TagList ON UserTag.tag = TagList.tag" +
|
||||||
" WHERE TagList.sellPriority > ?) AND asset=?", [ratedMin, asset]).then(result => {
|
" WHERE TagList.sellPriority > ?) AND asset=?", [ratedMin, asset]).then(result => {
|
||||||
resolve(result[0].value > 0);
|
resolve(result[0].value > 0);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -253,7 +253,7 @@ function refreshBlockchainData(nodeList = []) {
|
|||||||
|
|
||||||
function closeFund(fund_id, floID, ref) {
|
function closeFund(fund_id, floID, ref) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
DB.query("SELECT r_status, close_id FROM CloseFundTransact WHERE fund_id=?", [fund_id]).then(result => {
|
DB.query("SELECT r_status, close_id FROM CloseFundTransact WHERE fund_id=? AND floID=?", [fund_id, floID]).then(result => {
|
||||||
if (result.length)
|
if (result.length)
|
||||||
return reject(INVALID(eCode.DUPLICATE_ENTRY, result[0].r_status == pCode.STATUS_SUCCESS ? `Fund investment already closed (${result[0].close_id})` : `Fund closing already in process`));
|
return reject(INVALID(eCode.DUPLICATE_ENTRY, result[0].r_status == pCode.STATUS_SUCCESS ? `Fund investment already closed (${result[0].close_id})` : `Fund closing already in process`));
|
||||||
DB.query("SELECT * FROM BobsFund WHERE fund_id=?", [fund_id]).then(result => {
|
DB.query("SELECT * FROM BobsFund WHERE fund_id=?", [fund_id]).then(result => {
|
||||||
|
|||||||
@ -96,7 +96,7 @@ function convertToCoin(floID, txid, coin, amount) {
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error instanceof INVALID && error.ecode === eCode.INSUFFICIENT_FUND)
|
if (error instanceof INVALID && error.ecode === eCode.INSUFFICIENT_FUND)
|
||||||
DB.query("INSERT INTO DirectConvert(floID, in_txid, mode, coin, amount, r_status) VALUES (?)", [[floID, txid, pCode.CONVERT_MODE_GET, coin, amount, pCode.STATUS_REJECTED]]).then(result => {
|
DB.query("INSERT INTO DirectConvert(floID, in_txid, mode, coin, amount, r_status) VALUES (?)", [[floID, txid, pCode.CONVERT_MODE_GET, coin, amount, pCode.STATUS_REJECTED]]).then(result => {
|
||||||
DB.query("INSERT INTO RefundTransact(floID, in_txid, amount, r_status) VALUES (?)", [[floID, txid, amount, pCode.STATUS_PENDING]])
|
DB.query("INSERT INTO RefundTransact(floID, in_txid, asset_type, asset, r_status) VALUES (?)", [[floID, txid, pCode.ASSET_TYPE_TOKEN, floGlobals.currency, pCode.STATUS_PENDING]])
|
||||||
.then(_ => null).catch(error => console.error(error));
|
.then(_ => null).catch(error => console.error(error));
|
||||||
}).catch(error => console.error(error))
|
}).catch(error => console.error(error))
|
||||||
reject(error);
|
reject(error);
|
||||||
|
|||||||
@ -13,6 +13,8 @@ try {
|
|||||||
global[p] = param[p];
|
global[p] = param[p];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.toStandardDecimal = num => (parseInt(num * 1e8) * 1e-8)
|
||||||
|
|
||||||
if (!process.argv.includes("--debug"))
|
if (!process.argv.includes("--debug"))
|
||||||
global.console.debug = () => null;
|
global.console.debug = () => null;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user