diff --git a/data-service/api/API.ts b/data-service/api/API.ts index 2dff63ca89..e69182492b 100644 --- a/data-service/api/API.ts +++ b/data-service/api/API.ts @@ -17,6 +17,7 @@ import { import * as matchersApi from './matchers/matchers' import * as addressModule from './address'; import { getLastPrice } from './matcher/getLastPrice'; +import { getPairRestrictions } from './matcher/getPairRestrictions'; import { getAddressByAlias, getAliasesByAddress, getAliasesByIdList } from './aliases/aliases'; import { getFeeRates, getSettings } from './matcher/getSettings'; import * as pairsModule from './pairs/pairs'; @@ -30,7 +31,7 @@ export const node = { height }; export const matcher = { getOrderBook, getOrdersByPair, addSignature, clearSignature, getOrders, signatureTimeout, factory, getLastPrice, - getFeeRates, getSettings + getFeeRates, getSettings, getPairRestrictions }; export const matchers = matchersApi; diff --git a/data-service/api/matcher/getPairRestrictions.ts b/data-service/api/matcher/getPairRestrictions.ts new file mode 100644 index 0000000000..04e57c9b87 --- /dev/null +++ b/data-service/api/matcher/getPairRestrictions.ts @@ -0,0 +1,9 @@ +import { AssetPair } from '@waves/data-entities'; +import { request } from '../../utils/request'; +import { get } from '../../config'; + +export function getPairRestrictions(pair: AssetPair) { + return request({ + url: `${get('matcher')}/orderbook/${pair.amountAsset.id}/${pair.priceAsset.id}/info` + }); +} diff --git a/locale/it/app.dex.json b/locale/it/app.dex.json index 11a7c87628..ee7f154c4f 100644 --- a/locale/it/app.dex.json +++ b/locale/it/app.dex.json @@ -249,4 +249,4 @@ "waves": "Waves" } } -} \ No newline at end of file +} diff --git a/locale/it/app.utils.json b/locale/it/app.utils.json index c65c0971e4..ea771c00c4 100644 --- a/locale/it/app.utils.json +++ b/locale/it/app.utils.json @@ -985,4 +985,4 @@ "help": "Byte restanti: {{bytes}}" } } -} \ No newline at end of file +} diff --git a/locale/zh_CN/app.dex.json b/locale/zh_CN/app.dex.json index f8e6fe28e7..1c3ba7ebcd 100644 --- a/locale/zh_CN/app.dex.json +++ b/locale/zh_CN/app.dex.json @@ -249,4 +249,4 @@ "waves": "Waves" } } -} \ No newline at end of file +} diff --git a/src/modules/app/services/waves/matcher/Matcher.js b/src/modules/app/services/waves/matcher/Matcher.js index 6a156c4aa8..df95b00b3e 100644 --- a/src/modules/app/services/waves/matcher/Matcher.js +++ b/src/modules/app/services/waves/matcher/Matcher.js @@ -28,6 +28,11 @@ */ currentMatcherAddress = ''; + /** + * @type {TPairRestrictions | null} + */ + pairRestrictions = null; + constructor() { this._orderBookCacheHash = Object.create(null); @@ -113,6 +118,19 @@ return ds.api.matcher.getLastPrice(pair); } + /** + * @param {AssetPair} pair + * @return {Promise} + */ + @decorators.cachable(0.5) + getPairRestrictions(pair) { + return ds.api.matcher.getPairRestrictions(pair) + .then((restrictions) => { + this.pairRestrictions = restrictions; + return restrictions; + }); + } + /** * @param {string} address * @return {Promise>} @@ -456,3 +474,19 @@ * @typedef {object} Matcher#IFeeMap * @property {number} */ + +/** + * @typedef {object} TPairRestrictions + * @param {TRestrictions} restrictions + * @param {{tickSize: string}} matchingRules + */ + +/** + * @typedef {object} TRestrictions + * @property {string} maxAmount + * @property {string} maxPrice + * @property {string} stepPrice + * @property {string} stepAmount + * @property {string} minPrice + * @property {string} minAmount + */ diff --git a/src/modules/dex/controllers/DexCtrl.js b/src/modules/dex/controllers/DexCtrl.js index 82ad6288cf..e2f56207a9 100644 --- a/src/modules/dex/controllers/DexCtrl.js +++ b/src/modules/dex/controllers/DexCtrl.js @@ -14,6 +14,7 @@ * @param {Waves} waves * @param {utils} utils * @param {ModalManager} modalManager + * @param {Matcher} matcher * @return {DexCtrl} */ const controller = function ( @@ -26,7 +27,8 @@ createPoll, waves, utils, - modalManager) { + modalManager, + matcher) { const analytics = require('@waves/event-sender'); @@ -122,6 +124,7 @@ return this._showModalAndRedirect(userAssetIdPair.amount, userAssetIdPair.price); } const pair = await this._getPair(); + matcher.getPairRestrictions(pair); $location.search('assetId2', pair.amountAsset.id); $location.search('assetId1', pair.priceAsset.id); } @@ -278,7 +281,8 @@ 'createPoll', 'waves', 'utils', - 'modalManager' + 'modalManager', + 'matcher' ]; angular.module('app.dex') diff --git a/src/modules/dex/directives/createOrder/CreateOrder.js b/src/modules/dex/directives/createOrder/CreateOrder.js index 3e3d916763..974810b010 100644 --- a/src/modules/dex/directives/createOrder/CreateOrder.js +++ b/src/modules/dex/directives/createOrder/CreateOrder.js @@ -16,10 +16,11 @@ * @param {ModalManager} modalManager * @param {BalanceWatcher} balanceWatcher * @param {Transactions} transactions + * @param {Matcher} matcher * @return {CreateOrder} */ const controller = function (Base, waves, user, utils, createPoll, $scope, $element, notification, - dexDataService, ease, $state, modalManager, balanceWatcher, transactions) { + dexDataService, ease, $state, modalManager, balanceWatcher, transactions, matcher) { const { without, keys, last } = require('ramda'); const { Money } = require('@waves/data-entities'); @@ -45,7 +46,14 @@ } get loaded() { - return this.amountBalance && this.priceBalance && this.fee; + return !!(this.amountBalance && this.priceBalance && this.fee); + } + + /** + * @return {boolean} + */ + get loadedPairRestrictions() { + return Object.keys(this.pairRestrictions).length > 0; } /** @@ -103,6 +111,10 @@ * @type {number} */ ERROR_DISPLAY_INTERVAL = 3; + /** + * @type {number} + */ + ERROR_CLICKABLE_DISPLAY_INTERVAL = 6; /** * @type {{amount: string, price: string}} * @private @@ -158,21 +170,35 @@ * @type {Poll} */ feePoll = null; - + /** + * @type {boolean} + */ + isValidAmountPrecision = false; + /** + * @type {boolean} + */ + isValidPricePrecision = false; + /** + * @type {TPairRestrictions | {}} + */ + pairRestrictions = {}; constructor() { super(); - this.observe(['type', 'amount', 'price', 'amountBalance', 'fee'], this._currentMaxAmount); + this.observe(['type', 'amount', 'price', 'amountBalance', 'fee', 'pairRestrictions'], + this._currentMaxAmount); this.receive(dexDataService.chooseOrderBook, ({ type, price, amount }) => { this.expand(type); + const roundedPrice = this._getRoundPriceByPrecision(new BigNumber(price)).getTokens().toString(); + const roundedAmount = this._getClosestValidAmount(new BigNumber(amount)).getTokens().toString(); switch (type) { case 'buy': - this._onClickBuyOrder(price, amount); + this._onClickBuyOrder(roundedPrice, roundedAmount); break; case 'sell': - this._onClickSellOrder(price, amount); + this._onClickSellOrder(roundedPrice, roundedAmount); break; default: throw new Error('Wrong order type!'); @@ -195,9 +221,6 @@ * @type {Poll} */ const spreadPoll = createPoll(this, this._getData, this._setData, 1000); - /** - * @type {Poll} - */ const onChangeBalanceWatcher = () => { this._updateBalances(); @@ -205,6 +228,7 @@ this._updateFeeList(); } }; + this.receive(balanceWatcher.change, onChangeBalanceWatcher, this); this._updateBalances(); @@ -228,6 +252,9 @@ } }); + this.isValidAmountPrecision = this._validateAmountPrecision(); + this.isValidPricePrecision = this._validatePricePrecision(); + this.observe(['amountBalance', 'type', 'fee', 'priceBalance'], this._updateMaxAmountOrPriceBalance); this.observe('_assetIdPair', () => { @@ -261,13 +288,29 @@ this.observe(['priceBalance', 'total', 'maxPriceBalance'], this._setIfCanBuyOrder); - this.observe('amount', () => ( - !this._silenceNow && this._updateField({ amount: this.amount }) - )); + this.observe('amount', () => { + this.isValidAmountPrecision = this._validateAmountPrecision(); + if (!this._silenceNow) { + this._updateField({ amount: this.amount }); + } + }); - this.observe('price', () => ( - !this._silenceNow && this._updateField({ price: this.price }) - )); + this.observe('price', () => { + this.isValidPricePrecision = this._validatePricePrecision(); + if (this._silenceNow) { + this._updateField({ price: this.price }); + } + }); + + $scope.$watch('$ctrl.order.price.$viewValue', () => { + this.isValidPricePrecision = this._validatePricePrecision(); + utils.safeApply(); + }); + + $scope.$watch('$ctrl.order.amount.$viewValue', () => { + this.isValidAmountPrecision = this._validateAmountPrecision(); + utils.safeApply(); + }); this.observe('total', () => ( !this._silenceNow && this._updateField({ total: this.total }) @@ -299,6 +342,9 @@ } $scope.$apply(); }); + + this.receive(utils.observe(matcher, 'pairRestrictions'), this._onChangeMatcherPairRestrictions, this); + this.observe(['maxAmountBalance'], this._onChangeMatcherPairRestrictions); } /** @@ -312,14 +358,19 @@ return false; } - return this.maxAmount.cloneWithTokens(this.maxAmount.getTokens().mul(factor)).eq(amount); + return this.maxAmount.cloneWithTokens( + this._getRoundAmountByPrecision(this.maxAmount.getTokens().mul(factor)).getTokens() + ).eq(amount); } /** * @param {number} factor + * @return {Promise} */ setAmountByBalance(factor) { - const amount = this.maxAmount.cloneWithTokens(this.maxAmount.getTokens().mul(factor)); + const amount = this._getClosestValidAmount( + this.maxAmount.getTokens().mul(factor) + ); this._updateField({ amount }); return Promise.resolve(); } @@ -377,19 +428,35 @@ } setMaxAmount() { - const amount = this.maxAmount; + const amount = this._getClosestValidAmount(this.maxAmount.getTokens()); this._updateField({ amount }); } setMaxPrice() { - const amount = this.maxAmount; + const amount = this._getRoundAmountByPrecision(this.maxAmount.getTokens()); + const price = this._getClosestValidPrice(this.price.getTokens()); const total = this.priceBalance.cloneWithTokens( - this.price.getTokens().mul(amount.getTokens()) + price.getTokens().mul(amount.getTokens()) ); - const price = this.price; this._updateField({ amount, total, price }); } + /** + * @param {BigNumber} value + */ + setPrice(value) { + const price = this.priceBalance.cloneWithTokens(value); + this._updateField({ price }); + } + + /** + * @param {BigNumber} value + */ + setAmount(value) { + const amount = this.amountBalance.cloneWithTokens(value); + this._updateField({ amount }); + } + setBidPrice() { const price = this.priceBalance.cloneWithTokens(String(this.bid.price)); this._updateField({ price }); @@ -475,6 +542,88 @@ }); } + /** + * @param {BigNumber} price + * @return {Money} + * @private + */ + _getRoundPriceByPrecision(price) { + if (!this.loadedPairRestrictions) { + return this.priceBalance.cloneWithTokens(price); + } + const { pricePrecision } = this.pairRestrictions; + const roundedPrice = price.roundTo(pricePrecision); + return this.priceBalance.cloneWithTokens(roundedPrice); + } + + /** + * @param {BigNumber} price + * @return {Money} + * @private + */ + _getClosestValidPrice(price) { + if (!this.loadedPairRestrictions) { + return this.priceBalance.cloneWithTokens(price); + } + + const { minPrice, maxPrice } = this.pairRestrictions; + const roundedPrice = this._getRoundPriceByPrecision(price).getTokens(); + + if (roundedPrice.lt(minPrice)) { + return this.priceBalance.cloneWithTokens(minPrice); + } + + if (roundedPrice.gt(maxPrice)) { + return this.priceBalance.cloneWithTokens(maxPrice); + } + + return this.priceBalance.cloneWithTokens(roundedPrice); + } + + /** + * @param {BigNumber} value + * @return {Money} + * @private + */ + _getRoundAmountByPrecision(value) { + if (!this.loadedPairRestrictions) { + return this.amountBalance.cloneWithTokens(value); + } + + const { amountPrecision } = this.pairRestrictions; + + const roundedAmount = value.roundTo(amountPrecision, 1); + return this.amountBalance.cloneWithTokens(roundedAmount); + } + + /** + * @param {BigNumber} amount + * @return {Money} + * @private + */ + _getClosestValidAmount(amount) { + if (!this.loadedPairRestrictions) { + return this.amountBalance.cloneWithTokens(amount); + } + + const { amountPrecision, minAmount, maxAmount } = this.pairRestrictions; + const roundedAmount = this._getRoundAmountByPrecision(amount).getTokens(); + + if (roundedAmount.lt(minAmount)) { + return this.amountBalance.cloneWithTokens( + minAmount.roundTo(amountPrecision) + ); + } + + if (roundedAmount.gt(maxAmount)) { + return this.amountBalance.cloneWithTokens( + maxAmount.roundTo(amountPrecision) + ); + } + + return this.amountBalance.cloneWithTokens(roundedAmount); + } + /** * @param data * @return {*|Promise} @@ -790,9 +939,8 @@ } const price = this._validPrice(); const amount = this._validAmount(); - this._setDirtyField('total', this.priceBalance.cloneWithTokens( - price.mul(amount) - )); + const total = this.priceBalance.cloneWithTokens(price.mul(amount)); + this._setDirtyField('total', total); this._silenceNow = true; } @@ -805,7 +953,7 @@ } const total = this._validTotal(); const amount = this._validAmount(); - this._setDirtyField('price', this.priceBalance.cloneWithTokens( + this._setDirtyField('price', this._getClosestValidPrice( total.div(amount) )); this._silenceNow = true; @@ -821,7 +969,7 @@ const total = this._validTotal(); const price = this._validPrice(); - this._setDirtyField('amount', this.amountBalance.cloneWithTokens( + this._setDirtyField('amount', this._getClosestValidAmount( total.div(price) )); this._silenceNow = true; @@ -1005,6 +1153,117 @@ return [...currentFee, ...otherFee]; } + /** + * @return {void} + * @private + */ + _onChangeMatcherPairRestrictions() { + this.pairRestrictions = {}; + const defaultPairRestriction = this._getDefaultPairRestriction(); + const matcherSettings = matcher.pairRestrictions; + + if (!matcherSettings) { + this.pairRestrictions = defaultPairRestriction; + return; + } + + const restrictions = matcherSettings.restrictions; + const matchingRules = matcherSettings.matchingRules; + + if (!restrictions && !matchingRules) { + this.pairRestrictions = defaultPairRestriction; + return; + } + + if (!restrictions && matchingRules && matchingRules.tickSize) { + this.pairRestrictions = { + ...defaultPairRestriction, + pricePrecision: new BigNumber(matchingRules.tickSize).getDecimalsCount() + }; + return; + } + + const { maxAmount, maxPrice, stepPrice, stepAmount, minPrice, minAmount } = restrictions; + const restMaxAmount = maxAmount ? new BigNumber(maxAmount) : defaultPairRestriction.maxAmount; + + const priceSizes = [defaultPairRestriction.pricePrecision]; + if (stepPrice) { + priceSizes.push(new BigNumber(stepPrice).getDecimalsCount()); + } + if (matchingRules && matchingRules.tickSize) { + priceSizes.push(new BigNumber(matchingRules.tickSize).getDecimalsCount()); + } + const pricePrecision = Math.min(...priceSizes); + + const amountPrecision = stepAmount ? + Math.min( + new BigNumber(stepAmount).getDecimalsCount(), defaultPairRestriction.amountPrecision + ) : + defaultPairRestriction.amountPrecision; + + this.pairRestrictions = { + maxPrice: maxPrice ? new BigNumber(maxPrice) : defaultPairRestriction.maxPrice, + maxAmount: this.maxAmountBalance ? + BigNumber.min(this.maxAmountBalance.getTokens(), restMaxAmount) : + restMaxAmount, + minPrice: minPrice ? + BigNumber.max(new BigNumber(minPrice), defaultPairRestriction.minPrice) : + defaultPairRestriction.minPrice, + minAmount: minAmount ? + BigNumber.max(new BigNumber(minAmount), defaultPairRestriction.minAmount) : + defaultPairRestriction.minAmount, + pricePrecision, + amountPrecision + }; + } + + /** + * @return {TPairRestrictions} + * @private + */ + _getDefaultPairRestriction() { + const pricePrecision = this.priceBalance.asset.precision; + const amountPrecision = this.amountBalance.asset.precision; + const maxPrice = new BigNumber(Infinity); + const maxAmount = this.maxAmountBalance ? + this.maxAmountBalance.getTokens() : + new BigNumber(Infinity); + return ({ + maxPrice, + maxAmount, + pricePrecision, + amountPrecision, + minPrice: (new BigNumber(10)).pow(-pricePrecision), + minAmount: (new BigNumber(10)).pow(-amountPrecision) + }); + } + + /** + * @return {boolean} + */ + _validateAmountPrecision() { + if (!this.loadedPairRestrictions) { + return true; + } + + const { amountPrecision } = this.pairRestrictions; + + return (new BigNumber(this.order.amount.$viewValue)).getDecimalsCount() <= amountPrecision; + } + + /** + * @return {boolean} + */ + _validatePricePrecision() { + if (!this.loadedPairRestrictions) { + return true; + } + + const { pricePrecision } = this.pairRestrictions; + + return (new BigNumber(this.order.price.$viewValue)).getDecimalsCount() <= pricePrecision; + } + static _animateNotification($element) { return utils.animate($element, { t: 100 }, { duration: 1200, @@ -1044,7 +1303,8 @@ '$state', 'modalManager', 'balanceWatcher', - 'transactions' + 'transactions', + 'matcher' ]; angular.module('app.dex').component('wCreateOrder', { @@ -1054,3 +1314,13 @@ controller }); })(); + +/** + * @typedef {object} TPairRestrictions + * @property {BigNumber} maxPrice + * @property {BigNumber} maxAmount + * @property {BigNumber} minPrice + * @property {BigNumber} minAmount + * @property {number} pricePrecision + * @property {number} amountPrecision + */ diff --git a/src/modules/dex/directives/createOrder/createOrder.html b/src/modules/dex/directives/createOrder/createOrder.html index dab60f5cd7..6cf24eba3f 100644 --- a/src/modules/dex/directives/createOrder/createOrder.html +++ b/src/modules/dex/directives/createOrder/createOrder.html @@ -88,13 +88,15 @@
- {{$ctrl.priceDisplayName}}
- - + + + + {{$ctrl.pairRestrictions.maxPrice.toFormat()}} + - - + + + + {{$ctrl.pairRestrictions.minPrice.toFormat()}} + + + + +
@@ -134,14 +146,15 @@
-
- - + + + + {{$ctrl.pairRestrictions.minAmount.toFormat()}} + + + + + + + + {{$ctrl.pairRestrictions.maxAmount.toFormat()}} + + - + - + - - - + params="{precision: $ctrl.pairRestrictions.amountPrecision}"> +
diff --git a/src/modules/dex/directives/createOrder/createOrder.less b/src/modules/dex/directives/createOrder/createOrder.less index fe3a9065a3..7b062c3197 100644 --- a/src/modules/dex/directives/createOrder/createOrder.less +++ b/src/modules/dex/directives/createOrder/createOrder.less @@ -349,6 +349,12 @@ w-create-order { } } } + + .clickable-value { + text-decoration: underline; + font-weight: bold; + cursor: pointer; + } } } diff --git a/src/modules/dex/directives/orderBook/OrderBook.js b/src/modules/dex/directives/orderBook/OrderBook.js index 435dd0fcc5..65000c6e4b 100644 --- a/src/modules/dex/directives/orderBook/OrderBook.js +++ b/src/modules/dex/directives/orderBook/OrderBook.js @@ -13,6 +13,7 @@ * @param {app.i18n} i18n * @param {typeof OrderList} OrderList * @param {Transactions} transactions + * @param {Matcher} matcher * @return {OrderBook} */ const controller = function (Base, @@ -24,7 +25,8 @@ $scope, i18n, OrderList, - transactions) { + transactions, + matcher) { const SECTIONS = { ASKS: '.table__asks', @@ -106,6 +108,10 @@ * @public */ this.isScrolled = false; + /** + * @type {TRestrictions | null} + */ + this.pairRestrictions = null; this.receive(dexDataService.showSpread, () => { this._dom.$box.stop().animate({ scrollTop: this._getSpreadScrollPosition() }, 300); @@ -120,6 +126,8 @@ this._onChangeVisibleElements(); this._updateAssetData(); + + this.receive(utils.observe(matcher, 'pairRestrictions'), this._onChangeMatcherPairRestrictions, this); } $postLink() { @@ -192,6 +200,7 @@ this.priceAsset = priceAsset; this.amountAsset = amountAsset; this.pair = new AssetPair(amountAsset, priceAsset); + return this.pair; }); } @@ -280,8 +289,8 @@ }, Object.create(null)); const lastTrade = lastPrice; - const bids = OrderBook._sumAllOrders(dataBids, 'sell'); - const asks = OrderBook._sumAllOrders(dataAsks, 'buy').reverse(); + const bids = OrderBook._sumAllOrders(dataBids, 'sell', this.pairRestrictions); + const asks = OrderBook._sumAllOrders(dataAsks, 'buy', this.pairRestrictions).reverse(); const maxAmount = OrderBook._getMaxAmount(bids, asks, crop); @@ -333,8 +342,17 @@ }; const length = OrderList.ROWS_COUNT; - this._asks.render(toLength(data.asks, length), data.crop, data.priceHash, data.maxAmount, pair); - this._bids.render(data.bids, data.crop, data.priceHash, data.maxAmount, pair); + const stepPrecision = this.pairRestrictions ? + { + amount: (new BigNumber(this.pairRestrictions.stepAmount)).getDecimalsCount(), + price: (new BigNumber(this.pairRestrictions.stepPrice)).getDecimalsCount() + } : + null; + + this._asks.render( + toLength(data.asks, length), data.crop, data.priceHash, data.maxAmount, pair, stepPrecision + ); + this._bids.render(data.bids, data.crop, data.priceHash, data.maxAmount, pair, stepPrecision); if (this._showSpread) { this._showSpread = false; @@ -353,23 +371,53 @@ return info.offsetTop - box.offsetTop - box.clientHeight / 2 + info.clientHeight / 2; } + /** + * @private + */ + _onChangeMatcherPairRestrictions() { + const restrictions = matcher.pairRestrictions && matcher.pairRestrictions.restrictions; + const matchingRules = matcher.pairRestrictions && matcher.pairRestrictions.matchingRules; + if (restrictions && matchingRules) { + const stepPrice = (new BigNumber(matchingRules.tickSize)).gt(restrictions.stepPrice) ? + matchingRules.tickSize : + restrictions.stepPrice; + this.pairRestrictions = { + ...restrictions, + stepPrice + }; + } else if (matchingRules) { + this.pairRestrictions = { + stepPrice: matchingRules.tickSize + }; + } else { + this.pairRestrictions = null; + } + } + /** * @param {Array} list * @param {'buy'|'sell'} type + * @param {TRestrictions | null} restrictions * @return Array * @private */ - static _sumAllOrders(list, type) { + static _sumAllOrders(list, type, restrictions) { let total = new BigNumber(0); let amountTotal = new BigNumber(0); return list.map((item) => { total = total.add(item.total); amountTotal = amountTotal.add(item.amount); + const amount = this._roundToStep( + item.amount, restrictions && restrictions.stepAmount ? restrictions.stepAmount : undefined + ); + const price = this._roundToStep( + item.price, restrictions && restrictions.stepPrice ? restrictions.stepPrice : undefined + ); return { type, - amount: new BigNumber(item.amount), - price: new BigNumber(item.price), + amount, + price, total, totalAmount: amountTotal }; @@ -407,6 +455,20 @@ return list.filter((o) => o.price.gte(crop.min) && o.price.lte(crop.max)); } + /** + * @param {string | number} value + * @param {string | number} step? + * @private + */ + static _roundToStep(value, step) { + if (!step) { + return new BigNumber(value); + } + return new BigNumber(value).roundTo( + (new BigNumber(step).getDecimalsCount()) + ); + } + } return new OrderBook(); @@ -422,7 +484,8 @@ '$scope', 'i18n', 'OrderList', - 'transactions' + 'transactions', + 'matcher' ]; angular.module('app.dex').component('wDexOrderBook', { @@ -463,3 +526,4 @@ * @property {{price: Money, lastSide: string}} lastTrade * @property {BigNumber} spread */ + diff --git a/src/modules/dex/directives/orderBook/OrderList.js b/src/modules/dex/directives/orderBook/OrderList.js index d234cbb5b8..ff0469913c 100644 --- a/src/modules/dex/directives/orderBook/OrderList.js +++ b/src/modules/dex/directives/orderBook/OrderList.js @@ -54,14 +54,15 @@ * @param {OrderBook.ICrop} crop * @param {Record} priceHash * @param {BigNumber} maxAmount + * @param {TStepPrecision | null} stepPrecision * @param {AssetPair} pair */ - render(data, crop, priceHash, maxAmount, pair) { + render(data, crop, priceHash, maxAmount, pair, stepPrecision) { const widthList = []; this._rows.forEach((row, index) => { const item = data[index]; - row.render(item, crop, priceHash, pair); + row.render(item, crop, priceHash, pair, stepPrecision); widthList[index] = item && item.amount.div(maxAmount).mul(100).toFixed(2); }); diff --git a/src/modules/dex/directives/orderBook/OrderListItem.js b/src/modules/dex/directives/orderBook/OrderListItem.js index 0b627833be..f031ce603b 100644 --- a/src/modules/dex/directives/orderBook/OrderListItem.js +++ b/src/modules/dex/directives/orderBook/OrderListItem.js @@ -82,16 +82,17 @@ * @param {OrderBook.IOrder} data * @param {OrderBook.ICrop} crop * @param {Record} priceHash + * @param {TStepPrecision | null} stepPrecision * @param {AssetPair} pair */ - render(data, crop, priceHash, pair) { + render(data, crop, priceHash, pair, stepPrecision) { if (OrderListItem._isEqual(this._data, data)) { return null; } this._pair = pair; this._data = data; - this._draw(crop, priceHash, pair); + this._draw(crop, priceHash, pair, stepPrecision); } /** @@ -105,14 +106,25 @@ * @param {OrderBook.ICrop} [crop] * @param {Record} [priceHash] * @param {AssetPair} [pair] + * @param {TStepPrecision | null} [stepPrecision] * @private */ - _draw(crop, priceHash, pair) { + _draw(crop, priceHash, pair, stepPrecision) { if (this._data) { - this._amountNode.innerHTML = - utils.getNiceNumberTemplate(this._data.amount, pair.amountAsset.precision, true); - this._priceNode.innerHTML = - utils.getNiceNumberTemplate(this._data.price, pair.priceAsset.precision, 100000); + const amountPrecision = stepPrecision && stepPrecision.amount ? + Math.min(pair.amountAsset.precision, stepPrecision.amount) : + pair.amountAsset.precision; + + const pricePrecision = stepPrecision && stepPrecision.price ? + Math.min(pair.priceAsset.precision, stepPrecision.price) : + pair.priceAsset.precision; + + this._amountNode.innerHTML = utils.getNiceNumberTemplate( + this._data.amount, amountPrecision, true + ); + this._priceNode.innerHTML = utils.getNiceNumberTemplate( + this._data.price, pricePrecision, 100000 + ); this._totalNode.innerHTML = utils.getNiceNumberTemplate(this._data.total, pair.priceAsset.precision, true); @@ -255,3 +267,9 @@ /** * @name OrderListItem */ + +/** + * @typedef {object} TStepPrecision + * @property {number} price + * @property {number} amount + */ diff --git a/src/modules/utils/directives/validators/ValidateService.js b/src/modules/utils/directives/validators/ValidateService.js index c3bc497c26..98e57256b2 100644 --- a/src/modules/utils/directives/validators/ValidateService.js +++ b/src/modules/utils/directives/validators/ValidateService.js @@ -116,12 +116,12 @@ index: i, isAddress: this.address(item, value), isValidAlis: this.isValidAlias(item) - } + }; }); const aliases = data.filter(item => !item.isAddress || item.isValidAlis); return fetchAliases(aliases.map(item => item.alias)).then(result => { - aliases.forEach((item , index) => { + aliases.forEach((item, index) => { if (result[index] && result[index].address) { item.address = result[index].address; item.isValidAlis = true; @@ -156,7 +156,7 @@ isValidAlias(alias) { return waves.node.aliases.validate(alias || ''); } - + /** * @param {string} address * @param {'no-self'} [value] diff --git a/src/modules/utils/modals/sendAsset/components/massSend/MassSend.js b/src/modules/utils/modals/sendAsset/components/massSend/MassSend.js index 501ca91449..ba60c32b68 100644 --- a/src/modules/utils/modals/sendAsset/components/massSend/MassSend.js +++ b/src/modules/utils/modals/sendAsset/components/massSend/MassSend.js @@ -337,7 +337,7 @@ this.errors = errors; $scope.$digest(); - }) + }); } /** @@ -437,7 +437,7 @@ } static _isValidRecipients(recipientList) { - return validateService.wavesAddresses(recipientList) + return validateService.wavesAddresses(recipientList); } /**