diff --git a/server/api/controllers/admin.js b/server/api/controllers/admin.js index ce424f3c4e..5c6a311b32 100644 --- a/server/api/controllers/admin.js +++ b/server/api/controllers/admin.js @@ -2458,6 +2458,43 @@ const updateQuickTradeConfig = (req, res) => { }); }; +const getBalancesAdmin = (req, res) => { + loggerAdmin.verbose(req.uuid, 'controllers/admin/getBalancesAdmin/auth', req.auth); + + const { + user_id, + currency, + format + } = req.swagger.params; + + + if (format.value && req.auth.scopes.indexOf(ROLES.ADMIN) === -1) { + return res.status(403).json({ message: API_KEY_NOT_PERMITTED }); + } + + toolsLib.user.getAllBalancesAdmin({ + user_id: user_id.value, + currency: currency.value, + format: format.value, + additionalHeaders: { + 'x-forwarded-for': req.headers['x-forwarded-for'] + } + }) + .then((data) => { + if (format.value === 'all') { + res.setHeader('Content-disposition', `attachment; filename=${toolsLib.getKitConfig().api_name}-users.csv`); + res.set('Content-Type', 'text/csv'); + return res.status(202).send(data); + } else { + return res.json(data); + } + }) + .catch((err) => { + loggerAdmin.error(req.uuid, 'controllers/admin/getBalancesAdmin', err.message); + return res.status(err.statusCode || 400).json({ message: errorMessageConverter(err) }); + }); +} + module.exports = { createInitialAdmin, getAdminKit, @@ -2519,5 +2556,6 @@ module.exports = { revokeUserSessionByAdmin, sendEmailByAdmin, sendRawEmailByAdmin, - updateQuickTradeConfig + updateQuickTradeConfig, + getBalancesAdmin }; diff --git a/server/api/controllers/broker.js b/server/api/controllers/broker.js index a76247c4f5..0ab004079a 100644 --- a/server/api/controllers/broker.js +++ b/server/api/controllers/broker.js @@ -47,7 +47,6 @@ const createBrokerPair = (req, res) => { user_id, min_size, max_size, - increment_size, type, quote_expiry_time, rebalancing_symbol, @@ -67,7 +66,6 @@ const createBrokerPair = (req, res) => { user_id, min_size, max_size, - increment_size, type, quote_expiry_time, rebalancing_symbol, @@ -84,7 +82,6 @@ const createBrokerPair = (req, res) => { user_id, min_size, max_size, - increment_size, type, quote_expiry_time, rebalancing_symbol, @@ -115,13 +112,11 @@ const testBroker = (req, res) => { const { formula, spread, - increment_size } = req.swagger.params.data.value; toolsLib.broker.testBroker({ formula, spread, - increment_size }) .then((data) => { return res.json(data); @@ -181,7 +176,6 @@ function updateBrokerPair(req, res) { sell_price, min_size, max_size, - increment_size, paused, user_id, type, @@ -200,7 +194,6 @@ function updateBrokerPair(req, res) { sell_price, min_size, max_size, - increment_size, paused, user_id, type, @@ -264,7 +257,6 @@ function getBrokerPairs(req, res) { 'paused', 'min_size', 'max_size', - 'increment_size', 'type', 'quote_expiry_time', 'rebalancing_symbol', diff --git a/server/api/swagger/admin.yaml b/server/api/swagger/admin.yaml index 6d000d1f45..6aa1609d81 100644 --- a/server/api/swagger/admin.yaml +++ b/server/api/swagger/admin.yaml @@ -3187,4 +3187,48 @@ paths: - bearer - hmac x-security-scopes: - - admin \ No newline at end of file + - admin + /admin/balances: + x-swagger-router-controller: admin + get: + description: Get exchange balances of users for admin + operationId: getBalancesAdmin + tags: + - Admin + parameters: + - name: user_id + in: query + required: false + type: number + - name: currency + in: query + required: false + type: string + - in: query + name: format + description: Specify data format + required: false + enum: ['csv', 'all'] + type: string + responses: + 200: + description: Success + schema: + $ref: "#/definitions/ObjectResponse" + 202: + description: CSV + schema: + type: string + default: + description: Error + schema: + $ref: "#/definitions/MessageResponse" + security: + - Token: [] + x-security-types: + - bearer + - hmac + x-security-scopes: + - admin + x-token-permissions: + - can_read \ No newline at end of file diff --git a/server/db/migrations/20230628100835-remove-broker-increment-size.js b/server/db/migrations/20230628100835-remove-broker-increment-size.js new file mode 100644 index 0000000000..7d5ccb9c3f --- /dev/null +++ b/server/db/migrations/20230628100835-remove-broker-increment-size.js @@ -0,0 +1,14 @@ +'use strict'; + +const TABLE = 'Brokers'; +const COLUMN = 'increment_size'; + +module.exports = { + up: (queryInterface, Sequelize) => + queryInterface.removeColumn(TABLE, COLUMN), + down: (queryInterface, Sequelize) => + queryInterface.addColumn(TABLE, COLUMN, { + type: Sequelize.DOUBLE, + allowNull: false + }) +}; \ No newline at end of file diff --git a/server/db/models/broker.js b/server/db/models/broker.js index 02cb451748..56ef3f6de7 100644 --- a/server/db/models/broker.js +++ b/server/db/models/broker.js @@ -47,10 +47,6 @@ module.exports = function (sequelize, DataTypes) { type: DataTypes.DOUBLE, allowNull: false }, - increment_size: { - type: DataTypes.DOUBLE, - allowNull: false - }, type: { type: DataTypes.ENUM('manual', 'dynamic'), defaultValue: 'manual', diff --git a/server/init.js b/server/init.js index dd9b6ebb71..866021e982 100644 --- a/server/init.js +++ b/server/init.js @@ -125,7 +125,7 @@ const checkStatus = () => { status.constants ), Tier.findAll(), - Broker.findAll({ attributes: ['id', 'symbol', 'buy_price', 'sell_price', 'paused', 'min_size', 'max_size', 'increment_size']}), + Broker.findAll({ attributes: ['id', 'symbol', 'buy_price', 'sell_price', 'paused', 'min_size', 'max_size']}), QuickTrade.findAll(), status.dataValues ]); diff --git a/server/utils/hollaex-tools-lib/tools/broker.js b/server/utils/hollaex-tools-lib/tools/broker.js index 09b608349e..8b43381f5a 100644 --- a/server/utils/hollaex-tools-lib/tools/broker.js +++ b/server/utils/hollaex-tools-lib/tools/broker.js @@ -9,7 +9,7 @@ const { EXCHANGE_PLAN_INTERVAL_TIME, EXCHANGE_PLAN_PRICE_SOURCE } = require(`${S const { getNodeLib } = require(`${SERVER_PATH}/init`); const { client } = require('./database/redis'); const { getUserByKitId } = require('./user'); -const { validatePair, getKitTier, getKitConfig, getAssetsPrices, getQuickTrades } = require('./common'); +const { validatePair, getKitTier, getKitConfig, getAssetsPrices, getQuickTrades, getKitCoin } = require('./common'); const { sendEmail } = require('../../../mail'); const { MAILTYPE } = require('../../../mail/strings'); const { verifyBearerTokenPromise } = require('./security'); @@ -53,8 +53,6 @@ const validateBrokerPair = (brokerPair) => { throw new Error('Broker minimum order size must be bigger than zero.'); } else if (new BigNumber(brokerPair.max_size).comparedTo(new BigNumber(brokerPair.min_size)) !== 1) { throw new Error('Broker maximum order size must be bigger than minimum order size.'); - } else if (new BigNumber(brokerPair.increment_size).comparedTo(0) !== 1) { - throw new Error('Broker order price increment must be bigger than zero.'); } else if (brokerPair.symbol && !validatePair(brokerPair.symbol)) { throw new Error('invalid symbol'); } @@ -86,19 +84,19 @@ const getQuoteDynamicBroker = async (side, broker, user_id = null, orderData) => const baseCurrencyPrice = await calculatePrice(side, spread, formula, refresh_interval, id); - const decimalPoint = new BigNumber(broker.increment_size).dp(); - const roundedPrice = new BigNumber(baseCurrencyPrice).decimalPlaces(decimalPoint).toNumber(); const responseObject = { - price: roundedPrice + price: baseCurrencyPrice }; + const { size, receiving_amount, spending_amount } = calculateSize(orderData, side, responseObject, symbol); + responseObject.receiving_amount = receiving_amount; + responseObject.spending_amount = spending_amount; + //check if there is user_id, if so, assing token if (user_id) { - const size = calculateSize(orderData, side, responseObject, decimalPoint, symbol); - // Generate randomToken to be used during deal execution - const randomToken = generateRandomToken(user_id, symbol, side, quote_expiry_time, roundedPrice, size, 'broker'); + const randomToken = generateRandomToken(user_id, symbol, side, quote_expiry_time, baseCurrencyPrice, size, 'broker'); responseObject.token = randomToken; // set expiry const expiryDate = new Date(); @@ -111,21 +109,21 @@ const getQuoteDynamicBroker = async (side, broker, user_id = null, orderData) => }; const getQuoteManualBroker = async (broker, side, user_id = null, orderData) => { - const { symbol, quote_expiry_time, sell_price, buy_price, increment_size } = broker; + const { symbol, quote_expiry_time, sell_price, buy_price } = broker; const baseCurrencyPrice = side === 'buy' ? sell_price : buy_price; - const decimalPoint = new BigNumber(increment_size).dp(); - const roundedPrice = new BigNumber(baseCurrencyPrice).decimalPlaces(decimalPoint).toNumber(); - const responseObject = { - price: roundedPrice + price: baseCurrencyPrice }; - const size = calculateSize(orderData, side, responseObject, decimalPoint, symbol); + const { size, receiving_amount, spending_amount } = calculateSize(orderData, side, responseObject, symbol); + responseObject.receiving_amount = receiving_amount; + responseObject.spending_amount = spending_amount; if (user_id) { - const randomToken = generateRandomToken(user_id, symbol, side, quote_expiry_time, roundedPrice, size, 'broker'); + + const randomToken = generateRandomToken(user_id, symbol, side, quote_expiry_time, baseCurrencyPrice, size, 'broker'); responseObject.token = randomToken; // set expiry const expiryDate = new Date(); @@ -135,7 +133,7 @@ const getQuoteManualBroker = async (broker, side, user_id = null, orderData) => return responseObject; } -const calculateSize = (orderData, side, responseObject, decimalPoint, symbol) => { +const calculateSize = (orderData, side, responseObject, symbol) => { if (orderData == null) { throw new Error(COIN_INPUT_MISSING); } @@ -143,19 +141,38 @@ const calculateSize = (orderData, side, responseObject, decimalPoint, symbol) => let size = null; let { spending_currency, receiving_currency, spending_amount, receiving_amount } = orderData; + const coins = symbol.split('-'); + const baseCoinInfo = getKitCoin(coins[0]); + const quoteCointInfo = getKitCoin(coins[1]); + if (spending_currency == null && receiving_currency == null) { throw new Error(AMOUNTS_MISSING); } if (spending_amount != null) { - const sourceAmount = new BigNumber(side === 'buy' ? spending_amount / responseObject.price : spending_amount * responseObject.price) - .decimalPlaces(decimalPoint).toNumber(); - receiving_amount = sourceAmount; + const incrementUnit = side === 'buy' ? baseCoinInfo.increment_unit : quoteCointInfo.increment_unit; + const targetedAmount = side === 'buy' ? spending_amount / responseObject.price : spending_amount * responseObject.price; + + if (incrementUnit < 1) { + const decimalPoint = new BigNumber(incrementUnit).dp(); + const sourceAmount = new BigNumber(targetedAmount).decimalPlaces(decimalPoint).toNumber(); + receiving_amount = sourceAmount; + } else { + receiving_amount = targetedAmount - (targetedAmount % incrementUnit); + } + } else if (receiving_amount != null) { - const sourceAmount = new BigNumber(side === 'buy' ? receiving_amount * responseObject.price : receiving_amount / responseObject.price) - .decimalPlaces(decimalPoint).toNumber(); - spending_amount = sourceAmount; + const incrementUnit = side === 'buy' ? quoteCointInfo.increment_unit : baseCoinInfo.increment_unit + const targetedAmount = side === 'buy' ? receiving_amount * responseObject.price : receiving_amount / responseObject.price; + + if (incrementUnit < 1) { + const decimalPoint = new BigNumber(incrementUnit).dp(); + const sourceAmount = new BigNumber(targetedAmount).decimalPlaces(decimalPoint).toNumber(); + spending_amount = sourceAmount; + } else { + spending_amount = targetedAmount - (targetedAmount % incrementUnit); + } } if (`${spending_currency}-${receiving_currency}` === symbol) { @@ -163,7 +180,7 @@ const calculateSize = (orderData, side, responseObject, decimalPoint, symbol) => } else { size = receiving_amount } - return size; + return { size, spending_amount, receiving_amount }; } @@ -301,7 +318,7 @@ const fetchBrokerQuote = async (brokerQuote) => { }; const testBroker = async (data) => { - const { formula, spread, increment_size } = data; + const { formula, spread } = data; try { if (spread == null) { throw new Error(BROKER_FORMULA_NOT_FOUND); @@ -323,7 +340,7 @@ const testBroker = async (data) => { throw new Error(PRICE_NOT_FOUND); } - const decimalPoint = new BigNumber(increment_size).dp(); + const decimalPoint = new BigNumber(price).dp(); return { buy_price: new BigNumber(price * (1 - (spread / 100))).decimalPlaces(decimalPoint).toNumber(), sell_price: new BigNumber(price * (1 + (spread / 100))).decimalPlaces(decimalPoint).toNumber() @@ -408,7 +425,6 @@ const reverseTransaction = async (orderData) => { const quickTradeConfig = quickTrades.find(quickTrade => quickTrade.symbol === symbol); if (quickTradeConfig && quickTradeConfig.type === 'broker' && quickTradeConfig.active && broker && !broker.paused && broker.account) { - const decimalPoint = new BigNumber(broker.increment_size).dp(); const objectKeys = Object.keys(broker.account); const exchangeKey = objectKeys[0]; @@ -420,12 +436,7 @@ const reverseTransaction = async (orderData) => { }) const formattedRebalancingSymbol = broker.rebalancing_symbol && broker.rebalancing_symbol.split('-').join('/').toUpperCase(); - - const marketTicker = await exchange.fetchTicker(symbol); - - const roundedPrice = new BigNumber(side === 'buy' ? marketTicker.last * 1.01 : marketTicker.last * 0.99) - .decimalPlaces(decimalPoint).toNumber(); - exchange.createOrder(formattedRebalancingSymbol, 'limit', side, size, roundedPrice) + exchange.createOrder(formattedRebalancingSymbol, 'market', side, size) .catch((err) => { notifyUser(err.message, broker.user_id); }); } } @@ -459,7 +470,6 @@ const createBrokerPair = async (brokerPair) => { type, account, formula, - increment_size, rebalancing_symbol } = brokerPair; @@ -492,7 +502,7 @@ const createBrokerPair = async (brokerPair) => { } if (formula) { - const brokerPrice = await testBroker({ formula, spread, increment_size }); + const brokerPrice = await testBroker({ formula, spread }); if (!Number(brokerPrice.sell_price) || !Number(brokerPrice.buy_price)) { throw new Error(FORMULA_MARKET_PAIR_ERROR); } @@ -534,7 +544,6 @@ const updateBrokerPair = async (id, data) => { type, account, formula, - increment_size, rebalancing_symbol } = data; @@ -566,7 +575,7 @@ const updateBrokerPair = async (id, data) => { } if (formula) { - const brokerPrice = await testBroker({ formula, spread, increment_size }); + const brokerPrice = await testBroker({ formula, spread }); if (!Number(brokerPrice.sell_price) || !Number(brokerPrice.buy_price)) { throw new Error(FORMULA_MARKET_PAIR_ERROR); } @@ -586,7 +595,6 @@ const updateBrokerPair = async (id, data) => { 'sell_price', 'min_size', 'max_size', - 'increment_size', 'paused', 'type', 'quote_expiry_time', diff --git a/server/utils/hollaex-tools-lib/tools/order.js b/server/utils/hollaex-tools-lib/tools/order.js index 81c5576e1e..83b52663fa 100644 --- a/server/utils/hollaex-tools-lib/tools/order.js +++ b/server/utils/hollaex-tools-lib/tools/order.js @@ -162,7 +162,6 @@ const getUserQuickTrade = async (spending_currency, spending_amount, receiving_a } }) .then((brokerQuote) => { - const decimalPoint = new BigNumber(broker.increment_size).dp(); const responseObj = { spending_currency, receiving_currency, @@ -172,15 +171,9 @@ const getUserQuickTrade = async (spending_currency, spending_amount, receiving_a type: 'broker' } if (spending_amount != null) { - const sourceAmount = new BigNumber(side === 'buy' ? spending_amount / brokerQuote.price : spending_amount * brokerQuote.price) - .decimalPlaces(decimalPoint).toNumber(); - - responseObj.receiving_amount = sourceAmount; - + responseObj.receiving_amount = brokerQuote.receiving_amount; } else if (receiving_amount != null) { - const sourceAmount = new BigNumber(side === 'buy' ? receiving_amount * brokerQuote.price : receiving_amount / brokerQuote.price) - .decimalPlaces(decimalPoint).toNumber(); - responseObj.spending_amount = sourceAmount; + responseObj.spending_amount = brokerQuote.spending_amount;; } const baseCoinSize = side === 'buy' ? responseObj.receiving_amount : responseObj.spending_amount; diff --git a/server/utils/hollaex-tools-lib/tools/user.js b/server/utils/hollaex-tools-lib/tools/user.js index 16da9b8ca3..815dc96805 100644 --- a/server/utils/hollaex-tools-lib/tools/user.js +++ b/server/utils/hollaex-tools-lib/tools/user.js @@ -1987,6 +1987,57 @@ const revokeExchangeUserSession = async (sessionId, userId = null) => { return updatedSession.dataValues; } +const getAllBalancesAdmin = async (opts = { + user_id: null, + currency: null, + format: null, + additionalHeaders: null +}) => { + + let network_id = null; + if (opts.user_id) { + // check mapKitIdToNetworkId + const idDictionary = await mapKitIdToNetworkId([opts.user_id]); + if (!has(idDictionary, opts.user_id)) { + throw new Error(USER_NOT_FOUND); + } else if (!idDictionary[opts.user_id]) { + throw new Error(USER_NOT_REGISTERED_ON_NETWORK); + } else { + network_id = idDictionary[opts.user_id]; + } + } + + return getNodeLib().getBalances({ + userId: network_id, + currency: opts.currency, + format: opts.format, + additionalHeaders: opts.additionalHeaders + }) + .then(async (balances) => { + if (balances.data.length > 0) { + const networkIds = balances.data.map((balance) => balance.user_id).filter(id => id); + const idDictionary = await mapNetworkIdToKitId(networkIds); + for (let balance of balances.data) { + const user_kit_id = idDictionary[balance.user_id]; + balance.network_id = balance.user_id; + balance.user_id = user_kit_id; + if (balance.User) balance.User.id = user_kit_id; + } + } + + if (opts.format && opts.format === 'all') { + if (balances.data.length === 0) { + throw new Error(NO_DATA_FOR_CSV); + } + const csv = parse(balances.data, Object.keys(balances.data[0])); + return csv; + } else { + return balances; + } + }); +} + + module.exports = { loginUser, getUserTier, @@ -2043,5 +2094,6 @@ module.exports = { revokeExchangeUserSession, updateLoginStatus, findUserLatestLogin, - createUserLogin + createUserLogin, + getAllBalancesAdmin }; diff --git a/web/src/containers/Admin/AdminFinancials/Balances.js b/web/src/containers/Admin/AdminFinancials/Balances.js new file mode 100644 index 0000000000..100856d068 --- /dev/null +++ b/web/src/containers/Admin/AdminFinancials/Balances.js @@ -0,0 +1,130 @@ +import React, { useState } from 'react'; +import MultiFilter from './TableFilter'; +import { getExchangeBalances } from './action'; + +// const columns = [ +// { +// title: 'User Id', +// dataIndex: 'user_id', +// key: 'user_id', +// render: (user_id, data) => { +// return ( +//
+// +//
+// ); +// }, +// }, +// { +// title: 'Currency', +// dataIndex: 'symbol', +// key: 'symbol', +// }, +// { +// title: 'Available Balance', +// dataIndex: 'available', +// key: 'available', +// render: (available, data) => { +// return ( +//
+// {parseFloat(data.available)} +//
+// ); +// }, +// }, +// { +// title: 'Total Balance', +// dataIndex: 'balance', +// key: 'balance', +// render: (balance, data) => { +// return ( +//
+// {parseFloat(data.balance)} +//
+// ); +// }, +// }, +// ]; + +const filterFields = [ + { + label: 'User ID', + value: '', + placeholder: 'Input User ID', + type: 'number', + name: 'user_id', + }, + { + label: 'Currency', + value: '', + placeholder: 'Currency', + type: 'select', + name: 'currency', + }, +]; + +const filterOptions = [ + { + label: 'User ID', + value: 'user_id', + name: 'user_id', + }, + { + label: 'Currency', + value: 'currency', + name: 'currency', + } + +]; + +const Balances = () => { + const [isLoading, setIsLoading] = useState(false); + + // useEffect(() => { + // setIsLoading(true); + // getBalances(); + // }, []); + + + const requestDownload = (fieldValues = {}) => { + return getExchangeBalances({ ...fieldValues, format: 'all' }); + }; + + return ( +
+
Exchange balances
+
In this section you can download all current balances of the users. Apply the filters and click download to proceed. Please note that this function could take some time to complete.
+
+ {}} + setIsLoading={setIsLoading} + isLoading={isLoading} + buttonText={'Download'} + alwaysEnabled = {true} + onDownload={requestDownload} + /> +
+ {/*
+ { requestDownload(); }} + className="mb-2 underline-text cursor-pointer" + > + Download below CSV table + +
+ + + + + */} + + ); +}; + +export default Balances; diff --git a/web/src/containers/Admin/AdminFinancials/TableFilter.js b/web/src/containers/Admin/AdminFinancials/TableFilter.js index b8e34ea1f5..adbe8007d2 100644 --- a/web/src/containers/Admin/AdminFinancials/TableFilter.js +++ b/web/src/containers/Admin/AdminFinancials/TableFilter.js @@ -134,6 +134,9 @@ const MultiFilter = ({ coins, setIsLoading, isLoading, + buttonText = null, + alwaysEnabled = false, + onDownload = null }) => { const [options, setOptions] = useState(filterOptions); const [fieldsData, setFieldsData] = useState([]); @@ -232,7 +235,8 @@ const MultiFilter = ({ obj = { ...obj, [name]: filterData[name] }; } }); - onHandle(obj); + if (!onDownload) onHandle(obj); + else onDownload(obj); }; return ( @@ -270,15 +274,16 @@ const MultiFilter = ({ : 'filter-button green-btn' } disabled={ - isLoading || + !alwaysEnabled && + (isLoading || Object.keys(filterData).length === 0 || !Object.values(filterData) .map((field) => field === '') - .filter((item) => !item)?.length + .filter((item) => !item)?.length) } onClick={onHandleSearch} > - Search + {buttonText || 'Search'} ); diff --git a/web/src/containers/Admin/AdminFinancials/Wallet.js b/web/src/containers/Admin/AdminFinancials/Wallet.js index 62b52d2e51..d4b2956d80 100644 --- a/web/src/containers/Admin/AdminFinancials/Wallet.js +++ b/web/src/containers/Admin/AdminFinancials/Wallet.js @@ -1,8 +1,7 @@ import React, { useEffect, useState } from 'react'; import { message, Table, Button, Spin } from 'antd'; -import { requestUsersDownload } from '../User/actions'; import MultiFilter from './TableFilter'; -import { getExchangeWallet } from './action'; +import { getExchangeWallet, getExchangeWalletCsv } from './action'; const columns = [ { @@ -106,6 +105,7 @@ const filterOptions = [ const Wallet = () => { const [userData, setUserData] = useState([]); const [isLoading, setIsLoading] = useState(false); + const [queryValues, setQueryValues] = useState({}); useEffect(() => { setIsLoading(true); @@ -114,6 +114,7 @@ const Wallet = () => { const getWallet = async (values = {}) => { try { + setQueryValues(values); const res = await getExchangeWallet(values); if (res && res.data) { setUserData(res.data); @@ -125,8 +126,8 @@ const Wallet = () => { } }; - const requestDownload = (params = {}) => { - return requestUsersDownload({ ...params, format: 'csv' }); + const requestDownload = () => { + return getExchangeWalletCsv({ ...queryValues, format: 'csv' }); }; return ( @@ -143,7 +144,7 @@ const Wallet = () => {
{ requestDownload(); }} className="mb-2 underline-text cursor-pointer" > Download below CSV table diff --git a/web/src/containers/Admin/AdminFinancials/action.js b/web/src/containers/Admin/AdminFinancials/action.js index a02ecbcb1e..7bd34cb500 100644 --- a/web/src/containers/Admin/AdminFinancials/action.js +++ b/web/src/containers/Admin/AdminFinancials/action.js @@ -2,6 +2,7 @@ import axios from 'axios'; import { HOLLAEX_NETWORK_API_URL } from 'config'; import querystring from 'query-string'; import { requestAuthenticated, requestDashAuthenticated } from 'utils'; +import moment from 'moment'; export const storeMint = (values) => { const options = { @@ -122,3 +123,45 @@ export const getExchangeWallet = (values) => { values && Object.keys(values).length ? querystring.stringify(values) : ''; return requestAuthenticated(`/admin/user/wallet?${queryValues}`); }; + +export const getExchangeWalletCsv = (values) => { + const queryValues = + values && Object.keys(values).length ? querystring.stringify(values) : ''; + return axios({ + method: 'GET', + url: `/admin/user/wallet?${queryValues}`, + }) + .then((res) => { + const url = window.URL.createObjectURL(new Blob([res.data])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute( + 'download', + `wallets_${moment().format('YYYY-MM-DD')}.csv` + ); + document.body.appendChild(link); + link.click(); + }) + .catch((err) => {}); +}; + +export const getExchangeBalances = (values) => { + const queryValues = + values && Object.keys(values).length ? querystring.stringify(values) : ''; + return axios({ + method: 'GET', + url: `/admin/balances?${queryValues}`, + }) + .then((res) => { + const url = window.URL.createObjectURL(new Blob([res.data])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute( + 'download', + `balances_${moment().format('YYYY-MM-DD')}.csv` + ); + document.body.appendChild(link); + link.click(); + }) + .catch((err) => {}); +}; \ No newline at end of file diff --git a/web/src/containers/Admin/AdminFinancials/index.js b/web/src/containers/Admin/AdminFinancials/index.js index 0d991ff5d1..0c323f8d9d 100644 --- a/web/src/containers/Admin/AdminFinancials/index.js +++ b/web/src/containers/Admin/AdminFinancials/index.js @@ -11,6 +11,7 @@ import ExchangeOrdersContainer from '../Orders'; import Assets, { getTabParams } from './Assets'; import './index.css'; import Wallet from './Wallet'; +import Balances from './Balances'; const TabPane = Tabs.TabPane; @@ -56,26 +57,29 @@ const AdminFinancials = ({ router, location, user }) => { - + + + + - + - + - + - + - + diff --git a/web/src/containers/Admin/Trades/Otcdeskpopup.js b/web/src/containers/Admin/Trades/Otcdeskpopup.js index f11415e260..a2d0b8ab8c 100644 --- a/web/src/containers/Admin/Trades/Otcdeskpopup.js +++ b/web/src/containers/Admin/Trades/Otcdeskpopup.js @@ -666,7 +666,7 @@ const Otcdeskpopup = ({ {previewData && previewData.max_size}
-
+ {/*
Tradable increment
-
+ */}