From c75f89ea28939dc4fd2fbd176d6e713ef941aab0 Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Sat, 31 Jul 2021 17:20:20 +0500 Subject: [PATCH 1/8] init, addPair & addGlobalPair --- contracts/dmarket.js | 135 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 contracts/dmarket.js diff --git a/contracts/dmarket.js b/contracts/dmarket.js new file mode 100644 index 00000000..b8442b9c --- /dev/null +++ b/contracts/dmarket.js @@ -0,0 +1,135 @@ +/* eslint-disable no-await-in-loop */ +/* global actions, api */ + +const UTILITY_TOKEN_SYMBOL = 'BEE'; + +const CONTRACT_NAME = 'dmarket'; + +actions.createSSC = async () => { + const tableExists = await api.db.tableExists('params'); + + if (tableExists === false) { + await api.db.createTable('params'); + await api.db.createTable('pairs', ['symbol', 'precision', 'allowedSymbols']); + await api.db.createTable('buyBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); + await api.db.createTable('sellBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); + await api.db.createTable('tradesHistory', ['symbol', 'pair']); + await api.db.createTable('metrics', ['symbol', 'pair']); + + const params = {}; + params.pairCreationFee = '500'; + + // default global pairs + const pairs = [ + { + pair: 'BEE', + precision: 8, + allowedSymbols: true // true - all tokens are allowed + }, + { + pair: 'SWAP.BTC', + precision: 8, + allowedSymbols: true // true - all tokens are allowed + }, + ]; + + await api.db.insert('params', params); + + for (let i = 0; i < pairs.length; i++) { + await api.db.insert('pairs', pairs[i]); + } + } +}; + +actions.updateParams = async (payload) => { + if (api.assert(api.sender === api.owner, 'not authorized')) { + const { + pairCreationFee, + } = payload; + + const params = await api.db.findOne('params', {}); + + if (pairCreationFee) { + if (!api.assert(typeof pairCreationFee === 'string' && api.BigNumber(pairCreationFee).isFinite() && api.BigNumber(pairCreationFee).gte(0), 'invalid pairCreationFee')) return; + params.pairCreationFee = pairCreationFee; + } + + await api.db.update('params', params); + } +}; + +actions.addPair = async (payload) => { + const { + pair, + symbol, + isSignedWithActiveKey, + } = payload; + + if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; + if (!api.assert(symbol && typeof symbol === 'string', 'invalid symbol')) return; + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + if (!api.assert(token, 'symbol does not exist')) return; + + const pair = await api.db.findOne('pairs', { pair }); + + if (pair) { + if (api.assert(pair.allowedSymbols.indexOf(symbol) === -1, 'symbol is already in the pair')) { + pair.allowedSymbols.push(symbol); + } + + // add the new symbol in the pair + await api.db.update('pairs', pair); + } else { + const pairToken = await api.db.findOneInTable('tokens', 'tokens', { pair }); + if (!api.assert(pairToken, 'pair symbol does not exist')) return; + + // add new pair in the db + await api.db.insert('pairs', { + pair, + precision: pairToken.precision, + allowedSymbols: [symbol], + }); + } + + api.emit('addPair', { + pair, + symbol, + }); +}; + +actions.addGlobalPair = async (payload) => { + if (api.assert(api.sender === api.owner, 'not authorized')) { + const { + pair, + } = payload; + + if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; + + const pair = await api.db.findOne('pairs', { pair }); + + if (pair) { + if (api.assert(pair.allowedSymbols === true, 'pair is already global')) { + pair.allowedSymbols = true; + } + + // update the pair as global + await api.db.update('pairs', pair); + } else { + const pairToken = await api.db.findOneInTable('tokens', 'tokens', { pair }); + if (!api.assert(pairToken, 'pair symbol does not exist')) return; + + // add new global pair + await api.db.insert('pairs', { + pair, + precision: pairToken.precision, + allowedSymbols: true, + }); + } + + api.emit('addGlobalPair', { + pair, + }); + } +}; From dd53a2945d462e659e8e0bba855571397fb0daac Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Mon, 2 Aug 2021 17:08:06 +0500 Subject: [PATCH 2/8] buy, sell and utility functions --- contracts/dmarket.js | 893 ++++++++++++++++++++++++++++++++++++++++--- test/dmarket.js | 393 +++++++++++++++++++ 2 files changed, 1227 insertions(+), 59 deletions(-) create mode 100644 test/dmarket.js diff --git a/contracts/dmarket.js b/contracts/dmarket.js index b8442b9c..ada56aa9 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -3,61 +3,59 @@ const UTILITY_TOKEN_SYMBOL = 'BEE'; +// 30 days in seconds +const MAX_EXPIRATION_SECS = 2592000; +// fee in UTILITY TOKEN required to create a new pair +const PAIR_CREATION_FEE = '500'; + const CONTRACT_NAME = 'dmarket'; actions.createSSC = async () => { - const tableExists = await api.db.tableExists('params'); + const tableExists = await api.db.tableExists('pairs'); if (tableExists === false) { - await api.db.createTable('params'); await api.db.createTable('pairs', ['symbol', 'precision', 'allowedSymbols']); await api.db.createTable('buyBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); await api.db.createTable('sellBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); await api.db.createTable('tradesHistory', ['symbol', 'pair']); await api.db.createTable('metrics', ['symbol', 'pair']); - const params = {}; - params.pairCreationFee = '500'; - // default global pairs const pairs = [ { pair: 'BEE', precision: 8, - allowedSymbols: true // true - all tokens are allowed + allowedSymbols: true, // true - all tokens are allowed }, { pair: 'SWAP.BTC', precision: 8, - allowedSymbols: true // true - all tokens are allowed + allowedSymbols: true, // true - all tokens are allowed }, ]; - await api.db.insert('params', params); - - for (let i = 0; i < pairs.length; i++) { + for (let i = 0; i < pairs.length; i += 1) { await api.db.insert('pairs', pairs[i]); } } }; -actions.updateParams = async (payload) => { - if (api.assert(api.sender === api.owner, 'not authorized')) { - const { - pairCreationFee, - } = payload; - - const params = await api.db.findOne('params', {}); - - if (pairCreationFee) { - if (!api.assert(typeof pairCreationFee === 'string' && api.BigNumber(pairCreationFee).isFinite() && api.BigNumber(pairCreationFee).gte(0), 'invalid pairCreationFee')) return; - params.pairCreationFee = pairCreationFee; - } - - await api.db.update('params', params); +const transferIsSuccessful = (result, action, from, to, symbol, quantity) => { + if (result.errors === undefined + && result.events && result.events.find(el => el.contract === 'tokens' + && el.event === action + && el.data.from === from + && el.data.to === to + && api.BigNumber(el.data.quantity).eq(quantity) + && el.data.symbol === symbol) !== undefined) { + return true; } + + return false; }; +const countDecimals = value => api.BigNumber(value).dp(); + actions.addPair = async (payload) => { const { pair, @@ -65,38 +63,59 @@ actions.addPair = async (payload) => { isSignedWithActiveKey, } = payload; + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; if (!api.assert(symbol && typeof symbol === 'string', 'invalid symbol')) return; - if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + if (!api.assert(pair !== symbol, 'pair and symbol can not be the same')) return; const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); if (!api.assert(token, 'symbol does not exist')) return; - const pair = await api.db.findOne('pairs', { pair }); + const pairToken = await api.db.findOneInTable('tokens', 'tokens', { symbol: pair }); + if (!api.assert(pairToken, 'pair symbol does not exist')) return; - if (pair) { - if (api.assert(pair.allowedSymbols.indexOf(symbol) === -1, 'symbol is already in the pair')) { - pair.allowedSymbols.push(symbol); - } + const pairInDb = await api.db.findOne('pairs', { pair }); + const pairUpdate = pairInDb || { + pair, + precision: pairToken.precision, + allowedSymbols: [], + }; - // add the new symbol in the pair - await api.db.update('pairs', pair); - } else { - const pairToken = await api.db.findOneInTable('tokens', 'tokens', { pair }); - if (!api.assert(pairToken, 'pair symbol does not exist')) return; + if (api.assert(pairUpdate.allowedSymbols !== true, 'can not add symbol to a global pair') + && api.assert(pairUpdate.allowedSymbols.indexOf(symbol) === -1, 'symbol is already in the pair')) { + pairUpdate.allowedSymbols.push(symbol); + } - // add new pair in the db - await api.db.insert('pairs', { - pair, - precision: pairToken.precision, - allowedSymbols: [symbol], + const utilityToken = await api.db.findOneInTable('tokens', 'balances', { account: api.sender, symbol: UTILITY_TOKEN_SYMBOL }); + + if (api.assert(utilityToken && utilityToken.balance + && api.BigNumber(utilityToken.balance).gte(PAIR_CREATION_FEE), 'you must have enough tokens to cover the pair creation fee')) { + // transfer fee for pair creation + const feeTransfer = await api.executeSmartContract('tokens', 'transfer', { + to: 'null', symbol: UTILITY_TOKEN_SYMBOL, quantity: PAIR_CREATION_FEE, isSignedWithActiveKey, }); - } - api.emit('addPair', { - pair, - symbol, - }); + // make sure fee is transfered successfully + if (!api.assert(transferIsSuccessful(feeTransfer, + 'transfer', + api.sender, + 'null', + UTILITY_TOKEN_SYMBOL, + PAIR_CREATION_FEE), 'failed to transfer creation fee')) return; + + if (pairInDb) { + // add the new symbol in the pair + await api.db.update('pairs', pairUpdate); + } else { + // add new pair in the db + await api.db.insert('pairs', pairUpdate); + } + + api.emit('addPair', { + pair, + symbol, + }); + } }; actions.addGlobalPair = async (payload) => { @@ -107,25 +126,26 @@ actions.addGlobalPair = async (payload) => { if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; - const pair = await api.db.findOne('pairs', { pair }); + const pairToken = await api.db.findOneInTable('tokens', 'tokens', { symbol: pair }); + if (!api.assert(pairToken, 'pair symbol does not exist')) return; - if (pair) { - if (api.assert(pair.allowedSymbols === true, 'pair is already global')) { - pair.allowedSymbols = true; - } + const pairInDb = await api.db.findOne('pairs', { pair }); + const pairUpdate = pairInDb || { + pair, + precision: pairToken.precision, + allowedSymbols: [], + }; + + if (api.assert(pairUpdate.allowedSymbols !== true, 'pair is already global')) { + pairUpdate.allowedSymbols = true; + } + if (pairInDb) { // update the pair as global - await api.db.update('pairs', pair); + await api.db.update('pairs', pairUpdate); } else { - const pairToken = await api.db.findOneInTable('tokens', 'tokens', { pair }); - if (!api.assert(pairToken, 'pair symbol does not exist')) return; - // add new global pair - await api.db.insert('pairs', { - pair, - precision: pairToken.precision, - allowedSymbols: true, - }); + await api.db.insert('pairs', pairUpdate); } api.emit('addGlobalPair', { @@ -133,3 +153,758 @@ actions.addGlobalPair = async (payload) => { }); } }; + +const getMetric = async (symbol, pair) => { + let metric = await api.db.findOne('metrics', { symbol, pair }); + + if (metric === null) { + metric = {}; + metric.symbol = symbol; + metric.pair = pair; + metric.volume = '0'; + metric.volumeExpiration = 0; + metric.lastPrice = '0'; + metric.lowestAsk = '0'; + metric.highestBid = '0'; + metric.lastDayPrice = '0'; + metric.lastDayPriceExpiration = 0; + metric.priceChangePair = '0'; + metric.priceChangePercent = '0'; + + const newMetric = await api.db.insert('metrics', metric); + return newMetric; + } + + return metric; +}; + +const updateVolumeMetric = async (symbol, pair, pairPrecision, quantity, add = true) => { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + const metric = await getMetric(symbol, pair); + + if (add === true) { + if (metric.volumeExpiration < timestampSec) { + metric.volume = '0.000'; + } + metric.volume = api.BigNumber(metric.volume) + .plus(quantity) + .toFixed(pairPrecision); + metric.volumeExpiration = blockDate.setUTCDate(blockDate.getUTCDate() + 1) / 1000; + } else { + metric.volume = api.BigNumber(metric.volume) + .minus(quantity) + .toFixed(pairPrecision); + } + + if (api.BigNumber(metric.volume).lt(0)) { + metric.volume = '0.000'; + } + + await api.db.update('metrics', metric); +}; + +const updatePriceMetrics = async (symbol, pair, price) => { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + + const metric = await getMetric(symbol, pair); + + metric.lastPrice = price; + + if (metric.lastDayPriceExpiration < timestampSec) { + metric.lastDayPrice = price; + metric.lastDayPriceExpiration = blockDate.setUTCDate(blockDate.getUTCDate() + 1) / 1000; + metric.priceChangePair = '0'; + metric.priceChangePercent = '0%'; + } else { + metric.priceChangePair = api.BigNumber(price) + .minus(metric.lastDayPrice) + .toFixed(pairPrecision); + metric.priceChangePercent = `${api.BigNumber(metric.priceChangePair).dividedBy(metric.lastDayPrice).multipliedBy(100).toFixed(2)}%`; + } + + await api.db.update('metrics', metric); +}; + +const updateBidMetric = async (symbol, pair) => { + const metric = await getMetric(symbol, pair); + + const buyOrderBook = await api.db.find('buyBook', + { + symbol, + pair, + }, 1, 0, + [ + { index: 'priceDec', descending: true }, + ]); + + + if (buyOrderBook.length > 0) { + metric.highestBid = buyOrderBook[0].price; + } else { + metric.highestBid = '0'; + } + + await api.db.update('metrics', metric); +}; + +const updateAskMetric = async (symbol, pair) => { + const metric = await getMetric(symbol, pair); + + const sellOrderBook = await api.db.find('sellBook', + { + symbol, + pair, + }, 1, 0, + [ + { index: 'priceDec', descending: false }, + ]); + + if (sellOrderBook.length > 0) { + metric.lowestAsk = sellOrderBook[0].price; + } else { + metric.lowestAsk = '0'; + } + + await api.db.update('metrics', metric); +}; + +const updateTradesHistory = async (type, buyer, seller, symbol, pair, pairPrecision, quantity, price, volume, buyTxId, sellTxId) => { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + const timestampMinus24hrs = blockDate.setUTCDate(blockDate.getUTCDate() - 1) / 1000; + + // clean history + let tradesToDelete = await api.db.find( + 'tradesHistory', + { + symbol, + pair, + timestamp: { + $lt: timestampMinus24hrs, + }, + }, + ); + let nbTradesToDelete = tradesToDelete.length; + + while (nbTradesToDelete > 0) { + for (let index = 0; index < nbTradesToDelete; index += 1) { + const trade = tradesToDelete[index]; + await updateVolumeMetric(trade.symbol, trade.pair, pairPrecision, trade.volume, false); + await api.db.remove('tradesHistory', trade); + } + tradesToDelete = await api.db.find( + 'tradesHistory', + { + symbol, + pair, + timestamp: { + $lt: timestampMinus24hrs, + }, + }, + ); + nbTradesToDelete = tradesToDelete.length; + } + + // add order to the history + const newTrade = {}; + newTrade.type = type; + newTrade.buyer = buyer; + newTrade.seller = seller; + newTrade.symbol = symbol; + newTrade.pair = pair; + newTrade.quantity = quantity; + newTrade.price = price; + newTrade.timestamp = timestampSec; + newTrade.volume = volume; + newTrade.buyTxId = buyTxId; + newTrade.sellTxId = sellTxId; + await api.db.insert('tradesHistory', newTrade); + await updatePriceMetrics(symbol, pair, price); +}; + +const removeExpiredOrders = async (table) => { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + + // clean orders + let nbOrdersToDelete = 0; + let ordersToDelete = await api.db.find( + table, + { + expiration: { + $lte: timestampSec, + }, + }, + ); + + nbOrdersToDelete = ordersToDelete.length; + while (nbOrdersToDelete > 0) { + for (let index = 0; index < nbOrdersToDelete; index += 1) { + const order = ordersToDelete[index]; + let quantity; + let symbol; + + if (table === 'buyBook') { + symbol = order.pair; + quantity = order.tokensLocked; + } else { + // eslint-disable-next-line prefer-destructuring + symbol = order.symbol; + // eslint-disable-next-line prefer-destructuring + quantity = order.quantity; + } + + // unlock tokens + await api.transferTokens(order.account, symbol, quantity, 'user'); + + await api.db.remove(table, order); + + if (table === 'buyBook') { + api.emit('orderExpired', { type: 'buy', txId: order.txId }); + + await updateAskMetric(order.symbol); + } else { + api.emit('orderExpired', { type: 'sell', txId: order.txId }); + + await updateBidMetric(order.symbol); + } + } + + ordersToDelete = await api.db.find( + table, + { + expiration: { + $lte: timestampSec, + }, + }, + ); + + nbOrdersToDelete = ordersToDelete.length; + } +}; + +const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { + const { + account, + symbol, + pair, + priceDec, + } = order; + + const buyOrder = order; + let offset = 0; + let volumeTraded = 0; + + await removeExpiredOrders('sellBook'); + + // get the orders that match the symbol and the price + let sellOrderBook = await api.db.find('sellBook', { + symbol, + pair, + priceDec: { + $lte: priceDec, + }, + }, 1000, offset, + [ + { index: 'priceDec', descending: false }, + { index: '_id', descending: false }, + ]); + + do { + const nbOrders = sellOrderBook.length; + let inc = 0; + + while (inc < nbOrders && api.BigNumber(buyOrder.quantity).gt(0)) { + const sellOrder = sellOrderBook[inc]; + if (api.BigNumber(buyOrder.quantity).lte(sellOrder.quantity)) { + let qtyTokensToSend = api.BigNumber(sellOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairPrecision); + + if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { + qtyTokensToSend = api.BigNumber(sellOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(buyOrder.quantity).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(account, symbol, buyOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(sellOrder.account, pair, qtyTokensToSend, 'user'); + + // update the sell order + const qtyLeftSellOrder = api.BigNumber(sellOrder.quantity) + .minus(buyOrder.quantity) + .toFixed(tokenPrecision); + const nbTokensToFillOrder = api.BigNumber(sellOrder.price) + .multipliedBy(qtyLeftSellOrder) + .toFixed(pairPrecision); + + if (api.BigNumber(qtyLeftSellOrder).gt(0) + && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + sellOrder.quantity = qtyLeftSellOrder; + + await api.db.update('sellBook', sellOrder); + } else { + if (api.BigNumber(qtyLeftSellOrder).gt(0)) { + // transfer the leftover tokens to the seller since the order can no longer be filled + await api.transferTokens(sellOrder.account, symbol, qtyLeftSellOrder, 'user'); + } + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + await api.db.remove('sellBook', sellOrder); + } + + // unlock remaining tokens, update the quantity to get and remove the buy order + const tokensToUnlock = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairPrecision); + + if (api.BigNumber(tokensToUnlock).gt(0)) { + // transfer any dust tokens remaining to buyer + await api.transferTokens(account, pair, tokensToUnlock, 'user'); + } + + // add the trade to the history + await updateTradesHistory('buy', account, sellOrder.account, symbol, pair, pairPrecision, buyOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + + // set quantity to zero to stop the loop + buyOrder.quantity = '0'; + await api.db.remove('buyBook', buyOrder); + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + } + } else { + let qtyTokensToSend = api.BigNumber(sellOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairPrecision); + + if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { + qtyTokensToSend = api.BigNumber(sellOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(buyOrder.quantity).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(sellOrder.account, pair, qtyTokensToSend, 'user'); + + // remove the sell order + await api.db.remove('sellBook', sellOrder); + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + + // update tokensLocked and the quantity to get + buyOrder.tokensLocked = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairPrecision); + buyOrder.quantity = api.BigNumber(buyOrder.quantity) + .minus(sellOrder.quantity) + .toFixed(tokenPrecision); + + // check if the order can still be filled + const nbTokensToFillOrder = api.BigNumber(buyOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairPrecision); + + if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { + await api.transferTokens(account, pair, buyOrder.tokensLocked, 'user'); + + // stop the loop and remove buy order if it can not be filled + buyOrder.quantity = '0'; + await api.db.remove('buyBook', buyOrder); + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + } + + // add the trade to the history + await updateTradesHistory('buy', account, sellOrder.account, symbol, pair, pairPrecision, sellOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + } + } + + inc += 1; + } + + offset += 1000; + + if (api.BigNumber(buyOrder.quantity).gt(0)) { + // get the orders that match the symbol and the price + sellOrderBook = await api.db.find('sellBook', { + symbol, + pair, + priceDec: { + $lte: priceDec, + }, + }, 1000, offset, + [ + { index: 'priceDec', descending: false }, + { index: '_id', descending: false }, + ]); + } + } while (sellOrderBook.length > 0 && api.BigNumber(buyOrder.quantity).gt(0)); + + // update the buy order if partially filled + if (api.BigNumber(buyOrder.quantity).gt(0)) { + await api.db.update('buyBook', buyOrder); + } + + // update metrics + if (api.BigNumber(volumeTraded).gt(0)) { + await updateVolumeMetric(symbol, pair, pairPrecision, volumeTraded); + } + await updateAskMetric(symbol, pair); + await updateBidMetric(symbol, pair); +}; + +const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { + const { + account, + symbol, + pair, + priceDec, + } = order; + + const sellOrder = order; + let offset = 0; + let volumeTraded = 0; + + await removeExpiredOrders('buyBook'); + + // get the orders that match the symbol and the price + let buyOrderBook = await api.db.find('buyBook', { + symbol, + pair, + priceDec: { + $gte: priceDec, + }, + }, 1000, offset, + [ + { index: 'priceDec', descending: true }, + { index: '_id', descending: false }, + ]); + + do { + const nbOrders = buyOrderBook.length; + let inc = 0; + + while (inc < nbOrders && api.BigNumber(sellOrder.quantity).gt(0)) { + const buyOrder = buyOrderBook[inc]; + if (api.BigNumber(sellOrder.quantity).lte(buyOrder.quantity)) { + let qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairPrecision); + + if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { + qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(sellOrder.quantity).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(buyOrder.account, symbol, sellOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(account, pair, qtyTokensToSend, 'user'); + + // update the buy order + const qtyLeftBuyOrder = api.BigNumber(buyOrder.quantity) + .minus(sellOrder.quantity) + .toFixed(tokenPrecision); + + const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairPrecision); + const nbTokensToFillOrder = api.BigNumber(buyOrder.price) + .multipliedBy(qtyLeftBuyOrder) + .toFixed(pairPrecision); + + if (api.BigNumber(qtyLeftBuyOrder).gt(0) + && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + buyOrder.quantity = qtyLeftBuyOrder; + buyOrder.tokensLocked = buyOrdertokensLocked; + + await api.db.update('buyBook', buyOrder); + } else { + if (api.BigNumber(buyOrdertokensLocked).gt(0)) { + // transfer the leftover tokens to the buyer since the order can no longer be filled + await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + } + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + await api.db.remove('buyBook', buyOrder); + } + + // add the trade to the history + await updateTradesHistory('sell', buyOrder.account, account, symbol, pair, pairPrecision, sellOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + + // set quantity to zero to stop the loop + sellOrder.quantity = 0; + await api.db.remove('sellBook', sellOrder); + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + } + } else { + let qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairPrecision); + + if (qtyTokensToSend > buyOrder.tokensLocked) { + qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(sellOrder.quantity).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(buyOrder.account, symbol, buyOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(account, pair, qtyTokensToSend, 'user'); + + const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairPrecision); + + if (api.BigNumber(buyOrdertokensLocked).gt(0)) { + // transfer any dust tokens remaining to buyer + await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + } + + // remove the buy order + await api.db.remove('buyBook', buyOrder); + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + + // update the quantity to get + sellOrder.quantity = api.BigNumber(sellOrder.quantity) + .minus(buyOrder.quantity) + .toFixed(tokenPrecision); + + // check if the order can still be filled + const nbTokensToFillOrder = api.BigNumber(sellOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairPrecision); + + if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { + await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); + + // stop the loop and remove sell order if it can not be filled + sellOrder.quantity = '0'; + await api.db.remove('sellBook', sellOrder); + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + } + + // add the trade to the history + await updateTradesHistory('sell', buyOrder.account, account, symbol, pair, pairPrecision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + } + } + + inc += 1; + } + + offset += 1000; + + if (api.BigNumber(sellOrder.quantity).gt(0)) { + // get the orders that match the symbol and the price + buyOrderBook = await api.db.find('buyBook', { + symbol, + pair, + priceDec: { + $gte: priceDec, + }, + }, 1000, offset, + [ + { index: 'priceDec', descending: true }, + { index: '_id', descending: false }, + ]); + } + } while (buyOrderBook.length > 0 && api.BigNumber(sellOrder.quantity).gt(0)); + + // update the sell order if partially filled + if (api.BigNumber(sellOrder.quantity).gt(0)) { + await api.db.update('sellBook', sellOrder); + } + + if (api.BigNumber(volumeTraded).gt(0)) { + await updateVolumeMetric(symbol, pair, pairPrecision, volumeTraded); + } + await updateAskMetric(symbol, pair); + await updateBidMetric(symbol, pair); +}; + +actions.buy = async (payload) => { + const { + account, + txId, + symbol, + pair, + quantity, + price, + expiration, + isSignedWithActiveKey, + } = payload; + + const finalAccount = (account === undefined || api.sender !== 'null') ? api.sender : account; + const finalTxId = (txId === undefined || api.sender !== 'null') ? api.transactionId : txId; + + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + + if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) + && finalTxId && typeof finalTxId === 'string' && finalTxId.length > 0 + && symbol && typeof symbol === 'string' + && pair && typeof pair === 'string' + && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN() + && price && typeof price === 'string' && !api.BigNumber(price).isNaN() + && (expiration === undefined || (expiration && Number.isInteger(expiration) && expiration > 0)), 'invalid params') + ) return; + + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + + if (!api.assert(token, 'symbol does not exist')) return; + if (!api.assert(countDecimals(quantity) <= token.precision, 'invalid quantity')) return; + + const pairInDb = await api.db.findOne('pairs', { pair }); + + // check if symbol is included in allowedSymbols or the pair is global + if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pair.precision, 'invalid price')) return; + + const nbTokensToLock = api.BigNumber(price) + .multipliedBy(quantity) + .toFixed(pairInDb.precision); + + if (api.assert(api.BigNumber(nbTokensToLock).gte('0.00000001'), 'order cannot be placed as it cannot be filled')) { + // lock the tokens in contract for safekeeping + const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { + from: finalAccount, to: CONTRACT_NAME, symbol: pair, quantity: nbTokensToLock, + }); + + // make sure tokens are locked + if (api.assert(transferIsSuccessful(tokenTransfer, + 'transferToContract', + finalAccount, + CONTRACT_NAME, + pair, + nbTokensToLock), 'failed to transfer tokens')) { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + + // order + const order = {}; + + order.txId = finalTxId; + order.timestamp = timestampSec; + order.account = finalAccount; + order.symbol = symbol; + order.pair = pair; + order.quantity = api.BigNumber(quantity).toFixed(token.precision); + order.price = api.BigNumber(price).toFixed(pair.precision); + order.priceDec = { $numberDecimal: order.price }; + order.tokensLocked = nbTokensToLock; + order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS + ? timestampSec + MAX_EXPIRATION_SECS + : timestampSec + expiration; + + const orderInDb = await api.db.insert('buyBook', order); + + await findMatchingSellOrders(orderInDb, token.precision, pairInDb.precision); + } + } +}; + +actions.sell = async (payload) => { + const { + account, + txId, + symbol, + pair, + quantity, + price, + expiration, + isSignedWithActiveKey, + } = payload; + + const finalAccount = (account === undefined || api.sender !== 'null') ? api.sender : account; + const finalTxId = (txId === undefined || api.sender !== 'null') ? api.transactionId : txId; + + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + + if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) + && finalTxId && typeof finalTxId === 'string' && finalTxId.length > 0 + && symbol && typeof symbol === 'string' + && pair && typeof pair === 'string' + && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN() + && price && typeof price === 'string' && !api.BigNumber(price).isNaN() + && (expiration === undefined || (expiration && Number.isInteger(expiration) && expiration > 0)), 'invalid params') + ) return; + + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + + if (!api.assert(token, 'symbol does not exist')) return; + if (!api.assert(countDecimals(quantity) <= token.precision, 'invalid quantity')) return; + + const pairInDb = await api.db.findOne('pairs', { pair }); + + // check if symbol is included in allowedSymbols or the pair is global + if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pair.precision, 'invalid price')) return; + + const nbTokensToFillOrder = api.BigNumber(price) + .multipliedBy(quantity) + .toFixed(pairInDb.precision); + + // check if order can be filled + if (api.assert(api.BigNumber(nbTokensToFillOrder).gte('0.00000001'), 'order cannot be placed as it cannot be filled')) { + // lock the tokens in contract for safekeeping + const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { + from: finalAccount, to: CONTRACT_NAME, symbol, quantity, + }); + + // make sure tokens are locked + if (api.assert(transferIsSuccessful(tokenTransfer, + 'transferToContract', + finalAccount, + CONTRACT_NAME, + symbol, + quantity), 'failed to transfer tokens')) { + const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); + const timestampSec = blockDate.getTime() / 1000; + + // order + const order = {}; + + order.txId = finalTxId; + order.timestamp = timestampSec; + order.account = finalAccount; + order.symbol = symbol; + order.pair = pair; + order.quantity = api.BigNumber(quantity).toFixed(token.precision); + order.price = api.BigNumber(price).toFixed(pair.precision); + order.priceDec = { $numberDecimal: order.price }; + order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS + ? timestampSec + MAX_EXPIRATION_SECS + : timestampSec + expiration; + + const orderInDb = await api.db.insert('sellBook', order); + + await findMatchingBuyOrders(orderInDb, token.precision, pairInDb.precision); + } + } +}; diff --git a/test/dmarket.js b/test/dmarket.js new file mode 100644 index 00000000..ee57cb01 --- /dev/null +++ b/test/dmarket.js @@ -0,0 +1,393 @@ + +/* eslint-disable no-await-in-loop */ +/* eslint-disable no-undef */ +/* eslint-disable no-console */ +/* eslint-disable func-names */ + +const { fork } = require('child_process'); +const assert = require('assert'); +const { MongoClient } = require('mongodb'); + +const { default: BigNumber } = require('bignumber.js'); +const { CONSTANTS } = require('../libs/Constants'); +const { Database } = require('../libs/Database'); +const blockchain = require('../plugins/Blockchain'); +const { Transaction } = require('../libs/Transaction'); +const { setupContractPayload } = require('../libs/util/contractUtil'); + +const conf = { + chainId: 'test-chain-id', + genesisHiveBlock: 2000000, + dataDirectory: './test/data/', + databaseFileName: 'database.db', + autosaveInterval: 0, + javascriptVMTimeout: 10000, + databaseURL: 'mongodb://localhost:27017', + databaseName: 'testssc', + streamNodes: ['https://api.hive.blog'], +}; + +const plugins = {}; +let jobs = new Map(); +let currentJobId = 0; +let database1 = null; + +function send(pluginName, from, message) { + const plugin = plugins[pluginName]; + const newMessage = { + ...message, + to: plugin.name, + from, + type: 'request', + }; + currentJobId += 1; + newMessage.jobId = currentJobId; + plugin.cp.send(newMessage); + return new Promise((resolve) => { + jobs.set(currentJobId, { + message: newMessage, + resolve, + }); + }); +} + + +// function to route the IPC requests +const route = (message) => { + const { to, type, jobId } = message; + if (to) { + if (to === 'MASTER') { + if (type && type === 'request') { + // do something + } else if (type && type === 'response' && jobId) { + const job = jobs.get(jobId); + if (job && job.resolve) { + const { resolve } = job; + jobs.delete(jobId); + resolve(message); + } + } + } else if (type && type === 'broadcast') { + plugins.forEach((plugin) => { + plugin.cp.send(message); + }); + } else if (plugins[to]) { + plugins[to].cp.send(message); + } else { + console.error('ROUTING ERROR: ', message); + } + } +}; + +const loadPlugin = (newPlugin) => { + const plugin = {}; + plugin.name = newPlugin.PLUGIN_NAME; + plugin.cp = fork(newPlugin.PLUGIN_PATH, [], { silent: true }); + plugin.cp.on('message', msg => route(msg)); + plugin.cp.stdout.on('data', data => console.log(`[${newPlugin.PLUGIN_NAME}]`, data.toString())); + plugin.cp.stderr.on('data', data => console.error(`[${newPlugin.PLUGIN_NAME}]`, data.toString())); + + plugins[newPlugin.PLUGIN_NAME] = plugin; + + return send(newPlugin.PLUGIN_NAME, 'MASTER', { action: 'init', payload: conf }); +}; + +const unloadPlugin = (plugin) => { + plugins[plugin.PLUGIN_NAME].cp.kill('SIGINT'); + plugins[plugin.PLUGIN_NAME] = null; + jobs = new Map(); + currentJobId = 0; +}; + +const tknContractPayload = setupContractPayload('tokens', './contracts/tokens.js'); +const dmarketContractPayload = setupContractPayload('dmarket', './contracts/dmarket.js'); + +let txId = 1; +function getNextTxId() { + txId += 1; + return `TXID${txId.toString().padStart(8, '0')}`; +} + +async function assertBalances(accounts, balances, symbol) { + const res = await database1.find({ + contract: 'tokens', + table: 'balances', + query: { + account: { + $in: accounts, + }, + symbol, + }, + }); + + for (let i = 0; i < accounts.length; i += 1) { + const account = accounts[i]; + const { + balance, + } = res.find(el => el.account === account); + const expectedBalance = balances[i]; + + // console.log(expectedBalance, balance, account); + const isEqual = BigNumber(expectedBalance).eq(balance); + assert(isEqual, `expected @${account} balance ${expectedBalance} instead got ${balance}`); + } +} + +async function assertPair(pair, symbols) { + const res = await database1.findOne({ + contract: 'dmarket', + table: 'pairs', + query: { + pair, + }, + }); + + console.log(res); + + assert(res, 'pair not found'); + + if (symbols !== true) { + assert(res.allowedSymbols !== true, 'pair is global'); + symbols.forEach((symbol) => { + assert(res.allowedSymbols.includes(symbol), `symbol ${symbol} not found in pair`); + }); + } else assert(res.allowedSymbols === true, 'pair is not global'); +} + +function assertError(tx, message) { + const logs = JSON.parse(tx.logs); + assert(logs.errors, `No error in logs. Error expected with message ${message}`); + assert.equal(logs.errors[0], message, `Error expected with message ${message}. Instead got ${logs.errors[0]}`); +} + +async function assertNoErrorInLastBlock() { + const { transactions } = await database1.getLatestBlockInfo(); + for (let i = 0; i < transactions.length; i += 1) { + const logs = JSON.parse(transactions[i].logs); + assert(!logs.errors, `Tx #${i} had unexpected error ${logs.errors}`); + } +} + +describe('dMarket Smart Contract', function () { + this.timeout(20000); + + before((done) => { + new Promise(async (resolve) => { + client = await MongoClient.connect(conf.databaseURL, + { useNewUrlParser: true, useUnifiedTopology: true }); + db = await client.db(conf.databaseName); + await db.dropDatabase(); + resolve(); + }) + .then(() => { + done(); + }); + }); + + after((done) => { + new Promise(async (resolve) => { + await client.close(); + resolve(); + }) + .then(() => { + done(); + }); + }); + + beforeEach((done) => { + new Promise(async (resolve) => { + db = await client.db(conf.databaseName); + resolve(); + }) + .then(() => { + done(); + }); + }); + + afterEach((done) => { + // runs after each test in this block + new Promise(async (resolve) => { + await db.dropDatabase(); + resolve(); + }) + .then(() => { + done(); + }); + }); + + it('does not create a new pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": false, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": 5, "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": 5 }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "BEE", "symbol": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + assertError(txs[2], 'you must use a custom_json signed with your active key'); + assertError(txs[3], 'invalid pair'); + assertError(txs[4], 'invalid symbol'); + assertError(txs[5], 'pair and symbol can not be the same'); + assertError(txs[6], 'symbol does not exist'); + assertError(txs[7], 'pair symbol does not exist'); + assertError(txs[10], 'you must have enough tokens to cover the pair creation fee'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('creates a new pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + await assertPair('TKN', ['BEE']); + + await assertBalances(['ali-h'], ['0'], 'BEE'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('does not add symbol into existing pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'dmarket', 'addGlobalPair', '{ "pair": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "XYZ" }')); + + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + await assertPair('TKN', true); + + assertError(txs[6], 'symbol is already in the pair'); + assertError(txs[8], 'can not add symbol to a global pair'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('adds symbol into existing pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"1200", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "XYZ" }')); + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + await assertPair('TKN', ['BEE', 'XYZ']); + + await assertBalances(['ali-h'], ['0'], 'BEE'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); +}); From 5437a0d342a73536941f50661a0a21c9f6b5a420 Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Mon, 20 Sep 2021 16:41:34 +0500 Subject: [PATCH 3/8] marketBuy & test units --- contracts/dmarket.js | 233 ++++++++++++++++-- test/dmarket.js | 561 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 759 insertions(+), 35 deletions(-) diff --git a/contracts/dmarket.js b/contracts/dmarket.js index ada56aa9..5dddc225 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -204,7 +204,7 @@ const updateVolumeMetric = async (symbol, pair, pairPrecision, quantity, add = t await api.db.update('metrics', metric); }; -const updatePriceMetrics = async (symbol, pair, price) => { +const updatePriceMetrics = async (symbol, pair, pairPrecision, price) => { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; @@ -270,11 +270,21 @@ const updateAskMetric = async (symbol, pair) => { await api.db.update('metrics', metric); }; -const updateTradesHistory = async (type, buyer, seller, symbol, pair, pairPrecision, quantity, price, volume, buyTxId, sellTxId) => { +const updateTradesHistory = async (type, + buyer, + seller, + symbol, + pair, + pairPrecision, + quantity, + price, + volume, + buyTxId, + sellTxId) => { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; const timestampMinus24hrs = blockDate.setUTCDate(blockDate.getUTCDate() - 1) / 1000; - + // clean history let tradesToDelete = await api.db.find( 'tradesHistory', @@ -321,7 +331,7 @@ const updateTradesHistory = async (type, buyer, seller, symbol, pair, pairPrecis newTrade.buyTxId = buyTxId; newTrade.sellTxId = sellTxId; await api.db.insert('tradesHistory', newTrade); - await updatePriceMetrics(symbol, pair, price); + await updatePriceMetrics(symbol, pair, pairPrecision, price); }; const removeExpiredOrders = async (table) => { @@ -392,7 +402,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { pair, priceDec, } = order; - + const buyOrder = order; let offset = 0; let volumeTraded = 0; @@ -452,7 +462,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { await api.db.update('sellBook', sellOrder); } else { if (api.BigNumber(qtyLeftSellOrder).gt(0)) { - // transfer the leftover tokens to the seller since the order can no longer be filled + // transfer remaining tokens to seller since the order can no longer be filled await api.transferTokens(sellOrder.account, symbol, qtyLeftSellOrder, 'user'); } api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); @@ -558,7 +568,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { if (api.BigNumber(buyOrder.quantity).gt(0)) { await api.db.update('buyBook', buyOrder); } - + // update metrics if (api.BigNumber(volumeTraded).gt(0)) { await updateVolumeMetric(symbol, pair, pairPrecision, volumeTraded); @@ -639,7 +649,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { await api.db.update('buyBook', buyOrder); } else { if (api.BigNumber(buyOrdertokensLocked).gt(0)) { - // transfer the leftover tokens to the buyer since the order can no longer be filled + // transfer remaining tokens to buyer since the order can no longer be filled await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); } api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); @@ -777,14 +787,16 @@ actions.buy = async (payload) => { const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + // perform a few verifications if (!api.assert(token, 'symbol does not exist')) return; - if (!api.assert(countDecimals(quantity) <= token.precision, 'invalid quantity')) return; + if (!api.assert(countDecimals(quantity) <= token.precision + && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; const pairInDb = await api.db.findOne('pairs', { pair }); // check if symbol is included in allowedSymbols or the pair is global if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; - if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pair.precision, 'invalid price')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pairInDb.precision, 'invalid price')) return; const nbTokensToLock = api.BigNumber(price) .multipliedBy(quantity) @@ -797,12 +809,7 @@ actions.buy = async (payload) => { }); // make sure tokens are locked - if (api.assert(transferIsSuccessful(tokenTransfer, - 'transferToContract', - finalAccount, - CONTRACT_NAME, - pair, - nbTokensToLock), 'failed to transfer tokens')) { + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, pair, nbTokensToLock), 'failed to transfer tokens')) { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; @@ -815,7 +822,7 @@ actions.buy = async (payload) => { order.symbol = symbol; order.pair = pair; order.quantity = api.BigNumber(quantity).toFixed(token.precision); - order.price = api.BigNumber(price).toFixed(pair.precision); + order.price = api.BigNumber(price).toFixed(pairInDb.precision); order.priceDec = { $numberDecimal: order.price }; order.tokensLocked = nbTokensToLock; order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS @@ -857,14 +864,16 @@ actions.sell = async (payload) => { const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + // perform a few verifications if (!api.assert(token, 'symbol does not exist')) return; - if (!api.assert(countDecimals(quantity) <= token.precision, 'invalid quantity')) return; + if (!api.assert(countDecimals(quantity) <= token.precision + && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; const pairInDb = await api.db.findOne('pairs', { pair }); // check if symbol is included in allowedSymbols or the pair is global if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; - if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pair.precision, 'invalid price')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pairInDb.precision, 'invalid price')) return; const nbTokensToFillOrder = api.BigNumber(price) .multipliedBy(quantity) @@ -878,12 +887,7 @@ actions.sell = async (payload) => { }); // make sure tokens are locked - if (api.assert(transferIsSuccessful(tokenTransfer, - 'transferToContract', - finalAccount, - CONTRACT_NAME, - symbol, - quantity), 'failed to transfer tokens')) { + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, symbol, quantity), 'failed to transfer tokens')) { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; @@ -896,7 +900,7 @@ actions.sell = async (payload) => { order.symbol = symbol; order.pair = pair; order.quantity = api.BigNumber(quantity).toFixed(token.precision); - order.price = api.BigNumber(price).toFixed(pair.precision); + order.price = api.BigNumber(price).toFixed(pairInDb.precision); order.priceDec = { $numberDecimal: order.price }; order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS ? timestampSec + MAX_EXPIRATION_SECS @@ -908,3 +912,180 @@ actions.sell = async (payload) => { } } }; + +actions.marketBuy = async (payload) => { + const { + account, + symbol, + pair, + quantity, + isSignedWithActiveKey, + } = payload; + + const finalAccount = (account === undefined || api.sender !== 'null') ? api.sender : account; + + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + + if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) + && symbol && typeof symbol === 'string' + && pair && typeof pair === 'string' + && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN(), 'invalid params') + ) return; + + // get the token params + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + + // perform a few verifications + if (!api.assert(token, 'symbol does not exist')) return; + + const pairInDb = await api.db.findOne('pairs', { pair }); + + // check if symbol is included in allowedSymbols or the pair is global + if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; + if (!api.assert(countDecimals(quantity) <= pairInDb.precision + && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; + + // initiate a transfer from sender to contract balance + // lock pair tokens + const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { + from: finalAccount, to: CONTRACT_NAME, symbol: pair, quantity, + }); + + // make sure tokens are locked + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, pair, quantity), 'failed to transfer tokens')) { + let pairQtyRemaining = quantity; + let offset = 0; + let volumeTraded = 0; + + await removeExpiredOrders('sellBook'); + + // get the orders that match the symbol and the pair + let sellOrderBook = await api.db.find('sellBook', { + symbol, + pair, + }, 1000, offset, + [ + { index: 'priceDec', descending: false }, + { index: '_id', descending: false }, + ]); + + do { + const nbOrders = sellOrderBook.length; + let inc = 0; + + while (inc < nbOrders && api.BigNumber(pairQtyRemaining).gt(0)) { + const sellOrder = sellOrderBook[inc]; + const qtyTokensToSend = api.BigNumber(pairQtyRemaining) + .dividedBy(sellOrder.price) + .toFixed(token.precision, api.BigNumber.ROUND_DOWN); + + if (api.BigNumber(qtyTokensToSend).lte(sellOrder.quantity) + && api.BigNumber(qtyTokensToSend).gt(0)) { + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(pairQtyRemaining).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(finalAccount, symbol, qtyTokensToSend, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(sellOrder.account, pair, pairQtyRemaining, 'user'); + + // update the sell order + const qtyLeftSellOrder = api.BigNumber(sellOrder.quantity) + .minus(qtyTokensToSend) + .toFixed(token.precision); + const nbTokensToFillOrder = api.BigNumber(sellOrder.price) + .multipliedBy(qtyLeftSellOrder) + .toFixed(pairInDb.precision); + + if (api.BigNumber(qtyLeftSellOrder).gt(0) + && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + sellOrder.quantity = qtyLeftSellOrder; + + await api.db.update('sellBook', sellOrder); + } else { + if (api.BigNumber(qtyLeftSellOrder).gt(0)) { + // transfer remaining tokens to seller since the order can no longer be filled + await api.transferTokens(sellOrder.account, symbol, qtyLeftSellOrder, 'user'); + } + + // remove the sell order + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + await api.db.remove('sellBook', sellOrder); + } + + // add the trade to the history + await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, pair, pairInDb.precision, qtyTokensToSend, sellOrder.price, pairQtyRemaining, api.transactionId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(pairQtyRemaining); + + // set quantity to zero to stop the loop + pairQtyRemaining = '0'; + } + } else if (api.BigNumber(qtyTokensToSend).gt(0)) { + let qtyPairToSend = api.BigNumber(sellOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairInDb.precision); + + if (api.BigNumber(qtyPairToSend).gt(pairQtyRemaining)) { + qtyPairToSend = api.BigNumber(sellOrder.price) + .multipliedBy(sellOrder.quantity) + .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyPairToSend).gt(0) + && api.BigNumber(pairQtyRemaining).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(finalAccount, symbol, sellOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(sellOrder.account, pair, qtyPairToSend, 'user'); + + // remove the sell order + api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); + await api.db.remove('sellBook', sellOrder); + + // update tokensLocked and the quantity to get + pairQtyRemaining = api.BigNumber(pairQtyRemaining) + .minus(qtyPairToSend) + .toFixed(pairInDb.precision); + + // add the trade to the history + await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, pair, pairInDb.precision, sellOrder.quantity, sellOrder.price, qtyPairToSend, api.transactionId, sellOrder.txId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyPairToSend); + } + } + + inc += 1; + } + + offset += 1000; + + if (api.BigNumber(pairQtyRemaining).gt(0)) { + // get the orders that match the symbol and the price + sellOrderBook = await api.db.find('sellBook', { + symbol, + pair, + }, 1000, offset, + [ + { index: 'priceDec', descending: false }, + { index: '_id', descending: false }, + ]); + } + } while (sellOrderBook.length > 0 && api.BigNumber(pairQtyRemaining).gt(0)); + + // return the tokens if the buy order is not filled + if (api.BigNumber(pairQtyRemaining).gt(0)) { + await api.transferTokens(finalAccount, pair, pairQtyRemaining, 'user'); + } + + // update the volume and metrics + if (api.BigNumber(volumeTraded).gt(0)) { + await updateVolumeMetric(symbol, pair, pairInDb.precision, volumeTraded); + } + await updateAskMetric(symbol, pair); + await updateBidMetric(symbol, pair); + } +}; diff --git a/test/dmarket.js b/test/dmarket.js index ee57cb01..4bcbd410 100644 --- a/test/dmarket.js +++ b/test/dmarket.js @@ -108,10 +108,10 @@ function getNextTxId() { return `TXID${txId.toString().padStart(8, '0')}`; } -async function assertBalances(accounts, balances, symbol) { +async function assertBalances(accounts, balances, symbol, contract = false) { const res = await database1.find({ contract: 'tokens', - table: 'balances', + table: contract ? 'contractsBalances' : 'balances', query: { account: { $in: accounts, @@ -122,10 +122,15 @@ async function assertBalances(accounts, balances, symbol) { for (let i = 0; i < accounts.length; i += 1) { const account = accounts[i]; - const { - balance, - } = res.find(el => el.account === account); const expectedBalance = balances[i]; + let balance = '0'; + + try { + // eslint-disable-next-line + balance = (res.find(el => el.account === account)).balance; + } catch (e) { + assert(BigNumber(expectedBalance).isEqualTo(0), `no balance for @${account} found`); + } // console.log(expectedBalance, balance, account); const isEqual = BigNumber(expectedBalance).eq(balance); @@ -232,7 +237,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "BEE", "symbol": "TKN" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); const block = { @@ -276,7 +281,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); const block = { @@ -315,7 +320,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); @@ -361,7 +366,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"1200", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "XYZ" }')); @@ -390,4 +395,542 @@ describe('dMarket Smart Contract', function () { done(); }); }); + + it('creates a buy order for user added pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.1" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + const result = await database1.findOne({ + contract: 'dmarket', + table: 'buyBook', + query: { + symbol: 'BEE', + pair: 'TKN', + txId: txs[0].txId, + }, + }); + + console.log(result); + // confirm some things in the order + assert(BigNumber(result.quantity).eq(100)); + assert(BigNumber(result.price).eq(0.1)); + assert(BigNumber(result.tokensLocked).eq(10)); + + await assertBalances(['ali-h'], ['113.456'], 'TKN'); + await assertBalances(['dmarket'], ['10'], 'TKN', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('does not create a buy order if pair does not exist', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.1" }')); + + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + assertError(txs[5], 'pair does not exist'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('creates a sell order for user added pair', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + const result = await database1.findOne({ + contract: 'dmarket', + table: 'sellBook', + query: { + symbol: 'BEE', + pair: 'TKN', + txId: txs[0].txId, + }, + }); + + console.log(result); + // confirm some things in the order + assert(BigNumber(result.quantity).eq(100)); + assert(BigNumber(result.price).eq(0.16)); + + await assertBalances(['ali-h'], ['0'], 'BEE'); + await assertBalances(['dmarket'], ['100'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('does not create a sell order if pair does not exist', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + const transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + + + const block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + assertError(txs[4], 'pair does not exist'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('buys from one seller', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "18.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['0', '100'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['16', '2.17'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('buys from multiple sellers', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "24.3" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "135", "price": "0.18" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0', '0', '50', '135'], 'BEE'); + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['16', '1.7', '4.5', '2.1'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['25'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('sells to one buyer', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['0', '100'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['17', '38'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('sells to multiple buyers', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "18" }')); + + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.17" }')); + + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"140", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "140", "price": "0.16" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['40', '50', '50', '0'], 'BEE'); + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['2', '9.5', '9', '23.9'], 'TKN'); + await assertBalances(['dmarket'], ['9.6'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('market buys from multiple sellers', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "22.2" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "22.2" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + const res = await database1.getLatestBlockInfo(); + const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0', '0', '50', '135'], 'BEE'); + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['16', '1.7', '4.5', '0'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['25'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); }); From d29ab3200c6fa8652bfbb522f2eea72743f11f3e Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Tue, 21 Sep 2021 10:58:17 +0500 Subject: [PATCH 4/8] marketSell & test units for metrics --- contracts/dmarket.js | 194 +++++++++++++++++++++++++++++++++++++++ package.json | 3 +- test/dmarket.js | 214 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 407 insertions(+), 4 deletions(-) diff --git a/contracts/dmarket.js b/contracts/dmarket.js index 5dddc225..260f7bfb 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -1089,3 +1089,197 @@ actions.marketBuy = async (payload) => { await updateBidMetric(symbol, pair); } }; + +actions.marketSell = async (payload) => { + const { + account, + symbol, + pair, + quantity, + isSignedWithActiveKey, + } = payload; + + const finalAccount = (account === undefined || api.sender !== 'null') ? api.sender : account; + + if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; + + if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) + && symbol && typeof symbol === 'string' + && pair && typeof pair === 'string' + && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN(), 'invalid params') + ) return; + + // get the token params + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); + + // perform a few verifications + if (!api.assert(token, 'symbol does not exist')) return; + + const pairInDb = await api.db.findOne('pairs', { pair }); + + // check if symbol is included in allowedSymbols or the pair is global + if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; + if (!api.assert(countDecimals(quantity) <= token.precision + && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; + + // initiate a transfer from sender to contract balance + // lock symbol tokens + const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { + from: finalAccount, to: CONTRACT_NAME, symbol, quantity, + }); + + // make sure tokens are locked + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, symbol, quantity), 'failed to transfer tokens')) { + let tokensRemaining = quantity; + let offset = 0; + let volumeTraded = 0; + + await removeExpiredOrders('buyBook'); + + // get the orders that match the symbol + let buyOrderBook = await api.db.find('buyBook', { + symbol, + pair, + }, 1000, offset, + [ + { index: 'priceDec', descending: true }, + { index: '_id', descending: false }, + ]); + + do { + const nbOrders = buyOrderBook.length; + let inc = 0; + + while (inc < nbOrders && api.BigNumber(tokensRemaining).gt(0)) { + const buyOrder = buyOrderBook[inc]; + if (api.BigNumber(tokensRemaining).lte(buyOrder.quantity)) { + let qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(tokensRemaining) + .toFixed(pairInDb.precision); + + if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { + qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(tokensRemaining) + .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(tokensRemaining).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(buyOrder.account, symbol, tokensRemaining, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(finalAccount, pair, qtyTokensToSend, 'user'); + + // update the buy order + const qtyLeftBuyOrder = api.BigNumber(buyOrder.quantity) + .minus(tokensRemaining) + .toFixed(token.precision); + + const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairInDb.precision); + const nbTokensToFillOrder = api.BigNumber(buyOrder.price) + .multipliedBy(qtyLeftBuyOrder) + .toFixed(pairInDb.precision); + + if (api.BigNumber(qtyLeftBuyOrder).gt(0) + && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + buyOrder.quantity = qtyLeftBuyOrder; + buyOrder.tokensLocked = buyOrdertokensLocked; + + await api.db.update('buyBook', buyOrder); + } else { + if (api.BigNumber(buyOrdertokensLocked).gt(0)) { + // transfer remaining tokens to buyer since the order can no longer be filled + await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + } + + // remove the sell order + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + await api.db.remove('buyBook', buyOrder); + } + + // add the trade to the history + await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, pair, pairInDb.precision, tokensRemaining, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + + tokensRemaining = 0; + } + } else { + let qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairInDb.precision); + + if (qtyTokensToSend > buyOrder.tokensLocked) { + qtyTokensToSend = api.BigNumber(buyOrder.price) + .multipliedBy(buyOrder.quantity) + .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + } + + if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) + && api.BigNumber(tokensRemaining).gt(0), 'the order cannot be filled')) { + // transfer the tokens to the buyer + await api.transferTokens(buyOrder.account, symbol, buyOrder.quantity, 'user'); + + // transfer the tokens to the seller + await api.transferTokens(finalAccount, pair, qtyTokensToSend, 'user'); + + const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) + .minus(qtyTokensToSend) + .toFixed(pairInDb.precision); + + if (api.BigNumber(buyOrdertokensLocked).gt(0)) { + // transfer remaining tokens to buyer since the order can no longer be filled + await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + } + + // remove the buy order + api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); + await api.db.remove('buyBook', buyOrder); + + // update the quantity to get + tokensRemaining = api.BigNumber(tokensRemaining) + .minus(buyOrder.quantity) + .toFixed(token.precision); + + // add the trade to the history + await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, pair, pairInDb.precision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); + + // update the volume + volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); + } + } + + inc += 1; + } + + offset += 1000; + + if (api.BigNumber(tokensRemaining).gt(0)) { + // get the orders that match the symbol and the price + buyOrderBook = await api.db.find('buyBook', { + symbol, + pair, + }, 1000, offset, + [ + { index: 'priceDec', descending: true }, + { index: '_id', descending: false }, + ]); + } + } while (buyOrderBook.length > 0 && api.BigNumber(tokensRemaining).gt(0)); + + // send back the remaining tokens + if (api.BigNumber(tokensRemaining).gt(0)) { + await api.transferTokens(finalAccount, symbol, tokensRemaining, 'user'); + } + + if (api.BigNumber(volumeTraded).gt(0)) { + await updateVolumeMetric(symbol, pair, pairInDb.precision, volumeTraded); + } + await updateAskMetric(symbol, pair); + await updateBidMetric(symbol, pair); + } +}; diff --git a/package.json b/package.json index 856c78a2..59b21845 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "test13": "./node_modules/mocha/bin/mocha ./test/witnesses.js", "test14": "./node_modules/mocha/bin/mocha ./test/marketpools.js", "test15": "./node_modules/mocha/bin/mocha ./test/tokenfunds.js", - "test16": "./node_modules/mocha/bin/mocha ./test/nftauction.js" + "test16": "./node_modules/mocha/bin/mocha ./test/nftauction.js", + "test17": "./node_modules/mocha/bin/mocha ./test/dmarket.js" }, "engines": { "node": ">=13.7.0", diff --git a/test/dmarket.js b/test/dmarket.js index 4bcbd410..8fb9b61e 100644 --- a/test/dmarket.js +++ b/test/dmarket.js @@ -1,4 +1,5 @@ +/* eslint-disable */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-undef */ /* eslint-disable no-console */ @@ -138,6 +139,21 @@ async function assertBalances(accounts, balances, symbol, contract = false) { } } +async function verifyAskBid(symbol, pair, ask, bid) { + const res = await database1.findOne({ + contract: 'dmarket', + table: 'metrics', + query: { + symbol, + pair, + }, + }); + + assert(res, 'metric not found'); + assert(BigNumber(res.lowestAsk).isEqualTo(ask), `ask ${ask} not equal to ${res.lowestAsk}`); + assert(BigNumber(res.highestBid).isEqualTo(bid), `bid ${bid} not equal to ${res.highestBid}`); +} + async function assertPair(pair, symbols) { const res = await database1.findOne({ contract: 'dmarket', @@ -870,7 +886,7 @@ describe('dMarket Smart Contract', function () { }); }); - it('market buys from multiple sellers', (done) => { + it('market buy from multiple sellers', (done) => { new Promise(async (resolve) => { await loadPlugin(blockchain); database1 = new Database(); @@ -915,8 +931,8 @@ describe('dMarket Smart Contract', function () { await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); - const res = await database1.getLatestBlockInfo(); - const txs = res.transactions; + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; await assertNoErrorInLastBlock(); @@ -933,4 +949,196 @@ describe('dMarket Smart Contract', function () { done(); }); }); + + it('market sell to multiple buyers', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "10" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "50" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "12" }')); + + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "55", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "312", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.17" }')); + + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"210", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "210" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + // const res = await database1.getLatestBlockInfo(); + // const txs = res.transactions; + + await assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['105', '50', '55', '0'], 'BEE'); + await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0.08', '3.5', '0.1', '35.2'], 'TKN'); + await assertBalances(['dmarket'], ['33.12'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); + + it('verify metrics', (done) => { + new Promise(async (resolve) => { + await loadPlugin(blockchain); + database1 = new Database(); + + await database1.init(conf.databaseURL, conf.databaseName); + + let transactions = []; + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a001", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a002", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a003", "quantity":"100", "isSignedWithActiveKey":true }`)); + + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b001", "quantity": "100" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b002", "quantity": "100" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b003", "quantity": "100" }')); + + let block = { + refHiveBlockNumber: 12345678901, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + // bid + transactions = []; + transactions = []; + + transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.15" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b002', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.20" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b003', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.16" }')); + + block = { + refHiveBlockNumber: 12345678902, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + await verifyAskBid('BEE', 'TKN', '0', '0.20'); + + // ask + transactions = []; + transactions = []; + + transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.23" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a003', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.21" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a002', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.25" }')); + + block = { + refHiveBlockNumber: 12345678903, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + await verifyAskBid('BEE', 'TKN', '0.21', '0.20'); + + // update after order filling + transactions = []; + transactions = []; + + // sell to the highest bid + transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "20" }')); + + // buy from the lowest ask + transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "4.4" }')); + + block = { + refHiveBlockNumber: 12345678904, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + + await assertNoErrorInLastBlock(); + + await verifyAskBid('BEE', 'TKN', '0.25', '0.15'); + + const metric = await database1.findOne({ + contract: 'dmarket', + table: 'metrics', + query: { + symbol: 'BEE', + pair: 'TKN', + }, + }); + + console.log(metric); + + assert(BigNumber(metric.volume).eq('8.000'), 'invalid volume'); + assert(BigNumber(metric.lastPrice).eq('0.23'), 'invalid lastPrice'); + + resolve(); + }) + .then(() => { + unloadPlugin(blockchain); + database1.close(); + done(); + }); + }); }); From f432b75b43285bc7906d67e08206f2a74a28569c Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Sat, 9 Oct 2021 15:53:13 +0500 Subject: [PATCH 5/8] rename pair to quoteToken --- contracts/dmarket.js | 441 ++++++++++++++++++++++--------------------- test/dmarket.js | 152 +++++++-------- 2 files changed, 298 insertions(+), 295 deletions(-) diff --git a/contracts/dmarket.js b/contracts/dmarket.js index 260f7bfb..c45ca9e7 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -11,31 +11,33 @@ const PAIR_CREATION_FEE = '500'; const CONTRACT_NAME = 'dmarket'; actions.createSSC = async () => { - const tableExists = await api.db.tableExists('pairs'); + const tableExists = await api.db.tableExists('quoteTokens'); if (tableExists === false) { - await api.db.createTable('pairs', ['symbol', 'precision', 'allowedSymbols']); - await api.db.createTable('buyBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); - await api.db.createTable('sellBook', ['symbol', 'pair', 'account', 'priceDec', 'expiration', 'txId']); - await api.db.createTable('tradesHistory', ['symbol', 'pair']); - await api.db.createTable('metrics', ['symbol', 'pair']); - - // default global pairs - const pairs = [ + await api.db.createTable('quoteTokens', ['quoteToken']); + await api.db.createTable('buyBook', ['symbol', 'quoteToken', 'account', 'priceDec', 'expiration', 'txId']); + await api.db.createTable('sellBook', ['symbol', 'quoteToken', 'account', 'priceDec', 'expiration', 'txId']); + await api.db.createTable('tradesHistory', ['symbol', 'quoteToken']); + await api.db.createTable('metrics', ['symbol', 'quoteToken']); + + // default global quoteTokens + const quoteTokens = [ { - pair: 'BEE', + quoteToken: 'BEE', precision: 8, - allowedSymbols: true, // true - all tokens are allowed + allowedBaseTokens: [], + isGlobal: true, // true - all tokens are allowed }, { - pair: 'SWAP.BTC', + quoteToken: 'SWAP.BTC', precision: 8, - allowedSymbols: true, // true - all tokens are allowed + allowedBaseTokens: [], + isGlobal: true, // true - all tokens are allowed }, ]; - for (let i = 0; i < pairs.length; i += 1) { - await api.db.insert('pairs', pairs[i]); + for (let i = 0; i < quoteTokens.length; i += 1) { + await api.db.insert('quoteTokens', quoteTokens[i]); } } }; @@ -58,32 +60,33 @@ const countDecimals = value => api.BigNumber(value).dp(); actions.addPair = async (payload) => { const { - pair, - symbol, + quoteToken, + baseToken, isSignedWithActiveKey, } = payload; if (!api.assert(isSignedWithActiveKey === true, 'you must use a custom_json signed with your active key')) return; - if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; - if (!api.assert(symbol && typeof symbol === 'string', 'invalid symbol')) return; - if (!api.assert(pair !== symbol, 'pair and symbol can not be the same')) return; - - const token = await api.db.findOneInTable('tokens', 'tokens', { symbol }); - if (!api.assert(token, 'symbol does not exist')) return; - - const pairToken = await api.db.findOneInTable('tokens', 'tokens', { symbol: pair }); - if (!api.assert(pairToken, 'pair symbol does not exist')) return; - - const pairInDb = await api.db.findOne('pairs', { pair }); - const pairUpdate = pairInDb || { - pair, - precision: pairToken.precision, - allowedSymbols: [], + if (!api.assert(quoteToken && typeof quoteToken === 'string', 'invalid quoteToken')) return; + if (!api.assert(baseToken && typeof baseToken === 'string', 'invalid baseToken')) return; + if (!api.assert(quoteToken !== baseToken, 'quoteToken and baseToken can not be the same')) return; + + const token = await api.db.findOneInTable('tokens', 'tokens', { symbol: baseToken }); + if (!api.assert(token, 'baseToken does not exist')) return; + + const quoteTokenInfo = await api.db.findOneInTable('tokens', 'tokens', { symbol: quoteToken }); + if (!api.assert(quoteTokenInfo, 'quoteToken does not exist')) return; + + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); + const qtUpdate = qtInDb || { + quoteToken, + precision: quoteTokenInfo.precision, + allowedBaseTokens: [], + isGlobal: false, }; - if (api.assert(pairUpdate.allowedSymbols !== true, 'can not add symbol to a global pair') - && api.assert(pairUpdate.allowedSymbols.indexOf(symbol) === -1, 'symbol is already in the pair')) { - pairUpdate.allowedSymbols.push(symbol); + if (api.assert(qtUpdate.isGlobal !== true, 'can not add another baseToken to a global quoteToken') + && api.assert(qtUpdate.allowedBaseTokens.indexOf(baseToken) === -1, 'baseToken is already in this pair')) { + qtUpdate.allowedBaseTokens.push(baseToken); } const utilityToken = await api.db.findOneInTable('tokens', 'balances', { account: api.sender, symbol: UTILITY_TOKEN_SYMBOL }); @@ -95,7 +98,7 @@ actions.addPair = async (payload) => { to: 'null', symbol: UTILITY_TOKEN_SYMBOL, quantity: PAIR_CREATION_FEE, isSignedWithActiveKey, }); - // make sure fee is transfered successfully + // make sure fee is transferred successfully if (!api.assert(transferIsSuccessful(feeTransfer, 'transfer', api.sender, @@ -103,64 +106,64 @@ actions.addPair = async (payload) => { UTILITY_TOKEN_SYMBOL, PAIR_CREATION_FEE), 'failed to transfer creation fee')) return; - if (pairInDb) { - // add the new symbol in the pair - await api.db.update('pairs', pairUpdate); + if (qtInDb) { + // update QuoteToken in db with new baseToken + await api.db.update('quoteTokens', qtUpdate); } else { // add new pair in the db - await api.db.insert('pairs', pairUpdate); + await api.db.insert('quoteTokens', qtUpdate); } api.emit('addPair', { - pair, - symbol, + quoteToken, + baseToken, }); } }; -actions.addGlobalPair = async (payload) => { +actions.setGlobalQuoteToken = async (payload) => { if (api.assert(api.sender === api.owner, 'not authorized')) { const { - pair, + quoteToken, } = payload; - if (!api.assert(pair && typeof pair === 'string', 'invalid pair')) return; + if (!api.assert(quoteToken && typeof quoteToken === 'string', 'invalid quoteToken')) return; - const pairToken = await api.db.findOneInTable('tokens', 'tokens', { symbol: pair }); - if (!api.assert(pairToken, 'pair symbol does not exist')) return; + const quoteTokenInfo = await api.db.findOneInTable('tokens', 'tokens', { symbol: quoteToken }); + if (!api.assert(quoteTokenInfo, 'quoteToken does not exist')) return; - const pairInDb = await api.db.findOne('pairs', { pair }); - const pairUpdate = pairInDb || { - pair, - precision: pairToken.precision, - allowedSymbols: [], - }; + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); - if (api.assert(pairUpdate.allowedSymbols !== true, 'pair is already global')) { - pairUpdate.allowedSymbols = true; - } + if (qtInDb) { + if (api.assert(qtInDb.isGlobal !== true, 'quoteToken is already global')) { + qtInDb.isGlobal = true; + } - if (pairInDb) { - // update the pair as global - await api.db.update('pairs', pairUpdate); + // update the quoteToken as global + await api.db.update('quoteTokens', qtInDb); } else { - // add new global pair - await api.db.insert('pairs', pairUpdate); + // add new global quoteToken + await api.db.insert('quoteTokens', { + quoteToken, + precision: quoteTokenInfo.precision, + allowedBaseTokens: [], + isGlobal: true, + }); } - api.emit('addGlobalPair', { - pair, + api.emit('setGlobalQuoteToken', { + quoteToken, }); } }; -const getMetric = async (symbol, pair) => { - let metric = await api.db.findOne('metrics', { symbol, pair }); +const getMetric = async (symbol, quoteToken) => { + let metric = await api.db.findOne('metrics', { symbol, quoteToken }); if (metric === null) { metric = {}; metric.symbol = symbol; - metric.pair = pair; + metric.quoteToken = quoteToken; metric.volume = '0'; metric.volumeExpiration = 0; metric.lastPrice = '0'; @@ -168,7 +171,7 @@ const getMetric = async (symbol, pair) => { metric.highestBid = '0'; metric.lastDayPrice = '0'; metric.lastDayPriceExpiration = 0; - metric.priceChangePair = '0'; + metric.priceChangeQuoteToken = '0'; metric.priceChangePercent = '0'; const newMetric = await api.db.insert('metrics', metric); @@ -178,10 +181,10 @@ const getMetric = async (symbol, pair) => { return metric; }; -const updateVolumeMetric = async (symbol, pair, pairPrecision, quantity, add = true) => { +const updateVolumeMetric = async (symbol, quoteToken, qtPrecision, quantity, add = true) => { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; - const metric = await getMetric(symbol, pair); + const metric = await getMetric(symbol, quoteToken); if (add === true) { if (metric.volumeExpiration < timestampSec) { @@ -189,12 +192,12 @@ const updateVolumeMetric = async (symbol, pair, pairPrecision, quantity, add = t } metric.volume = api.BigNumber(metric.volume) .plus(quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); metric.volumeExpiration = blockDate.setUTCDate(blockDate.getUTCDate() + 1) / 1000; } else { metric.volume = api.BigNumber(metric.volume) .minus(quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); } if (api.BigNumber(metric.volume).lt(0)) { @@ -204,36 +207,36 @@ const updateVolumeMetric = async (symbol, pair, pairPrecision, quantity, add = t await api.db.update('metrics', metric); }; -const updatePriceMetrics = async (symbol, pair, pairPrecision, price) => { +const updatePriceMetrics = async (symbol, quoteToken, qtPrecision, price) => { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; - const metric = await getMetric(symbol, pair); + const metric = await getMetric(symbol, quoteToken); metric.lastPrice = price; if (metric.lastDayPriceExpiration < timestampSec) { metric.lastDayPrice = price; metric.lastDayPriceExpiration = blockDate.setUTCDate(blockDate.getUTCDate() + 1) / 1000; - metric.priceChangePair = '0'; + metric.priceChangeQuoteToken = '0'; metric.priceChangePercent = '0%'; } else { - metric.priceChangePair = api.BigNumber(price) + metric.priceChangeQuoteToken = api.BigNumber(price) .minus(metric.lastDayPrice) - .toFixed(pairPrecision); - metric.priceChangePercent = `${api.BigNumber(metric.priceChangePair).dividedBy(metric.lastDayPrice).multipliedBy(100).toFixed(2)}%`; + .toFixed(qtPrecision); + metric.priceChangePercent = `${api.BigNumber(metric.priceChangeQuoteToken).dividedBy(metric.lastDayPrice).multipliedBy(100).toFixed(2)}%`; } await api.db.update('metrics', metric); }; -const updateBidMetric = async (symbol, pair) => { - const metric = await getMetric(symbol, pair); +const updateBidMetric = async (symbol, quoteToken) => { + const metric = await getMetric(symbol, quoteToken); const buyOrderBook = await api.db.find('buyBook', { symbol, - pair, + quoteToken, }, 1, 0, [ { index: 'priceDec', descending: true }, @@ -249,13 +252,13 @@ const updateBidMetric = async (symbol, pair) => { await api.db.update('metrics', metric); }; -const updateAskMetric = async (symbol, pair) => { - const metric = await getMetric(symbol, pair); +const updateAskMetric = async (symbol, quoteToken) => { + const metric = await getMetric(symbol, quoteToken); const sellOrderBook = await api.db.find('sellBook', { symbol, - pair, + quoteToken, }, 1, 0, [ { index: 'priceDec', descending: false }, @@ -274,8 +277,8 @@ const updateTradesHistory = async (type, buyer, seller, symbol, - pair, - pairPrecision, + quoteToken, + qtPrecision, quantity, price, volume, @@ -290,7 +293,7 @@ const updateTradesHistory = async (type, 'tradesHistory', { symbol, - pair, + quoteToken, timestamp: { $lt: timestampMinus24hrs, }, @@ -301,14 +304,14 @@ const updateTradesHistory = async (type, while (nbTradesToDelete > 0) { for (let index = 0; index < nbTradesToDelete; index += 1) { const trade = tradesToDelete[index]; - await updateVolumeMetric(trade.symbol, trade.pair, pairPrecision, trade.volume, false); + await updateVolumeMetric(trade.symbol, trade.quoteToken, qtPrecision, trade.volume, false); await api.db.remove('tradesHistory', trade); } tradesToDelete = await api.db.find( 'tradesHistory', { symbol, - pair, + quoteToken, timestamp: { $lt: timestampMinus24hrs, }, @@ -323,7 +326,7 @@ const updateTradesHistory = async (type, newTrade.buyer = buyer; newTrade.seller = seller; newTrade.symbol = symbol; - newTrade.pair = pair; + newTrade.quoteToken = quoteToken; newTrade.quantity = quantity; newTrade.price = price; newTrade.timestamp = timestampSec; @@ -331,7 +334,7 @@ const updateTradesHistory = async (type, newTrade.buyTxId = buyTxId; newTrade.sellTxId = sellTxId; await api.db.insert('tradesHistory', newTrade); - await updatePriceMetrics(symbol, pair, pairPrecision, price); + await updatePriceMetrics(symbol, quoteToken, qtPrecision, price); }; const removeExpiredOrders = async (table) => { @@ -357,7 +360,7 @@ const removeExpiredOrders = async (table) => { let symbol; if (table === 'buyBook') { - symbol = order.pair; + symbol = order.quoteToken; quantity = order.tokensLocked; } else { // eslint-disable-next-line prefer-destructuring @@ -395,11 +398,11 @@ const removeExpiredOrders = async (table) => { } }; -const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { +const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { const { account, symbol, - pair, + quoteToken, priceDec, } = order; @@ -412,7 +415,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // get the orders that match the symbol and the price let sellOrderBook = await api.db.find('sellBook', { symbol, - pair, + quoteToken, priceDec: { $lte: priceDec, }, @@ -431,12 +434,12 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { if (api.BigNumber(buyOrder.quantity).lte(sellOrder.quantity)) { let qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -445,7 +448,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { await api.transferTokens(account, symbol, buyOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(sellOrder.account, pair, qtyTokensToSend, 'user'); + await api.transferTokens(sellOrder.account, quoteToken, qtyTokensToSend, 'user'); // update the sell order const qtyLeftSellOrder = api.BigNumber(sellOrder.quantity) @@ -453,7 +456,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { .toFixed(tokenPrecision); const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(qtyLeftSellOrder) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(qtyLeftSellOrder).gt(0) && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { @@ -472,15 +475,15 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // unlock remaining tokens, update the quantity to get and remove the buy order const tokensToUnlock = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(tokensToUnlock).gt(0)) { // transfer any dust tokens remaining to buyer - await api.transferTokens(account, pair, tokensToUnlock, 'user'); + await api.transferTokens(account, quoteToken, tokensToUnlock, 'user'); } // add the trade to the history - await updateTradesHistory('buy', account, sellOrder.account, symbol, pair, pairPrecision, buyOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + await updateTradesHistory('buy', account, sellOrder.account, symbol, quoteToken, qtPrecision, buyOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -493,12 +496,12 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { } else { let qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -507,7 +510,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(sellOrder.account, pair, qtyTokensToSend, 'user'); + await api.transferTokens(sellOrder.account, quoteToken, qtyTokensToSend, 'user'); // remove the sell order await api.db.remove('sellBook', sellOrder); @@ -516,7 +519,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // update tokensLocked and the quantity to get buyOrder.tokensLocked = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairPrecision); + .toFixed(qtPrecision); buyOrder.quantity = api.BigNumber(buyOrder.quantity) .minus(sellOrder.quantity) .toFixed(tokenPrecision); @@ -524,10 +527,10 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // check if the order can still be filled const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { - await api.transferTokens(account, pair, buyOrder.tokensLocked, 'user'); + await api.transferTokens(account, quoteToken, buyOrder.tokensLocked, 'user'); // stop the loop and remove buy order if it can not be filled buyOrder.quantity = '0'; @@ -536,7 +539,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { } // add the trade to the history - await updateTradesHistory('buy', account, sellOrder.account, symbol, pair, pairPrecision, sellOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + await updateTradesHistory('buy', account, sellOrder.account, symbol, quoteToken, qtPrecision, sellOrder.quantity, sellOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -552,7 +555,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // get the orders that match the symbol and the price sellOrderBook = await api.db.find('sellBook', { symbol, - pair, + quoteToken, priceDec: { $lte: priceDec, }, @@ -571,17 +574,17 @@ const findMatchingSellOrders = async (order, tokenPrecision, pairPrecision) => { // update metrics if (api.BigNumber(volumeTraded).gt(0)) { - await updateVolumeMetric(symbol, pair, pairPrecision, volumeTraded); + await updateVolumeMetric(symbol, quoteToken, qtPrecision, volumeTraded); } - await updateAskMetric(symbol, pair); - await updateBidMetric(symbol, pair); + await updateAskMetric(symbol, quoteToken); + await updateBidMetric(symbol, quoteToken); }; -const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { +const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { const { account, symbol, - pair, + quoteToken, priceDec, } = order; @@ -594,7 +597,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { // get the orders that match the symbol and the price let buyOrderBook = await api.db.find('buyBook', { symbol, - pair, + quoteToken, priceDec: { $gte: priceDec, }, @@ -613,12 +616,12 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { if (api.BigNumber(sellOrder.quantity).lte(buyOrder.quantity)) { let qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -627,7 +630,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { await api.transferTokens(buyOrder.account, symbol, sellOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(account, pair, qtyTokensToSend, 'user'); + await api.transferTokens(account, quoteToken, qtyTokensToSend, 'user'); // update the buy order const qtyLeftBuyOrder = api.BigNumber(buyOrder.quantity) @@ -636,10 +639,10 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairPrecision); + .toFixed(qtPrecision); const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(qtyLeftBuyOrder) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(qtyLeftBuyOrder).gt(0) && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { @@ -650,14 +653,14 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { } else { if (api.BigNumber(buyOrdertokensLocked).gt(0)) { // transfer remaining tokens to buyer since the order can no longer be filled - await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + await api.transferTokens(buyOrder.account, quoteToken, buyOrdertokensLocked, 'user'); } api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); await api.db.remove('buyBook', buyOrder); } // add the trade to the history - await updateTradesHistory('sell', buyOrder.account, account, symbol, pair, pairPrecision, sellOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + await updateTradesHistory('sell', buyOrder.account, account, symbol, quoteToken, qtPrecision, sellOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -670,12 +673,12 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { } else { let qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (qtyTokensToSend > buyOrder.tokensLocked) { qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairPrecision, api.BigNumber.ROUND_DOWN); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -684,15 +687,15 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { await api.transferTokens(buyOrder.account, symbol, buyOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(account, pair, qtyTokensToSend, 'user'); + await api.transferTokens(account, quoteToken, qtyTokensToSend, 'user'); const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(buyOrdertokensLocked).gt(0)) { // transfer any dust tokens remaining to buyer - await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + await api.transferTokens(buyOrder.account, quoteToken, buyOrdertokensLocked, 'user'); } // remove the buy order @@ -707,7 +710,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { // check if the order can still be filled const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairPrecision); + .toFixed(qtPrecision); if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); @@ -719,7 +722,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { } // add the trade to the history - await updateTradesHistory('sell', buyOrder.account, account, symbol, pair, pairPrecision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); + await updateTradesHistory('sell', buyOrder.account, account, symbol, quoteToken, qtPrecision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, sellOrder.txId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -735,7 +738,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { // get the orders that match the symbol and the price buyOrderBook = await api.db.find('buyBook', { symbol, - pair, + quoteToken, priceDec: { $gte: priceDec, }, @@ -753,10 +756,10 @@ const findMatchingBuyOrders = async (order, tokenPrecision, pairPrecision) => { } if (api.BigNumber(volumeTraded).gt(0)) { - await updateVolumeMetric(symbol, pair, pairPrecision, volumeTraded); + await updateVolumeMetric(symbol, quoteToken, qtPrecision, volumeTraded); } - await updateAskMetric(symbol, pair); - await updateBidMetric(symbol, pair); + await updateAskMetric(symbol, quoteToken); + await updateBidMetric(symbol, quoteToken); }; actions.buy = async (payload) => { @@ -764,7 +767,7 @@ actions.buy = async (payload) => { account, txId, symbol, - pair, + quoteToken, quantity, price, expiration, @@ -779,7 +782,7 @@ actions.buy = async (payload) => { if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) && finalTxId && typeof finalTxId === 'string' && finalTxId.length > 0 && symbol && typeof symbol === 'string' - && pair && typeof pair === 'string' + && quoteToken && typeof quoteToken === 'string' && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN() && price && typeof price === 'string' && !api.BigNumber(price).isNaN() && (expiration === undefined || (expiration && Number.isInteger(expiration) && expiration > 0)), 'invalid params') @@ -792,24 +795,24 @@ actions.buy = async (payload) => { if (!api.assert(countDecimals(quantity) <= token.precision && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; - const pairInDb = await api.db.findOne('pairs', { pair }); + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); - // check if symbol is included in allowedSymbols or the pair is global - if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; - if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pairInDb.precision, 'invalid price')) return; + // check if symbol is included in allowedBaseTokens or the quoteToken is global + if (!api.assert(qtInDb && (qtInDb.allowedBaseTokens.indexOf(symbol) !== -1 || qtInDb.isGlobal === true), 'pair does not exist')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= qtInDb.precision, 'invalid price')) return; const nbTokensToLock = api.BigNumber(price) .multipliedBy(quantity) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (api.assert(api.BigNumber(nbTokensToLock).gte('0.00000001'), 'order cannot be placed as it cannot be filled')) { // lock the tokens in contract for safekeeping const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { - from: finalAccount, to: CONTRACT_NAME, symbol: pair, quantity: nbTokensToLock, + from: finalAccount, to: CONTRACT_NAME, symbol: quoteToken, quantity: nbTokensToLock, }); // make sure tokens are locked - if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, pair, nbTokensToLock), 'failed to transfer tokens')) { + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, quoteToken, nbTokensToLock), 'failed to transfer tokens')) { const blockDate = new Date(`${api.hiveBlockTimestamp}.000Z`); const timestampSec = blockDate.getTime() / 1000; @@ -820,9 +823,9 @@ actions.buy = async (payload) => { order.timestamp = timestampSec; order.account = finalAccount; order.symbol = symbol; - order.pair = pair; + order.quoteToken = quoteToken; order.quantity = api.BigNumber(quantity).toFixed(token.precision); - order.price = api.BigNumber(price).toFixed(pairInDb.precision); + order.price = api.BigNumber(price).toFixed(qtInDb.precision); order.priceDec = { $numberDecimal: order.price }; order.tokensLocked = nbTokensToLock; order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS @@ -831,7 +834,7 @@ actions.buy = async (payload) => { const orderInDb = await api.db.insert('buyBook', order); - await findMatchingSellOrders(orderInDb, token.precision, pairInDb.precision); + await findMatchingSellOrders(orderInDb, token.precision, qtInDb.precision); } } }; @@ -841,7 +844,7 @@ actions.sell = async (payload) => { account, txId, symbol, - pair, + quoteToken, quantity, price, expiration, @@ -856,7 +859,7 @@ actions.sell = async (payload) => { if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) && finalTxId && typeof finalTxId === 'string' && finalTxId.length > 0 && symbol && typeof symbol === 'string' - && pair && typeof pair === 'string' + && quoteToken && typeof quoteToken === 'string' && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN() && price && typeof price === 'string' && !api.BigNumber(price).isNaN() && (expiration === undefined || (expiration && Number.isInteger(expiration) && expiration > 0)), 'invalid params') @@ -869,15 +872,15 @@ actions.sell = async (payload) => { if (!api.assert(countDecimals(quantity) <= token.precision && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; - const pairInDb = await api.db.findOne('pairs', { pair }); + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); - // check if symbol is included in allowedSymbols or the pair is global - if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; - if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= pairInDb.precision, 'invalid price')) return; + // check if symbol is included in allowedBaseTokens or the quoteToken is global + if (!api.assert(qtInDb && (qtInDb.allowedBaseTokens.indexOf(symbol) !== -1 || qtInDb.isGlobal === true), 'pair does not exist')) return; + if (!api.assert(api.BigNumber(price).gt(0) && countDecimals(price) <= qtInDb.precision, 'invalid price')) return; const nbTokensToFillOrder = api.BigNumber(price) .multipliedBy(quantity) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); // check if order can be filled if (api.assert(api.BigNumber(nbTokensToFillOrder).gte('0.00000001'), 'order cannot be placed as it cannot be filled')) { @@ -898,9 +901,9 @@ actions.sell = async (payload) => { order.timestamp = timestampSec; order.account = finalAccount; order.symbol = symbol; - order.pair = pair; + order.quoteToken = quoteToken; order.quantity = api.BigNumber(quantity).toFixed(token.precision); - order.price = api.BigNumber(price).toFixed(pairInDb.precision); + order.price = api.BigNumber(price).toFixed(qtInDb.precision); order.priceDec = { $numberDecimal: order.price }; order.expiration = expiration === undefined || expiration > MAX_EXPIRATION_SECS ? timestampSec + MAX_EXPIRATION_SECS @@ -908,7 +911,7 @@ actions.sell = async (payload) => { const orderInDb = await api.db.insert('sellBook', order); - await findMatchingBuyOrders(orderInDb, token.precision, pairInDb.precision); + await findMatchingBuyOrders(orderInDb, token.precision, qtInDb.precision); } } }; @@ -917,7 +920,7 @@ actions.marketBuy = async (payload) => { const { account, symbol, - pair, + quoteToken, quantity, isSignedWithActiveKey, } = payload; @@ -928,7 +931,7 @@ actions.marketBuy = async (payload) => { if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) && symbol && typeof symbol === 'string' - && pair && typeof pair === 'string' + && quoteToken && typeof quoteToken === 'string' && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN(), 'invalid params') ) return; @@ -938,31 +941,31 @@ actions.marketBuy = async (payload) => { // perform a few verifications if (!api.assert(token, 'symbol does not exist')) return; - const pairInDb = await api.db.findOne('pairs', { pair }); + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); - // check if symbol is included in allowedSymbols or the pair is global - if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; - if (!api.assert(countDecimals(quantity) <= pairInDb.precision + // check if symbol is included in allowedBaseTokens or the quoteToken is global + if (!api.assert(qtInDb && (qtInDb.allowedBaseTokens.indexOf(symbol) !== -1 || qtInDb.isGlobal === true), 'pair does not exist')) return; + if (!api.assert(countDecimals(quantity) <= qtInDb.precision && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; // initiate a transfer from sender to contract balance - // lock pair tokens + // lock quoteToken const tokenTransfer = await api.executeSmartContract('tokens', 'transferToContract', { - from: finalAccount, to: CONTRACT_NAME, symbol: pair, quantity, + from: finalAccount, to: CONTRACT_NAME, symbol: quoteToken, quantity, }); // make sure tokens are locked - if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, pair, quantity), 'failed to transfer tokens')) { - let pairQtyRemaining = quantity; + if (api.assert(transferIsSuccessful(tokenTransfer, 'transferToContract', finalAccount, CONTRACT_NAME, quoteToken, quantity), 'failed to transfer tokens')) { + let qtQtyRemaining = quantity; let offset = 0; let volumeTraded = 0; await removeExpiredOrders('sellBook'); - // get the orders that match the symbol and the pair + // get the orders that match the symbol and the quoteToken let sellOrderBook = await api.db.find('sellBook', { symbol, - pair, + quoteToken, }, 1000, offset, [ { index: 'priceDec', descending: false }, @@ -973,21 +976,21 @@ actions.marketBuy = async (payload) => { const nbOrders = sellOrderBook.length; let inc = 0; - while (inc < nbOrders && api.BigNumber(pairQtyRemaining).gt(0)) { + while (inc < nbOrders && api.BigNumber(qtQtyRemaining).gt(0)) { const sellOrder = sellOrderBook[inc]; - const qtyTokensToSend = api.BigNumber(pairQtyRemaining) + const qtyTokensToSend = api.BigNumber(qtQtyRemaining) .dividedBy(sellOrder.price) .toFixed(token.precision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(qtyTokensToSend).lte(sellOrder.quantity) && api.BigNumber(qtyTokensToSend).gt(0)) { if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) - && api.BigNumber(pairQtyRemaining).gt(0), 'the order cannot be filled')) { + && api.BigNumber(qtQtyRemaining).gt(0), 'the order cannot be filled')) { // transfer the tokens to the buyer await api.transferTokens(finalAccount, symbol, qtyTokensToSend, 'user'); // transfer the tokens to the seller - await api.transferTokens(sellOrder.account, pair, pairQtyRemaining, 'user'); + await api.transferTokens(sellOrder.account, quoteToken, qtQtyRemaining, 'user'); // update the sell order const qtyLeftSellOrder = api.BigNumber(sellOrder.quantity) @@ -995,7 +998,7 @@ actions.marketBuy = async (payload) => { .toFixed(token.precision); const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(qtyLeftSellOrder) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (api.BigNumber(qtyLeftSellOrder).gt(0) && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { @@ -1014,47 +1017,47 @@ actions.marketBuy = async (payload) => { } // add the trade to the history - await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, pair, pairInDb.precision, qtyTokensToSend, sellOrder.price, pairQtyRemaining, api.transactionId, sellOrder.txId); + await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, quoteToken, qtInDb.precision, qtyTokensToSend, sellOrder.price, qtQtyRemaining, api.transactionId, sellOrder.txId); // update the volume - volumeTraded = api.BigNumber(volumeTraded).plus(pairQtyRemaining); + volumeTraded = api.BigNumber(volumeTraded).plus(qtQtyRemaining); // set quantity to zero to stop the loop - pairQtyRemaining = '0'; + qtQtyRemaining = '0'; } } else if (api.BigNumber(qtyTokensToSend).gt(0)) { - let qtyPairToSend = api.BigNumber(sellOrder.price) + let qtyQuoteTokenToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); - if (api.BigNumber(qtyPairToSend).gt(pairQtyRemaining)) { - qtyPairToSend = api.BigNumber(sellOrder.price) + if (api.BigNumber(qtyQuoteTokenToSend).gt(qtQtyRemaining)) { + qtyQuoteTokenToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); } - if (api.assert(api.BigNumber(qtyPairToSend).gt(0) - && api.BigNumber(pairQtyRemaining).gt(0), 'the order cannot be filled')) { + if (api.assert(api.BigNumber(qtyQuoteTokenToSend).gt(0) + && api.BigNumber(qtQtyRemaining).gt(0), 'the order cannot be filled')) { // transfer the tokens to the buyer await api.transferTokens(finalAccount, symbol, sellOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(sellOrder.account, pair, qtyPairToSend, 'user'); + await api.transferTokens(sellOrder.account, quoteToken, qtyQuoteTokenToSend, 'user'); // remove the sell order api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); await api.db.remove('sellBook', sellOrder); // update tokensLocked and the quantity to get - pairQtyRemaining = api.BigNumber(pairQtyRemaining) - .minus(qtyPairToSend) - .toFixed(pairInDb.precision); + qtQtyRemaining = api.BigNumber(qtQtyRemaining) + .minus(qtyQuoteTokenToSend) + .toFixed(qtInDb.precision); // add the trade to the history - await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, pair, pairInDb.precision, sellOrder.quantity, sellOrder.price, qtyPairToSend, api.transactionId, sellOrder.txId); + await updateTradesHistory('buy', finalAccount, sellOrder.account, symbol, quoteToken, qtInDb.precision, sellOrder.quantity, sellOrder.price, qtyQuoteTokenToSend, api.transactionId, sellOrder.txId); // update the volume - volumeTraded = api.BigNumber(volumeTraded).plus(qtyPairToSend); + volumeTraded = api.BigNumber(volumeTraded).plus(qtyQuoteTokenToSend); } } @@ -1063,30 +1066,30 @@ actions.marketBuy = async (payload) => { offset += 1000; - if (api.BigNumber(pairQtyRemaining).gt(0)) { + if (api.BigNumber(qtQtyRemaining).gt(0)) { // get the orders that match the symbol and the price sellOrderBook = await api.db.find('sellBook', { symbol, - pair, + quoteToken, }, 1000, offset, [ { index: 'priceDec', descending: false }, { index: '_id', descending: false }, ]); } - } while (sellOrderBook.length > 0 && api.BigNumber(pairQtyRemaining).gt(0)); + } while (sellOrderBook.length > 0 && api.BigNumber(qtQtyRemaining).gt(0)); // return the tokens if the buy order is not filled - if (api.BigNumber(pairQtyRemaining).gt(0)) { - await api.transferTokens(finalAccount, pair, pairQtyRemaining, 'user'); + if (api.BigNumber(qtQtyRemaining).gt(0)) { + await api.transferTokens(finalAccount, quoteToken, qtQtyRemaining, 'user'); } // update the volume and metrics if (api.BigNumber(volumeTraded).gt(0)) { - await updateVolumeMetric(symbol, pair, pairInDb.precision, volumeTraded); + await updateVolumeMetric(symbol, quoteToken, qtInDb.precision, volumeTraded); } - await updateAskMetric(symbol, pair); - await updateBidMetric(symbol, pair); + await updateAskMetric(symbol, quoteToken); + await updateBidMetric(symbol, quoteToken); } }; @@ -1094,7 +1097,7 @@ actions.marketSell = async (payload) => { const { account, symbol, - pair, + quoteToken, quantity, isSignedWithActiveKey, } = payload; @@ -1105,7 +1108,7 @@ actions.marketSell = async (payload) => { if (!api.assert(finalAccount && typeof finalAccount === 'string' && api.isValidAccountName(finalAccount) && symbol && typeof symbol === 'string' - && pair && typeof pair === 'string' + && quoteToken && typeof quoteToken === 'string' && quantity && typeof quantity === 'string' && !api.BigNumber(quantity).isNaN(), 'invalid params') ) return; @@ -1115,10 +1118,10 @@ actions.marketSell = async (payload) => { // perform a few verifications if (!api.assert(token, 'symbol does not exist')) return; - const pairInDb = await api.db.findOne('pairs', { pair }); + const qtInDb = await api.db.findOne('quoteTokens', { quoteToken }); - // check if symbol is included in allowedSymbols or the pair is global - if (!api.assert(pairInDb && (pairInDb.allowedSymbols.indexOf(symbol) !== -1 || pairInDb.allowedSymbols === true), 'pair does not exist')) return; + // check if symbol is included in allowedBaseTokens or the quoteToken is global + if (!api.assert(qtInDb && (qtInDb.allowedBaseTokens.indexOf(symbol) !== -1 || qtInDb.isGlobal === true), 'pair does not exist')) return; if (!api.assert(countDecimals(quantity) <= token.precision && api.BigNumber(quantity).gt(0), 'invalid quantity')) return; @@ -1139,7 +1142,7 @@ actions.marketSell = async (payload) => { // get the orders that match the symbol let buyOrderBook = await api.db.find('buyBook', { symbol, - pair, + quoteToken, }, 1000, offset, [ { index: 'priceDec', descending: true }, @@ -1155,12 +1158,12 @@ actions.marketSell = async (payload) => { if (api.BigNumber(tokensRemaining).lte(buyOrder.quantity)) { let qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(tokensRemaining) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(tokensRemaining) - .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -1169,7 +1172,7 @@ actions.marketSell = async (payload) => { await api.transferTokens(buyOrder.account, symbol, tokensRemaining, 'user'); // transfer the tokens to the seller - await api.transferTokens(finalAccount, pair, qtyTokensToSend, 'user'); + await api.transferTokens(finalAccount, quoteToken, qtyTokensToSend, 'user'); // update the buy order const qtyLeftBuyOrder = api.BigNumber(buyOrder.quantity) @@ -1178,10 +1181,10 @@ actions.marketSell = async (payload) => { const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(qtyLeftBuyOrder) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (api.BigNumber(qtyLeftBuyOrder).gt(0) && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { @@ -1192,7 +1195,7 @@ actions.marketSell = async (payload) => { } else { if (api.BigNumber(buyOrdertokensLocked).gt(0)) { // transfer remaining tokens to buyer since the order can no longer be filled - await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + await api.transferTokens(buyOrder.account, quoteToken, buyOrdertokensLocked, 'user'); } // remove the sell order @@ -1201,7 +1204,7 @@ actions.marketSell = async (payload) => { } // add the trade to the history - await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, pair, pairInDb.precision, tokensRemaining, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); + await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, quoteToken, qtInDb.precision, tokensRemaining, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -1211,12 +1214,12 @@ actions.marketSell = async (payload) => { } else { let qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (qtyTokensToSend > buyOrder.tokensLocked) { qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(pairInDb.precision, api.BigNumber.ROUND_DOWN); + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); } if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) @@ -1225,15 +1228,15 @@ actions.marketSell = async (payload) => { await api.transferTokens(buyOrder.account, symbol, buyOrder.quantity, 'user'); // transfer the tokens to the seller - await api.transferTokens(finalAccount, pair, qtyTokensToSend, 'user'); + await api.transferTokens(finalAccount, quoteToken, qtyTokensToSend, 'user'); const buyOrdertokensLocked = api.BigNumber(buyOrder.tokensLocked) .minus(qtyTokensToSend) - .toFixed(pairInDb.precision); + .toFixed(qtInDb.precision); if (api.BigNumber(buyOrdertokensLocked).gt(0)) { // transfer remaining tokens to buyer since the order can no longer be filled - await api.transferTokens(buyOrder.account, pair, buyOrdertokensLocked, 'user'); + await api.transferTokens(buyOrder.account, quoteToken, buyOrdertokensLocked, 'user'); } // remove the buy order @@ -1246,7 +1249,7 @@ actions.marketSell = async (payload) => { .toFixed(token.precision); // add the trade to the history - await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, pair, pairInDb.precision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); + await updateTradesHistory('sell', buyOrder.account, finalAccount, symbol, quoteToken, qtInDb.precision, buyOrder.quantity, buyOrder.price, qtyTokensToSend, buyOrder.txId, api.transactionId); // update the volume volumeTraded = api.BigNumber(volumeTraded).plus(qtyTokensToSend); @@ -1262,7 +1265,7 @@ actions.marketSell = async (payload) => { // get the orders that match the symbol and the price buyOrderBook = await api.db.find('buyBook', { symbol, - pair, + quoteToken, }, 1000, offset, [ { index: 'priceDec', descending: true }, @@ -1277,9 +1280,9 @@ actions.marketSell = async (payload) => { } if (api.BigNumber(volumeTraded).gt(0)) { - await updateVolumeMetric(symbol, pair, pairInDb.precision, volumeTraded); + await updateVolumeMetric(symbol, quoteToken, qtInDb.precision, volumeTraded); } - await updateAskMetric(symbol, pair); - await updateBidMetric(symbol, pair); + await updateAskMetric(symbol, quoteToken); + await updateBidMetric(symbol, quoteToken); } }; diff --git a/test/dmarket.js b/test/dmarket.js index 8fb9b61e..9ed5ecc3 100644 --- a/test/dmarket.js +++ b/test/dmarket.js @@ -139,13 +139,13 @@ async function assertBalances(accounts, balances, symbol, contract = false) { } } -async function verifyAskBid(symbol, pair, ask, bid) { +async function verifyAskBid(symbol, quoteToken, ask, bid) { const res = await database1.findOne({ contract: 'dmarket', table: 'metrics', query: { symbol, - pair, + quoteToken, }, }); @@ -154,25 +154,25 @@ async function verifyAskBid(symbol, pair, ask, bid) { assert(BigNumber(res.highestBid).isEqualTo(bid), `bid ${bid} not equal to ${res.highestBid}`); } -async function assertPair(pair, symbols) { +async function assertPair(quoteToken, symbols) { const res = await database1.findOne({ contract: 'dmarket', - table: 'pairs', + table: 'quoteTokens', query: { - pair, + quoteToken, }, }); console.log(res); - assert(res, 'pair not found'); + assert(res, 'quoteToken not found'); if (symbols !== true) { - assert(res.allowedSymbols !== true, 'pair is global'); + assert(res.isGlobal !== true, 'quoteToken is global'); symbols.forEach((symbol) => { - assert(res.allowedSymbols.includes(symbol), `symbol ${symbol} not found in pair`); + assert(res.allowedBaseTokens.includes(symbol), `symbol ${symbol} not found in this pair`); }); - } else assert(res.allowedSymbols === true, 'pair is not global'); + } else assert(res.isGlobal === true, 'quoteToken is not global'); } function assertError(tx, message) { @@ -246,15 +246,15 @@ describe('dMarket Smart Contract', function () { const transactions = []; transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": false, "pair": "TKN", "symbol": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": 5, "symbol": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": 5 }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "BEE", "symbol": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": false, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": 5, "baseToken": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": 5 }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "BEE", "baseToken": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"100", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); const block = { refHiveBlockNumber: 12345678901, @@ -270,11 +270,11 @@ describe('dMarket Smart Contract', function () { const txs = res.transactions; assertError(txs[2], 'you must use a custom_json signed with your active key'); - assertError(txs[3], 'invalid pair'); - assertError(txs[4], 'invalid symbol'); - assertError(txs[5], 'pair and symbol can not be the same'); - assertError(txs[6], 'symbol does not exist'); - assertError(txs[7], 'pair symbol does not exist'); + assertError(txs[3], 'invalid quoteToken'); + assertError(txs[4], 'invalid baseToken'); + assertError(txs[5], 'quoteToken and baseToken can not be the same'); + assertError(txs[6], 'baseToken does not exist'); + assertError(txs[7], 'quoteToken does not exist'); assertError(txs[10], 'you must have enough tokens to cover the pair creation fee'); resolve(); @@ -298,7 +298,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); const block = { refHiveBlockNumber: 12345678901, @@ -325,7 +325,7 @@ describe('dMarket Smart Contract', function () { }); }); - it('does not add symbol into existing pair', (done) => { + it('does not add baseToken into existing quoteToken', (done) => { new Promise(async (resolve) => { await loadPlugin(blockchain); database1 = new Database(); @@ -338,10 +338,10 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'dmarket', 'addGlobalPair', '{ "pair": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "XYZ" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'dmarket', 'setGlobalQuoteToken', '{ "quoteToken": "TKN" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); const block = { @@ -359,8 +359,8 @@ describe('dMarket Smart Contract', function () { await assertPair('TKN', true); - assertError(txs[6], 'symbol is already in the pair'); - assertError(txs[8], 'can not add symbol to a global pair'); + assertError(txs[6], 'baseToken is already in this pair'); + assertError(txs[8], 'can not add another baseToken to a global quoteToken'); resolve(); }) @@ -371,7 +371,7 @@ describe('dMarket Smart Contract', function () { }); }); - it('adds symbol into existing pair', (done) => { + it('adds baseToken into existing quoteToken', (done) => { new Promise(async (resolve) => { await loadPlugin(blockchain); database1 = new Database(); @@ -384,8 +384,8 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"1200", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "XYZ" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); const block = { refHiveBlockNumber: 12345678901, @@ -424,7 +424,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); let block = { @@ -440,7 +440,7 @@ describe('dMarket Smart Contract', function () { await assertNoErrorInLastBlock(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.1" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); block = { refHiveBlockNumber: 12345678902, @@ -462,7 +462,7 @@ describe('dMarket Smart Contract', function () { table: 'buyBook', query: { symbol: 'BEE', - pair: 'TKN', + quoteToken: 'TKN', txId: txs[0].txId, }, }); @@ -498,7 +498,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.1" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); const block = { @@ -537,7 +537,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -552,7 +552,7 @@ describe('dMarket Smart Contract', function () { await assertNoErrorInLastBlock(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); block = { refHiveBlockNumber: 12345678902, @@ -574,7 +574,7 @@ describe('dMarket Smart Contract', function () { table: 'sellBook', query: { symbol: 'BEE', - pair: 'TKN', + quoteToken: 'TKN', txId: txs[0].txId, }, }); @@ -608,7 +608,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); const block = { @@ -647,7 +647,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -662,9 +662,9 @@ describe('dMarket Smart Contract', function () { await assertNoErrorInLastBlock(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "18.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); block = { refHiveBlockNumber: 12345678902, @@ -709,7 +709,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -724,11 +724,11 @@ describe('dMarket Smart Contract', function () { await assertNoErrorInLastBlock(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "24.3" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "135", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "135", "price": "0.18" }')); block = { refHiveBlockNumber: 12345678902, @@ -771,7 +771,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -787,8 +787,8 @@ describe('dMarket Smart Contract', function () { transactions = []; transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); block = { refHiveBlockNumber: 12345678902, @@ -831,7 +831,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -850,12 +850,12 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "18" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"140", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "140", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "140", "price": "0.16" }')); block = { refHiveBlockNumber: 12345678902, @@ -900,7 +900,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -915,11 +915,11 @@ describe('dMarket Smart Contract', function () { await assertNoErrorInLastBlock(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "22.2" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "22.2" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "22.2" }')); block = { refHiveBlockNumber: 12345678902, @@ -962,7 +962,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { refHiveBlockNumber: 12345678901, @@ -981,12 +981,12 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "50" }')); transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "12" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "55", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "312", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "50", "price": "0.17" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "55", "price": "0.18" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "312", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"210", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "210" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "210" }')); block = { refHiveBlockNumber: 12345678902, @@ -1029,7 +1029,7 @@ describe('dMarket Smart Contract', function () { transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "pair": "TKN", "symbol": "BEE" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a001", "quantity":"100", "isSignedWithActiveKey":true }`)); transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a002", "quantity":"100", "isSignedWithActiveKey":true }`)); @@ -1055,9 +1055,9 @@ describe('dMarket Smart Contract', function () { transactions = []; transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.15" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'b002', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.20" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'b003', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.16" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.15" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b002', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.20" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b003', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.16" }')); block = { refHiveBlockNumber: 12345678902, @@ -1077,9 +1077,9 @@ describe('dMarket Smart Contract', function () { transactions = []; transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.23" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'a003', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.21" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'a002', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "10", "price": "0.25" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.23" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a003', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.21" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a002', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.25" }')); block = { refHiveBlockNumber: 12345678903, @@ -1100,10 +1100,10 @@ describe('dMarket Smart Contract', function () { transactions = []; // sell to the highest bid - transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "20" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "20" }')); // buy from the lowest ask - transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "pair": "TKN", "quantity": "4.4" }')); + transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "4.4" }')); block = { refHiveBlockNumber: 12345678904, @@ -1124,7 +1124,7 @@ describe('dMarket Smart Contract', function () { table: 'metrics', query: { symbol: 'BEE', - pair: 'TKN', + quoteToken: 'TKN', }, }); From 607652d3f9598da2e33de030407704435b3bf11c Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Sat, 9 Oct 2021 18:01:07 +0500 Subject: [PATCH 6/8] fixes --- contracts/dmarket.js | 106 ++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 68 deletions(-) diff --git a/contracts/dmarket.js b/contracts/dmarket.js index c45ca9e7..c38d7e99 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -431,16 +431,13 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { while (inc < nbOrders && api.BigNumber(buyOrder.quantity).gt(0)) { const sellOrder = sellOrderBook[inc]; + // gets the minimum quantity of quoteToken required to fill the order + const minimumToFillOrder = api.BigNumber(1).shiftedBy(-qtPrecision); + if (api.BigNumber(buyOrder.quantity).lte(sellOrder.quantity)) { - let qtyTokensToSend = api.BigNumber(sellOrder.price) + const qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(qtPrecision); - - if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { - qtyTokensToSend = api.BigNumber(sellOrder.price) - .multipliedBy(buyOrder.quantity) - .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(buyOrder.quantity).gt(0), 'the order cannot be filled')) { @@ -456,10 +453,10 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { .toFixed(tokenPrecision); const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(qtyLeftSellOrder) - .toFixed(qtPrecision); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(qtyLeftSellOrder).gt(0) - && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + && (api.BigNumber(nbTokensToFillOrder).gte(minimumToFillOrder))) { sellOrder.quantity = qtyLeftSellOrder; await api.db.update('sellBook', sellOrder); @@ -494,15 +491,9 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { api.emit('orderClosed', { account: buyOrder.account, type: 'buy', txId: buyOrder.txId }); } } else { - let qtyTokensToSend = api.BigNumber(sellOrder.price) + const qtyTokensToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(qtPrecision); - - if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { - qtyTokensToSend = api.BigNumber(sellOrder.price) - .multipliedBy(sellOrder.quantity) - .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(buyOrder.quantity).gt(0), 'the order cannot be filled')) { @@ -527,9 +518,9 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { // check if the order can still be filled const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(qtPrecision); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { + if (api.BigNumber(nbTokensToFillOrder).lt(minimumToFillOrder)) { await api.transferTokens(account, quoteToken, buyOrder.tokensLocked, 'user'); // stop the loop and remove buy order if it can not be filled @@ -613,16 +604,13 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { while (inc < nbOrders && api.BigNumber(sellOrder.quantity).gt(0)) { const buyOrder = buyOrderBook[inc]; + // gets the minimum quantity of quoteToken required to fill the order + const minimumToFillOrder = api.BigNumber(1).shiftedBy(-qtPrecision); + if (api.BigNumber(sellOrder.quantity).lte(buyOrder.quantity)) { - let qtyTokensToSend = api.BigNumber(buyOrder.price) + const qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(qtPrecision); - - if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { - qtyTokensToSend = api.BigNumber(buyOrder.price) - .multipliedBy(sellOrder.quantity) - .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(sellOrder.quantity).gt(0), 'the order cannot be filled')) { @@ -642,10 +630,10 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { .toFixed(qtPrecision); const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(qtyLeftBuyOrder) - .toFixed(qtPrecision); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(qtyLeftBuyOrder).gt(0) - && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + && (api.BigNumber(nbTokensToFillOrder).gte(minimumToFillOrder))) { buyOrder.quantity = qtyLeftBuyOrder; buyOrder.tokensLocked = buyOrdertokensLocked; @@ -671,15 +659,9 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { api.emit('orderClosed', { account: sellOrder.account, type: 'sell', txId: sellOrder.txId }); } } else { - let qtyTokensToSend = api.BigNumber(buyOrder.price) + const qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(qtPrecision); - - if (qtyTokensToSend > buyOrder.tokensLocked) { - qtyTokensToSend = api.BigNumber(buyOrder.price) - .multipliedBy(buyOrder.quantity) - .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(sellOrder.quantity).gt(0), 'the order cannot be filled')) { @@ -710,9 +692,9 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { // check if the order can still be filled const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(qtPrecision); + .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); - if (api.BigNumber(nbTokensToFillOrder).lt('0.00000001')) { + if (api.BigNumber(nbTokensToFillOrder).lt(minimumToFillOrder)) { await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); // stop the loop and remove sell order if it can not be filled @@ -978,6 +960,9 @@ actions.marketBuy = async (payload) => { while (inc < nbOrders && api.BigNumber(qtQtyRemaining).gt(0)) { const sellOrder = sellOrderBook[inc]; + // gets the minimum quantity of quoteToken required to fill the order + const minimumToFillOrder = api.BigNumber(1).shiftedBy(-qtInDb.precision); + const qtyTokensToSend = api.BigNumber(qtQtyRemaining) .dividedBy(sellOrder.price) .toFixed(token.precision, api.BigNumber.ROUND_DOWN); @@ -998,10 +983,10 @@ actions.marketBuy = async (payload) => { .toFixed(token.precision); const nbTokensToFillOrder = api.BigNumber(sellOrder.price) .multipliedBy(qtyLeftSellOrder) - .toFixed(qtInDb.precision); + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(qtyLeftSellOrder).gt(0) - && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + && (api.BigNumber(nbTokensToFillOrder).gte(minimumToFillOrder))) { sellOrder.quantity = qtyLeftSellOrder; await api.db.update('sellBook', sellOrder); @@ -1026,15 +1011,9 @@ actions.marketBuy = async (payload) => { qtQtyRemaining = '0'; } } else if (api.BigNumber(qtyTokensToSend).gt(0)) { - let qtyQuoteTokenToSend = api.BigNumber(sellOrder.price) + const qtyQuoteTokenToSend = api.BigNumber(sellOrder.price) .multipliedBy(sellOrder.quantity) - .toFixed(qtInDb.precision); - - if (api.BigNumber(qtyQuoteTokenToSend).gt(qtQtyRemaining)) { - qtyQuoteTokenToSend = api.BigNumber(sellOrder.price) - .multipliedBy(sellOrder.quantity) - .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyQuoteTokenToSend).gt(0) && api.BigNumber(qtQtyRemaining).gt(0), 'the order cannot be filled')) { @@ -1155,16 +1134,13 @@ actions.marketSell = async (payload) => { while (inc < nbOrders && api.BigNumber(tokensRemaining).gt(0)) { const buyOrder = buyOrderBook[inc]; + // gets the minimum quantity of quoteToken required to fill the order + const minimumToFillOrder = api.BigNumber(1).shiftedBy(-qtInDb.precision); + if (api.BigNumber(tokensRemaining).lte(buyOrder.quantity)) { - let qtyTokensToSend = api.BigNumber(buyOrder.price) + const qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(tokensRemaining) - .toFixed(qtInDb.precision); - - if (api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)) { - qtyTokensToSend = api.BigNumber(buyOrder.price) - .multipliedBy(tokensRemaining) - .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(tokensRemaining).gt(0), 'the order cannot be filled')) { @@ -1184,10 +1160,10 @@ actions.marketSell = async (payload) => { .toFixed(qtInDb.precision); const nbTokensToFillOrder = api.BigNumber(buyOrder.price) .multipliedBy(qtyLeftBuyOrder) - .toFixed(qtInDb.precision); + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(qtyLeftBuyOrder).gt(0) - && (api.BigNumber(nbTokensToFillOrder).gte('0.00000001'))) { + && (api.BigNumber(nbTokensToFillOrder).gte(minimumToFillOrder))) { buyOrder.quantity = qtyLeftBuyOrder; buyOrder.tokensLocked = buyOrdertokensLocked; @@ -1212,15 +1188,9 @@ actions.marketSell = async (payload) => { tokensRemaining = 0; } } else { - let qtyTokensToSend = api.BigNumber(buyOrder.price) + const qtyTokensToSend = api.BigNumber(buyOrder.price) .multipliedBy(buyOrder.quantity) - .toFixed(qtInDb.precision); - - if (qtyTokensToSend > buyOrder.tokensLocked) { - qtyTokensToSend = api.BigNumber(buyOrder.price) - .multipliedBy(buyOrder.quantity) - .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); - } + .toFixed(qtInDb.precision, api.BigNumber.ROUND_DOWN); if (api.assert(api.BigNumber(qtyTokensToSend).gt(0) && api.BigNumber(tokensRemaining).gt(0), 'the order cannot be filled')) { From 548ef8b624b03943b517a05fa08a48255aa68e0d Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Sun, 10 Oct 2021 22:20:05 +0500 Subject: [PATCH 7/8] new fixtures method --- test/dmarket.js | 723 ++++++++++++++++++++---------------------------- 1 file changed, 300 insertions(+), 423 deletions(-) diff --git a/test/dmarket.js b/test/dmarket.js index 9ed5ecc3..2d4e5ad8 100644 --- a/test/dmarket.js +++ b/test/dmarket.js @@ -1,116 +1,30 @@ - /* eslint-disable */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-undef */ /* eslint-disable no-console */ /* eslint-disable func-names */ -const { fork } = require('child_process'); const assert = require('assert'); +const BigNumber = require('bignumber.js'); const { MongoClient } = require('mongodb'); -const { default: BigNumber } = require('bignumber.js'); const { CONSTANTS } = require('../libs/Constants'); const { Database } = require('../libs/Database'); const blockchain = require('../plugins/Blockchain'); const { Transaction } = require('../libs/Transaction'); const { setupContractPayload } = require('../libs/util/contractUtil'); +const { Fixture, conf } = require('../libs/util/testing/Fixture'); +const { TableAsserts } = require('../libs/util/testing/TableAsserts'); +const { assertError } = require('../libs/util/testing/Asserts'); -const conf = { - chainId: 'test-chain-id', - genesisHiveBlock: 2000000, - dataDirectory: './test/data/', - databaseFileName: 'database.db', - autosaveInterval: 0, - javascriptVMTimeout: 10000, - databaseURL: 'mongodb://localhost:27017', - databaseName: 'testssc', - streamNodes: ['https://api.hive.blog'], -}; - -const plugins = {}; -let jobs = new Map(); -let currentJobId = 0; -let database1 = null; - -function send(pluginName, from, message) { - const plugin = plugins[pluginName]; - const newMessage = { - ...message, - to: plugin.name, - from, - type: 'request', - }; - currentJobId += 1; - newMessage.jobId = currentJobId; - plugin.cp.send(newMessage); - return new Promise((resolve) => { - jobs.set(currentJobId, { - message: newMessage, - resolve, - }); - }); -} - - -// function to route the IPC requests -const route = (message) => { - const { to, type, jobId } = message; - if (to) { - if (to === 'MASTER') { - if (type && type === 'request') { - // do something - } else if (type && type === 'response' && jobId) { - const job = jobs.get(jobId); - if (job && job.resolve) { - const { resolve } = job; - jobs.delete(jobId); - resolve(message); - } - } - } else if (type && type === 'broadcast') { - plugins.forEach((plugin) => { - plugin.cp.send(message); - }); - } else if (plugins[to]) { - plugins[to].cp.send(message); - } else { - console.error('ROUTING ERROR: ', message); - } - } -}; - -const loadPlugin = (newPlugin) => { - const plugin = {}; - plugin.name = newPlugin.PLUGIN_NAME; - plugin.cp = fork(newPlugin.PLUGIN_PATH, [], { silent: true }); - plugin.cp.on('message', msg => route(msg)); - plugin.cp.stdout.on('data', data => console.log(`[${newPlugin.PLUGIN_NAME}]`, data.toString())); - plugin.cp.stderr.on('data', data => console.error(`[${newPlugin.PLUGIN_NAME}]`, data.toString())); - - plugins[newPlugin.PLUGIN_NAME] = plugin; - - return send(newPlugin.PLUGIN_NAME, 'MASTER', { action: 'init', payload: conf }); -}; - -const unloadPlugin = (plugin) => { - plugins[plugin.PLUGIN_NAME].cp.kill('SIGINT'); - plugins[plugin.PLUGIN_NAME] = null; - jobs = new Map(); - currentJobId = 0; -}; +const fixture = new Fixture(); +const tableAsserts = new TableAsserts(fixture); const tknContractPayload = setupContractPayload('tokens', './contracts/tokens.js'); const dmarketContractPayload = setupContractPayload('dmarket', './contracts/dmarket.js'); -let txId = 1; -function getNextTxId() { - txId += 1; - return `TXID${txId.toString().padStart(8, '0')}`; -} - async function assertBalances(accounts, balances, symbol, contract = false) { - const res = await database1.find({ + const res = await fixture.database.find({ contract: 'tokens', table: contract ? 'contractsBalances' : 'balances', query: { @@ -140,7 +54,7 @@ async function assertBalances(accounts, balances, symbol, contract = false) { } async function verifyAskBid(symbol, quoteToken, ask, bid) { - const res = await database1.findOne({ + const res = await fixture.database.findOne({ contract: 'dmarket', table: 'metrics', query: { @@ -155,7 +69,7 @@ async function verifyAskBid(symbol, quoteToken, ask, bid) { } async function assertPair(quoteToken, symbols) { - const res = await database1.findOne({ + const res = await fixture.database.findOne({ contract: 'dmarket', table: 'quoteTokens', query: { @@ -175,27 +89,12 @@ async function assertPair(quoteToken, symbols) { } else assert(res.isGlobal === true, 'quoteToken is not global'); } -function assertError(tx, message) { - const logs = JSON.parse(tx.logs); - assert(logs.errors, `No error in logs. Error expected with message ${message}`); - assert.equal(logs.errors[0], message, `Error expected with message ${message}. Instead got ${logs.errors[0]}`); -} - -async function assertNoErrorInLastBlock() { - const { transactions } = await database1.getLatestBlockInfo(); - for (let i = 0; i < transactions.length; i += 1) { - const logs = JSON.parse(transactions[i].logs); - assert(!logs.errors, `Tx #${i} had unexpected error ${logs.errors}`); - } -} - describe('dMarket Smart Contract', function () { this.timeout(20000); before((done) => { new Promise(async (resolve) => { - client = await MongoClient.connect(conf.databaseURL, - { useNewUrlParser: true, useUnifiedTopology: true }); + client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true, useUnifiedTopology: true }); db = await client.db(conf.databaseName); await db.dropDatabase(); resolve(); @@ -238,35 +137,34 @@ describe('dMarket Smart Contract', function () { it('does not create a new pair', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - - await database1.init(conf.databaseURL, conf.databaseName); + + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": false, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": 5, "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": 5 }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "BEE", "baseToken": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": false, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": 5, "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": 5 }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "TKN" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "BEE", "baseToken": "TKN" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; assertError(txs[2], 'you must use a custom_json signed with your active key'); @@ -280,37 +178,35 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('creates a new pair', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertPair('TKN', ['BEE']); @@ -319,42 +215,40 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('does not add baseToken into existing quoteToken', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'dmarket', 'setGlobalQuoteToken', '{ "quoteToken": "TKN" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'dmarket', 'setGlobalQuoteToken', '{ "quoteToken": "TKN" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; await assertPair('TKN', true); @@ -365,39 +259,37 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('adds baseToken into existing quoteToken', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"1200", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"1200", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "XYZ", "precision": 8, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "XYZ" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertPair('TKN', ['BEE', 'XYZ']); @@ -406,58 +298,57 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('creates a buy order for user added pair', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); - const result = await database1.findOne({ + const result = await fixture.database.findOne({ contract: 'dmarket', table: 'buyBook', query: { @@ -479,39 +370,37 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('does not create a buy order if pair does not exist', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - - await database1.init(conf.databaseURL, conf.databaseName); + + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "123.456" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.1" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; assertError(txs[5], 'pair does not exist'); @@ -519,57 +408,56 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('creates a sell order for user added pair', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); - const result = await database1.findOne({ + const result = await fixture.database.findOne({ contract: 'dmarket', table: 'sellBook', query: { @@ -590,38 +478,36 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('does not create a sell order if pair does not exist', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + const refBlockNumber = fixture.getNextRefBlockNumber(); const transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); const block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - const res = await database1.getLatestBlockInfo(); + const res = await fixture.database.getLatestBlockInfo(); const txs = res.transactions; assertError(txs[4], 'pair does not exist'); @@ -629,57 +515,56 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('buys from one seller', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - - await database1.init(conf.databaseURL, conf.databaseName); + + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "18.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "18.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'james'], ['0', '100'], 'BEE'); await assertBalances(['ali-h', 'james'], ['16', '2.17'], 'TKN'); @@ -689,61 +574,60 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('buys from multiple sellers', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - - await database1.init(conf.databaseURL, conf.databaseName); + + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "24.3" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "135", "price": "0.18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "24.3" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "135", "price": "0.18" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0', '0', '50', '135'], 'BEE'); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['16', '1.7', '4.5', '2.1'], 'TKN'); @@ -753,57 +637,56 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('sells to one buyer', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.17" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'james'], ['0', '100'], 'BEE'); await assertBalances(['ali-h', 'james'], ['17', '38'], 'TKN'); @@ -813,64 +696,63 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('sells to multiple buyers', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"140", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "140", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"140", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "140", "price": "0.16" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['40', '50', '50', '0'], 'BEE'); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['2', '9.5', '9', '23.9'], 'TKN'); @@ -880,61 +762,60 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('market buy from multiple sellers', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"nomi", "quantity":"10", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"punkman", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "22.2" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "22.2" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'punkman', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'nomi', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "22.2" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "22.2" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0', '0', '50', '135'], 'BEE'); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['16', '1.7', '4.5', '0'], 'TKN'); @@ -944,64 +825,63 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('market sell to multiple buyers', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "10" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "50" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "12" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "punkman", "quantity": "10" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "ali-h", "quantity": "50" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "nomi", "quantity": "12" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "55", "price": "0.18" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "312", "price": "0.16" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'punkman', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "55", "price": "0.18" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "312", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'nomi', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.17" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"210", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'james', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "210" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"james", "quantity":"210", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "210" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - // const res = await database1.getLatestBlockInfo(); + // const res = await fixture.database.getLatestBlockInfo(); // const txs = res.transactions; - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['105', '50', '55', '0'], 'BEE'); await assertBalances(['ali-h', 'nomi', 'punkman', 'james'], ['0.08', '3.5', '0.1', '35.2'], 'TKN'); @@ -1011,115 +891,113 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); it('verify metrics', (done) => { new Promise(async (resolve) => { - await loadPlugin(blockchain); - database1 = new Database(); - await database1.init(conf.databaseURL, conf.databaseName); + await fixture.setUp(); + let refBlockNumber = fixture.getNextRefBlockNumber(); let transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(12345678901, getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"600", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a001", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a002", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a003", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a001", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a002", "quantity":"100", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"a003", "quantity":"100", "isSignedWithActiveKey":true }`)); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b001", "quantity": "100" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b002", "quantity": "100" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b003", "quantity": "100" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b001", "quantity": "100" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b002", "quantity": "100" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "b003", "quantity": "100" }')); let block = { - refHiveBlockNumber: 12345678901, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:00', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); + refBlockNumber = fixture.getNextRefBlockNumber(); // bid transactions = []; - transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.15" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'b002', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.20" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'b003', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'b001', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.15" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'b002', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.20" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'b003', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.16" }')); block = { - refHiveBlockNumber: 12345678902, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await verifyAskBid('BEE', 'TKN', '0', '0.20'); + refBlockNumber = fixture.getNextRefBlockNumber(); // ask transactions = []; - transactions = []; - transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.23" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'a003', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.21" }')); - transactions.push(new Transaction(38145386, getNextTxId(), 'a002', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.25" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'a001', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.23" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'a003', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.21" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'a002', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10", "price": "0.25" }')); block = { - refHiveBlockNumber: 12345678903, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await verifyAskBid('BEE', 'TKN', '0.21', '0.20'); + refBlockNumber = fixture.getNextRefBlockNumber(); // update after order filling transactions = []; - transactions = []; // sell to the highest bid - transactions.push(new Transaction(38145386, getNextTxId(), 'a001', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "20" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'a001', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "20" }')); // buy from the lowest ask - transactions.push(new Transaction(38145386, getNextTxId(), 'b001', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "4.4" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'b001', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "4.4" }')); block = { - refHiveBlockNumber: 12345678904, + refHiveBlockNumber: refBlockNumber, refHiveBlockId: 'ABCD1', prevRefHiveBlockId: 'ABCD2', timestamp: '2021-03-12T00:00:03', transactions, }; - await send(blockchain.PLUGIN_NAME, 'MASTER', { action: blockchain.PLUGIN_ACTIONS.PRODUCE_NEW_BLOCK_SYNC, payload: block }); + await fixture.sendBlock(block); - await assertNoErrorInLastBlock(); + await tableAsserts.assertNoErrorInLastBlock(); await verifyAskBid('BEE', 'TKN', '0.25', '0.15'); - const metric = await database1.findOne({ + const metric = await fixture.database.findOne({ contract: 'dmarket', table: 'metrics', query: { @@ -1136,8 +1014,7 @@ describe('dMarket Smart Contract', function () { resolve(); }) .then(() => { - unloadPlugin(blockchain); - database1.close(); + fixture.tearDown(); done(); }); }); From 1886f82845b7270fccd1efab8f784c0a33f3212d Mon Sep 17 00:00:00 2001 From: Ali H <34513931+ali-h@users.noreply.github.com> Date: Thu, 14 Oct 2021 16:12:25 +0500 Subject: [PATCH 8/8] indexes optimization, new test units --- contracts/dmarket.js | 40 ++--- test/dmarket.js | 344 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 366 insertions(+), 18 deletions(-) diff --git a/contracts/dmarket.js b/contracts/dmarket.js index c38d7e99..766260b7 100644 --- a/contracts/dmarket.js +++ b/contracts/dmarket.js @@ -15,10 +15,10 @@ actions.createSSC = async () => { if (tableExists === false) { await api.db.createTable('quoteTokens', ['quoteToken']); - await api.db.createTable('buyBook', ['symbol', 'quoteToken', 'account', 'priceDec', 'expiration', 'txId']); - await api.db.createTable('sellBook', ['symbol', 'quoteToken', 'account', 'priceDec', 'expiration', 'txId']); - await api.db.createTable('tradesHistory', ['symbol', 'quoteToken']); - await api.db.createTable('metrics', ['symbol', 'quoteToken']); + await api.db.createTable('buyBook', [{ name: 'byPriceDecDesc', index: { quoteToken: 1, symbol: 1, priceDec: -1 } }, 'expiration']); + await api.db.createTable('sellBook', [{ name: 'byPriceDecAsc', index: { quoteToken: 1, symbol: 1, priceDec: 1 } }, 'expiration']); + await api.db.createTable('tradesHistory', [{ name: 'byTimestamp', index: { quoteToken: 1, symbol: 1, timestamp: 1 } }]); + await api.db.createTable('metrics', [], { primaryKey: ['quoteToken', 'symbol'] }); // default global quoteTokens const quoteTokens = [ @@ -239,7 +239,7 @@ const updateBidMetric = async (symbol, quoteToken) => { quoteToken, }, 1, 0, [ - { index: 'priceDec', descending: true }, + { index: 'byPriceDecDesc', descending: false }, ]); @@ -261,7 +261,7 @@ const updateAskMetric = async (symbol, quoteToken) => { quoteToken, }, 1, 0, [ - { index: 'priceDec', descending: false }, + { index: 'byPriceDecAsc', descending: false }, ]); if (sellOrderBook.length > 0) { @@ -377,11 +377,11 @@ const removeExpiredOrders = async (table) => { if (table === 'buyBook') { api.emit('orderExpired', { type: 'buy', txId: order.txId }); - await updateAskMetric(order.symbol); + await updateAskMetric(order.symbol, order.quoteToken); } else { api.emit('orderExpired', { type: 'sell', txId: order.txId }); - await updateBidMetric(order.symbol); + await updateBidMetric(order.symbol, order.quoteToken); } } @@ -421,7 +421,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { }, }, 1000, offset, [ - { index: 'priceDec', descending: false }, + { index: 'byPriceDecAsc', descending: false }, { index: '_id', descending: false }, ]); @@ -521,7 +521,9 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(nbTokensToFillOrder).lt(minimumToFillOrder)) { - await api.transferTokens(account, quoteToken, buyOrder.tokensLocked, 'user'); + if (api.BigNumber(buyOrder.tokensLocked).gt(0)) { + await api.transferTokens(account, quoteToken, buyOrder.tokensLocked, 'user'); + } // stop the loop and remove buy order if it can not be filled buyOrder.quantity = '0'; @@ -552,7 +554,7 @@ const findMatchingSellOrders = async (order, tokenPrecision, qtPrecision) => { }, }, 1000, offset, [ - { index: 'priceDec', descending: false }, + { index: 'byPriceDecAsc', descending: false }, { index: '_id', descending: false }, ]); } @@ -594,7 +596,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { }, }, 1000, offset, [ - { index: 'priceDec', descending: true }, + { index: 'byPriceDecDesc', descending: false }, { index: '_id', descending: false }, ]); @@ -695,7 +697,9 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { .toFixed(qtPrecision, api.BigNumber.ROUND_DOWN); if (api.BigNumber(nbTokensToFillOrder).lt(minimumToFillOrder)) { - await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); + if (api.BigNumber(sellOrder.quantity).gt(0)) { + await api.transferTokens(account, symbol, sellOrder.quantity, 'user'); + } // stop the loop and remove sell order if it can not be filled sellOrder.quantity = '0'; @@ -726,7 +730,7 @@ const findMatchingBuyOrders = async (order, tokenPrecision, qtPrecision) => { }, }, 1000, offset, [ - { index: 'priceDec', descending: true }, + { index: 'byPriceDecDesc', descending: false }, { index: '_id', descending: false }, ]); } @@ -950,7 +954,7 @@ actions.marketBuy = async (payload) => { quoteToken, }, 1000, offset, [ - { index: 'priceDec', descending: false }, + { index: 'byPriceDecAsc', descending: false }, { index: '_id', descending: false }, ]); @@ -1052,7 +1056,7 @@ actions.marketBuy = async (payload) => { quoteToken, }, 1000, offset, [ - { index: 'priceDec', descending: false }, + { index: 'byPriceDecAsc', descending: false }, { index: '_id', descending: false }, ]); } @@ -1124,7 +1128,7 @@ actions.marketSell = async (payload) => { quoteToken, }, 1000, offset, [ - { index: 'priceDec', descending: true }, + { index: 'byPriceDecDesc', descending: false }, { index: '_id', descending: false }, ]); @@ -1238,7 +1242,7 @@ actions.marketSell = async (payload) => { quoteToken, }, 1000, offset, [ - { index: 'priceDec', descending: true }, + { index: 'byPriceDecDesc', descending: false }, { index: '_id', descending: false }, ]); } diff --git a/test/dmarket.js b/test/dmarket.js index 2d4e5ad8..d71386ba 100644 --- a/test/dmarket.js +++ b/test/dmarket.js @@ -53,6 +53,20 @@ async function assertBalances(accounts, balances, symbol, contract = false) { } } +async function verifyOpenOrders(table, account, symbol, quoteToken, num) { + const book = await fixture.database.find({ + contract: 'dmarket', + table, + query: { + account, + symbol, + quoteToken + } + }); + + assert.equal(book.length, num); +} + async function verifyAskBid(symbol, quoteToken, ask, bid) { const res = await fixture.database.findOne({ contract: 'dmarket', @@ -895,6 +909,336 @@ describe('dMarket Smart Contract', function () { done(); }); }); + + it('removes dust sell orders', (done) => { + new Promise(async (resolve) => { + + await fixture.setUp(); + + let refBlockNumber = fixture.getNextRefBlockNumber(); + let transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "16" }')); + + let block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await fixture.sendBlock(block); + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "99.99999999", "price": "0.16" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await verifyOpenOrders('sellBook', 'ali-h', 'BEE', 'TKN', 0); + + await assertBalances(['ali-h', 'james'], ['0.00000001', '99.99999999'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['15.999', '0.001'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + fixture.tearDown(); + done(); + }); + }); + + it('removes dust buy orders', (done) => { + new Promise(async (resolve) => { + + await fixture.setUp(); + + let refBlockNumber = fixture.getNextRefBlockNumber(); + let transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "16" }')); + + let block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await fixture.sendBlock(block); + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100", "price": "0.16" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "100.00000001", "price": "0.16" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await verifyOpenOrders('buyBook', 'james', 'BEE', 'TKN', 0); + + await assertBalances(['ali-h', 'james'], ['0', '100'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['16', '0'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + fixture.tearDown(); + done(); + }); + }); + + it('removes expired buy orders', (done) => { + new Promise(async (resolve) => { + + await fixture.setUp(); + + let refBlockNumber = fixture.getNextRefBlockNumber(); + let transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); + + let block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await fixture.sendBlock(block); + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "20", "price": "0.19", "expiration": 100 }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'buy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "20", "price": "0.18" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + // should hit the 0.18 order and expire 0.19 one + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:10:00', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['90', '10'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['1.8', '51.4'], 'TKN'); + await assertBalances(['dmarket'], ['1.8'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + // should hit the 0.18 order and expire 0.19 one + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'marketSell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "10" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-04-12T00:00:06', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['90', '10'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['1.8', '53.2'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + fixture.tearDown(); + done(); + }); + }); + + it('removes expired sell orders', (done) => { + new Promise(async (resolve) => { + + await fixture.setUp(); + + let refBlockNumber = fixture.getNextRefBlockNumber(); + let transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'update', JSON.stringify(tknContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'contract', 'deploy', JSON.stringify(dmarketContractPayload))); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), CONSTANTS.HIVE_ENGINE_ACCOUNT, 'tokens', 'transfer', `{ "symbol":"${CONSTANTS.UTILITY_TOKEN_SYMBOL}", "to":"ali-h", "quantity":"700", "isSignedWithActiveKey":true }`)); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'create', '{ "isSignedWithActiveKey": true, "name": "token", "url": "https://token.com", "symbol": "TKN", "precision": 3, "maxSupply": "1000" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'addPair', '{ "isSignedWithActiveKey": true, "quoteToken": "TKN", "baseToken": "BEE" }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'tokens', 'issue', '{ "isSignedWithActiveKey": true, "symbol": "TKN", "to": "james", "quantity": "55" }')); + + let block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:00', + transactions, + }; + + await fixture.sendBlock(block); + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.18", "expiration": 100 }')); + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'ali-h', 'dmarket', 'sell', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "50", "price": "0.19" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:00:03', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + // should hit the 0.18 order and expire 0.19 one + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "4.75" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-03-12T00:10:00', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['50', '25'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['4.75', '50.25'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['25'], 'BEE', true); + + refBlockNumber = fixture.getNextRefBlockNumber(); + transactions = []; + // should hit the 0.18 order and expire 0.19 one + transactions.push(new Transaction(refBlockNumber, fixture.getNextTxId(), 'james', 'dmarket', 'marketBuy', '{ "isSignedWithActiveKey": true, "symbol": "BEE", "quoteToken": "TKN", "quantity": "4.75" }')); + + block = { + refHiveBlockNumber: refBlockNumber, + refHiveBlockId: 'ABCD1', + prevRefHiveBlockId: 'ABCD2', + timestamp: '2021-04-12T00:00:06', + transactions, + }; + + await fixture.sendBlock(block); + + // const res = await fixture.database.getLatestBlockInfo(); + // const txs = res.transactions; + + await tableAsserts.assertNoErrorInLastBlock(); + + await assertBalances(['ali-h', 'james'], ['75', '25'], 'BEE'); + await assertBalances(['ali-h', 'james'], ['4.75', '50.25'], 'TKN'); + await assertBalances(['dmarket'], ['0'], 'TKN', true); + await assertBalances(['dmarket'], ['0'], 'BEE', true); + + resolve(); + }) + .then(() => { + fixture.tearDown(); + done(); + }); + }); it('verify metrics', (done) => { new Promise(async (resolve) => {