diff --git a/lib/exchange.js b/lib/exchange.js
index 0431a7d5..f9811bb8 100644
--- a/lib/exchange.js
+++ b/lib/exchange.js
@@ -1,6 +1,10 @@
+const _ = require('lodash/fp')
+const { ALL_CRYPTOS } = require('@lamassu/coins')
+
const configManager = require('./new-config-manager')
const ccxt = require('./plugins/exchange/ccxt')
const mockExchange = require('./plugins/exchange/mock-exchange')
+const accounts = require('./new-admin/config/accounts')
function lookupExchange (settings, cryptoCode) {
const exchange = configManager.getWalletSettings(cryptoCode, settings.config).exchange
@@ -45,8 +49,26 @@ function active (settings, cryptoCode) {
return !!lookupExchange(settings, cryptoCode)
}
+function getMarkets () {
+ const filterExchanges = _.filter(it => it.class === 'exchange')
+ const availableExchanges = _.map(it => it.code, filterExchanges(accounts.ACCOUNT_LIST))
+
+ return _.reduce(
+ (acc, value) =>
+ Promise.all([acc, ccxt.getMarkets(value, ALL_CRYPTOS)])
+ .then(([a, markets]) => Promise.resolve({
+ ...a,
+ [value]: markets
+ })),
+ Promise.resolve({}),
+ availableExchanges
+ )
+}
+
module.exports = {
+ fetchExchange,
buy,
sell,
- active
+ active,
+ getMarkets
}
diff --git a/lib/new-admin/graphql/resolvers/index.js b/lib/new-admin/graphql/resolvers/index.js
index a20d9216..ea3cb3fa 100644
--- a/lib/new-admin/graphql/resolvers/index.js
+++ b/lib/new-admin/graphql/resolvers/index.js
@@ -11,6 +11,7 @@ const funding = require('./funding.resolver')
const log = require('./log.resolver')
const loyalty = require('./loyalty.resolver')
const machine = require('./machine.resolver')
+const market = require('./market.resolver')
const notification = require('./notification.resolver')
const pairing = require('./pairing.resolver')
const rates = require('./rates.resolver')
@@ -35,6 +36,7 @@ const resolvers = [
log,
loyalty,
machine,
+ market,
notification,
pairing,
rates,
diff --git a/lib/new-admin/graphql/resolvers/market.resolver.js b/lib/new-admin/graphql/resolvers/market.resolver.js
new file mode 100644
index 00000000..49864417
--- /dev/null
+++ b/lib/new-admin/graphql/resolvers/market.resolver.js
@@ -0,0 +1,9 @@
+const exchange = require('../../../exchange')
+
+const resolvers = {
+ Query: {
+ getMarkets: () => exchange.getMarkets()
+ }
+}
+
+module.exports = resolvers
diff --git a/lib/new-admin/graphql/types/index.js b/lib/new-admin/graphql/types/index.js
index f4794b67..e33c50b5 100644
--- a/lib/new-admin/graphql/types/index.js
+++ b/lib/new-admin/graphql/types/index.js
@@ -11,6 +11,7 @@ const funding = require('./funding.type')
const log = require('./log.type')
const loyalty = require('./loyalty.type')
const machine = require('./machine.type')
+const market = require('./market.type')
const notification = require('./notification.type')
const pairing = require('./pairing.type')
const rates = require('./rates.type')
@@ -35,6 +36,7 @@ const types = [
log,
loyalty,
machine,
+ market,
notification,
pairing,
rates,
diff --git a/lib/new-admin/graphql/types/market.type.js b/lib/new-admin/graphql/types/market.type.js
new file mode 100644
index 00000000..2413a9fe
--- /dev/null
+++ b/lib/new-admin/graphql/types/market.type.js
@@ -0,0 +1,9 @@
+const { gql } = require('apollo-server-express')
+
+const typeDef = gql`
+ type Query {
+ getMarkets: JSONObject @auth
+ }
+`
+
+module.exports = typeDef
diff --git a/lib/plugins.js b/lib/plugins.js
index d5bfcb4f..157c67ed 100644
--- a/lib/plugins.js
+++ b/lib/plugins.js
@@ -475,25 +475,28 @@ function plugins (settings, deviceId) {
function buyAndSell (rec, doBuy, tx) {
const cryptoCode = rec.cryptoCode
- const fiatCode = rec.fiatCode
- const cryptoAtoms = doBuy ? commissionMath.fiatToCrypto(tx, rec, deviceId, settings.config) : rec.cryptoAtoms.negated()
+ return exchange.fetchExchange(settings, cryptoCode)
+ .then(_exchange => {
+ const fiatCode = _exchange.account.currencyMarket
+ const cryptoAtoms = doBuy ? commissionMath.fiatToCrypto(tx, rec, deviceId, settings.config) : rec.cryptoAtoms.negated()
- const market = [fiatCode, cryptoCode].join('')
+ const market = [fiatCode, cryptoCode].join('')
- if (!exchange.active(settings, cryptoCode)) return
+ if (!exchange.active(settings, cryptoCode)) return
- const direction = doBuy ? 'cashIn' : 'cashOut'
- const internalTxId = tx ? tx.id : rec.id
- logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms)
- if (!tradesQueues[market]) tradesQueues[market] = []
- tradesQueues[market].push({
- direction,
- internalTxId,
- fiatCode,
- cryptoAtoms,
- cryptoCode,
- timestamp: Date.now()
- })
+ const direction = doBuy ? 'cashIn' : 'cashOut'
+ const internalTxId = tx ? tx.id : rec.id
+ logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms)
+ if (!tradesQueues[market]) tradesQueues[market] = []
+ tradesQueues[market].push({
+ direction,
+ internalTxId,
+ fiatCode,
+ cryptoAtoms,
+ cryptoCode,
+ timestamp: Date.now()
+ })
+ })
}
function consolidateTrades (cryptoCode, fiatCode) {
@@ -550,19 +553,22 @@ function plugins (settings, deviceId) {
const deviceIds = devices.map(device => device.deviceId)
const lists = deviceIds.map(deviceId => {
const localeConfig = configManager.getLocale(deviceId, settings.config)
- const fiatCode = localeConfig.fiatCurrency
const cryptoCodes = localeConfig.cryptoCurrencies
- return cryptoCodes.map(cryptoCode => ({
- fiatCode,
- cryptoCode
+ return Promise.all(cryptoCodes.map(cryptoCode => {
+ return exchange.fetchExchange(settings, cryptoCode)
+ .then(exchange => ({
+ fiatCode: exchange.account.currencyMarket,
+ cryptoCode
+ }))
}))
})
-
- const tradesPromises = _.uniq(_.flatten(lists))
- .map(r => executeTradesForMarket(settings, r.fiatCode, r.cryptoCode))
-
- return Promise.all(tradesPromises)
+
+ return Promise.all(lists)
+ })
+ .then(lists => {
+ return Promise.all(_.uniq(_.flatten(lists))
+ .map(r => executeTradesForMarket(settings, r.fiatCode, r.cryptoCode)))
})
.catch(logger.error)
}
diff --git a/lib/plugins/common/ccxt.js b/lib/plugins/common/ccxt.js
index db98b460..1acdaa95 100644
--- a/lib/plugins/common/ccxt.js
+++ b/lib/plugins/common/ccxt.js
@@ -33,11 +33,8 @@ function buildMarket (fiatCode, cryptoCode, serviceName) {
if (!_.includes(cryptoCode, ALL[serviceName].CRYPTO)) {
throw new Error('Unsupported crypto: ' + cryptoCode)
}
- const fiatSupported = ALL[serviceName].FIAT
- if (fiatSupported !== 'ALL_CURRENCIES' && !_.includes(fiatCode, fiatSupported)) {
- logger.info('Building a market for an unsupported fiat. Defaulting to EUR market')
- return cryptoCode + '/' + 'EUR'
- }
+
+ if (_.isNil(fiatCode)) throw new Error('Market pair building failed: Missing fiat code')
return cryptoCode + '/' + fiatCode
}
diff --git a/lib/plugins/exchange/binance.js b/lib/plugins/exchange/binance.js
index 8a45723c..47c498e7 100644
--- a/lib/plugins/exchange/binance.js
+++ b/lib/plugins/exchange/binance.js
@@ -7,7 +7,8 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, BCH, XMR, ETH, LTC, ZEC, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, ZEC, BCH, XMR, LN]
const FIAT = ['USD', 'EUR']
-const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
+const DEFAULT_FIAT_MARKET = 'EUR'
+const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket']
const loadConfig = (account) => {
const mapper = {
@@ -17,4 +18,4 @@ const loadConfig = (account) => {
return { ...mapped, timeout: 3000 }
}
-module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
+module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
diff --git a/lib/plugins/exchange/binanceus.js b/lib/plugins/exchange/binanceus.js
index ecf058b6..e8f0c371 100644
--- a/lib/plugins/exchange/binanceus.js
+++ b/lib/plugins/exchange/binanceus.js
@@ -7,7 +7,8 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, USDT_TRON, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, USDT, USDT_TRON, LN]
const FIAT = ['USD']
-const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
+const DEFAULT_FIAT_MARKET = 'USD'
+const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket']
const loadConfig = (account) => {
const mapper = {
@@ -17,4 +18,4 @@ const loadConfig = (account) => {
return { ...mapped, timeout: 3000 }
}
-module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
+module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
diff --git a/lib/plugins/exchange/bitfinex.js b/lib/plugins/exchange/bitfinex.js
index 4feccb0c..4e4d85ce 100644
--- a/lib/plugins/exchange/bitfinex.js
+++ b/lib/plugins/exchange/bitfinex.js
@@ -7,6 +7,7 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, ETH, LTC, BCH, USDT, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, BCH, USDT, LN]
const FIAT = ['USD', 'EUR']
+const DEFAULT_FIAT_MARKET = 'EUR'
const AMOUNT_PRECISION = 8
const REQUIRED_CONFIG_FIELDS = ['key', 'secret']
@@ -18,4 +19,4 @@ const loadConfig = (account) => {
return { ...mapped, timeout: 3000 }
}
-module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
+module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, DEFAULT_FIAT_MARKET, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
diff --git a/lib/plugins/exchange/bitstamp.js b/lib/plugins/exchange/bitstamp.js
index 5494ff1c..bd745d49 100644
--- a/lib/plugins/exchange/bitstamp.js
+++ b/lib/plugins/exchange/bitstamp.js
@@ -7,8 +7,9 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, ETH, LTC, BCH, USDT, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, BCH, USDT, LN]
const FIAT = ['USD', 'EUR']
+const DEFAULT_FIAT_MARKET = 'EUR'
const AMOUNT_PRECISION = 8
-const REQUIRED_CONFIG_FIELDS = ['key', 'secret', 'clientId']
+const REQUIRED_CONFIG_FIELDS = ['key', 'secret', 'clientId', 'currencyMarket']
const loadConfig = (account) => {
const mapper = {
@@ -19,4 +20,4 @@ const loadConfig = (account) => {
return { ...mapped, timeout: 3000 }
}
-module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
+module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
diff --git a/lib/plugins/exchange/ccxt.js b/lib/plugins/exchange/ccxt.js
index 5de324f5..63b57fa9 100644
--- a/lib/plugins/exchange/ccxt.js
+++ b/lib/plugins/exchange/ccxt.js
@@ -1,9 +1,13 @@
const { utils: coinUtils } = require('@lamassu/coins')
const _ = require('lodash/fp')
const ccxt = require('ccxt')
+const mem = require('mem')
const { buildMarket, ALL, isConfigValid } = require('../common/ccxt')
const { ORDER_TYPES } = require('./consts')
+const logger = require('../../logger')
+const { currencies } = require('../../new-admin/config')
+const T = require('../../time')
const DEFAULT_PRICE_PRECISION = 2
const DEFAULT_AMOUNT_PRECISION = 8
@@ -18,7 +22,8 @@ function trade (side, account, tradeEntry, exchangeName) {
const { USER_REF, loadOptions, loadConfig = _.noop, REQUIRED_CONFIG_FIELDS, ORDER_TYPE, AMOUNT_PRECISION } = exchangeConfig
if (!isConfigValid(account, REQUIRED_CONFIG_FIELDS)) throw Error('Invalid config')
- const symbol = buildMarket(fiatCode, cryptoCode, exchangeName)
+ const selectedFiatMarket = account.currencyMarket
+ const symbol = buildMarket(selectedFiatMarket, cryptoCode, exchangeName)
const precision = _.defaultTo(DEFAULT_AMOUNT_PRECISION, AMOUNT_PRECISION)
const amount = coinUtils.toUnit(cryptoAtoms, cryptoCode).toFixed(precision)
const accountOptions = _.isFunction(loadOptions) ? loadOptions(account) : {}
@@ -50,4 +55,36 @@ function calculatePrice (side, amount, orderBook) {
throw new Error('Insufficient market depth')
}
-module.exports = { trade }
+function _getMarkets (exchangeName, availableCryptos) {
+ try {
+ const exchange = new ccxt[exchangeName]()
+ const cryptosToQuoteAgainst = ['USDT']
+ const currencyCodes = _.concat(_.map(it => it.code, currencies), cryptosToQuoteAgainst)
+
+ return exchange.fetchMarkets()
+ .then(_.filter(it => (it.type === 'spot' || it.spot)))
+ .then(res =>
+ _.reduce((acc, value) => {
+ if (_.includes(value.base, availableCryptos) && _.includes(value.quote, currencyCodes)) {
+ if (value.quote === value.base) return acc
+
+ if (_.isNil(acc[value.quote])) {
+ return { ...acc, [value.quote]: [value.base] }
+ }
+
+ acc[value.quote].push(value.base)
+ }
+ return acc
+ }, {}, res)
+ )
+ } catch (e) {
+ logger.debug(`No CCXT exchange found for ${exchangeName}`)
+ }
+}
+
+const getMarkets = mem(_getMarkets, {
+ maxAge: T.week,
+ cacheKey: (exchangeName, availableCryptos) => exchangeName
+})
+
+module.exports = { trade, getMarkets }
diff --git a/lib/plugins/exchange/cex.js b/lib/plugins/exchange/cex.js
index 525eb427..b9687e15 100644
--- a/lib/plugins/exchange/cex.js
+++ b/lib/plugins/exchange/cex.js
@@ -7,7 +7,8 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, BCH, DASH, ETH, LTC, USDT, TRX, USDT_TRON, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, DASH, BCH, USDT, TRX, USDT_TRON, LN]
const FIAT = ['USD', 'EUR']
-const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
+const DEFAULT_FIAT_MARKET = 'EUR'
+const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket']
const loadConfig = (account) => {
const mapper = {
@@ -17,4 +18,4 @@ const loadConfig = (account) => {
return { ...mapped, timeout: 3000 }
}
-module.exports = { loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
+module.exports = { loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE }
diff --git a/lib/plugins/exchange/itbit.js b/lib/plugins/exchange/itbit.js
index 02572335..d80268e1 100644
--- a/lib/plugins/exchange/itbit.js
+++ b/lib/plugins/exchange/itbit.js
@@ -7,8 +7,9 @@ const ORDER_TYPE = ORDER_TYPES.LIMIT
const { BTC, ETH, USDT, LN } = COINS
const CRYPTO = [BTC, ETH, USDT, LN]
const FIAT = ['USD']
+const DEFAULT_FIAT_MARKET = 'USD'
const AMOUNT_PRECISION = 4
-const REQUIRED_CONFIG_FIELDS = ['clientKey', 'clientSecret', 'userId', 'walletId']
+const REQUIRED_CONFIG_FIELDS = ['clientKey', 'clientSecret', 'userId', 'walletId', 'currencyMarket']
const loadConfig = (account) => {
const mapper = {
@@ -21,4 +22,4 @@ const loadConfig = (account) => {
}
const loadOptions = ({ walletId }) => ({ walletId })
-module.exports = { loadOptions, loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
+module.exports = { loadOptions, loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
diff --git a/lib/plugins/exchange/kraken.js b/lib/plugins/exchange/kraken.js
index 849af0e5..0f050ccf 100644
--- a/lib/plugins/exchange/kraken.js
+++ b/lib/plugins/exchange/kraken.js
@@ -7,8 +7,9 @@ const ORDER_TYPE = ORDER_TYPES.MARKET
const { BTC, BCH, DASH, ETH, LTC, ZEC, XMR, USDT, TRX, USDT_TRON, LN } = COINS
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, XMR, USDT, TRX, USDT_TRON, LN]
const FIAT = ['USD', 'EUR']
+const DEFAULT_FIAT_MARKET = 'EUR'
const AMOUNT_PRECISION = 6
-const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
+const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey', 'currencyMarket']
const USER_REF = 'userref'
const loadConfig = (account) => {
@@ -26,4 +27,4 @@ const loadConfig = (account) => {
const loadOptions = () => ({ expiretm: '+60' })
-module.exports = { USER_REF, loadOptions, loadConfig, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
+module.exports = { USER_REF, loadOptions, loadConfig, DEFAULT_FIAT_MARKET, REQUIRED_CONFIG_FIELDS, CRYPTO, FIAT, ORDER_TYPE, AMOUNT_PRECISION }
diff --git a/migrations/1732874039534-market-currency.js b/migrations/1732874039534-market-currency.js
new file mode 100644
index 00000000..359db4bd
--- /dev/null
+++ b/migrations/1732874039534-market-currency.js
@@ -0,0 +1,30 @@
+const _ = require('lodash/fp')
+const { loadLatest, saveAccounts } = require('../lib/new-settings-loader')
+const { ACCOUNT_LIST } = require('../lib/new-admin/config/accounts')
+const { ALL } = require('../lib/plugins/common/ccxt')
+
+exports.up = function (next) {
+ return loadLatest()
+ .then(({ accounts }) => {
+ const allExchanges = _.map(it => it.code)(_.filter(it => it.class === 'exchange', ACCOUNT_LIST))
+ const configuredExchanges = _.intersection(allExchanges, _.keys(accounts))
+
+ const newAccounts = _.reduce(
+ (acc, value) => {
+ if (!_.isNil(accounts[value].currencyMarket)) return acc
+ if (_.includes('EUR', ALL[value].FIAT)) return { ...acc, [value]: { currencyMarket: 'EUR' } }
+ return { ...acc, [value]: { currencyMarket: ALL[value].DEFAULT_FIAT_CURRENCY } }
+ },
+ {},
+ configuredExchanges
+ )
+
+ return saveAccounts(newAccounts)
+ })
+ .then(next)
+ .catch(next)
+}
+
+module.exports.down = function (next) {
+ next()
+}
diff --git a/new-lamassu-admin/src/components/inputs/base/Autocomplete.js b/new-lamassu-admin/src/components/inputs/base/Autocomplete.js
index 996fb909..e5f9b941 100644
--- a/new-lamassu-admin/src/components/inputs/base/Autocomplete.js
+++ b/new-lamassu-admin/src/components/inputs/base/Autocomplete.js
@@ -1,8 +1,13 @@
+import { Box } from '@material-ui/core'
import MAutocomplete from '@material-ui/lab/Autocomplete'
import sort from 'match-sorter'
import * as R from 'ramda'
import React from 'react'
+import { HoverableTooltip } from 'src/components/Tooltip'
+import { P } from 'src/components/typography'
+import { errorColor, orangeYellow, spring4 } from 'src/styling/variables'
+
import TextInput from './TextInput'
const Autocomplete = ({
@@ -95,6 +100,39 @@ const Autocomplete = ({
/>
)
}}
+ renderOption={props => {
+ if (!props.warning && !props.warningMessage)
+ return R.path([labelProp])(props)
+
+ const warningColors = {
+ clean: spring4,
+ partial: orangeYellow,
+ important: errorColor
+ }
+
+ const hoverableElement = (
+
+ )
+
+ return (
+
+ {R.path([labelProp])(props)}
+
+ {props.warningMessage}
+
+
+ )
+ }}
/>
)
}
diff --git a/new-lamassu-admin/src/pages/Services/Services.js b/new-lamassu-admin/src/pages/Services/Services.js
index 72eab97b..c1d5b408 100644
--- a/new-lamassu-admin/src/pages/Services/Services.js
+++ b/new-lamassu-admin/src/pages/Services/Services.js
@@ -12,7 +12,7 @@ import SingleRowTable from 'src/components/single-row-table/SingleRowTable'
import { formatLong } from 'src/utils/string'
import FormRenderer from './FormRenderer'
-import schemas from './schemas'
+import _schemas from './schemas'
const GET_INFO = gql`
query getData {
@@ -21,6 +21,12 @@ const GET_INFO = gql`
}
`
+const GET_MARKETS = gql`
+ query getMarkets {
+ getMarkets
+ }
+`
+
const SAVE_ACCOUNT = gql`
mutation Save($accounts: JSONObject) {
saveAccounts(accounts: $accounts)
@@ -40,12 +46,17 @@ const useStyles = makeStyles(styles)
const Services = () => {
const [editingSchema, setEditingSchema] = useState(null)
- const { data } = useQuery(GET_INFO)
+ const { data, loading: configLoading } = useQuery(GET_INFO)
+ const { data: marketsData, loading: marketsLoading } = useQuery(GET_MARKETS)
const [saveAccount] = useMutation(SAVE_ACCOUNT, {
onCompleted: () => setEditingSchema(null),
refetchQueries: ['getData']
})
+ const markets = marketsData?.getMarkets
+
+ const schemas = _schemas(markets)
+
const classes = useStyles()
const accounts = data?.accounts ?? {}
@@ -101,40 +112,44 @@ const Services = () => {
const getValidationSchema = ({ code, getValidationSchema }) =>
getValidationSchema(accounts[code])
+ const loading = marketsLoading || configLoading
+
return (
-
-
-
- {R.values(schemas).map(schema => (
-
- setEditingSchema(schema)}
- items={getItems(schema.code, schema.elements)}
+ !loading && (
+
+
+
+ {R.values(schemas).map(schema => (
+
+ setEditingSchema(schema)}
+ items={getItems(schema.code, schema.elements)}
+ />
+
+ ))}
+
+ {editingSchema && (
+ setEditingSchema(null)}
+ open={true}>
+
+ saveAccount({
+ variables: { accounts: { [editingSchema.code]: it } }
+ })
+ }
+ elements={getElements(editingSchema)}
+ validationSchema={getValidationSchema(editingSchema)}
+ value={getAccounts(editingSchema)}
/>
-
- ))}
-
- {editingSchema && (
- setEditingSchema(null)}
- open={true}>
-
- saveAccount({
- variables: { accounts: { [editingSchema.code]: it } }
- })
- }
- elements={getElements(editingSchema)}
- validationSchema={getValidationSchema(editingSchema)}
- value={getAccounts(editingSchema)}
- />
-
- )}
-
+
+ )}
+
+ )
)
}
diff --git a/new-lamassu-admin/src/pages/Services/schemas/binance.js b/new-lamassu-admin/src/pages/Services/schemas/binance.js
index 6be4be26..faec0e35 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/binance.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/binance.js
@@ -1,36 +1,57 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'binance',
- name: 'Binance',
- title: 'Binance (Exchange)',
- elements: [
- {
- code: 'apiKey',
- display: 'API key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'privateKey',
- display: 'Private key',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'binance',
+ name: 'Binance',
+ title: 'Binance (Exchange)',
+ elements: [
+ {
+ code: 'apiKey',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'privateKey',
+ display: 'Private key',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ apiKey: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ privateKey: Yup.string('The private key must be a string')
+ .max(100, 'The private key is too long')
+ .test(secretTest(account?.privateKey, 'private key')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- apiKey: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- privateKey: Yup.string('The private key must be a string')
- .max(100, 'The private key is too long')
- .test(secretTest(account?.privateKey, 'private key'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/binanceus.js b/new-lamassu-admin/src/pages/Services/schemas/binanceus.js
index 7afd724b..74795e24 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/binanceus.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/binanceus.js
@@ -1,36 +1,57 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'binanceus',
- name: 'Binance.us',
- title: 'Binance.us (Exchange)',
- elements: [
- {
- code: 'apiKey',
- display: 'API key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'privateKey',
- display: 'Private key',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'binanceus',
+ name: 'Binance.us',
+ title: 'Binance.us (Exchange)',
+ elements: [
+ {
+ code: 'apiKey',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'privateKey',
+ display: 'Private key',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ apiKey: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ privateKey: Yup.string('The private key must be a string')
+ .max(100, 'The private key is too long')
+ .test(secretTest(account?.privateKey, 'private key')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- apiKey: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- privateKey: Yup.string('The private key must be a string')
- .max(100, 'The private key is too long')
- .test(secretTest(account?.privateKey, 'private key'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/bitfinex.js b/new-lamassu-admin/src/pages/Services/schemas/bitfinex.js
index 0609807a..c0485af1 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/bitfinex.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/bitfinex.js
@@ -1,36 +1,57 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'bitfinex',
- name: 'Bitfinex',
- title: 'Bitfinex (Exchange)',
- elements: [
- {
- code: 'key',
- display: 'API Key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'secret',
- display: 'API Secret',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'bitfinex',
+ name: 'Bitfinex',
+ title: 'Bitfinex (Exchange)',
+ elements: [
+ {
+ code: 'key',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'secret',
+ display: 'API secret',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency Market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ key: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ secret: Yup.string('The API secret must be a string')
+ .max(100, 'The API secret is too long')
+ .test(secretTest(account?.secret, 'API secret')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- key: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- secret: Yup.string('The API secret must be a string')
- .max(100, 'The API secret is too long')
- .test(secretTest(account?.secret, 'API secret'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/bitstamp.js b/new-lamassu-admin/src/pages/Services/schemas/bitstamp.js
index 431fcfb5..e9061e9e 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/bitstamp.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/bitstamp.js
@@ -1,46 +1,67 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'bitstamp',
- name: 'Bitstamp',
- title: 'Bitstamp (Exchange)',
- elements: [
- {
- code: 'clientId',
- display: 'Client ID',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'key',
- display: 'API key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'secret',
- display: 'API secret',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'bitstamp',
+ name: 'Bitstamp',
+ title: 'Bitstamp (Exchange)',
+ elements: [
+ {
+ code: 'clientId',
+ display: 'Client ID',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'key',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'secret',
+ display: 'API secret',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ clientId: Yup.string('The client ID must be a string')
+ .max(100, 'The client ID is too long')
+ .required('The client ID is required'),
+ key: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ secret: Yup.string('The API secret must be a string')
+ .max(100, 'The API secret is too long')
+ .test(secretTest(account?.secret, 'API secret')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- clientId: Yup.string('The client ID must be a string')
- .max(100, 'The client ID is too long')
- .required('The client ID is required'),
- key: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- secret: Yup.string('The API secret must be a string')
- .max(100, 'The API secret is too long')
- .test(secretTest(account?.secret, 'API secret'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/cex.js b/new-lamassu-admin/src/pages/Services/schemas/cex.js
index f8374c6f..b887db93 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/cex.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/cex.js
@@ -1,46 +1,67 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'cex',
- name: 'CEX.IO',
- title: 'CEX.IO (Exchange)',
- elements: [
- {
- code: 'apiKey',
- display: 'API key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'uid',
- display: 'User ID',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'privateKey',
- display: 'Private key',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'cex',
+ name: 'CEX.IO',
+ title: 'CEX.IO (Exchange)',
+ elements: [
+ {
+ code: 'apiKey',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'uid',
+ display: 'User ID',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'privateKey',
+ display: 'Private key',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency Market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ apiKey: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ uid: Yup.string('The User ID must be a string')
+ .max(100, 'The User ID is too long')
+ .required('The User ID is required'),
+ privateKey: Yup.string('The private key must be a string')
+ .max(100, 'The private key is too long')
+ .test(secretTest(account?.privateKey, 'private key')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- apiKey: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- uid: Yup.string('The User ID must be a string')
- .max(100, 'The User ID is too long')
- .required('The User ID is required'),
- privateKey: Yup.string('The private key must be a string')
- .max(100, 'The private key is too long')
- .test(secretTest(account?.privateKey, 'private key'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/helper.js b/new-lamassu-admin/src/pages/Services/schemas/helper.js
index ccb49a79..c1c97870 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/helper.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/helper.js
@@ -1,5 +1,12 @@
+import { ALL_CRYPTOS } from '@lamassu/coins'
import * as R from 'ramda'
+const WARNING_LEVELS = {
+ CLEAN: 'clean',
+ PARTIAL: 'partial',
+ IMPORTANT: 'important'
+}
+
const secretTest = (secret, message) => ({
name: 'secret-test',
message: message ? `The ${message} is invalid` : 'Invalid field',
@@ -21,4 +28,35 @@ const leadingZerosTest = (value, context) => {
return true
}
-export { secretTest, leadingZerosTest }
+const buildCurrencyOptions = markets => {
+ return R.map(it => {
+ const unavailableCryptos = R.difference(ALL_CRYPTOS, markets[it])
+ const unavailableCryptosFiltered = R.difference(unavailableCryptos, [it]) // As the markets can have stablecoins to trade against other crypto, filter them out, as there can't be pairs such as USDT/USDT
+
+ const unavailableMarketsStr =
+ R.length(unavailableCryptosFiltered) > 1
+ ? `${R.join(
+ ', ',
+ R.slice(0, -1, unavailableCryptosFiltered)
+ )} and ${R.last(unavailableCryptosFiltered)}`
+ : unavailableCryptosFiltered[0]
+
+ const warningLevel = R.isEmpty(unavailableCryptosFiltered)
+ ? WARNING_LEVELS.CLEAN
+ : !R.isEmpty(unavailableCryptosFiltered) &&
+ R.length(unavailableCryptosFiltered) < R.length(ALL_CRYPTOS)
+ ? WARNING_LEVELS.PARTIAL
+ : WARNING_LEVELS.IMPORTANT
+
+ return {
+ code: R.toUpper(it),
+ display: R.toUpper(it),
+ warning: warningLevel,
+ warningMessage: !R.isEmpty(unavailableCryptosFiltered)
+ ? `No market pairs available for ${unavailableMarketsStr}`
+ : `All market pairs are available`
+ }
+ }, R.keys(markets))
+}
+
+export { secretTest, leadingZerosTest, buildCurrencyOptions }
diff --git a/new-lamassu-admin/src/pages/Services/schemas/index.js b/new-lamassu-admin/src/pages/Services/schemas/index.js
index 22368537..e952771b 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/index.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/index.js
@@ -1,16 +1,16 @@
-import binance from './binance'
-import binanceus from './binanceus'
-import bitfinex from './bitfinex'
+import _binance from './binance'
+import _binanceus from './binanceus'
+import _bitfinex from './bitfinex'
import bitgo from './bitgo'
-import bitstamp from './bitstamp'
+import _bitstamp from './bitstamp'
import blockcypher from './blockcypher'
-import cex from './cex'
+import _cex from './cex'
import elliptic from './elliptic'
import galoy from './galoy'
import inforu from './inforu'
import infura from './infura'
-import itbit from './itbit'
-import kraken from './kraken'
+import _itbit from './itbit'
+import _kraken from './kraken'
import mailgun from './mailgun'
import scorechain from './scorechain'
import sumsub from './sumsub'
@@ -19,25 +19,37 @@ import trongrid from './trongrid'
import twilio from './twilio'
import vonage from './vonage'
-export default {
- [bitgo.code]: bitgo,
- [galoy.code]: galoy,
- [bitstamp.code]: bitstamp,
- [blockcypher.code]: blockcypher,
- [elliptic.code]: elliptic,
- [inforu.code]: inforu,
- [infura.code]: infura,
- [itbit.code]: itbit,
- [kraken.code]: kraken,
- [mailgun.code]: mailgun,
- [telnyx.code]: telnyx,
- [vonage.code]: vonage,
- [twilio.code]: twilio,
- [binanceus.code]: binanceus,
- [cex.code]: cex,
- [scorechain.code]: scorechain,
- [trongrid.code]: trongrid,
- [binance.code]: binance,
- [bitfinex.code]: bitfinex,
- [sumsub.code]: sumsub
+const schemas = (markets = {}) => {
+ const binance = _binance(markets?.binance)
+ const bitfinex = _bitfinex(markets?.bitfinex)
+ const binanceus = _binanceus(markets?.binanceus)
+ const bitstamp = _bitstamp(markets?.bitstamp)
+ const cex = _cex(markets?.cex)
+ const itbit = _itbit(markets?.itbit)
+ const kraken = _kraken(markets?.kraken)
+
+ return {
+ [bitgo.code]: bitgo,
+ [galoy.code]: galoy,
+ [bitstamp.code]: bitstamp,
+ [blockcypher.code]: blockcypher,
+ [elliptic.code]: elliptic,
+ [inforu.code]: inforu,
+ [infura.code]: infura,
+ [itbit.code]: itbit,
+ [kraken.code]: kraken,
+ [mailgun.code]: mailgun,
+ [telnyx.code]: telnyx,
+ [vonage.code]: vonage,
+ [twilio.code]: twilio,
+ [binanceus.code]: binanceus,
+ [cex.code]: cex,
+ [scorechain.code]: scorechain,
+ [trongrid.code]: trongrid,
+ [binance.code]: binance,
+ [bitfinex.code]: bitfinex,
+ [sumsub.code]: sumsub
+ }
}
+
+export default schemas
diff --git a/new-lamassu-admin/src/pages/Services/schemas/itbit.js b/new-lamassu-admin/src/pages/Services/schemas/itbit.js
index 949ba692..d6607461 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/itbit.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/itbit.js
@@ -1,54 +1,75 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { buildCurrencyOptions, secretTest } from './helper'
-export default {
- code: 'itbit',
- name: 'itBit',
- title: 'itBit (Exchange)',
- elements: [
- {
- code: 'userId',
- display: 'User ID',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'walletId',
- display: 'Wallet ID',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'clientKey',
- display: 'Client key',
- component: TextInputFormik
- },
- {
- code: 'clientSecret',
- display: 'Client secret',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'itbit',
+ name: 'itBit',
+ title: 'itBit (Exchange)',
+ elements: [
+ {
+ code: 'userId',
+ display: 'User ID',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'walletId',
+ display: 'Wallet ID',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'clientKey',
+ display: 'Client key',
+ component: TextInput
+ },
+ {
+ code: 'clientSecret',
+ display: 'Client secret',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ userId: Yup.string('The user ID must be a string')
+ .max(100, 'The user ID is too long')
+ .required('The user ID is required'),
+ walletId: Yup.string('The wallet ID must be a string')
+ .max(100, 'The wallet ID is too long')
+ .required('The wallet ID is required'),
+ clientKey: Yup.string('The client key must be a string')
+ .max(100, 'The client key is too long')
+ .required('The client key is required'),
+ clientSecret: Yup.string('The client secret must be a string')
+ .max(100, 'The client secret is too long')
+ .test(secretTest(account?.clientSecret, 'client secret')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- userId: Yup.string('The user ID must be a string')
- .max(100, 'The user ID is too long')
- .required('The user ID is required'),
- walletId: Yup.string('The wallet ID must be a string')
- .max(100, 'The wallet ID is too long')
- .required('The wallet ID is required'),
- clientKey: Yup.string('The client key must be a string')
- .max(100, 'The client key is too long')
- .required('The client key is required'),
- clientSecret: Yup.string('The client secret must be a string')
- .max(100, 'The client secret is too long')
- .test(secretTest(account?.clientSecret, 'client secret'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/pages/Services/schemas/kraken.js b/new-lamassu-admin/src/pages/Services/schemas/kraken.js
index 733cebe4..2c0ee271 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/kraken.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/kraken.js
@@ -1,36 +1,57 @@
import * as Yup from 'yup'
-import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
-import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import {
+ SecretInput,
+ TextInput,
+ Autocomplete
+} from 'src/components/inputs/formik'
-import { secretTest } from './helper'
+import { secretTest, buildCurrencyOptions } from './helper'
-export default {
- code: 'kraken',
- name: 'Kraken',
- title: 'Kraken (Exchange)',
- elements: [
- {
- code: 'apiKey',
- display: 'API key',
- component: TextInputFormik,
- face: true,
- long: true
- },
- {
- code: 'privateKey',
- display: 'Private key',
- component: SecretInputFormik
+const schema = markets => {
+ return {
+ code: 'kraken',
+ name: 'Kraken',
+ title: 'Kraken (Exchange)',
+ elements: [
+ {
+ code: 'apiKey',
+ display: 'API key',
+ component: TextInput,
+ face: true,
+ long: true
+ },
+ {
+ code: 'privateKey',
+ display: 'Private key',
+ component: SecretInput
+ },
+ {
+ code: 'currencyMarket',
+ display: 'Currency market',
+ component: Autocomplete,
+ inputProps: {
+ options: buildCurrencyOptions(markets),
+ labelProp: 'display',
+ valueProp: 'code'
+ },
+ face: true
+ }
+ ],
+ getValidationSchema: account => {
+ return Yup.object().shape({
+ apiKey: Yup.string('The API key must be a string')
+ .max(100, 'The API key is too long')
+ .required('The API key is required'),
+ privateKey: Yup.string('The private key must be a string')
+ .max(100, 'The private key is too long')
+ .test(secretTest(account?.privateKey, 'private key')),
+ currencyMarket: Yup.string(
+ 'The currency market must be a string'
+ ).required('The currency market is required')
+ })
}
- ],
- getValidationSchema: account => {
- return Yup.object().shape({
- apiKey: Yup.string('The API key must be a string')
- .max(100, 'The API key is too long')
- .required('The API key is required'),
- privateKey: Yup.string('The private key must be a string')
- .max(100, 'The private key is too long')
- .test(secretTest(account?.privateKey, 'private key'))
- })
}
}
+
+export default schema
diff --git a/new-lamassu-admin/src/styling/variables.js b/new-lamassu-admin/src/styling/variables.js
index 2cb84f7f..63289223 100644
--- a/new-lamassu-admin/src/styling/variables.js
+++ b/new-lamassu-admin/src/styling/variables.js
@@ -32,6 +32,9 @@ const mistyRose = '#ffeceb'
const pumpkin = '#ff7311'
const linen = '#fbf3ec'
+// Warning
+const orangeYellow = '#ffcc00'
+
// Color Variables
const primaryColor = zodiac
@@ -136,6 +139,7 @@ export {
java,
neon,
linen,
+ orangeYellow,
// named colors
primaryColor,
secondaryColor,