From a7c96e9ce255f1fae74afd8ee4287d3cc9650710 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Thu, 25 Nov 2021 05:10:39 +0530 Subject: [PATCH] Priority case: FLO brought from outside FLO brought from outside the source will be processed after those brought from source-market. --- src/group.js | 102 ++++++++++++++++++++++++++++++-------------------- src/market.js | 94 +++++++++++++++++++++------------------------- 2 files changed, 105 insertions(+), 91 deletions(-) diff --git a/src/group.js b/src/group.js index be09158..f6da65f 100644 --- a/src/group.js +++ b/src/group.js @@ -43,25 +43,35 @@ const bestPair = function(cur_rate, tags_buy, tags_sell) { Promise.all([getBuyOrder(), getSellOrder()]).then(results => { resolve({ buyOrder: results[0], - sellOrder: results[1] + sellOrder: results[1], + null_base: getSellOrder.cache.mode_null }) }).catch(error => reject(error)) }); - this.next = () => { + this.next = (tx_quantity, incomplete_sell, flag_sell) => { let buy = getBuyOrder.cache, sell = getSellOrder.cache; if (buy.cur_order && sell.cur_order) { - if (buy.cur_order.quantity > sell.cur_order.quantity) { - buy.cur_order.quantity -= sell.cur_order.quantity; - sell.cur_order = null; - } else if (buy.cur_order.quantity < sell.cur_order.quantity) { - sell.cur_order.quantity -= buy.cur_order.quantity; + //buy order + if (tx_quantity < buy.cur_order.quantity) + buy.cur_order.quantity -= tx_quantity; + else if (tx_quantity == buy.cur_order.quantity) buy.cur_order = null; - } else { + else + throw Error("Tx quantity cannot be more than order quantity"); + //sell order + if (tx_quantity < sell.cur_order.quantity) { + sell.cur_order.quantity -= tx_quantity; + if (incomplete_sell) { + if (!sell.mode_null && flag_sell) + sell.null_queue.push(sell.cur_order); + sell.cur_order = null; + } + } else if (tx_quantity == sell.cur_order.quantity) sell.cur_order = null; - buy.cur_order = null; - } + else + throw Error("Tx quantity cannot be more than order quantity"); } else throw Error("No current order found"); }; @@ -69,7 +79,7 @@ const bestPair = function(cur_rate, tags_buy, tags_sell) { const getSellOrder = () => new Promise((resolve, reject) => { let cache = getSellOrder.cache; if (cache.cur_order) { //If cache already has a pending order - verifySellOrder(cache.cur_order, currentRate).then(result => { + verifySellOrder(cache.cur_order, currentRate, cache.mode_null).then(result => { cache.cur_order = result; resolve(result); }).catch(error => { @@ -82,7 +92,7 @@ const bestPair = function(cur_rate, tags_buy, tags_sell) { .catch(error => reject(error)) }) } else if (cache.orders && cache.orders.length) { //If cache already has orders in priority - getTopValidSellOrder(cache.orders, currentRate).then(result => { + getTopValidSellOrder(cache.orders, currentRate, cache.mode_null).then(result => { cache.cur_order = result; resolve(result); }).catch(error => { @@ -111,11 +121,20 @@ const bestPair = function(cur_rate, tags_buy, tags_sell) { .then(result => resolve(result)) .catch(error => reject(error)) }).catch(error => reject(error)); + } else if (!mode_null) { //Lowest priority Coins (FLO Brought from other sources) + cache.orders = cache.null_queue.reverse(); //Reverse it so that we can pop the highest priority + cache.mode_null = true; + cache.null_queue = null; + getSellOrder() + .then(result => resolve(result)) + .catch(error => reject(error)) } else reject(false); }); getSellOrder.cache = { - tags: tags_sell + tags: tags_sell, + null_queue: [], + mode_null: false }; const getBuyOrder = () => new Promise((resolve, reject) => { @@ -258,53 +277,56 @@ function fetch_api(api, id) { }) } -function getTopValidSellOrder(orders, cur_price) { +function getTopValidSellOrder(orders, cur_price, mode_null) { return new Promise((resolve, reject) => { if (!orders.length) return reject(false) - verifySellOrder(orders.pop(), cur_price) //pop: as the orders are sorted in ascending (highest point should be checked 1st) + verifySellOrder(orders.pop(), cur_price, mode_null) //pop: as the orders are sorted in ascending (highest point should be checked 1st) .then(result => resolve(result)) .catch(error => { if (error !== false) return reject(error); - getTopValidSellOrder(orders, cur_price) + getTopValidSellOrder(orders, cur_price, mode_null) .then(result => resolve(result)) .catch(error => reject(error)); }); }); } -function verifySellOrder(sellOrder, cur_price) { +function verifySellOrder(sellOrder, cur_price, mode_null) { return new Promise((resolve, reject) => { - DB.query("SELECT id, quantity, base FROM Vault WHERE floID=? ORDER BY base", [sellOrder.floID]).then(result => { - let rem = sellOrder.quantity, - sell_base = 0, - base_quantity = 0; - for (let i = 0; i < result.length && rem > 0; i++) { - if (rem < result[i].quantity) { - if (result[i].base) { + if (!mode_null) + DB.query("SELECT quantity, base FROM Vault WHERE floID=? AND base IS NOT NULL ORDER BY base", [sellOrder.floID]).then(result => { + let rem = sellOrder.quantity, + sell_base = 0, + base_quantity = 0; + for (let i = 0; i < result.length && rem > 0; i++) { + if (rem < result[i].quantity) { sell_base += (rem * result[i].base); base_quantity += rem; - } - rem = 0; - } else { - if (result[i].base) { + rem = 0; + } else { sell_base += (result[i].quantity * result[i].base); base_quantity += result[i].quantity; + rem -= result[i].quantity; } - rem -= result[i].quantity; } - } - if (base_quantity) - sell_base = sell_base / base_quantity; - if (rem > 0 || sell_base > cur_price) { - //1st condition (rem>0) should not happen (sell order placement was success when insufficient FLO). - if (rem > 0) - console.warn(`Sell order ${sellOrder.id} is active, but FLO is insufficient`); - reject(false); - } else - resolve(sellOrder); - }).catch(error => reject(error)); + if (base_quantity) + sell_base = sell_base / base_quantity; + if (sell_base > cur_price) + reject(false); + else + resolve(sellOrder); + }).catch(error => reject(error)); + else if (mode_null) + DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [sellOrder.floID]).then(result => { + if (result.total < sellOrder.quantity) + console.warn(`Sell Order ${sellOrder.id} was made without enough FLO. This should not happen`); + if (result.total > 0) + resolve(sellOrder); + else + reject(false); + }).catch(error => reject(error)) }) } diff --git a/src/market.js b/src/market.js index eb40b4c..3044b70 100644 --- a/src/market.js +++ b/src/market.js @@ -177,24 +177,20 @@ function initiateCoupling() { } function processCoupling(bestPairQueue) { - bestPairQueue.get().then(result => { - let buyer_best = result.buyOrder, - seller_best = result.sellOrder; + bestPairQueue.get().then(pair_result => { + let buyer_best = pair_result.buyOrder, + seller_best = pair_result.sellOrder; console.debug("Sell:", seller_best); console.debug("Buy:", buyer_best); - spendFLO(buyer_best, seller_best).then(txQueries => { - //process the Txn - var tx_quantity; - if (seller_best.quantity > buyer_best.quantity) - tx_quantity = processBuyOrder(seller_best, buyer_best, txQueries); - else if (seller_best.quantity < buyer_best.quantity) - tx_quantity = processSellOrder(seller_best, buyer_best, txQueries); - else - tx_quantity = processBuyAndSellOrder(seller_best, buyer_best, txQueries); + spendFLO(buyer_best, seller_best, pair_result.null_base).then(spend_result => { + let tx_quantity = spend_result.quantity, + txQueries = spend_result.txQueries, + clear_sell = spend_result.incomplete && !spend_result.flag_baseNull; //clear_sell can be true only if an order is placed without enough FLO + processOrders(seller_best, buyer_best, txQueries, tx_quantity, clear_sell); updateBalance(seller_best, buyer_best, txQueries, bestPairQueue.cur_rate, tx_quantity); //process txn query in SQL - DB.transaction(txQueries).then(results => { - bestPairQueue.next(); + DB.transaction(txQueries).then(_ => { + bestPairQueue.next(quantity, spend_result.incomplete, spend_result.flag_baseNull); console.log(`Transaction was successful! BuyOrder:${buyer_best.id}| SellOrder:${seller_best.id}`); //Since a tx was successful, match again processCoupling(bestPairQueue); @@ -208,49 +204,46 @@ function processCoupling(bestPairQueue) { }); } -function spendFLO(buyOrder, sellOrder) { +function spendFLO(buyOrder, sellOrder, null_base) { return new Promise((resolve, reject) => { DB.query("SELECT id, quantity, base FROM Vault WHERE floID=? ORDER BY base", [sellOrder.floID]).then(result => { let rem = Math.min(buyOrder.quantity, sellOrder.quantity), - txQueries = []; - for (let i = 0; i < result.length && rem > 0; i++) { - if (rem < result[i].quantity) { - txQueries.push(["UPDATE Vault SET quantity=quantity-? WHERE id=?", [rem, result[i].id]]); - rem = 0; - } else { - txQueries.push(["DELETE FROM Vault WHERE id=?", [result[i].id]]); - rem -= result[i].quantity; - } - } - if (rem > 0) - reject(`Sell order ${sellOrder.id} is active, but FLO is insufficient`); - else - resolve(txQueries); + txQueries = [] + flag_baseNull = false; + for (let i = 0; i < result.length && rem > 0; i++) + if (result[i].base || null_base) { + if (rem < result[i].quantity) { + txQueries.push(["UPDATE Vault SET quantity=quantity-? WHERE id=?", [rem, result[i].id]]); + rem = 0; + } else { + txQueries.push(["DELETE FROM Vault WHERE id=?", [result[i].id]]); + rem -= result[i].quantity; + } + } else + flag_baseNull = true; + resolve({ + quantity: Math.min(buyOrder.quantity, sellOrder.quantity) - rem, + txQueries, + incomplete: rem > 0, + flag_baseNull + }); }).catch(error => reject(error)); }) } -function processBuyOrder(seller_best, buyer_best, txQueries) { - let quantity = buyer_best.quantity; - //Buy order is completed, sell order is partially done. - txQueries.push(["DELETE FROM BuyOrder WHERE id=?", [buyer_best.id]]); - txQueries.push(["UPDATE SellOrder SET quantity=quantity-? WHERE id=?", [quantity, seller_best.id]]); - return quantity; -} - -function processSellOrder(seller_best, buyer_best, txQueries) { - let quantity = seller_best.quantity; - //Sell order is completed, buy order is partially done. - txQueries.push(["DELETE FROM SellOrder WHERE id=?", [seller_best.id]]); - txQueries.push(["UPDATE BuyOrder SET quantity=quantity-? WHERE id=?", [quantity, buyer_best.id]]); - return quantity; -} - -function processBuyAndSellOrder(seller_best, buyer_best, txQueries) { - //Both sell order and buy order is completed - txQueries.push(["DELETE FROM SellOrder WHERE id=?", [seller_best.id]]); - txQueries.push(["DELETE FROM BuyOrder WHERE id=?", [buyer_best.id]]); - return seller_best.quantity; +function processOrders(seller_best, buyer_best, txQueries, quantity, clear_sell) { + if (quantity > buyer_best.quantity || quantity > seller_best.quantity) + throw Error("Tx quantity cannot be more than order quantity"); + //Process Buy Order + if (quantity == buyer_best.quantity) + txQueries.push(["DELETE FROM BuyOrder WHERE id=?", [buyer_best.id]]); + else + txQueries.push(["UPDATE BuyOrder SET quantity=quantity-? WHERE id=?", [quantity, buyer_best.id]]); + //Process Sell Order + if (quantity == seller_best.quantity || clear_sell) + txQueries.push(["DELETE FROM SellOrder WHERE id=?", [seller_best.id]]); + else + txQueries.push(["UPDATE SellOrder SET quantity=quantity-? WHERE id=?", [quantity, seller_best.id]]); } function updateBalance(seller_best, buyer_best, txQueries, cur_price, quantity) { @@ -262,7 +255,6 @@ function updateBalance(seller_best, buyer_best, txQueries, cur_price, quantity) txQueries.push(["INSERT INTO Vault(floID, base, quantity) VALUES (?, ?, ?)", [buyer_best.floID, cur_price, quantity]]) //Record transaction txQueries.push(["INSERT INTO Transactions (seller, buyer, quantity, unitValue) VALUES (?, ?, ?, ?)", [seller_best.floID, buyer_best.floID, quantity, cur_price]]); - return; } function getAccountDetails(floID) {