Diff Contract: market

(fe7e9a0 vs. 5be8186)
  • const STEEM_PEGGED_SYMBOL="STEEMP",STEEM_PEGGED_SYMBOL_PRESICION=8,CONTRACT_NAME="market",getMetric=async symbol=>{let metric=await api.db.findOne("metrics",{symbol:symbol});if(null===metric){metric={},metric.symbol=symbol,metric.volume="0",metric.volumeExpiration=0,metric.lastPrice="0",metric.lowestAsk="0",metric.highestBid="0",metric.lastDayPrice="0",metric.lastDayPriceExpiration=0,metric.priceChangeSteem="0",metric.priceChangePercent="0";return await api.db.insert("metrics",metric)}return metric},updateVolumeMetric=async(symbol,quantity,add=!0)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,metric=await getMetric(symbol);!0===add?(metric.volumeExpiration<timestampSec&&(metric.volume="0.000"),metric.volume=api.BigNumber(metric.volume).plus(quantity).toFixed(8),metric.volumeExpiration=blockDate.setDate(blockDate.getDate()+1)/1e3):metric.volume=api.BigNumber(metric.volume).minus(quantity).toFixed(8),api.BigNumber(metric.volume).lt(0)&&(metric.volume="0.000"),await api.db.update("metrics",metric)},updatePriceMetrics=async(symbol,price)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,metric=await getMetric(symbol);metric.lastPrice=price,metric.lastDayPriceExpiration<timestampSec?(metric.lastDayPrice=price,metric.lastDayPriceExpiration=blockDate.setDate(blockDate.getDate()+1)/1e3,metric.priceChangeSteem="0",metric.priceChangePercent="0%"):(metric.priceChangeSteem=api.BigNumber(price).minus(metric.lastDayPrice).toFixed(8),metric.priceChangePercent=api.BigNumber(metric.priceChangeSteem).dividedBy(metric.lastDayPrice).multipliedBy(100).toFixed(2)+"%"),await api.db.update("metrics",metric)},updateBidMetric=async symbol=>{const metric=await getMetric(symbol),buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1,0,[{index:"priceDec",descending:!0}]);buyOrderBook.length>0?metric.highestBid=buyOrderBook[0].price:metric.highestBid="0",await api.db.update("metrics",metric)},updateAskMetric=async symbol=>{const metric=await getMetric(symbol),sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1,0,[{index:"priceDec",descending:!1}]);sellOrderBook.length>0?metric.lowestAsk=sellOrderBook[0].price:metric.lowestAsk="0",await api.db.update("metrics",metric)},updateTradesHistory=async(type,buyer,seller,symbol,quantity,price,volume,buyTxId,sellTxId)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,timestampMinus24hrs=blockDate.setDate(blockDate.getDate()-1)/1e3;let tradesToDelete=await api.db.find("tradesHistory",{symbol:symbol,timestamp:{$lt:timestampMinus24hrs}}),nbTradesToDelete=tradesToDelete.length;for(;nbTradesToDelete>0;){for(let index=0;index<nbTradesToDelete;index+=1){const trade=tradesToDelete[index];await updateVolumeMetric(trade.symbol,trade.volume,!1),await api.db.remove("tradesHistory",trade)}tradesToDelete=await api.db.find("tradesHistory",{symbol:symbol,timestamp:{$lt:timestampMinus24hrs}}),nbTradesToDelete=tradesToDelete.length}const newTrade={};newTrade.type=type,newTrade.buyer=buyer,newTrade.seller=seller,newTrade.symbol=symbol,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,price)},countDecimals=value=>api.BigNumber(value).dp(),removeExpiredOrders=async table=>{const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber();let nbOrdersToDelete=0,ordersToDelete=await api.db.find(table,{expiration:{$lte:timestampSec}});for(nbOrdersToDelete=ordersToDelete.length;nbOrdersToDelete>0;){for(let index=0;index<nbOrdersToDelete;index+=1){const order=ordersToDelete[index];let quantity,symbol;"buyBook"===table?(symbol="STEEMP",quantity=order.tokensLocked):(symbol=order.symbol,quantity=order.quantity),await api.transferTokens(order.account,symbol,quantity,"user"),await api.db.remove(table,order),"buyBook"===table?await updateAskMetric(order.symbol):await updateBidMetric(order.symbol)}ordersToDelete=await api.db.find(table,{expiration:{$lte:timestampSec}}),nbOrdersToDelete=ordersToDelete.length}};actions.createSSC=async()=>{!1===await api.db.tableExists("buyBook")&&(await api.db.createTable("buyBook",["symbol","account","priceDec","expiration","txId"]),await api.db.createTable("sellBook",["symbol","account","priceDec","expiration","txId"]),await api.db.createTable("tradesHistory",["symbol"]),await api.db.createTable("metrics",["symbol"]))},actions.cancel=async payload=>{const{account:account,type:type,id:id,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(type&&["buy","sell"].includes(type)&&id,"invalid params")){const table="buy"===type?"buyBook":"sellBook";let order=null;if("string"==typeof id&&id.length<50&&(order=await api.db.findOne(table,{txId:id})),api.assert(null!==order,"order does not exist or invalid params")&&order.account===finalAccount){let quantity,symbol;"buy"===type?(symbol="STEEMP",quantity=order.tokensLocked):(symbol=order.symbol,quantity=order.quantity),await api.transferTokens(finalAccount,symbol,quantity,"user"),await api.db.remove(table,order),"sell"===type?await updateAskMetric(order.symbol):await updateBidMetric(order.symbol)}}};const findMatchingSellOrders=async(order,tokenPrecision)=>{const{account:account,symbol:symbol,priceDec:priceDec}=order,buyOrder=order;let offset=0,volumeTraded=0;await removeExpiredOrders("sellBook");let sellOrderBook=await api.db.find("sellBook",{symbol:symbol,priceDec:{$lte:priceDec}},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]);do{const nbOrders=sellOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(buyOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftSellOrder=api.BigNumber(sellOrder.quantity).minus(buyOrder.quantity).toFixed(tokenPrecision),nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(qtyLeftSellOrder).toFixed(8);api.BigNumber(qtyLeftSellOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(sellOrder.quantity=qtyLeftSellOrder,await api.db.update("sellBook",sellOrder)):(api.BigNumber(qtyLeftSellOrder).gt(0)&&await api.transferTokens(sellOrder.account,symbol,qtyLeftSellOrder,"user"),await api.db.remove("sellBook",sellOrder));const tokensToUnlock=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(tokensToUnlock).gt(0)&&await api.transferTokens(account,"STEEMP",tokensToUnlock,"user"),await updateTradesHistory("buy",account,sellOrder.account,symbol,buyOrder.quantity,sellOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),buyOrder.quantity="0",await api.db.remove("buyBook",buyOrder)}}else{let qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(buyOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(account,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(account),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtyTokensToSend)),await api.db.remove("sellBook",sellOrder),buyOrder.tokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),buyOrder.quantity=api.BigNumber(buyOrder.quantity).minus(sellOrder.quantity).toFixed(tokenPrecision);const nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);api.BigNumber(nbTokensToFillOrder).lt("0.00000001")&&(await api.transferTokens(account,"STEEMP",buyOrder.tokensLocked,"user"),buyOrder.quantity="0",await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("buy",account,sellOrder.account,symbol,sellOrder.quantity,sellOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(buyOrder.quantity).gt(0)&&(sellOrderBook=await api.db.find("sellBook",{symbol:symbol,priceDec:{$lte:priceDec}},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]))}while(sellOrderBook.length>0&&api.BigNumber(buyOrder.quantity).gt(0));api.BigNumber(buyOrder.quantity).gt(0)&&await api.db.update("buyBook",buyOrder),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)},findMatchingBuyOrders=async(order,tokenPrecision)=>{const{account:account,symbol:symbol,priceDec:priceDec}=order,sellOrder=order;let offset=0,volumeTraded=0;await removeExpiredOrders("buyBook");let buyOrderBook=await api.db.find("buyBook",{symbol:symbol,priceDec:{$gte:priceDec}},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]);do{const nbOrders=buyOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(sellOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftBuyOrder=api.BigNumber(buyOrder.quantity).minus(sellOrder.quantity).toFixed(tokenPrecision),buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(qtyLeftBuyOrder).toFixed(8);api.BigNumber(qtyLeftBuyOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(buyOrder.quantity=qtyLeftBuyOrder,buyOrder.tokensLocked=buyOrdertokensLocked,await api.db.update("buyBook",buyOrder)):(api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("sell",buyOrder.account,account,symbol,sellOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),sellOrder.quantity=0,await api.db.remove("sellBook",sellOrder)}}else{let qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);if(qtyTokensToSend>buyOrder.tokensLocked&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(sellOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder),sellOrder.quantity=api.BigNumber(sellOrder.quantity).minus(buyOrder.quantity).toFixed(tokenPrecision);const nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);api.BigNumber(nbTokensToFillOrder).lt("0.00000001")&&(await api.transferTokens(account,symbol,sellOrder.quantity,"user"),sellOrder.quantity="0",await api.db.remove("sellBook",sellOrder)),await updateTradesHistory("sell",buyOrder.account,account,symbol,buyOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(sellOrder.quantity).gt(0)&&(buyOrderBook=await api.db.find("buyBook",{symbol:symbol,priceDec:{$gte:priceDec}},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]))}while(buyOrderBook.length>0&&api.BigNumber(sellOrder.quantity).gt(0));api.BigNumber(sellOrder.quantity).gt(0)&&await api.db.update("sellBook",sellOrder),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)};actions.buy=async payload=>{const{account:account,symbol:symbol,quantity:quantity,price:price,expiration:expiration,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(price&&"string"==typeof price&&!api.BigNumber(price).isNaN()&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&(void 0===expiration||expiration&&Number.isInteger(expiration)&&expiration>0),"invalid params")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&api.BigNumber(price).gt(0)&&countDecimals(price)<=8&&countDecimals(quantity)<=token.precision,"invalid params")){const nbTokensToLock=api.BigNumber(price).multipliedBy(quantity).toFixed(8);if(api.assert(api.BigNumber(nbTokensToLock).gte("0.00000001"),"order cannot be placed as it cannot be filled")){const res=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:"STEEMP",quantity:nbTokensToLock,to:"market"});if(void 0===res.errors&&res.events&&void 0!==res.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===nbTokensToLock&&"STEEMP"===el.data.symbol)){const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber(),order={};order.txId=api.transactionId,order.timestamp=timestampSec,order.account=finalAccount,order.symbol=symbol,order.quantity=api.BigNumber(quantity).toFixed(token.precision),order.price=api.BigNumber(price).toFixed(8),order.priceDec={$numberDecimal:order.price},order.tokensLocked=nbTokensToLock,order.expiration=void 0===expiration||expiration>2592e3?timestampSec+2592e3:timestampSec+expiration;const orderInDb=await api.db.insert("buyBook",order);await findMatchingSellOrders(orderInDb,token.precision)}}}}},actions.sell=async payload=>{const{account:account,symbol:symbol,quantity:quantity,price:price,expiration:expiration,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(price&&"string"==typeof price&&!api.BigNumber(price).isNaN()&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&(void 0===expiration||expiration&&Number.isInteger(expiration)&&expiration>0),"invalid params")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&api.BigNumber(price).gt(0)&&countDecimals(price)<=8&&countDecimals(quantity)<=token.precision,"invalid params")){const nbTokensToFillOrder=api.BigNumber(price).multipliedBy(quantity).toFixed(8);if(api.assert(api.BigNumber(nbTokensToFillOrder).gte("0.00000001"),"order cannot be placed as it cannot be filled")){const res=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:symbol,quantity:quantity,to:"market"});if(void 0===res.errors&&res.events&&void 0!==res.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&el.data.symbol===symbol)){const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber(),order={};order.txId=api.transactionId,order.timestamp=timestampSec,order.account=finalAccount,order.symbol=symbol,order.quantity=api.BigNumber(quantity).toFixed(token.precision),order.price=api.BigNumber(price).toFixed(8),order.priceDec={$numberDecimal:order.price},order.expiration=void 0===expiration||expiration>2592e3?timestampSec+2592e3:timestampSec+expiration;const orderInDb=await api.db.insert("sellBook",order);await findMatchingBuyOrders(orderInDb,token.precision)}}}}},actions.marketBuy=async payload=>{const{account:account,symbol:symbol,quantity:quantity,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&api.BigNumber(quantity).gt(0)){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&countDecimals(quantity)<=8,"invalid params")){const result=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:"STEEMP",quantity:quantity,to:"market"});if(void 0===result.errors&&result.events&&void 0!==result.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&"STEEMP"===el.data.symbol)){let steempRemaining=quantity,offset=0,volumeTraded=0;await removeExpiredOrders("sellBook");let sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]);do{const nbOrders=sellOrderBook.length;let inc=0;for(;inc<nbOrders&&api.BigNumber(steempRemaining).gt(0);){const sellOrder=sellOrderBook[inc],qtyTokensToSend=api.BigNumber(steempRemaining).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(steempRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(finalAccount,symbol,qtyTokensToSend,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug(symbol),api.debug(qtyTokensToSend)),res=await api.transferTokens(sellOrder.account,"STEEMP",steempRemaining,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(steempRemaining));const qtyLeftSellOrder=api.BigNumber(sellOrder.quantity).minus(qtyTokensToSend).toFixed(token.precision),nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(qtyLeftSellOrder).toFixed(8);api.BigNumber(qtyLeftSellOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(sellOrder.quantity=qtyLeftSellOrder,await api.db.update("sellBook",sellOrder)):(api.BigNumber(qtyLeftSellOrder).gt(0)&&await api.transferTokens(sellOrder.account,symbol,qtyLeftSellOrder,"user"),await api.db.remove("sellBook",sellOrder)),await updateTradesHistory("buy",finalAccount,sellOrder.account,symbol,qtyTokensToSend,sellOrder.price,steempRemaining,api.transactionId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(steempRemaining),steempRemaining="0"}}else if(api.BigNumber(qtyTokensToSend).gt(0)){let qtySteempToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);if(api.BigNumber(qtySteempToSend).gt(steempRemaining)&&(qtySteempToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtySteempToSend).gt(0)&&api.BigNumber(steempRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(finalAccount,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtySteempToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtySteempToSend)),await api.db.remove("sellBook",sellOrder),steempRemaining=api.BigNumber(steempRemaining).minus(qtySteempToSend).toFixed(8),await updateTradesHistory("buy",finalAccount,sellOrder.account,symbol,sellOrder.quantity,sellOrder.price,qtySteempToSend,api.transactionId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtySteempToSend)}}inc+=1}offset+=1e3,api.BigNumber(steempRemaining).gt(0)&&(sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]))}while(sellOrderBook.length>0&&api.BigNumber(steempRemaining).gt(0));api.BigNumber(steempRemaining).gt(0)&&await api.transferTokens(finalAccount,"STEEMP",steempRemaining,"user"),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)}}}},actions.marketSell=async payload=>{const{account:account,symbol:symbol,quantity:quantity,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&api.BigNumber(quantity).gt(0)){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&countDecimals(quantity)<=token.precision,"invalid params")){const result=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:symbol,quantity:quantity,to:"market"});if(void 0===result.errors&&result.events&&void 0!==result.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&el.data.symbol===symbol)){let tokensRemaining=quantity,offset=0,volumeTraded=0;await removeExpiredOrders("buyBook");let buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]);do{const nbOrders=buyOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(tokensRemaining).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(tokensRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,tokensRemaining,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(tokensRemaining)),res=await api.transferTokens(finalAccount,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftBuyOrder=api.BigNumber(buyOrder.quantity).minus(tokensRemaining).toFixed(token.precision),buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(qtyLeftBuyOrder).toFixed(8);api.BigNumber(qtyLeftBuyOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(buyOrder.quantity=qtyLeftBuyOrder,buyOrder.tokensLocked=buyOrdertokensLocked,await api.db.update("buyBook",buyOrder)):(api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("sell",buyOrder.account,finalAccount,symbol,tokensRemaining,buyOrder.price,qtyTokensToSend,buyOrder.txId,api.transactionId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),tokensRemaining=0}}else{let qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);if(qtyTokensToSend>buyOrder.tokensLocked&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(tokensRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(finalAccount,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug("STEEMP"),api.debug(qtyTokensToSend));const buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder),tokensRemaining=api.BigNumber(tokensRemaining).minus(buyOrder.quantity).toFixed(token.precision),await updateTradesHistory("sell",buyOrder.account,finalAccount,symbol,buyOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,api.transactionId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(tokensRemaining).gt(0)&&(buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]))}while(buyOrderBook.length>0&&api.BigNumber(tokensRemaining).gt(0));api.BigNumber(tokensRemaining).gt(0)&&await api.transferTokens(finalAccount,symbol,tokensRemaining,"user"),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)}}}};
  • const STEEM_PEGGED_SYMBOL="STEEMP",STEEM_PEGGED_SYMBOL_PRESICION=8,CONTRACT_NAME="market",getMetric=async symbol=>{let metric=await api.db.findOne("metrics",{symbol:symbol});if(null===metric){metric={},metric.symbol=symbol,metric.volume="0",metric.volumeExpiration=0,metric.lastPrice="0",metric.lowestAsk="0",metric.highestBid="0",metric.lastDayPrice="0",metric.lastDayPriceExpiration=0,metric.priceChangeSteem="0",metric.priceChangePercent="0";return await api.db.insert("metrics",metric)}return metric},updateVolumeMetric=async(symbol,quantity,add=!0)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,metric=await getMetric(symbol);!0===add?(metric.volumeExpiration<timestampSec&&(metric.volume="0.000"),metric.volume=api.BigNumber(metric.volume).plus(quantity).toFixed(8),metric.volumeExpiration=blockDate.setDate(blockDate.getDate()+1)/1e3):metric.volume=api.BigNumber(metric.volume).minus(quantity).toFixed(8),api.BigNumber(metric.volume).lt(0)&&(metric.volume="0.000"),await api.db.update("metrics",metric)},updatePriceMetrics=async(symbol,price)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,metric=await getMetric(symbol);metric.lastPrice=price,metric.lastDayPriceExpiration<timestampSec?(metric.lastDayPrice=price,metric.lastDayPriceExpiration=blockDate.setDate(blockDate.getDate()+1)/1e3,metric.priceChangeSteem="0",metric.priceChangePercent="0%"):(metric.priceChangeSteem=api.BigNumber(price).minus(metric.lastDayPrice).toFixed(8),metric.priceChangePercent=api.BigNumber(metric.priceChangeSteem).dividedBy(metric.lastDayPrice).multipliedBy(100).toFixed(2)+"%"),await api.db.update("metrics",metric)},updateBidMetric=async symbol=>{const metric=await getMetric(symbol),buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1,0,[{index:"priceDec",descending:!0}]);buyOrderBook.length>0?metric.highestBid=buyOrderBook[0].price:metric.highestBid="0",await api.db.update("metrics",metric)},updateAskMetric=async symbol=>{const metric=await getMetric(symbol),sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1,0,[{index:"priceDec",descending:!1}]);sellOrderBook.length>0?metric.lowestAsk=sellOrderBook[0].price:metric.lowestAsk="0",await api.db.update("metrics",metric)},updateTradesHistory=async(type,buyer,seller,symbol,quantity,price,volume,buyTxId,sellTxId)=>{const blockDate=new Date(api.steemBlockTimestamp+".000Z"),timestampSec=blockDate.getTime()/1e3,timestampMinus24hrs=blockDate.setDate(blockDate.getDate()-1)/1e3;let tradesToDelete=await api.db.find("tradesHistory",{symbol:symbol,timestamp:{$lt:timestampMinus24hrs}}),nbTradesToDelete=tradesToDelete.length;for(;nbTradesToDelete>0;){for(let index=0;index<nbTradesToDelete;index+=1){const trade=tradesToDelete[index];await updateVolumeMetric(trade.symbol,trade.volume,!1),await api.db.remove("tradesHistory",trade)}tradesToDelete=await api.db.find("tradesHistory",{symbol:symbol,timestamp:{$lt:timestampMinus24hrs}}),nbTradesToDelete=tradesToDelete.length}const newTrade={};newTrade.type=type,newTrade.buyer=buyer,newTrade.seller=seller,newTrade.symbol=symbol,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,price)},countDecimals=value=>api.BigNumber(value).dp(),removeExpiredOrders=async table=>{const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber();let nbOrdersToDelete=0,ordersToDelete=await api.db.find(table,{expiration:{$lte:timestampSec}});for(nbOrdersToDelete=ordersToDelete.length;nbOrdersToDelete>0;){for(let index=0;index<nbOrdersToDelete;index+=1){const order=ordersToDelete[index];let quantity,symbol;"buyBook"===table?(symbol="STEEMP",quantity=order.tokensLocked):(symbol=order.symbol,quantity=order.quantity),await api.transferTokens(order.account,symbol,quantity,"user"),await api.db.remove(table,order),"buyBook"===table?await updateAskMetric(order.symbol):await updateBidMetric(order.symbol)}ordersToDelete=await api.db.find(table,{expiration:{$lte:timestampSec}}),nbOrdersToDelete=ordersToDelete.length}};actions.createSSC=async()=>{!1===await api.db.tableExists("buyBook")&&(await api.db.createTable("buyBook",["symbol","account","priceDec","expiration","txId"]),await api.db.createTable("sellBook",["symbol","account","priceDec","expiration","txId"]),await api.db.createTable("tradesHistory",["symbol"]),await api.db.createTable("metrics",["symbol"]))},actions.cancel=async payload=>{const{account:account,type:type,id:id,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(type&&["buy","sell"].includes(type)&&id,"invalid params")){const table="buy"===type?"buyBook":"sellBook";let order=null;if("string"==typeof id&&id.length<50?order=await api.db.findOne(table,{txId:id}):"number"==typeof id&&Number.isInteger(id)&&id>0&&(order=await api.db.findOne(table,{_id:id})),api.assert(null!==order,"order does not exist or invalid params")&&order.account===finalAccount){let quantity,symbol;"buy"===type?(symbol="STEEMP",quantity=order.tokensLocked):(symbol=order.symbol,quantity=order.quantity),await api.transferTokens(finalAccount,symbol,quantity,"user"),await api.db.remove(table,order),"sell"===type?await updateAskMetric(order.symbol):await updateBidMetric(order.symbol)}}};const findMatchingSellOrders=async(order,tokenPrecision)=>{const{account:account,symbol:symbol,priceDec:priceDec}=order,buyOrder=order;let offset=0,volumeTraded=0;await removeExpiredOrders("sellBook");let sellOrderBook=await api.db.find("sellBook",{symbol:symbol,priceDec:{$lte:priceDec}},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]);do{const nbOrders=sellOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(buyOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+buyOrder.txId),api.debug(account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+buyOrder.txId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftSellOrder=api.BigNumber(sellOrder.quantity).minus(buyOrder.quantity).toFixed(tokenPrecision),nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(qtyLeftSellOrder).toFixed(8);api.BigNumber(qtyLeftSellOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(sellOrder.quantity=qtyLeftSellOrder,await api.db.update("sellBook",sellOrder)):(api.BigNumber(qtyLeftSellOrder).gt(0)&&await api.transferTokens(sellOrder.account,symbol,qtyLeftSellOrder,"user"),await api.db.remove("sellBook",sellOrder));const tokensToUnlock=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(tokensToUnlock).gt(0)&&await api.transferTokens(account,"STEEMP",tokensToUnlock,"user"),await updateTradesHistory("buy",account,sellOrder.account,symbol,buyOrder.quantity,sellOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),buyOrder.quantity="0",await api.db.remove("buyBook",buyOrder)}}else{let qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(buyOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(account,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+buyOrder.txId),api.debug(account),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+buyOrder.txId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtyTokensToSend)),await api.db.remove("sellBook",sellOrder),buyOrder.tokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),buyOrder.quantity=api.BigNumber(buyOrder.quantity).minus(sellOrder.quantity).toFixed(tokenPrecision);const nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);api.BigNumber(nbTokensToFillOrder).lt("0.00000001")&&(await api.transferTokens(account,"STEEMP",buyOrder.tokensLocked,"user"),buyOrder.quantity="0",await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("buy",account,sellOrder.account,symbol,sellOrder.quantity,sellOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(buyOrder.quantity).gt(0)&&(sellOrderBook=await api.db.find("sellBook",{symbol:symbol,priceDec:{$lte:priceDec}},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]))}while(sellOrderBook.length>0&&api.BigNumber(buyOrder.quantity).gt(0));api.BigNumber(buyOrder.quantity).gt(0)&&await api.db.update("buyBook",buyOrder),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)},findMatchingBuyOrders=async(order,tokenPrecision)=>{const{account:account,symbol:symbol,priceDec:priceDec}=order,sellOrder=order;let offset=0,volumeTraded=0;await removeExpiredOrders("buyBook");let buyOrderBook=await api.db.find("buyBook",{symbol:symbol,priceDec:{$gte:priceDec}},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]);do{const nbOrders=buyOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(sellOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+sellOrder.txId),api.debug(buyOrder.account),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+sellOrder.txId),api.debug(account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftBuyOrder=api.BigNumber(buyOrder.quantity).minus(sellOrder.quantity).toFixed(tokenPrecision),buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(qtyLeftBuyOrder).toFixed(8);api.BigNumber(qtyLeftBuyOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(buyOrder.quantity=qtyLeftBuyOrder,buyOrder.tokensLocked=buyOrdertokensLocked,await api.db.update("buyBook",buyOrder)):(api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("sell",buyOrder.account,account,symbol,sellOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),sellOrder.quantity=0,await api.db.remove("sellBook",sellOrder)}}else{let qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);if(qtyTokensToSend>buyOrder.tokensLocked&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(sellOrder.quantity).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+sellOrder.txId),api.debug(buyOrder.account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(account,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+sellOrder.txId),api.debug(account),api.debug("STEEMP"),api.debug(qtyTokensToSend));const buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder),sellOrder.quantity=api.BigNumber(sellOrder.quantity).minus(buyOrder.quantity).toFixed(tokenPrecision);const nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);api.BigNumber(nbTokensToFillOrder).lt("0.00000001")&&(await api.transferTokens(account,symbol,sellOrder.quantity,"user"),sellOrder.quantity="0",await api.db.remove("sellBook",sellOrder)),await updateTradesHistory("sell",buyOrder.account,account,symbol,buyOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(sellOrder.quantity).gt(0)&&(buyOrderBook=await api.db.find("buyBook",{symbol:symbol,priceDec:{$gte:priceDec}},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]))}while(buyOrderBook.length>0&&api.BigNumber(sellOrder.quantity).gt(0));api.BigNumber(sellOrder.quantity).gt(0)&&await api.db.update("sellBook",sellOrder),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)};actions.buy=async payload=>{const{account:account,txId:txId,symbol:symbol,quantity:quantity,price:price,expiration:expiration,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account,finalTxId=void 0===txId||"null"!==api.sender?api.transactionId:txId;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(price&&"string"==typeof price&&!api.BigNumber(price).isNaN()&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&finalTxId&&"string"==typeof finalTxId&&finalTxId.length>0&&(void 0===expiration||expiration&&Number.isInteger(expiration)&&expiration>0),"invalid params")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&api.BigNumber(price).gt(0)&&countDecimals(price)<=8&&countDecimals(quantity)<=token.precision,"invalid params")){const nbTokensToLock=api.BigNumber(price).multipliedBy(quantity).toFixed(8);if(api.assert(api.BigNumber(nbTokensToLock).gte("0.00000001"),"order cannot be placed as it cannot be filled")){const res=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:"STEEMP",quantity:nbTokensToLock,to:"market"});if(void 0===res.errors&&res.events&&void 0!==res.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===nbTokensToLock&&"STEEMP"===el.data.symbol)){const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber(),order={};order.txId=finalTxId,order.timestamp=timestampSec,order.account=finalAccount,order.symbol=symbol,order.quantity=api.BigNumber(quantity).toFixed(token.precision),order.price=api.BigNumber(price).toFixed(8),order.priceDec={$numberDecimal:order.price},order.tokensLocked=nbTokensToLock,order.expiration=void 0===expiration||expiration>2592e3?timestampSec+2592e3:timestampSec+expiration;const orderInDb=await api.db.insert("buyBook",order);await findMatchingSellOrders(orderInDb,token.precision)}}}}},actions.sell=async payload=>{const{account:account,txId:txId,symbol:symbol,quantity:quantity,price:price,expiration:expiration,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account,finalTxId=void 0===txId||"null"!==api.sender?api.transactionId:txId;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&api.assert(price&&"string"==typeof price&&!api.BigNumber(price).isNaN()&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&finalTxId&&"string"==typeof finalTxId&&finalTxId.length>0&&(void 0===expiration||expiration&&Number.isInteger(expiration)&&expiration>0),"invalid params")){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&api.BigNumber(price).gt(0)&&countDecimals(price)<=8&&countDecimals(quantity)<=token.precision,"invalid params")){const nbTokensToFillOrder=api.BigNumber(price).multipliedBy(quantity).toFixed(8);if(api.assert(api.BigNumber(nbTokensToFillOrder).gte("0.00000001"),"order cannot be placed as it cannot be filled")){const res=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:symbol,quantity:quantity,to:"market"});if(void 0===res.errors&&res.events&&void 0!==res.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&el.data.symbol===symbol)){const timestampSec=api.BigNumber(new Date(api.steemBlockTimestamp+".000Z").getTime()).dividedBy(1e3).toNumber(),order={};order.txId=finalTxId,order.timestamp=timestampSec,order.account=finalAccount,order.symbol=symbol,order.quantity=api.BigNumber(quantity).toFixed(token.precision),order.price=api.BigNumber(price).toFixed(8),order.priceDec={$numberDecimal:order.price},order.expiration=void 0===expiration||expiration>2592e3?timestampSec+2592e3:timestampSec+expiration;const orderInDb=await api.db.insert("sellBook",order);await findMatchingBuyOrders(orderInDb,token.precision)}}}}},actions.marketBuy=async payload=>{const{account:account,symbol:symbol,quantity:quantity,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&api.BigNumber(quantity).gt(0)){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&countDecimals(quantity)<=8,"invalid params")){const result=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:"STEEMP",quantity:quantity,to:"market"});if(void 0===result.errors&&result.events&&void 0!==result.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&"STEEMP"===el.data.symbol)){let steempRemaining=quantity,offset=0,volumeTraded=0;await removeExpiredOrders("sellBook");let sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]);do{const nbOrders=sellOrderBook.length;let inc=0;for(;inc<nbOrders&&api.BigNumber(steempRemaining).gt(0);){const sellOrder=sellOrderBook[inc],qtyTokensToSend=api.BigNumber(steempRemaining).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(steempRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(finalAccount,symbol,qtyTokensToSend,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug(symbol),api.debug(qtyTokensToSend)),res=await api.transferTokens(sellOrder.account,"STEEMP",steempRemaining,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(steempRemaining));const qtyLeftSellOrder=api.BigNumber(sellOrder.quantity).minus(qtyTokensToSend).toFixed(token.precision),nbTokensToFillOrder=api.BigNumber(sellOrder.price).multipliedBy(qtyLeftSellOrder).toFixed(8);api.BigNumber(qtyLeftSellOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(sellOrder.quantity=qtyLeftSellOrder,await api.db.update("sellBook",sellOrder)):(api.BigNumber(qtyLeftSellOrder).gt(0)&&await api.transferTokens(sellOrder.account,symbol,qtyLeftSellOrder,"user"),await api.db.remove("sellBook",sellOrder)),await updateTradesHistory("buy",finalAccount,sellOrder.account,symbol,qtyTokensToSend,sellOrder.price,steempRemaining,api.transactionId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(steempRemaining),steempRemaining="0"}}else if(api.BigNumber(qtyTokensToSend).gt(0)){let qtySteempToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8);if(api.BigNumber(qtySteempToSend).gt(steempRemaining)&&(qtySteempToSend=api.BigNumber(sellOrder.price).multipliedBy(sellOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtySteempToSend).gt(0)&&api.BigNumber(steempRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(finalAccount,symbol,sellOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug(symbol),api.debug(sellOrder.quantity)),res=await api.transferTokens(sellOrder.account,"STEEMP",qtySteempToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(sellOrder.account),api.debug("STEEMP"),api.debug(qtySteempToSend)),await api.db.remove("sellBook",sellOrder),steempRemaining=api.BigNumber(steempRemaining).minus(qtySteempToSend).toFixed(8),await updateTradesHistory("buy",finalAccount,sellOrder.account,symbol,sellOrder.quantity,sellOrder.price,qtySteempToSend,api.transactionId,sellOrder.txId),volumeTraded=api.BigNumber(volumeTraded).plus(qtySteempToSend)}}inc+=1}offset+=1e3,api.BigNumber(steempRemaining).gt(0)&&(sellOrderBook=await api.db.find("sellBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!1},{index:"_id",descending:!1}]))}while(sellOrderBook.length>0&&api.BigNumber(steempRemaining).gt(0));api.BigNumber(steempRemaining).gt(0)&&await api.transferTokens(finalAccount,"STEEMP",steempRemaining,"user"),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)}}}},actions.marketSell=async payload=>{const{account:account,symbol:symbol,quantity:quantity,isSignedWithActiveKey:isSignedWithActiveKey}=payload,finalAccount=void 0===account||"null"!==api.sender?api.sender:account;if(api.assert(!0===isSignedWithActiveKey||"null"===api.sender,"you must use a custom_json signed with your active key")&&symbol&&"string"==typeof symbol&&"STEEMP"!==symbol&&quantity&&"string"==typeof quantity&&!api.BigNumber(quantity).isNaN()&&api.BigNumber(quantity).gt(0)){const token=await api.db.findOneInTable("tokens","tokens",{symbol:symbol});if(api.assert(token&&countDecimals(quantity)<=token.precision,"invalid params")){const result=await api.executeSmartContract("tokens","transferToContract",{from:finalAccount,symbol:symbol,quantity:quantity,to:"market"});if(void 0===result.errors&&result.events&&void 0!==result.events.find(el=>"tokens"===el.contract&&"transferToContract"===el.event&&el.data.from===finalAccount&&"market"===el.data.to&&el.data.quantity===quantity&&el.data.symbol===symbol)){let tokensRemaining=quantity,offset=0,volumeTraded=0;await removeExpiredOrders("buyBook");let buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]);do{const nbOrders=buyOrderBook.length;let inc=0;for(;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(8);if(api.BigNumber(qtyTokensToSend).gt(buyOrder.tokensLocked)&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(tokensRemaining).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(tokensRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,tokensRemaining,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(tokensRemaining)),res=await api.transferTokens(finalAccount,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug("STEEMP"),api.debug(qtyTokensToSend));const qtyLeftBuyOrder=api.BigNumber(buyOrder.quantity).minus(tokensRemaining).toFixed(token.precision),buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8),nbTokensToFillOrder=api.BigNumber(buyOrder.price).multipliedBy(qtyLeftBuyOrder).toFixed(8);api.BigNumber(qtyLeftBuyOrder).gt(0)&&api.BigNumber(nbTokensToFillOrder).gte("0.00000001")?(buyOrder.quantity=qtyLeftBuyOrder,buyOrder.tokensLocked=buyOrdertokensLocked,await api.db.update("buyBook",buyOrder)):(api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder)),await updateTradesHistory("sell",buyOrder.account,finalAccount,symbol,tokensRemaining,buyOrder.price,qtyTokensToSend,buyOrder.txId,api.transactionId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend),tokensRemaining=0}}else{let qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8);if(qtyTokensToSend>buyOrder.tokensLocked&&(qtyTokensToSend=api.BigNumber(buyOrder.price).multipliedBy(buyOrder.quantity).toFixed(8,api.BigNumber.ROUND_DOWN)),api.assert(api.BigNumber(qtyTokensToSend).gt(0)&&api.BigNumber(tokensRemaining).gt(0),"the order cannot be filled")){let res=await api.transferTokens(buyOrder.account,symbol,buyOrder.quantity,"user");res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(buyOrder.account),api.debug(symbol),api.debug(buyOrder.quantity)),res=await api.transferTokens(finalAccount,"STEEMP",qtyTokensToSend,"user"),res.errors&&(api.debug(res.errors),api.debug("TXID: "+api.transactionId),api.debug(finalAccount),api.debug("STEEMP"),api.debug(qtyTokensToSend));const buyOrdertokensLocked=api.BigNumber(buyOrder.tokensLocked).minus(qtyTokensToSend).toFixed(8);api.BigNumber(buyOrdertokensLocked).gt(0)&&await api.transferTokens(buyOrder.account,"STEEMP",buyOrdertokensLocked,"user"),await api.db.remove("buyBook",buyOrder),tokensRemaining=api.BigNumber(tokensRemaining).minus(buyOrder.quantity).toFixed(token.precision),await updateTradesHistory("sell",buyOrder.account,finalAccount,symbol,buyOrder.quantity,buyOrder.price,qtyTokensToSend,buyOrder.txId,api.transactionId),volumeTraded=api.BigNumber(volumeTraded).plus(qtyTokensToSend)}}inc+=1}offset+=1e3,api.BigNumber(tokensRemaining).gt(0)&&(buyOrderBook=await api.db.find("buyBook",{symbol:symbol},1e3,offset,[{index:"priceDec",descending:!0},{index:"_id",descending:!1}]))}while(buyOrderBook.length>0&&api.BigNumber(tokensRemaining).gt(0));api.BigNumber(tokensRemaining).gt(0)&&await api.transferTokens(finalAccount,symbol,tokensRemaining,"user"),api.BigNumber(volumeTraded).gt(0)&&await updateVolumeMetric(symbol,volumeTraded),await updateAskMetric(symbol),await updateBidMetric(symbol)}}}};