Merge pull request #1113 from josepfo/fix/fee-priority-selector-up-to-spec

feat: advanced wallet settings up to spec
This commit is contained in:
Rafael Taranto 2022-02-18 16:59:53 +00:00 committed by GitHub
commit 6f0c735be8
11 changed files with 263 additions and 153 deletions

View file

@ -2,6 +2,7 @@ const _ = require('lodash/fp')
const { getCustomInfoRequests } = require('./new-admin/services/customInfoRequests') const { getCustomInfoRequests } = require('./new-admin/services/customInfoRequests')
const namespaces = { const namespaces = {
ADVANCED: 'advanced',
WALLETS: 'wallets', WALLETS: 'wallets',
OPERATOR_INFO: 'operatorInfo', OPERATOR_INFO: 'operatorInfo',
NOTIFICATIONS: 'notifications', NOTIFICATIONS: 'notifications',
@ -54,7 +55,16 @@ const getLocale = (deviceId, it) => {
const getGlobalLocale = it => getLocale(null, it) const getGlobalLocale = it => getLocale(null, it)
const getWalletSettings = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.WALLETS))(it) const getWalletSettings = (key, it) => {
const filter = _.matches({ cryptoCurrency: key })
const getAdvancedSettings = it => {
const advancedSettings = fromNamespace(namespaces.ADVANCED)(it)
return _.omit(['overrides', 'cryptoCurrency', 'id'], _.assignAll([advancedSettings, ..._.filter(filter)(advancedSettings.overrides)]))
}
const walletsSettings = fromNamespace(namespaces.WALLETS)(it)
return _.assign(fromNamespace(key)(walletsSettings), getAdvancedSettings(walletsSettings))
}
const getCashOut = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.CASH_OUT))(it) const getCashOut = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.CASH_OUT))(it)
const getGlobalCashOut = fromNamespace(namespaces.CASH_OUT) const getGlobalCashOut = fromNamespace(namespaces.CASH_OUT)
const getOperatorInfo = fromNamespace(namespaces.OPERATOR_INFO) const getOperatorInfo = fromNamespace(namespaces.OPERATOR_INFO)

View file

@ -11,6 +11,8 @@ const unitScale = cryptoRec.unitScale
const rpcConfig = jsonRpc.rpcConfig(cryptoRec) const rpcConfig = jsonRpc.rpcConfig(cryptoRec)
const SUPPORTS_BATCHING = true
function fetch (method, params) { function fetch (method, params) {
return jsonRpc.fetch(rpcConfig, method, params) return jsonRpc.fetch(rpcConfig, method, params)
} }
@ -63,7 +65,10 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => calculateFeeDiscount(feeMultiplier)) .then(() => calculateFeeDiscount(feeMultiplier))
.then(newFee => fetch('settxfee', [newFee])) .then(newFee => {
logger.info('** DEBUG MINERS FEE ** - Calculated fee discount: ', newFee)
return fetch('settxfee', [newFee])
})
.then(() => fetch('sendtoaddress', [toAddress, coins])) .then(() => fetch('sendtoaddress', [toAddress, coins]))
.then((txId) => fetch('gettransaction', [txId])) .then((txId) => fetch('gettransaction', [txId]))
.then((res) => _.pick(['fee', 'txid'], res)) .then((res) => _.pick(['fee', 'txid'], res))
@ -82,7 +87,10 @@ function sendCoins (account, tx, settings, operatorId, feeMultiplier) {
function sendCoinsBatch (account, txs, cryptoCode, feeMultiplier) { function sendCoinsBatch (account, txs, cryptoCode, feeMultiplier) {
return checkCryptoCode(cryptoCode) return checkCryptoCode(cryptoCode)
.then(() => calculateFeeDiscount(feeMultiplier)) .then(() => calculateFeeDiscount(feeMultiplier))
.then(newFee => fetch('settxfee', [newFee])) .then(newFee => {
logger.info('** DEBUG MINERS FEE ** - Calculated fee discount: ', newFee)
return fetch('settxfee', [newFee])
})
.then(() => { .then(() => {
const txAddressAmountPairs = _.map(tx => [tx.toAddress, BN(tx.cryptoAtoms).shiftedBy(-unitScale).toFixed(8)], txs) const txAddressAmountPairs = _.map(tx => [tx.toAddress, BN(tx.cryptoAtoms).shiftedBy(-unitScale).toFixed(8)], txs)
return _.fromPairs(txAddressAmountPairs) return _.fromPairs(txAddressAmountPairs)
@ -186,5 +194,6 @@ module.exports = {
fetchRBF, fetchRBF,
estimateFee, estimateFee,
sendCoinsBatch, sendCoinsBatch,
checkBlockchainStatus checkBlockchainStatus,
SUPPORTS_BATCHING
} }

View file

@ -3,7 +3,6 @@ const mem = require('mem')
const hkdf = require('futoin-hkdf') const hkdf = require('futoin-hkdf')
const configManager = require('./new-config-manager') const configManager = require('./new-config-manager')
const { loadLatestConfig } = require('./new-settings-loader')
const pify = require('pify') const pify = require('pify')
const fs = pify(require('fs')) const fs = pify(require('fs'))
@ -63,7 +62,8 @@ function _balance (settings, cryptoCode) {
function sendCoins (settings, tx) { function sendCoins (settings, tx) {
return fetchWallet(settings, tx.cryptoCode) return fetchWallet(settings, tx.cryptoCode)
.then(r => { .then(r => {
const feeMultiplier = settings[`wallets_${tx.cryptoCode}_feeMultiplier`] const feeMultiplier = configManager.getWalletSettings(tx.cryptoCode, settings.config).feeMultiplier
console.log('** DEBUG MINERS FEE ** - Fee multiplier: ', feeMultiplier)
return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier) return r.wallet.sendCoins(r.account, tx, settings, r.operatorId, feeMultiplier)
.then(res => { .then(res => {
mem.clear(module.exports.balance) mem.clear(module.exports.balance)
@ -81,7 +81,7 @@ function sendCoins (settings, tx) {
function sendCoinsBatch (settings, txs, cryptoCode) { function sendCoinsBatch (settings, txs, cryptoCode) {
return fetchWallet(settings, cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => { .then(r => {
const feeMultiplier = settings[`wallets_${cryptoCode}_feeMultiplier`] const feeMultiplier = configManager.getWalletSettings(cryptoCode, settings.config).feeMultiplier
return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier) return r.wallet.sendCoinsBatch(r.account, txs, cryptoCode, feeMultiplier)
.then(res => { .then(res => {
mem.clear(module.exports.balance) mem.clear(module.exports.balance)
@ -233,7 +233,9 @@ function isStrictAddress (settings, cryptoCode, toAddress) {
} }
function supportsBatching (settings, cryptoCode) { function supportsBatching (settings, cryptoCode) {
return Promise.resolve(!!configManager.getWalletSettings(cryptoCode, settings.config).allowTransactionBatching) return fetchWallet(settings, cryptoCode).then(r => {
return Promise.resolve(!!r.wallet.SUPPORTS_BATCHING && !!configManager.getWalletSettings(cryptoCode, settings.config).allowTransactionBatching)
})
} }
function checkBlockchainStatus (settings, cryptoCode) { function checkBlockchainStatus (settings, cryptoCode) {

View file

@ -1,21 +0,0 @@
const _ = require('lodash/fp')
const { migrationSaveConfig, loadLatest } = require('../lib/new-settings-loader')
const { getCryptosFromWalletNamespace } = require('../lib/new-config-manager')
exports.up = function (next) {
const newConfig = {}
return loadLatest()
.then(config => {
const coins = getCryptosFromWalletNamespace(config)
_.map(coin => { newConfig[`wallets_${coin}_feeMultiplier`] = '1' }, coins)
return migrationSaveConfig(newConfig)
})
.then(next)
.catch(err => {
return next(err)
})
}
module.exports.down = function (next) {
next()
}

View file

@ -0,0 +1,22 @@
const uuid = require('uuid')
const { saveConfig, loadLatest } = require('../lib/new-settings-loader')
exports.up = function (next) {
const newConfig = {}
return loadLatest()
.then(config => {
newConfig[`wallets_advanced_feeMultiplier`] = '1'
newConfig[`wallets_advanced_cryptoUnits`] = 'full'
newConfig[`wallets_advanced_allowTransactionBatching`] = false
newConfig[`wallets_advanced_id`] = uuid.v4()
return saveConfig(newConfig)
})
.then(next)
.catch(err => {
return next(err)
})
}
module.exports.down = function (next) {
next()
}

View file

@ -11,7 +11,7 @@ const CheckboxInput = memo(
disabledMessage = '', disabledMessage = '',
...props ...props
}) => { }) => {
const { name, onChange, value } = props.field const { name, onChange, value = true } = props.field
const settings = { const settings = {
enabled: enabled, enabled: enabled,

View file

@ -2,27 +2,28 @@ import { useQuery, useMutation } from '@apollo/react-hooks'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { utils as coinUtils } from 'lamassu-coins' import { utils as coinUtils } from 'lamassu-coins'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React, { useState } from 'react'
import { NamespacedTable as EditableTable } from 'src/components/editableTable' import { Table as EditableTable } from 'src/components/editableTable'
import Section from 'src/components/layout/Section'
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config' import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
import { import {
WalletSchema,
AdvancedWalletSchema, AdvancedWalletSchema,
getAdvancedWalletElements getAdvancedWalletElements,
getAdvancedWalletElementsOverrides,
OverridesDefaults,
OverridesSchema
} from './helper' } from './helper'
const SAVE_CONFIG = gql` const SAVE_CONFIG = gql`
mutation Save($config: JSONObject, $accounts: JSONObject) { mutation Save($config: JSONObject) {
saveConfig(config: $config) saveConfig(config: $config)
saveAccounts(accounts: $accounts)
} }
` `
const GET_INFO = gql` const GET_INFO = gql`
query getData { query getData {
config config
accounts
cryptoCurrencies { cryptoCurrencies {
code code
display display
@ -31,34 +32,101 @@ const GET_INFO = gql`
` `
const AdvancedWallet = () => { const AdvancedWallet = () => {
const ADVANCED = namespaces.ADVANCED
const CRYPTOCURRENCY_KEY = 'cryptoCurrency'
const SCREEN_KEY = namespaces.WALLETS const SCREEN_KEY = namespaces.WALLETS
const { data } = useQuery(GET_INFO) const { data } = useQuery(GET_INFO)
const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = useState(false)
const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
refetchQueries: () => ['getData'] refetchQueries: () => ['getData']
}) })
const save = (rawConfig, accounts) => { const save = rawConfig => {
const config = toNamespace(SCREEN_KEY)(rawConfig) const config = toNamespace(SCREEN_KEY)(
return saveConfig({ variables: { config, accounts } }) toNamespace(ADVANCED)(rawConfig.wallets[0])
)
return saveConfig({ variables: { config } })
} }
const config = data?.config && fromNamespace(SCREEN_KEY)(data.config) const saveOverrides = rawConfig => {
const config = toNamespace(SCREEN_KEY)(toNamespace(ADVANCED)(rawConfig))
return saveConfig({ variables: { config } })
}
const onEditingDefault = (it, editing) => setEditingDefault(editing)
const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
const cryptoCurrencies = data?.cryptoCurrencies ?? [] const cryptoCurrencies = data?.cryptoCurrencies ?? []
const AdvancedWalletSettings = fromNamespace(ADVANCED)(
fromNamespace(SCREEN_KEY)(data?.config)
)
const AdvancedWalletSettingsOverrides = AdvancedWalletSettings.overrides ?? []
const overridenCryptos = R.map(R.prop(CRYPTOCURRENCY_KEY))(
AdvancedWalletSettingsOverrides
)
const suggestionFilter = R.filter(
it => !R.contains(it.code, overridenCryptos)
)
const coinSuggestions = suggestionFilter(cryptoCurrencies)
const findSuggestion = it => {
const coin = R.compose(R.find(R.propEq('code', it?.cryptoCurrency)))(
cryptoCurrencies
)
return coin ? [coin] : []
}
return ( return (
<>
<Section>
<EditableTable <EditableTable
name="advancedWallet" name="wallets"
namespaces={R.map(R.path(['code']))(cryptoCurrencies)} data={R.of(AdvancedWalletSettings)}
data={config}
error={error?.message} error={error?.message}
enableEdit enableEdit
editWidth={174} editWidth={174}
save={save} save={save}
stripeWhen={it => !WalletSchema.isValidSync(it)} stripeWhen={it => !AdvancedWalletSchema.isValidSync(it)}
inialValues={R.of(AdvancedWalletSettings)}
validationSchema={AdvancedWalletSchema} validationSchema={AdvancedWalletSchema}
elements={getAdvancedWalletElements(cryptoCurrencies, coinUtils, config)} elements={getAdvancedWalletElements(
coinUtils,
AdvancedWalletSettings
)}
setEditing={onEditingDefault}
forceDisable={isEditingOverrides}
/> />
</Section>
<Section>
<EditableTable
error={error?.message}
title="Overrides"
titleLg
name="overrides"
enableDelete
enableEdit
enableCreate
inialValues={OverridesDefaults}
save={saveOverrides}
validationSchema={OverridesSchema}
data={AdvancedWalletSettingsOverrides ?? []}
elements={getAdvancedWalletElementsOverrides(
coinSuggestions,
findSuggestion,
coinUtils
)}
disableAdd={!coinSuggestions?.length}
setEditing={onEditingOverrides}
forceDisable={isEditingDefault}
/>
</Section>
</>
) )
} }

View file

@ -1,18 +1,14 @@
import { useQuery, useMutation } from '@apollo/react-hooks' import { useQuery, useMutation } from '@apollo/react-hooks'
import { DialogActions, makeStyles, Box } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import Modal from 'src/components/Modal' import Modal from 'src/components/Modal'
import { IconButton, Button } from 'src/components/buttons'
import { NamespacedTable as EditableTable } from 'src/components/editableTable' import { NamespacedTable as EditableTable } from 'src/components/editableTable'
import { RadioGroup } from 'src/components/inputs'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import { P, Label1 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer' import FormRenderer from 'src/pages/Services/FormRenderer'
import schemas from 'src/pages/Services/schemas' import schemas from 'src/pages/Services/schemas'
import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle buttons/settings/white.svg' import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle buttons/settings/white.svg'
import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg' import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg'
import { fromNamespace, toNamespace } from 'src/utils/config' import { fromNamespace, toNamespace } from 'src/utils/config'
@ -59,8 +55,6 @@ const useStyles = makeStyles(styles)
const Wallet = ({ name: SCREEN_KEY }) => { const Wallet = ({ name: SCREEN_KEY }) => {
const classes = useStyles() const classes = useStyles()
const [editingFeeDiscount, setEditingFeeDiscount] = useState(null)
const [selectedDiscount, setSelectedDiscount] = useState(null)
const [editingSchema, setEditingSchema] = useState(null) const [editingSchema, setEditingSchema] = useState(null)
const [onChangeFunction, setOnChangeFunction] = useState(null) const [onChangeFunction, setOnChangeFunction] = useState(null)
const [wizard, setWizard] = useState(false) const [wizard, setWizard] = useState(false)
@ -116,25 +110,6 @@ const Wallet = ({ name: SCREEN_KEY }) => {
return it return it
}) })
const saveFeeDiscount = rawConfig => {
const config = toNamespace(SCREEN_KEY)(rawConfig)
setEditingFeeDiscount(false)
return saveConfig({ variables: { config } })
}
const handleRadioButtons = evt => {
const selectedDiscount = R.path(['target', 'value'])(evt)
setSelectedDiscount(selectedDiscount)
}
const radioButtonOptions = [
{ display: '+20%', code: '1.2' },
{ display: 'Default', code: '1' },
{ display: '-20%', code: '0.8' },
{ display: '-40%', code: '0.6' },
{ display: '-60%', code: '0.4' }
]
return ( return (
<> <>
<div className={classes.header}> <div className={classes.header}>
@ -149,19 +124,6 @@ const Wallet = ({ name: SCREEN_KEY }) => {
} }
]} ]}
/> />
<Box alignItems="center" justifyContent="end">
<Label1 className={classes.feeDiscountLabel}>Fee discount</Label1>
<Box
display="flex"
alignItems="center"
justifyContent="end"
mr="-4px">
<P className={classes.selection}>{selectedDiscount}</P>
<IconButton onClick={() => setEditingFeeDiscount(true)}>
<EditIcon />
</IconButton>
</Box>
</Box>
</div> </div>
{!advancedSettings && ( {!advancedSettings && (
<> <>
@ -209,32 +171,6 @@ const Wallet = ({ name: SCREEN_KEY }) => {
</> </>
)} )}
{advancedSettings && <AdvancedWallet></AdvancedWallet>} {advancedSettings && <AdvancedWallet></AdvancedWallet>}
{editingFeeDiscount && (
<Modal
title={'Fee discount for BTC'}
width={478}
handleClose={() => setEditingFeeDiscount(null)}
open={true}>
<P>
Set a priority level for your outgoing BTC transactions, selecting a
percentage off of the fee estimate your wallet uses.
</P>
<RadioGroup
name="set-automatic-reset"
value={selectedDiscount}
options={radioButtonOptions}
onChange={handleRadioButtons}
/>
<DialogActions>
<Button
onClick={() =>
saveFeeDiscount({ BTC_feeMultiplier: selectedDiscount })
}>
Confirm
</Button>
</DialogActions>
</Modal>
)}
</> </>
) )
} }

View file

@ -1,16 +1,7 @@
import { offColor } from 'src/styling/variables'
export default { export default {
header: { header: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between' justifyContent: 'space-between'
},
feeDiscountLabel: {
color: offColor,
margin: [[13, 0, -5, 20]]
},
selection: {
marginRight: 12
} }
} }

View file

@ -33,32 +33,50 @@ const WalletSchema = Yup.object().shape({
const AdvancedWalletSchema = Yup.object().shape({ const AdvancedWalletSchema = Yup.object().shape({
cryptoUnits: Yup.string().required(), cryptoUnits: Yup.string().required(),
feeMultiplier: Yup.string().required(),
allowTransactionBatching: Yup.boolean() allowTransactionBatching: Yup.boolean()
}) })
const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => { const OverridesSchema = Yup.object().shape({
const viewCryptoCurrency = it => cryptoUnits: Yup.string().required(),
R.compose( feeMultiplier: Yup.string().required(),
R.prop(['display']), cryptoCurrency: Yup.string().required(),
R.find(R.propEq('code', it)) allowTransactionBatching: Yup.boolean()
)(cryptoCurrencies) })
const getOptions = R.curry((coinUtils, it) => { const OverridesDefaults = {
const options = R.keys(coinUtils.getCryptoCurrency(it.id).units) cryptoUnits: '',
feeMultiplier: '',
cryptoCurrency: '',
allowTransactionBatching: null
}
const viewFeeMultiplier = it =>
R.compose(R.prop(['display']), R.find(R.propEq('code', it)))(feeOptions)
const feeOptions = [
{ display: '+20%', code: '1.2' },
{ display: 'Default', code: '1' },
{ display: '-20%', code: '0.8' },
{ display: '-40%', code: '0.6' },
{ display: '-60%', code: '0.4' }
]
const cryptoUnitsDefaultOptions = [
{ display: 'mili', code: 'mili' },
{ display: 'full', code: 'full' }
]
const getCryptoUnitsOptions = R.curry((coinUtils, it) => {
if (R.isNil(it.cryptoCurrency)) return cryptoUnitsDefaultOptions
const options = R.keys(coinUtils.getCryptoCurrency(it.cryptoCurrency).units)
return R.map(option => { return R.map(option => {
return { code: option, display: option } return { code: option, display: option }
})(options) })(options)
}) })
const getAdvancedWalletElements = () => {
return [ return [
{
name: 'id',
header: 'Cryptocurrency',
width: 180,
view: viewCryptoCurrency,
size: 'sm',
editable: false
},
{ {
name: 'cryptoUnits', name: 'cryptoUnits',
size: 'sm', size: 'sm',
@ -66,7 +84,7 @@ const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => {
width: 190, width: 190,
input: Autocomplete, input: Autocomplete,
inputProps: { inputProps: {
options: getOptions(coinUtils), options: cryptoUnitsDefaultOptions,
valueProp: 'code', valueProp: 'code',
labelProp: 'display' labelProp: 'display'
} }
@ -77,12 +95,83 @@ const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => {
stripe: true, stripe: true,
width: 250, width: 250,
view: (_, ite) => { view: (_, ite) => {
if (ite.id !== 'BTC') return ite.allowTransactionBatching ? 'Yes' : `No`
},
input: Checkbox
},
{
name: 'feeMultiplier',
header: `Miner's Fee`,
size: 'sm',
stripe: true,
width: 250,
view: viewFeeMultiplier,
input: Autocomplete,
inputProps: {
options: feeOptions,
valueProp: 'code',
labelProp: 'display'
}
}
]
}
const getAdvancedWalletElementsOverrides = (
coinSuggestions,
findSuggestion,
coinUtils
) => {
return [
{
name: 'cryptoCurrency',
width: 180,
input: Autocomplete,
inputProps: {
options: it => R.concat(coinSuggestions, findSuggestion(it)),
optionsLimit: null,
valueProp: 'code',
labelProp: 'display'
},
size: 'sm'
},
{
name: 'cryptoUnits',
size: 'sm',
stripe: true,
width: 190,
input: Autocomplete,
inputProps: {
options: getCryptoUnitsOptions(coinUtils),
valueProp: 'code',
labelProp: 'display'
}
},
{
name: 'allowTransactionBatching',
size: 'sm',
stripe: true,
width: 250,
view: (_, ite) => {
if (ite.cryptoCurrency !== 'BTC')
return <span style={classes.editDisabled}>{`No`}</span> return <span style={classes.editDisabled}>{`No`}</span>
return config[`${ite.id}_allowTransactionBatching`] ? 'Yes' : 'No' return ite.allowTransactionBatching ? 'Yes' : 'No'
}, },
input: Checkbox, input: Checkbox,
editable: it => it.id === 'BTC' editable: it => it.cryptoCurrency === 'BTC'
},
{
name: 'feeMultiplier',
header: `Miner's Fee`,
size: 'sm',
stripe: true,
width: 250,
view: viewFeeMultiplier,
input: Autocomplete,
inputProps: {
options: feeOptions,
valueProp: 'code',
labelProp: 'display'
}
} }
] ]
} }
@ -199,5 +288,8 @@ export {
AdvancedWalletSchema, AdvancedWalletSchema,
getElements, getElements,
filterClass, filterClass,
getAdvancedWalletElements getAdvancedWalletElements,
getAdvancedWalletElementsOverrides,
OverridesDefaults,
OverridesSchema
} }

View file

@ -1,6 +1,7 @@
import * as R from 'ramda' import * as R from 'ramda'
const namespaces = { const namespaces = {
ADVANCED: 'advanced',
CASH_IN: 'cashIn', CASH_IN: 'cashIn',
CASH_OUT: 'cashOut', CASH_OUT: 'cashOut',
WALLETS: 'wallets', WALLETS: 'wallets',