Deposit and withdraw request processing
- Processing of Deposit/Withdraw request for FLO/Rupee. - renamed database's TxQuery to transaction
This commit is contained in:
parent
84b914bc9b
commit
20fd7aaeb1
@ -54,6 +54,12 @@ module.exports = function App(secret, DB) {
|
|||||||
//get account details
|
//get account details
|
||||||
app.get('/account', Request.Account);
|
app.get('/account', Request.Account);
|
||||||
|
|
||||||
|
//withdraw and deposit request
|
||||||
|
app.post('deposit-flo', Request.DepositFLO);
|
||||||
|
app.post('withdraw-flo', Request.WithdrawFLO);
|
||||||
|
app.post('deposit-rupee', Request.DepositRupee);
|
||||||
|
app.post('withdraw-rupee', Request.WithdrawRupee);
|
||||||
|
|
||||||
Request.DB = DB;
|
Request.DB = DB;
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ function Database(user, password, dbname, host = 'localhost') {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(db, "TxQuery", {
|
Object.defineProperty(db, "transaction", {
|
||||||
value: (queries) => new Promise((resolve, reject) => {
|
value: (queries) => new Promise((resolve, reject) => {
|
||||||
db.connect.then(conn => {
|
db.connect.then(conn => {
|
||||||
conn.beginTransaction(err => {
|
conn.beginTransaction(err => {
|
||||||
|
|||||||
@ -8,8 +8,8 @@ const floGlobals = {
|
|||||||
FLO: ['https://livenet.flocha.in/', 'https://flosight.duckdns.org/'],
|
FLO: ['https://livenet.flocha.in/', 'https://flosight.duckdns.org/'],
|
||||||
FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/']
|
FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/']
|
||||||
},
|
},
|
||||||
//sendAmt: 0.001,
|
sendAmt: 0.001,
|
||||||
//fee: 0.0005,
|
fee: 0.0005,
|
||||||
};
|
};
|
||||||
|
|
||||||
(typeof global !== "undefined" ? global : window).cryptocoin = floGlobals.blockchain;
|
(typeof global !== "undefined" ? global : window).cryptocoin = floGlobals.blockchain;
|
||||||
|
|||||||
404
src/market.js
404
src/market.js
@ -1,7 +1,60 @@
|
|||||||
|
const floGlobals = require("./floGlobals");
|
||||||
|
|
||||||
var net_FLO_price; //container for FLO price (from API or by model)
|
var net_FLO_price; //container for FLO price (from API or by model)
|
||||||
var DB; //container for database
|
var DB; //container for database
|
||||||
const REFRESH_INTERVAL = 60 * 1000; //1 min
|
const REFRESH_INTERVAL = 60 * 1000; //1 min
|
||||||
|
|
||||||
|
const tokenAPI = {
|
||||||
|
fetch_api: function(apicall) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log(floGlobals.tokenURL + apicall);
|
||||||
|
fetch(floGlobals.tokenURL + apicall).then(response => {
|
||||||
|
if (response.ok)
|
||||||
|
response.json().then(data => resolve(data));
|
||||||
|
else
|
||||||
|
reject(response)
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getBalance: function(floID, token = 'rupee') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
|
||||||
|
.then(result => resolve(result.balance || 0))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getTx: function(txID) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
|
||||||
|
if (res.result === "error")
|
||||||
|
reject(res.description);
|
||||||
|
else if (!res.parsedFloData)
|
||||||
|
reject("Data piece (parsedFloData) missing");
|
||||||
|
else if (!res.transactionDetails)
|
||||||
|
reject("Data piece (transactionDetails) missing");
|
||||||
|
else
|
||||||
|
resolve(res);
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendToken: function(privKey, amount, message = "", receiverID = floGlobals.adminID, token = 'rupee') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let senderID = floCrypto.getFloID(privKey);
|
||||||
|
if (typeof amount !== "number" || amount <= 0)
|
||||||
|
return reject("Invalid amount");
|
||||||
|
this.getBalance(senderID, token).then(bal => {
|
||||||
|
if (amount > bal)
|
||||||
|
return reject("Insufficiant token balance");
|
||||||
|
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID)
|
||||||
|
.then(txid => resolve(txid))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getRates() {
|
function getRates() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getRates.FLO_USD().then(FLO_rate => {
|
getRates.FLO_USD().then(FLO_rate => {
|
||||||
@ -11,7 +64,7 @@ function getRates() {
|
|||||||
resolve(net_FLO_price);
|
resolve(net_FLO_price);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRates.FLO_USD = function() {
|
getRates.FLO_USD = function() {
|
||||||
@ -65,32 +118,6 @@ function addSellOrder(floID, quantity, min_price) {
|
|||||||
}).catch(error => reject(error));
|
}).catch(error => reject(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
function addSellOrder(floID, quantity, min_price) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
DB.query("SELECT id, base, (quantity - locked) as available FROM Vault WHERE floID=? ORDER BY base", [floID]).then(result => {
|
|
||||||
console.debug(result);
|
|
||||||
let rem = quantity,
|
|
||||||
sell_base = 0,
|
|
||||||
txQueries = [];
|
|
||||||
for (let i = 0; i < result.length && rem > 0; i++) {
|
|
||||||
var lock = (rem < result[i].available ? rem : result[i].available);
|
|
||||||
rem -= lock;
|
|
||||||
sell_base += (lock * result[i].base);
|
|
||||||
txQueries.push(["UPDATE Vault SET locked=locked-? WHERE id=?", [lock, result[i].id]]);
|
|
||||||
}
|
|
||||||
if (rem > 0)
|
|
||||||
return reject(INVALID("Insufficient FLO"));
|
|
||||||
sell_base = sell_base / quantity;
|
|
||||||
Promise.all(txQueries.map(a => DB.query(a[0], a[1]))).then(results => {
|
|
||||||
DB.query("INSERT INTO SellOrder(floID, quantity, minPrice, sellBase) VALUES (?, ?, ?)", [floID, quantity, min_price, sell_base])
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error));
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function addBuyOrder(floID, quantity, max_price) {
|
function addBuyOrder(floID, quantity, max_price) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -164,7 +191,7 @@ function matchBuyAndSell() {
|
|||||||
tx_quantity = processBuyAndSellOrder(seller_best, buyer_best, txQueries);
|
tx_quantity = processBuyAndSellOrder(seller_best, buyer_best, txQueries);
|
||||||
updateBalance(seller_best, buyer_best, txQueries, cur_price, tx_quantity);
|
updateBalance(seller_best, buyer_best, txQueries, cur_price, tx_quantity);
|
||||||
//process txn query in SQL
|
//process txn query in SQL
|
||||||
DB.TxQuery(txQueries).then(results => {
|
DB.transaction(txQueries).then(results => {
|
||||||
console.log(`Transaction was successful! BuyOrder:${buyer_best.id}| SellOrder:${seller_best.id}`);
|
console.log(`Transaction was successful! BuyOrder:${buyer_best.id}| SellOrder:${seller_best.id}`);
|
||||||
//Since a tx was successful, match again
|
//Since a tx was successful, match again
|
||||||
matchBuyAndSell();
|
matchBuyAndSell();
|
||||||
@ -316,13 +343,330 @@ function getAccountDetails(floID) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function depositCoins(floID, txid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
DB.query("SELECT status FROM inputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => {
|
||||||
|
if (result.length) {
|
||||||
|
switch (result[0].status) {
|
||||||
|
case "PENDING":
|
||||||
|
return reject(INVALID("Transaction already in process"));
|
||||||
|
case "REJECTED":
|
||||||
|
return reject(INVALID("Transaction already rejected"));
|
||||||
|
case "SUCCESS":
|
||||||
|
return reject(INVALID("Transaction already used to add coins"));
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DB.query("INSERT INTO inputFLO(txid, floID, status) VALUES (?, ?, ?)", [txid, floID, "PENDING"])
|
||||||
|
.then(result => resolve("Deposit request in process"))
|
||||||
|
.catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmDepositFLO() {
|
||||||
|
DB.query("SELECT id, floID, txid FROM inputFLO WHERE status=?", ["PENDING"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
confirmDepositFLO.checkTx(req.floID, req.txid).then(amount => {
|
||||||
|
let txQueries = [];
|
||||||
|
txQueries.push("INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [floID, amount]);
|
||||||
|
txQueries.push("UPDATE inputFLO SET status=?, amount=? WHERE id=?", ["SUCCESS", amount, req.id]);
|
||||||
|
DB.transaction(txQueries)
|
||||||
|
.then(result => console.debug("FLO deposited for ", floID))
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}).catch(error => {
|
||||||
|
if (!Array.isArray(error))
|
||||||
|
console.error(error);
|
||||||
|
else if (error[0])
|
||||||
|
DB.query("UPDATE inputFLO SET status=? WHERE id=?", ["REJECTED", req.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}).catch(error => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDepositFLO.checkTx = function(sender, txid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const receiver = globals.myFloID; //receiver should be market's floID (ie, adminID)
|
||||||
|
floBlockchainAPI.getTx(txid).then(tx => {
|
||||||
|
let vin_sender = tx.vin.filter(v => v.addr === sender).length
|
||||||
|
if (!vin_sender.length)
|
||||||
|
return reject([true, "Transaction not sent by the sender"]);
|
||||||
|
if (vin_sender.length !== tx.vin.length)
|
||||||
|
return reject([true, "Transaction input containes other floIDs"]);
|
||||||
|
if (!tx.blockheight)
|
||||||
|
return reject([false, "Transaction not included in any block yet"]);
|
||||||
|
if (!tx.confirmations)
|
||||||
|
return reject([false, "Transaction not confirmed yet"]);
|
||||||
|
let amount = tx.vout.reduce(v => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0);
|
||||||
|
if (amount == 0)
|
||||||
|
return reject([true, "Transaction receiver is not market ID"]);
|
||||||
|
else
|
||||||
|
resolve(amount);
|
||||||
|
}).catch(error => reject([false, error]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdrawCoins(floID, amount) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!floID || !floCrypto.validateAddr(floID))
|
||||||
|
return reject(INVALID("Invalid FLO ID"));
|
||||||
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
|
return reject(INVALID(`Invalid amount (${amount})`));
|
||||||
|
DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [floID]).then(result => {
|
||||||
|
let total = result.pop()["total"] || 0;
|
||||||
|
if (total < amount)
|
||||||
|
return reject(INVALID("Insufficient FLO"));
|
||||||
|
DB.query("SELECT SUM(quantity) as locked FROM SellOrder WHERE floID=?", [floID]).then(result => {
|
||||||
|
let locked = result.pop()["locked"] || 0;
|
||||||
|
let available = total - locked;
|
||||||
|
if (available < amount)
|
||||||
|
return reject(INVALID("Insufficient FLO (Some FLO are locked in sell orders)"));
|
||||||
|
DB.query("SELECT id, quantity, base FROM Vault WHERE floID=? ORDER BY locktime", [floID]).then(coins => {
|
||||||
|
let rem = amount,
|
||||||
|
txQueries = [];
|
||||||
|
for (let i = 0; i < coins.length && rem > 0; i++) {
|
||||||
|
if (rem < coins[i].quantity) {
|
||||||
|
txQueries.push(["UPDATE Vault SET quantity=quantity-? WHERE id=?", [rem, coins[i].id]]);
|
||||||
|
rem = 0;
|
||||||
|
} else {
|
||||||
|
txQueries.push(["DELETE FROM Vault WHERE id=?", [coins[i].id]]);
|
||||||
|
rem -= result[i].quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rem > 0) //should not happen as the total and net is checked already
|
||||||
|
return reject(INVALID("Insufficient FLO"));
|
||||||
|
DB.transaction(txQueries).then(result => {
|
||||||
|
//Send FLO to user via blockchain API
|
||||||
|
floBlockchainAPI.sendTx(global.myFloID, floID, amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(result => {
|
||||||
|
let txid = result.txid.result;
|
||||||
|
if (!txid || result.txid.error)
|
||||||
|
throw Error("Transaction not successful");
|
||||||
|
//Transaction was successful, Add in DB
|
||||||
|
DB.query("INSERT INTO outputFLO (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "WAITING_CONFIRMATION"])
|
||||||
|
.then(_ => null).catch(error => console.error(error))
|
||||||
|
.finally(_ => resolve("Withdrawal was successful"));
|
||||||
|
}).catch(error => {
|
||||||
|
DB.query("INSERT INTO outputFLO (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "PENDING"])
|
||||||
|
.then(_ => null).catch(error => console.error(error))
|
||||||
|
.finally(_ => resolve("Withdrawal request is in process"));
|
||||||
|
});
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryWithdrawalFLO() {
|
||||||
|
DB.query("SELECT id, floID, amount FROM outputFLO WHERE status=?", ["PENDING"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
floBlockchainAPI.sendTx(global.myFloID, req.floID, req.amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(result => {
|
||||||
|
let txid = result.txid.result;
|
||||||
|
if (!txid || result.txid.error) {
|
||||||
|
console.error(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Transaction was successful, Add in DB
|
||||||
|
DB.query("UPDATE outputFLO SET status=? WHERE id=?", ["WAITING_CONFIRMATION", req.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
})
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmWithdrawalFLO() {
|
||||||
|
DB.query("SELECT id, floID, txid FROM outputFLO WHERE status=?", ["WAITING_CONFIRMATION"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
floBlockchainAPI.getTx(req.txid).then(tx => {
|
||||||
|
if (!tx.blockheight || !tx.confirmations) //Still not confirmed
|
||||||
|
return;
|
||||||
|
DB.query("UPDATE outputFLO SET status=? WHERE id=?", ["SUCCESS", req.id])
|
||||||
|
.then(result => console.debug("FLO withdrawed for ", req.floID))
|
||||||
|
.catch(error => console.error(error))
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
})
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function depositTokens(floID, txid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
DB.query("SELECT status FROM inputRupee WHERE txid=? AND floID=?", [txid, floID]).then(result => {
|
||||||
|
if (result.length) {
|
||||||
|
switch (result[0].status) {
|
||||||
|
case "PENDING":
|
||||||
|
return reject(INVALID("Transaction already in process"));
|
||||||
|
case "REJECTED":
|
||||||
|
return reject(INVALID("Transaction already rejected"));
|
||||||
|
case "SUCCESS":
|
||||||
|
return reject(INVALID("Transaction already used to add tokens"));
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DB.query("INSERT INTO inputRupee(txid, floID, status) VALUES (?, ?, ?)", [txid, floID, "PENDING"])
|
||||||
|
.then(result => resolve("Deposit request in process"))
|
||||||
|
.catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmDepositRupee() {
|
||||||
|
DB.query("SELECT id, floID, txid FROM inputRupee WHERE status=?", ["PENDING"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
confirmDepositRupee.checkTx(req.floID, req.txid).then(amounts => {
|
||||||
|
DB.query("SELECT id FROM inputFLO where floID=? AND txid=?").then(result => {
|
||||||
|
let txQueries = [],
|
||||||
|
amount_rupee = amounts[0];
|
||||||
|
//Add the FLO balance if necessary
|
||||||
|
if (!result.length) {
|
||||||
|
let amount_flo = amounts[1];
|
||||||
|
txQueries.push("INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [req.floID, amount_flo]);
|
||||||
|
txQueries.push("INSERT INTO inputFLO(txid, floID, status) VALUES (?, ?, ?)", [req.txid, req.floID, "SUCCESS"]);
|
||||||
|
}
|
||||||
|
txQueries.push("UPDATE inputRupee SET status=? WHERE id=?", ["SUCCESS", req.id]);
|
||||||
|
txQueries.push("UPDATE Users SET rupeeBalance=rupeeBalance+? WHERE floID=?", [amount_rupee, req.floID]);
|
||||||
|
DB.transaction(txQueries)
|
||||||
|
.then(result => console.debug("Rupee deposited for ", req.floID))
|
||||||
|
.catch(error => console.error(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => {
|
||||||
|
if (!Array.isArray(error))
|
||||||
|
console.error(error);
|
||||||
|
else if (error[0])
|
||||||
|
DB.query("UPDATE inputRupee SET status=? WHERE id=?", ["REJECTED", req.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}).catch(error => console.error(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDepositRupee.checkTx = function(sender, txid) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const receiver = globals.myFloID; //receiver should be market's floID (ie, adminID)
|
||||||
|
tokenAPI.getTx(txid).then(tx => {
|
||||||
|
if (tx.parsedFloData.type !== "transfer")
|
||||||
|
return reject([true, "Transaction type not 'transfer'"]);
|
||||||
|
else if (tx.parsedFloData.transferType !== "token")
|
||||||
|
return reject([true, "Transaction transfer is not 'token'"]);
|
||||||
|
else if (tx.parsedFloData.tokenIdentification !== "rupee")
|
||||||
|
return reject([true, "Transaction token is not 'rupee'"]);
|
||||||
|
var amount_rupee = tx.parsedFloData.tokenAmount;
|
||||||
|
let vin_sender = tx.transactionDetails.vin.filter(v => v.addr === sender).length
|
||||||
|
if (!vin_sender.length)
|
||||||
|
return reject([true, "Transaction not sent by the sender"]);
|
||||||
|
let amount_flo = tx.transactionDetails.vout.reduce(v => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0);
|
||||||
|
if (amount_flo == 0)
|
||||||
|
return reject([true, "Transaction receiver is not market ID"]);
|
||||||
|
else
|
||||||
|
resolve([amount_rupee, amount_flo]);
|
||||||
|
}).catch(error => reject([false, error]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdrawTokens(floID, amount) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!floID || !floCrypto.validateAddr(floID))
|
||||||
|
return reject(INVALID("Invalid FLO ID"));
|
||||||
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
|
return reject(INVALID(`Invalid amount (${amount})`));
|
||||||
|
DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [floID]).then(result => {
|
||||||
|
let required_flo = floGlobals.sendAmt + floGlobals.fee,
|
||||||
|
total = result.pop()["total"] || 0;
|
||||||
|
if (total < required_flo)
|
||||||
|
return reject(INVALID(`Insufficient FLO! Required ${required_flo} FLO to withdraw tokens`));
|
||||||
|
DB.query("SELECT SUM(quantity) as locked FROM SellOrder WHERE floID=?", [floID]).then(result => {
|
||||||
|
let locked = result.pop()["locked"] || 0;
|
||||||
|
let available = total - locked;
|
||||||
|
if (available < amount)
|
||||||
|
return reject(INVALID(`Insufficient FLO (Some FLO are locked in sell orders)! Required ${required_flo} FLO to withdraw tokens`));
|
||||||
|
DB.query("SELECT rupeeBalance FROM Users WHERE floID=?", [floID]).then(result => {
|
||||||
|
if (result.length < 1)
|
||||||
|
return reject(INVALID(`FLO_ID: ${floID} not registered`));
|
||||||
|
if (result[0].rupeeBalance < amount)
|
||||||
|
return reject(INVALID('Insufficient Rupee balance'));
|
||||||
|
DB.query("SELECT id, quantity, base FROM Vault WHERE floID=? ORDER BY locktime", [floID]).then(coins => {
|
||||||
|
let rem = required_flo,
|
||||||
|
txQueries = [];
|
||||||
|
for (let i = 0; i < coins.length && rem > 0; i++) {
|
||||||
|
if (rem < coins[i].quantity) {
|
||||||
|
txQueries.push(["UPDATE Vault SET quantity=quantity-? WHERE id=?", [rem, coins[i].id]]);
|
||||||
|
rem = 0;
|
||||||
|
} else {
|
||||||
|
txQueries.push(["DELETE FROM Vault WHERE id=?", [coins[i].id]]);
|
||||||
|
rem -= result[i].quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rem > 0) //should not happen as the total and net is checked already
|
||||||
|
return reject(INVALID("Insufficient FLO"));
|
||||||
|
txQueries.push(["UPDATE Users SET rupeeBalance=rupeeBalance-? WHERE floID=?", [amount, floID]]);
|
||||||
|
|
||||||
|
DB.transaction(txQueries).then(result => {
|
||||||
|
//Send FLO to user via blockchain API
|
||||||
|
tokenAPI.sendToken(global.myPrivKey, amount, '(withdrawal from market)', floID).then(result => {
|
||||||
|
let txid = result.txid.result;
|
||||||
|
if (!txid || result.txid.error)
|
||||||
|
throw Error("Transaction not successful");
|
||||||
|
//Transaction was successful, Add in DB
|
||||||
|
DB.query("INSERT INTO outputRupee (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "WAITING_CONFIRMATION"])
|
||||||
|
.then(_ => null).catch(error => console.error(error))
|
||||||
|
.finally(_ => resolve("Withdrawal was successful"));
|
||||||
|
}).catch(error => {
|
||||||
|
DB.query("INSERT INTO outputRupee (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "PENDING"])
|
||||||
|
.then(_ => null).catch(error => console.error(error))
|
||||||
|
.finally(_ => resolve("Withdrawal request is in process"));
|
||||||
|
});
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function retryWithdrawalRupee() {
|
||||||
|
DB.query("SELECT id, floID, amount FROM outputRupee WHERE status=?", ["PENDING"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
tokenAPI.sendToken(global.myPrivKey, req.amount, '(withdrawal from market)', req.floID).then(result => {
|
||||||
|
let txid = result.txid.result;
|
||||||
|
if (!txid || result.txid.error) {
|
||||||
|
console.error(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Transaction was successful, Add in DB
|
||||||
|
DB.query("UPDATE outputRupee SET status=? WHERE id=?", ["WAITING_CONFIRMATION", req.id])
|
||||||
|
.then(_ => null).catch(error => console.error(error));
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
});
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmWithdrawalRupee() {
|
||||||
|
DB.query("SELECT id, floID, amount, txid FROM outputRupee WHERE status=?", ["WAITING_CONFIRMATION"]).then(results => {
|
||||||
|
results.forEach(req => {
|
||||||
|
tokenAPI.getTx(req.txid).then(tx => {
|
||||||
|
DB.query("UPDATE outputRupee SET status=? WHERE id=?", ["SUCCESS", req.id])
|
||||||
|
.then(result => console.debug("Rupee withdrawed for ", req.floID))
|
||||||
|
.catch(error => console.error(error));
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
})
|
||||||
|
}).catch(error => console.error(error));
|
||||||
|
}
|
||||||
|
|
||||||
function intervalFunction() {
|
function intervalFunction() {
|
||||||
let old_rate = net_FLO_price;
|
let old_rate = net_FLO_price;
|
||||||
getRates().then(cur_rate => {
|
getRates().then(cur_rate => {
|
||||||
|
transactionReCheck();
|
||||||
matchBuyAndSell();
|
matchBuyAndSell();
|
||||||
}).catch(error => console.error(error));
|
}).catch(error => console.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transactionReCheck() {
|
||||||
|
confirmDepositFLO();
|
||||||
|
confirmDepositRupee();
|
||||||
|
retryWithdrawalFLO();
|
||||||
|
retryWithdrawalRupee();
|
||||||
|
confirmWithdrawalFLO();
|
||||||
|
confirmWithdrawalRupee();
|
||||||
|
}
|
||||||
|
|
||||||
intervalFunction();
|
intervalFunction();
|
||||||
|
|
||||||
let refresher = setInterval(intervalFunction, REFRESH_INTERVAL);
|
let refresher = setInterval(intervalFunction, REFRESH_INTERVAL);
|
||||||
@ -332,6 +676,10 @@ module.exports = {
|
|||||||
addSellOrder,
|
addSellOrder,
|
||||||
cancelOrder,
|
cancelOrder,
|
||||||
getAccountDetails,
|
getAccountDetails,
|
||||||
|
depositCoins,
|
||||||
|
withdrawCoins,
|
||||||
|
depositTokens,
|
||||||
|
withdrawTokens,
|
||||||
set DB(db) {
|
set DB(db) {
|
||||||
DB = db;
|
DB = db;
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/request.js
128
src/request.js
@ -273,6 +273,130 @@ function Account(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DepositFLO(req, res) {
|
||||||
|
let data = req.body,
|
||||||
|
session = req.session;
|
||||||
|
if (!session.user_id)
|
||||||
|
return res.status(INVALID.e_code).send("Login required");
|
||||||
|
validateRequestFromFloID({
|
||||||
|
type: "deposit_FLO",
|
||||||
|
txid: data.txid,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}, data.sign, session.user_id).then(req_str => {
|
||||||
|
market.depositCoins(session.user_id, data.txid).then(result => {
|
||||||
|
storeRequest(session.user_id, req_str, data.sign);
|
||||||
|
res.send(result);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function WithdrawFLO(req, res) {
|
||||||
|
let data = req.body,
|
||||||
|
session = req.session;
|
||||||
|
if (!session.user_id)
|
||||||
|
return res.status(INVALID.e_code).send("Login required");
|
||||||
|
validateRequestFromFloID({
|
||||||
|
type: "withdraw_FLO",
|
||||||
|
amount: data.amount,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}, data.sign, session.user_id).then(req_str => {
|
||||||
|
market.withdrawCoins(session.user_id, data.amount).then(result => {
|
||||||
|
storeRequest(session.user_id, req_str, data.sign);
|
||||||
|
res.send(result);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function DepositRupee(req, res) {
|
||||||
|
let data = req.body,
|
||||||
|
session = req.session;
|
||||||
|
if (!session.user_id)
|
||||||
|
return res.status(INVALID.e_code).send("Login required");
|
||||||
|
validateRequestFromFloID({
|
||||||
|
type: "deposit_Rupee",
|
||||||
|
txid: data.txid,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}, data.sign, session.user_id).then(req_str => {
|
||||||
|
market.depositTokens(session.user_id, data.txid).then(result => {
|
||||||
|
storeRequest(session.user_id, req_str, data.sign);
|
||||||
|
res.send(result);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function WithdrawRupee(req, res) {
|
||||||
|
let data = req.body,
|
||||||
|
session = req.session;
|
||||||
|
if (!session.user_id)
|
||||||
|
return res.status(INVALID.e_code).send("Login required");
|
||||||
|
validateRequestFromFloID({
|
||||||
|
type: "withdraw_Rupee",
|
||||||
|
amount: data.amount,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}, data.sign, session.user_id).then(req_str => {
|
||||||
|
market.withdrawTokens(session.user_id, data.amount).then(result => {
|
||||||
|
storeRequest(session.user_id, req_str, data.sign);
|
||||||
|
res.send(result);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
SignUp,
|
SignUp,
|
||||||
Login,
|
Login,
|
||||||
@ -284,6 +408,10 @@ module.exports = {
|
|||||||
ListBuyOrders,
|
ListBuyOrders,
|
||||||
ListTransactions,
|
ListTransactions,
|
||||||
Account,
|
Account,
|
||||||
|
DepositFLO,
|
||||||
|
WithdrawFLO,
|
||||||
|
DepositRupee,
|
||||||
|
WithdrawRupee,
|
||||||
set DB(db) {
|
set DB(db) {
|
||||||
DB = db;
|
DB = db;
|
||||||
market.DB = db;
|
market.DB = db;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user