From 0381c447338e4dcbee39b3c695c2ab59eb7d12b3 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 19 Oct 2022 21:26:11 +0530 Subject: [PATCH] Convert fund management - Added Withdrawal of convert fund - Convert fund is moved to a different table (ConvertFund) - Added APIs for withdraw fund - Changed add-convert-fund to deposit-convert-fund - Fixed minor bugs --- docs/scripts/floExchangeAPI.js | 80 +++++++++++++++++++++++++++++++--- src/app.js | 6 ++- src/background.js | 76 ++++++++++++++++++++++++-------- src/blockchain.js | 13 ++++++ src/request.js | 46 +++++++++++++++---- src/services/conversion.js | 66 +++++++++++++++++++++++----- 6 files changed, 244 insertions(+), 43 deletions(-) diff --git a/docs/scripts/floExchangeAPI.js b/docs/scripts/floExchangeAPI.js index 015fdc6..1b13174 100644 --- a/docs/scripts/floExchangeAPI.js +++ b/docs/scripts/floExchangeAPI.js @@ -1314,7 +1314,7 @@ }) } - exchangeAPI.addConvertFundCurrency = function (amount, floID, sinkID, privKey) { + exchangeAPI.depositConvertFundCurrency = function (amount, floID, sinkID, privKey) { return new Promise((resolve, reject) => { if (!floCrypto.verifyPrivKey(privKey, floID)) return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); @@ -1328,14 +1328,14 @@ timestamp: Date.now() }; request.sign = signRequest({ - type: "add_convert_currency_fund", + type: "deposit_convert_currency_fund", coin: request.coin, txid: txid, timestamp: request.timestamp }, privKey); console.debug(request); - fetch_api('/add-convert-currency-fund', { + fetch_api('/deposit-convert-currency-fund', { method: "POST", headers: { 'Content-Type': 'application/json' @@ -1350,7 +1350,7 @@ }) } - exchangeAPI.addConvertFundBTC = function (quantity, floID, sinkID, privKey) { + exchangeAPI.depositConvertFundBTC = function (quantity, floID, sinkID, privKey) { return new Promise((resolve, reject) => { if (!floCrypto.verifyPrivKey(privKey, floID)) return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); @@ -1366,14 +1366,14 @@ timestamp: Date.now() }; request.sign = signRequest({ - type: "add_convert_coin_fund", + type: "deposit_convert_coin_fund", coin: request.coin, txid: data.txid, timestamp: request.timestamp }, proxySecret || privKey); console.debug(request); - fetch_api('/add-convert-coin-fund', { + fetch_api('/deposit-convert-coin-fund', { method: "POST", headers: { 'Content-Type': 'application/json' @@ -1388,6 +1388,74 @@ }) } + exchangeAPI.withdrawConvertFundCurrency = function (amount, floID, privKey) { + return new Promise((resolve, reject) => { + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); + if (floID !== floGlobals.adminID) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Access Denied", errorCode.ACCESS_DENIED)); + let request = { + floID: floID, + amount: amount, + coin: "BTC", + timestamp: Date.now() + }; + request.sign = signRequest({ + type: "withdraw_convert_currency_fund", + coin: request.coin, + amount: amount, + timestamp: request.timestamp + }, privKey); + console.debug(request); + + fetch_api('/withdraw-convert-currency-fund', { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(request) + }).then(result => { + responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }) + } + + exchangeAPI.withdrawConvertFundCurrency = function (quantity, floID, privKey) { + return new Promise((resolve, reject) => { + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); + if (floID !== floGlobals.adminID) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Access Denied", errorCode.ACCESS_DENIED)); + let request = { + floID: floID, + quantity: quantity, + coin: "BTC", + timestamp: Date.now() + }; + request.sign = signRequest({ + type: "withdraw_convert_coin_fund", + coin: request.coin, + quantity: quantity, + timestamp: request.timestamp + }, privKey); + console.debug(request); + + fetch_api('/withdraw-convert-coin-fund', { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(request) + }).then(result => { + responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }) + } + exchangeAPI.closeBlockchainBond = function (bond_id, floID, privKey) { return new Promise((resolve, reject) => { if (!floCrypto.verifyPrivKey(privKey, floID)) diff --git a/src/app.js b/src/app.js index ccbc8a4..7e74db6 100644 --- a/src/app.js +++ b/src/app.js @@ -88,8 +88,10 @@ module.exports = function App(secret, DB) { //convert from or to coin app.post('/convert-to', Request.ConvertTo); app.post('/convert-from', Request.ConvertFrom); - app.post('/add-convert-coin-fund', Request.AddConvertCoinFund); - app.post('/add-convert-currency-fund', Request.AddConvertCurrencyFund); + app.post('/deposit-convert-coin-fund', Request.DepositConvertCoinFund); + app.post('/deposit-convert-currency-fund', Request.DepositConvertCurrencyFund); + app.post('/withdraw-convert-coin-fund', Request.WithdrawConvertCoinFund); + app.post('/withdraw-convert-currency-fund', Request.WithdrawConvertCurrencyFund); //close blockchain-bond app.post('/close-blockchain-bonds', Request.CloseBlockchainBond); diff --git a/src/background.js b/src/background.js index ce079a3..c3e0631 100644 --- a/src/background.js +++ b/src/background.js @@ -222,7 +222,7 @@ verifyTx.BTC = function (sender, txid) { function verifyConvert() { DB.query("SELECT id, floID, mode, in_txid, amount, quantity FROM DirectConvert WHERE status=? AND coin=?", ["PENDING", "BTC"]).then(results => { results.forEach(r => { - if (mode == _sql.CONVERT_MODE_GET) { + if (r.mode == _sql.CONVERT_MODE_GET) { verifyTx.token(r.floID, r.in_txid, true).then(({ amount }) => { if (r.amount !== amount) throw ([true, "Transaction amount mismatched in blockchain"]); @@ -235,7 +235,7 @@ function verifyConvert() { DB.query("UPDATE DirectConvert SET status=? WHERE id=?", ["REJECTED", r.id]) .then(_ => null).catch(error => console.error(error)); }); - } else if (mode == _sql.CONVERT_MODE_PUT) { + } else if (r.mode == _sql.CONVERT_MODE_PUT) { verifyTx.BTC(r.floID, r.in_txid).then(quantity => { if (r.quantity !== quantity) throw ([true, "Transaction quantity mismatched in blockchain"]); @@ -256,9 +256,9 @@ function verifyConvert() { function retryConvert() { DB.query("SELECT id, floID, mode, amount, quantity FROM DirectConvert WHERE status=? AND coin=?", ["PROCESSING", "BTC"]).then(results => { results.forEach(r => { - if (mode == _sql.CONVERT_MODE_GET) + if (r.mode == _sql.CONVERT_MODE_GET) blockchain.convertToCoin.retry(r.floID, "BTC", r.quantity, r.id); - else if (mode == _sql.CONVERT_MODE_PUT) + else if (r.mode == _sql.CONVERT_MODE_PUT) blockchain.convertFromCoin.retry(r.floID, r.amount, r.id) }) }).catch(error => console.error(error)) @@ -267,7 +267,7 @@ function retryConvert() { function confirmConvert() { DB.query("SELECT id, floID, mode, amount, quantity, out_txid FROM DirectConvert WHERE status=? AND coin=?", ["WAITING_CONFIRMATION", "BTC"]).then(results => { results.forEach(r => { - if (mode == _sql.CONVERT_MODE_GET) + if (r.mode == _sql.CONVERT_MODE_GET) btcOperator.getTx(r.out_txid).then(tx => { if (!tx.blockhash || !tx.confirmations) //Still not confirmed return; @@ -275,7 +275,7 @@ function confirmConvert() { .then(result => console.debug(`${r.floID} converted ${amount} to ${r.quantity} BTC`)) .catch(error => console.error(error)) }).catch(error => console.error(error)); - else if (mode == _sql.CONVERT_MODE_PUT) + else if (r.mode == _sql.CONVERT_MODE_PUT) floTokenAPI.getTx(r.out_txid).then(tx => { if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed return; @@ -287,27 +287,27 @@ function confirmConvert() { }).catch(error => console.error(error)); } -function convert_depositFund() { - DB.query("SELECT id, floID, mode, in_txid FROM DirectConvert WHERE status=? AND coin=?", ["DEPOSIT_PENDING", "BTC"]).then(results => { +function verifyConvertFundDeposit() { + DB.query("SELECT id, floID, mode, txid FROM ConvertFund WHERE status=? AND coin=?", ["PROCESSING", "BTC"]).then(results => { results.forEach(r => { - if (mode == _sql.CONVERT_MODE_GET) { - verifyTx.token(r.floID, r.in_txid, true).then(({ amount }) => { - DB.query("UPDATE DirectConvert SET status=?, amount=? WHERE id=?", ["DEPOSIT_SUCCESS", amount, r.id]) + if (r.mode == _sql.CONVERT_MODE_GET) { //deposit currency + verifyTx.token(r.floID, r.txid, true).then(({ amount }) => { + DB.query("UPDATE ConvertFund SET status=?, amount=? WHERE id=?", ["SUCCESS", amount, r.id]) .then(_ => null).catch(error => console.error(error)); }).catch(error => { console.error(error); if (error[0]) - DB.query("UPDATE DirectConvert SET status=? WHERE id=?", ["REJECTED", r.id]) + DB.query("UPDATE ConvertFund SET status=? WHERE id=?", ["REJECTED", r.id]) .then(_ => null).catch(error => console.error(error)); }); - } else if (mode == _sql.CONVERT_MODE_PUT) { - verifyTx.BTC(r.floID, r.in_txid).then(quantity => { - DB.query("UPDATE DirectConvert SET status=?, quantity=? WHERE id=?", ["DEPOSIT_SUCCESS", quantity, r.id]) + } else if (r.mode == _sql.CONVERT_MODE_PUT) {//deposit coin + verifyTx.BTC(r.floID, r.txid).then(quantity => { + DB.query("UPDATE ConvertFund SET status=?, quantity=? WHERE id=?", ["SUCCESS", quantity, r.id]) .then(_ => null).catch(error => console.error(error)); }).catch(error => { console.error(error); if (error[0]) - DB.query("UPDATE DirectConvert SET status=? WHERE id=?", ["REJECTED", r.id]) + DB.query("UPDATE ConvertFund SET status=? WHERE id=?", ["REJECTED", r.id]) .then(_ => null).catch(error => console.error(error)); }); } @@ -315,6 +315,41 @@ function convert_depositFund() { }).catch(error => console.error(error)) } +function retryConvertFundWithdraw() { + DB.query("SELECT id, mode, coin, quantity, amount FROM ConvertFund WHERE status=? AND coin=?", ["PENDING", "BTC"]).then(results => { + results.forEach(r => { + if (r.mode == _sql.CONVERT_MODE_GET) //withdraw coin + blockchain.convertFundWithdraw.retry(r.coin, r.quantity, r.id); + else if (r.mode == _sql.CONVERT_MODE_PUT) //withdraw currency + blockchain.convertFundWithdraw.retry(floGlobals.currency, r.amount, r.id); + }) + }).catch(error => console.error(error)) +} + +function confirmConvertFundWithdraw() { + DB.query("SELECT * FROM ConvertFund WHERE status=? AND coin=?", ["WAITING_CONFIRMATION", "BTC"]).then(results => { + results.forEach(r => { + if (r.mode == _sql.CONVERT_MODE_GET) { //withdraw coin + btcOperator.getTx(r.txid).then(tx => { + if (!tx.blockhash || !tx.confirmations) //Still not confirmed + return; + DB.query("UPDATE ConvertFund SET status=? WHERE id=?", ["SUCCESS", r.id]) + .then(result => console.debug(`Withdraw-fund ${r.quantity} ${r.coin} successful`)) + .catch(error => console.error(error)) + }).catch(error => console.error(error)); + } else if (r.mode == _sql.CONVERT_MODE_PUT) {//withdraw currency + floTokenAPI.getTx(r.txid).then(tx => { + if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed + return; + DB.query("UPDATE ConvertFund SET status=? WHERE id=?", ["SUCCESS", r.id]) + .then(result => console.debug(`Withdraw-fund ${r.amount} ${floGlobals.currency} successful`)) + .catch(error => console.error(error)); + }).catch(error => console.error(error)); + } + }) + }).catch(error => console.error(error)) +} + function verifyRefund() { DB.query("SELECT id, floID, in_txid FROM RefundTransact WHERE status=?", ["PENDING"]).then(results => { verifyTx.token(r.floID, r.in_txid, true) @@ -395,6 +430,7 @@ function confirmFundClosing() { } function processAll() { + //deposit-withdraw asset balance confirmDepositFLO(); confirmDepositToken(); retryWithdrawalCoin(); @@ -402,14 +438,20 @@ function processAll() { confirmWithdrawalFLO(); confirmWithdrawalBTC(); confirmWithdrawalToken(); + //convert service verifyConvert(); retryConvert(); confirmConvert(); - convert_depositFund(); + verifyConvertFundDeposit(); + retryConvertFundWithdraw(); + confirmConvertFundWithdraw(); + //blockchain-bond service retryBondClosing(); confirmBondClosing(); + //bob's fund service retryFundClosing(); confirmFundClosing(); + //refund transactions verifyRefund(); retryRefund(); confirmRefund(); diff --git a/src/blockchain.js b/src/blockchain.js index a33e631..892eedd 100644 --- a/src/blockchain.js +++ b/src/blockchain.js @@ -7,6 +7,7 @@ var DB; //container for database const TYPE_TOKEN = "TOKEN", TYPE_COIN = "COIN", TYPE_CONVERT = "CONVERT", + TYPE_CONVERT_POOL = "CONVERT_POOL", TYPE_REFUND = "REFUND", TYPE_BOND = "BOND", TYPE_FUND = "BOB-FUND"; @@ -17,6 +18,7 @@ const balance_locked = {}, [TYPE_COIN]: {}, [TYPE_TOKEN]: {}, [TYPE_CONVERT]: {}, + [TYPE_CONVERT_POOL]: {}, [TYPE_REFUND]: {}, [TYPE_BOND]: {}, [TYPE_FUND]: {} @@ -65,6 +67,7 @@ const WITHDRAWAL_MESSAGE = { [TYPE_COIN]: "(withdrawal from market)", [TYPE_TOKEN]: "(withdrawal from market)", [TYPE_CONVERT]: "(convert coin)", + [TYPE_CONVERT_POOL]: "(convert fund)", [TYPE_REFUND]: "(refund from market)", [TYPE_BOND]: "(bond closing)", [TYPE_FUND]: "(fund investment closing)" @@ -87,6 +90,7 @@ const updateSyntax = { [TYPE_COIN]: "UPDATE WithdrawCoin SET status=?, txid=? WHERE id=?", [TYPE_TOKEN]: "UPDATE WithdrawToken SET status=?, txid=? WHERE id=?", [TYPE_CONVERT]: "UPDATE DirectConvert SET status=?, out_txid=? WHERE id=?", + [TYPE_CONVERT_POOL]: "UPDATE ConvertFund SET status=?, txid=? WHERE id=?", [TYPE_REFUND]: "UPDATE RefundTransact SET status=?, out_txid=? WHERE id=?", [TYPE_BOND]: "UPDATE CloseBondTransact SET status=?, txid=? WHERE id=?", [TYPE_FUND]: "UPDATE CloseFundTransact SET status=?, txid=? WHERE id=?" @@ -163,6 +167,12 @@ function convertFromCoin_retry(floID, currency_amount, id) { else sendAsset(floID, floGlobals.currency, currency_amount, TYPE_CONVERT, id); } +function convertFundWithdraw_retry(asset, amount, id) { + if (id in callbackCollection[TYPE_CONVERT_POOL]) + console.debug("A callback is already pending for this Convert fund withdrawal"); + else sendAsset(floGlobals.adminID, asset, amount, TYPE_CONVERT_POOL, id); +} + function bondTransact_retry(floID, amount, id) { if (id in callbackCollection[TYPE_BOND]) console.debug("A callback is already pending for this Bond closing"); @@ -213,6 +223,9 @@ module.exports = { init: convertFromCoin_init, retry: convertFromCoin_retry }, + convertFundWithdraw: { + retry: convertFundWithdraw_retry + }, bondTransact: { retry: bondTransact_retry }, diff --git a/src/request.js b/src/request.js index 1c606fa..f5f9600 100644 --- a/src/request.js +++ b/src/request.js @@ -326,32 +326,60 @@ function ConvertFrom(req, res) { }, () => conversion.convertFromCoin(data.floID, data.txid, data.tx_hex, data.coin, data.quantity)); } -function AddConvertCoinFund(req, res) { +function DepositConvertCoinFund(req, res) { let data = req.body; if (data.floID !== floGlobals.adminID) res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); else if (!data.pubKey) res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); else processRequest(res, data.floID, data.pubKey, data.sign, "Conversion Fund", { - type: "add_convert_coin_fund", + type: "deposit_convert_coin_fund", coin: data.coin, txid: data.txid, timestamp: data.timestamp - }, () => conversion.addFund.coin(data.floID, data.txid, data.coin)); + }, () => conversion.depositFund.coin(data.floID, data.txid, data.coin)); } -function AddConvertCurrencyFund(req, res) { +function DepositConvertCurrencyFund(req, res) { let data = req.body; if (data.floID !== floGlobals.adminID) res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); else if (!data.pubKey) res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); else processRequest(res, data.floID, data.pubKey, data.sign, "Conversion Fund", { - type: "add_convert_currency_fund", + type: "deposit_convert_currency_fund", coin: data.coin, txid: data.txid, timestamp: data.timestamp - }, () => conversion.addFund.currency(data.floID, data.txid, data.coin)); + }, () => conversion.depositFund.currency(data.floID, data.txid, data.coin)); +} + +function WithdrawConvertCoinFund(req, res) { + let data = req.body; + if (data.floID !== floGlobals.adminID) + res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); + else if (!data.pubKey) + res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); + else processRequest(res, data.floID, data.pubKey, data.sign, "Conversion Fund", { + type: "withdraw_convert_coin_fund", + coin: data.coin, + quantity: data.quantity, + timestamp: data.timestamp + }, () => conversion.withdrawFund.coin(data.floID, data.coin, data.quantity)); +} + +function WithdrawConvertCurrencyFund(req, res) { + let data = req.body; + if (data.floID !== floGlobals.adminID) + res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); + else if (!data.pubKey) + res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); + else processRequest(res, data.floID, data.pubKey, data.sign, "Conversion Fund", { + type: "withdraw_convert_currency_fund", + coin: data.coin, + amount: data.amount, + timestamp: data.timestamp + }, () => conversion.withdrawFund.currency(data.floID, data.coin, data.amount)); } function CloseBlockchainBond(req, res) { @@ -573,8 +601,10 @@ module.exports = { RemoveDistributor, ConvertTo, ConvertFrom, - AddConvertCoinFund, - AddConvertCurrencyFund, + DepositConvertCoinFund, + DepositConvertCurrencyFund, + WithdrawConvertCoinFund, + WithdrawConvertCurrencyFund, CloseBlockchainBond, CloseBobsFund, set trustedIDs(ids) { diff --git a/src/services/conversion.js b/src/services/conversion.js index bc58303..7ec8c72 100644 --- a/src/services/conversion.js +++ b/src/services/conversion.js @@ -48,7 +48,11 @@ function checkPoolBalance(coin, req_value, mode) { return new Promise((resolve, reject) => { if (!allowedConversion.includes(coin)) return reject(INVALID(eCode.INVALID_TOKEN_NAME, `Invalid coin (${coin})`)); - DB.query("SELECT mode, SUM(quantity) AS coin_val, SUM(amount) AS cash_val FROM DirectConvert WHERE coin=? AND status NOT IN (?) GROUP BY mode", [coin, ["REJECTED", "REFUND"]]).then(result => { + let q = "SELECT mode, SUM(quantity) AS coin_val, SUM(amount) AS cash_val FROM (" + + "(SELECT amount, coin, quantity, mode, status FROM DirectConvert) UNION " + + "(SELECT amount, coin, quantity, mode, status FROM ConvertFund) " + + ") WHERE coin=? AND status NOT IN (?) GROUP BY mode" + DB.query(q, [coin, ["REJECTED", "REFUND"]]).then(result => { let coin_net = 0, cash_net = 0; for (let r of result) if (r.mode == _sql.CONVERT_MODE_GET) { @@ -130,38 +134,76 @@ function convertFromCoin(floID, txid, tx_hex, coin, quantity) { }) } -function addCurrencyFund(floID, txid, coin) { +function depositCurrencyFund(floID, txid, coin) { return new Promise((resolve, reject) => { if (floID !== floGlobals.adminID) return reject(INVALID(eCode.ACCESS_DENIED, 'Access Denied')); else if (!allowedConversion.includes(coin)) return reject(INVALID(eCode.INVALID_TOKEN_NAME, `Invalid coin (${coin})`)); - DB.query("SELECT status FROM DirectConvert WHERE in_txid=? AND floID=? AND mode=?", [txid, floID, _sql.CONVERT_MODE_GET]).then(result => { + DB.query("SELECT status FROM ConvertFund WHERE txid=? AND mode=?", [txid, _sql.CONVERT_MODE_GET]).then(result => { if (result.length) return reject(INVALID(eCode.DUPLICATE_ENTRY, "Transaction already in process")); - DB.query("INSERT INTO DirectConvert(floID, in_txid, mode, coin, status) VALUES (?)", [[floID, b_txid, _sql.CONVERT_MODE_GET, coin, "DEPOSIT_PENDING"]]) + DB.query("INSERT INTO ConvertFund(txid, mode, coin, status) VALUES (?)", [[b_txid, _sql.CONVERT_MODE_GET, coin, "PROCESSING"]]) .then(result => resolve("Add currency fund in process")) .catch(error => reject(error)) }).catch(error => reject(error)) }) } -function addCoinFund(floID, txid, coin) { +function depositCoinFund(floID, txid, coin) { return new Promise((resolve, reject) => { if (floID !== floGlobals.adminID) return reject(INVALID(eCode.ACCESS_DENIED, 'Access Denied')); else if (!allowedConversion.includes(coin)) return reject(INVALID(eCode.INVALID_TOKEN_NAME, `Invalid coin (${coin})`)); - DB.query("SELECT status FROM DirectConvert WHERE in_txid=? AND floID=? AND mode=?", [txid, floID, _sql.CONVERT_MODE_PUT]).then(result => { + DB.query("SELECT status FROM ConvertFund WHERE txid=? AND mode=?", [txid, _sql.CONVERT_MODE_PUT]).then(result => { if (result.length) return reject(INVALID(eCode.DUPLICATE_ENTRY, "Transaction already in process")); - DB.query("INSERT INTO DirectConvert(floID, in_txid, mode, coin, status) VALUES (?)", [[floID, b_txid, _sql.CONVERT_MODE_PUT, coin, "DEPOSIT_PENDING"]]) + DB.query("INSERT INTO ConvertFund(txid, mode, coin, status) VALUES (?)", [[b_txid, _sql.CONVERT_MODE_PUT, coin, "PROCESSING"]]) .then(result => resolve("Add coin fund in process")) .catch(error => reject(error)) }).catch(error => reject(error)) }) } +function withdrawCurrencyFund(floID, coin, amount) { + return new Promise((resolve, reject) => { + if (floID !== floGlobals.adminID) + return reject(INVALID(eCode.ACCESS_DENIED, 'Access Denied')); + else if (!allowedConversion.includes(coin)) + return reject(INVALID(eCode.INVALID_TOKEN_NAME, `Invalid coin (${coin})`)); + DB.query("SELECT SUM(amount) AS deposit_amount FROM ConvertFund WHERE mode=? AND status=?", [_sql.CONVERT_MODE_GET, "SUCCESS"]).then(r1 => { + DB.query("SELECT SUM(amount) AS withdraw_amount FROM ConvertFund WHERE mode=? AND status IN (?)", [_sql.CONVERT_MODE_PUT, ["SUCCESS", "PENDING", "WAITING_CONFIRMATION"]]).then(r2 => { + let available_amount = (r1[0].deposit_amount || 0) - (r2[0].withdraw_amount || 0); + if (available_amount < amount) + return reject(INVALID(eCode.INSUFFICIENT_BALANCE, "Insufficient convert-fund deposits to withdraw")); + DB.query("INSERT INTO ConvertFund(mode, coin, amount, status) VALUES (?)", [[_sql.CONVERT_MODE_PUT, coin, amount, "PENDING"]]) + .then(result => resolve("Add currency fund in process")) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) +} + +function withdrawCoinFund(floID, coin, quantity) { + return new Promise((resolve, reject) => { + if (floID !== floGlobals.adminID) + return reject(INVALID(eCode.ACCESS_DENIED, 'Access Denied')); + else if (!allowedConversion.includes(coin)) + return reject(INVALID(eCode.INVALID_TOKEN_NAME, `Invalid coin (${coin})`)); + DB.query("SELECT SUM(quantity) AS deposit_quantity FROM ConvertFund WHERE mode=? AND status=?", [_sql.CONVERT_MODE_PUT, "SUCCESS"]).then(r1 => { + DB.query("SELECT SUM(quantity) AS withdraw_quantity FROM ConvertFund WHERE mode=? AND status IN (?)", [_sql.CONVERT_MODE_GET, ["SUCCESS", "PENDING"]]).then(r2 => { + let available_quantity = (r1[0].deposit_quantity || 0) - (r2[0].withdraw_quantity || 0); + if (available_quantity < quantity) + return reject(INVALID(eCode.INSUFFICIENT_BALANCE, "Insufficient convert-fund deposits to withdraw")); + DB.query("INSERT INTO ConvertFund(mode, coin, quantity, status) VALUES (?)", [[_sql.CONVERT_MODE_GET, coin, quantity, "PENDING"]]) + .then(result => resolve("Add currency fund in process")) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) +} + module.exports = { getRate: { BTC_USD, @@ -170,9 +212,13 @@ module.exports = { }, convertToCoin, convertFromCoin, - addFund: { - coin: addCoinFund, - currency: addCurrencyFund + depositFund: { + coin: depositCoinFund, + currency: depositCurrencyFund + }, + withdrawFund: { + coin: withdrawCoinFund, + currency: withdrawCurrencyFund }, set DB(db) { DB = db;