From 17c0f77e16abf9fbac5f6e4a806302f7de0bc72e Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Wed, 17 Apr 2019 19:30:41 +0200 Subject: [PATCH 1/6] cleanup eth regexps --- .../ethereum/SendFormValidationActions.js | 29 ++------- src/utils/__tests__/ethUtils.test.js | 60 +++++++++++++++++++ src/utils/ethUtils.js | 12 ++++ 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/actions/ethereum/SendFormValidationActions.js b/src/actions/ethereum/SendFormValidationActions.js index c3f9a4d0..6d2a77a2 100644 --- a/src/actions/ethereum/SendFormValidationActions.js +++ b/src/actions/ethereum/SendFormValidationActions.js @@ -11,22 +11,6 @@ import * as ethUtils from 'utils/ethUtils'; import type { Dispatch, GetState, PayloadAction } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormEthereumReducer'; -// general regular expressions -const NUMBER_RE: RegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?|\\.[0-9]+)$'); -const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); -const ABS_RE = new RegExp('^[0-9]+$'); -const ETH_18_RE = new RegExp( - '^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$' -); -const dynamicRegexp = (decimals: number): RegExp => { - if (decimals > 0) { - return new RegExp( - `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?|\\.[0-9]{1,${decimals}})$` - ); - } - return ABS_RE; -}; - /* * Called from SendFormActions.observe * Reaction for WEB3.GAS_PRICE_UPDATED action @@ -168,7 +152,7 @@ export const addressValidation = ($state: State): PayloadAction => (): St state.errors.address = 'Address is not set'; } else if (!EthereumjsUtil.isValidAddress(address)) { state.errors.address = 'Address is not valid'; - } else if (address.match(UPPERCASE_RE) && !EthereumjsUtil.isValidChecksumAddress(address)) { + } else if (ethUtils.hasUppercase(address) && !EthereumjsUtil.isValidChecksumAddress(address)) { state.errors.address = 'Address is not a valid checksum'; } return state; @@ -244,7 +228,7 @@ export const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { state.errors.amount = 'Amount is not set'; - } else if (amount.length > 0 && !amount.match(NUMBER_RE)) { + } else if (amount.length > 0 && !ethUtils.isEthereumNumber(amount)) { state.errors.amount = 'Amount is not a number'; } else { const isToken: boolean = state.currency !== state.networkSymbol; @@ -258,9 +242,8 @@ export const amountValidation = ($state: State): PayloadAction => ( account.deviceState ); if (!token) return state; - const decimalRegExp = dynamicRegexp(parseInt(token.decimals, 0)); - if (!state.amount.match(decimalRegExp)) { + if (!ethUtils.isEthereumNumber(state.amount, parseInt(token.decimals, 0))) { state.errors.amount = `Maximum ${token.decimals} decimals allowed`; } else if (new BigNumber(state.total).isGreaterThan(account.balance)) { state.errors.amount = `Not enough ${state.networkSymbol} to cover transaction fee`; @@ -273,7 +256,7 @@ export const amountValidation = ($state: State): PayloadAction => ( } else if (new BigNumber(state.amount).isLessThanOrEqualTo('0')) { state.errors.amount = 'Amount is too low'; } - } else if (!state.amount.match(ETH_18_RE)) { + } else if (!ethUtils.isEthereumNumber(state.amount, 18)) { state.errors.amount = 'Maximum 18 decimals allowed'; } else if ( new BigNumber(state.total).isGreaterThan( @@ -302,7 +285,7 @@ export const gasLimitValidation = ($state: State): PayloadAction => ( const { gasLimit } = state; if (gasLimit.length < 1) { state.errors.gasLimit = 'Gas limit is not set'; - } else if (gasLimit.length > 0 && !gasLimit.match(NUMBER_RE)) { + } else if (gasLimit.length > 0 && !ethUtils.isEthereumNumber(gasLimit)) { state.errors.gasLimit = 'Gas limit is not a number'; } else { const gl: BigNumber = new BigNumber(gasLimit); @@ -331,7 +314,7 @@ export const gasPriceValidation = ($state: State): PayloadAction => (): S const { gasPrice } = state; if (gasPrice.length < 1) { state.errors.gasPrice = 'Gas price is not set'; - } else if (gasPrice.length > 0 && !gasPrice.match(NUMBER_RE)) { + } else if (gasPrice.length > 0 && !ethUtils.isEthereumNumber(gasPrice)) { state.errors.gasPrice = 'Gas price is not a number'; } else { const gp: BigNumber = new BigNumber(gasPrice); diff --git a/src/utils/__tests__/ethUtils.test.js b/src/utils/__tests__/ethUtils.test.js index 44d0e08e..ab842b44 100644 --- a/src/utils/__tests__/ethUtils.test.js +++ b/src/utils/__tests__/ethUtils.test.js @@ -52,4 +52,64 @@ describe('eth utils', () => { 'Address is not valid' ); }); + + it('isEthereumNumber', () => { + expect(utils.isEthereumNumber('0')).toBe(true); + expect(utils.isEthereumNumber('0.0')).toBe(true); + expect(utils.isEthereumNumber('0.00000000')).toBe(true); + expect(utils.isEthereumNumber('0.00000001')).toBe(true); + expect(utils.isEthereumNumber('+0.0')).toBe(false); + expect(utils.isEthereumNumber('-0.0')).toBe(false); + expect(utils.isEthereumNumber('1')).toBe(true); + expect(utils.isEthereumNumber('+1')).toBe(false); + expect(utils.isEthereumNumber('+100000')).toBe(false); + expect(utils.isEthereumNumber('.')).toBe(false); + expect(utils.isEthereumNumber('-.1')).toBe(false); + expect(utils.isEthereumNumber('0.1')).toBe(true); + expect(utils.isEthereumNumber('0.12314841')).toBe(true); + expect(utils.isEthereumNumber('0.1381841848184814818391931933')).toBe(false); //28 decimals + expect(utils.isEthereumNumber('0.100000000000000000')).toBe(true); //18s decimals + + expect(utils.isEthereumNumber('100.')).toBe(true); + expect(utils.isEthereumNumber('.1')).toBe(false); + expect(utils.isEthereumNumber('.000000001')).toBe(false); + expect(utils.isEthereumNumber('.13134818481481841')).toBe(false); + + expect(utils.isEthereumNumber('001.12314841')).toBe(false); + expect(utils.isEthereumNumber('83819319391491949941')).toBe(true); + expect(utils.isEthereumNumber('-83819319391491949941')).toBe(false); + expect(utils.isEthereumNumber('+0.131831848184')).toBe(false); + expect(utils.isEthereumNumber('0.127373193981774718318371831731761626162613')).toBe(false); + + expect(utils.isEthereumNumber('0.131831848184a')).toBe(false); + expect(utils.isEthereumNumber('100a')).toBe(false); + expect(utils.isEthereumNumber('.100a')).toBe(false); + expect(utils.isEthereumNumber('a.100')).toBe(false); + expect(utils.isEthereumNumber('abc')).toBe(false); + expect(utils.isEthereumNumber('1abc0')).toBe(false); + }); + + it('hasUppercase', () => { + expect(utils.hasUppercase('0')).toBe(false); + expect(utils.hasUppercase('abc')).toBe(false); + expect(utils.hasUppercase('abcD')).toBe(true); + expect(utils.hasUppercase('Abcd')).toBe(true); + expect(utils.hasUppercase('aBcd')).toBe(true); + expect(utils.hasUppercase('123abc123')).toBe(false); + expect(utils.hasUppercase('0x123abc456')).toBe(false); + expect(utils.hasUppercase('0x123aBc456')).toBe(true); + }); + + it('isEthereumNumber decimals=0', () => { + expect(utils.isEthereumNumber('0', 0)).toBe(true); + expect(utils.isEthereumNumber('0.1', 0)).toBe(false); + expect(utils.isEthereumNumber('0.12345', 0)).toBe(false); + expect(utils.isEthereumNumber('1', 0)).toBe(true); + expect(utils.isEthereumNumber('1.1', 0)).toBe(false); + expect(utils.isEthereumNumber('1000000', 0)).toBe(true); + expect(utils.isEthereumNumber('-1000000', 0)).toBe(false); + expect(utils.isEthereumNumber('.0', 0)).toBe(false); + expect(utils.isEthereumNumber('0.', 0)).toBe(true); + expect(utils.isEthereumNumber('.', 0)).toBe(false); + }); }); diff --git a/src/utils/ethUtils.js b/src/utils/ethUtils.js index 0f8ba01a..b997cded 100644 --- a/src/utils/ethUtils.js +++ b/src/utils/ethUtils.js @@ -46,3 +46,15 @@ export const isHex = (str: string): boolean => { const regExp = /^(0x|0X)?[0-9A-Fa-f]+$/g; return regExp.test(str); }; + +export const hasUppercase = (value: string) => { + const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); + return UPPERCASE_RE.test(value); +}; + +export const isEthereumNumber = (value: string, decimals: number = 18) => { + const ETH_18_RE = new RegExp( + `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$` + ); + return ETH_18_RE.test(value); +}; From 39ff4833871604607a2e223bffd1df4f366a5467 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 22 Apr 2019 11:31:55 +0200 Subject: [PATCH 2/6] extract xrp regexps, cleanup --- src/actions/LocalStorageActions.js | 4 ++ .../ethereum/SendFormValidationActions.js | 18 +++--- .../ripple/SendFormValidationActions.js | 13 ++-- src/utils/__tests__/ethUtils.test.js | 60 ------------------ src/utils/__tests__/validators.test.js | 63 +++++++++++++++++++ src/utils/ethUtils.js | 12 ---- src/utils/validators.js | 30 +++++++++ 7 files changed, 114 insertions(+), 86 deletions(-) create mode 100644 src/utils/__tests__/validators.test.js create mode 100644 src/utils/validators.js diff --git a/src/actions/LocalStorageActions.js b/src/actions/LocalStorageActions.js index 84fb9710..54c91c57 100644 --- a/src/actions/LocalStorageActions.js +++ b/src/actions/LocalStorageActions.js @@ -136,6 +136,10 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch) } if (event.key === KEY_ACCOUNTS) { + console.log('omg'); + console.log(event); + console.log(JSON.parse(event.newValue)); + console.log(event.oldValue); dispatch({ type: ACCOUNT.FROM_STORAGE, payload: JSON.parse(event.newValue), diff --git a/src/actions/ethereum/SendFormValidationActions.js b/src/actions/ethereum/SendFormValidationActions.js index 6d2a77a2..c8a77056 100644 --- a/src/actions/ethereum/SendFormValidationActions.js +++ b/src/actions/ethereum/SendFormValidationActions.js @@ -7,6 +7,7 @@ import { findDevice, getPendingAmount, findToken } from 'reducers/utils'; import { toFiatCurrency } from 'utils/fiatConverter'; import * as SEND from 'actions/constants/send'; import * as ethUtils from 'utils/ethUtils'; +import * as validators from 'utils/validators'; import type { Dispatch, GetState, PayloadAction } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormEthereumReducer'; @@ -152,7 +153,10 @@ export const addressValidation = ($state: State): PayloadAction => (): St state.errors.address = 'Address is not set'; } else if (!EthereumjsUtil.isValidAddress(address)) { state.errors.address = 'Address is not valid'; - } else if (ethUtils.hasUppercase(address) && !EthereumjsUtil.isValidChecksumAddress(address)) { + } else if ( + validators.hasUppercase(address) && + !EthereumjsUtil.isValidChecksumAddress(address) + ) { state.errors.address = 'Address is not a valid checksum'; } return state; @@ -228,7 +232,7 @@ export const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { state.errors.amount = 'Amount is not set'; - } else if (amount.length > 0 && !ethUtils.isEthereumNumber(amount)) { + } else if (amount.length > 0 && !validators.isEthereumNumber(amount)) { state.errors.amount = 'Amount is not a number'; } else { const isToken: boolean = state.currency !== state.networkSymbol; @@ -243,7 +247,7 @@ export const amountValidation = ($state: State): PayloadAction => ( ); if (!token) return state; - if (!ethUtils.isEthereumNumber(state.amount, parseInt(token.decimals, 0))) { + if (!validators.isNumber(state.amount, parseInt(token.decimals, 0))) { state.errors.amount = `Maximum ${token.decimals} decimals allowed`; } else if (new BigNumber(state.total).isGreaterThan(account.balance)) { state.errors.amount = `Not enough ${state.networkSymbol} to cover transaction fee`; @@ -256,7 +260,7 @@ export const amountValidation = ($state: State): PayloadAction => ( } else if (new BigNumber(state.amount).isLessThanOrEqualTo('0')) { state.errors.amount = 'Amount is too low'; } - } else if (!ethUtils.isEthereumNumber(state.amount, 18)) { + } else if (!validators.isEthereumNumber(state.amount)) { state.errors.amount = 'Maximum 18 decimals allowed'; } else if ( new BigNumber(state.total).isGreaterThan( @@ -285,7 +289,7 @@ export const gasLimitValidation = ($state: State): PayloadAction => ( const { gasLimit } = state; if (gasLimit.length < 1) { state.errors.gasLimit = 'Gas limit is not set'; - } else if (gasLimit.length > 0 && !ethUtils.isEthereumNumber(gasLimit)) { + } else if (gasLimit.length > 0 && !validators.isEthereumNumber(gasLimit)) { state.errors.gasLimit = 'Gas limit is not a number'; } else { const gl: BigNumber = new BigNumber(gasLimit); @@ -314,7 +318,7 @@ export const gasPriceValidation = ($state: State): PayloadAction => (): S const { gasPrice } = state; if (gasPrice.length < 1) { state.errors.gasPrice = 'Gas price is not set'; - } else if (gasPrice.length > 0 && !ethUtils.isEthereumNumber(gasPrice)) { + } else if (gasPrice.length > 0 && !validators.isEthereumNumber(gasPrice)) { state.errors.gasPrice = 'Gas price is not a number'; } else { const gp: BigNumber = new BigNumber(gasPrice); @@ -343,7 +347,7 @@ export const nonceValidation = ($state: State): PayloadAction => ( const { nonce } = state; if (nonce.length < 1) { state.errors.nonce = 'Nonce is not set'; - } else if (!nonce.match(ABS_RE)) { + } else if (!validators.isAbs(nonce)) { state.errors.nonce = 'Nonce is not a valid number'; } else { const n: BigNumber = new BigNumber(nonce); diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index 3155d994..3de47265 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -5,6 +5,7 @@ import * as SEND from 'actions/constants/send'; import { findDevice, getPendingAmount } from 'reducers/utils'; import { toDecimalAmount } from 'utils/formatUtils'; import { toFiatCurrency } from 'utils/fiatConverter'; +import * as validators from 'utils/validators'; import type { Dispatch, @@ -16,10 +17,8 @@ import type { import type { State, FeeLevel } from 'reducers/SendFormRippleReducer'; import AddressValidator from 'wallet-address-validator'; + // general regular expressions -const ABS_RE = new RegExp('^[0-9]+$'); -const NUMBER_RE: RegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?|\\.[0-9]+)$'); -const XRP_6_RE = new RegExp('^(0|0\\.([0-9]{0,6})?|[1-9][0-9]*\\.?([0-9]{0,6})?|\\.[0-9]{0,6})$'); const U_INT_32 = 0xffffffff; /* @@ -270,11 +269,11 @@ const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { state.errors.amount = 'Amount is not set'; - } else if (amount.length > 0 && !amount.match(NUMBER_RE)) { + } else if (amount.length > 0 && !validators.isRippleNumber(amount)) { state.errors.amount = 'Amount is not a number'; } else { const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol); - if (!state.amount.match(XRP_6_RE)) { + if (!validators.isRippleNumber(state.amount)) { state.errors.amount = 'Maximum 6 decimals allowed'; } else if ( new BigNumber(state.total).isGreaterThan( @@ -319,7 +318,7 @@ export const feeValidation = ($state: State): PayloadAction => ( const { fee } = state; if (fee.length < 1) { state.errors.fee = 'Fee is not set'; - } else if (fee.length > 0 && !fee.match(ABS_RE)) { + } else if (fee.length > 0 && !validators.isAbs(fee)) { state.errors.fee = 'Fee must be an absolute number'; } else { const gl: BigNumber = new BigNumber(fee); @@ -340,7 +339,7 @@ export const destinationTagValidation = ($state: State): PayloadAction => const { destinationTag } = state; - if (destinationTag.length > 0 && !destinationTag.match(ABS_RE)) { + if (destinationTag.length > 0 && !validators.isAbs(destinationTag)) { state.errors.destinationTag = 'Destination tag must be an absolute number'; } diff --git a/src/utils/__tests__/ethUtils.test.js b/src/utils/__tests__/ethUtils.test.js index ab842b44..44d0e08e 100644 --- a/src/utils/__tests__/ethUtils.test.js +++ b/src/utils/__tests__/ethUtils.test.js @@ -52,64 +52,4 @@ describe('eth utils', () => { 'Address is not valid' ); }); - - it('isEthereumNumber', () => { - expect(utils.isEthereumNumber('0')).toBe(true); - expect(utils.isEthereumNumber('0.0')).toBe(true); - expect(utils.isEthereumNumber('0.00000000')).toBe(true); - expect(utils.isEthereumNumber('0.00000001')).toBe(true); - expect(utils.isEthereumNumber('+0.0')).toBe(false); - expect(utils.isEthereumNumber('-0.0')).toBe(false); - expect(utils.isEthereumNumber('1')).toBe(true); - expect(utils.isEthereumNumber('+1')).toBe(false); - expect(utils.isEthereumNumber('+100000')).toBe(false); - expect(utils.isEthereumNumber('.')).toBe(false); - expect(utils.isEthereumNumber('-.1')).toBe(false); - expect(utils.isEthereumNumber('0.1')).toBe(true); - expect(utils.isEthereumNumber('0.12314841')).toBe(true); - expect(utils.isEthereumNumber('0.1381841848184814818391931933')).toBe(false); //28 decimals - expect(utils.isEthereumNumber('0.100000000000000000')).toBe(true); //18s decimals - - expect(utils.isEthereumNumber('100.')).toBe(true); - expect(utils.isEthereumNumber('.1')).toBe(false); - expect(utils.isEthereumNumber('.000000001')).toBe(false); - expect(utils.isEthereumNumber('.13134818481481841')).toBe(false); - - expect(utils.isEthereumNumber('001.12314841')).toBe(false); - expect(utils.isEthereumNumber('83819319391491949941')).toBe(true); - expect(utils.isEthereumNumber('-83819319391491949941')).toBe(false); - expect(utils.isEthereumNumber('+0.131831848184')).toBe(false); - expect(utils.isEthereumNumber('0.127373193981774718318371831731761626162613')).toBe(false); - - expect(utils.isEthereumNumber('0.131831848184a')).toBe(false); - expect(utils.isEthereumNumber('100a')).toBe(false); - expect(utils.isEthereumNumber('.100a')).toBe(false); - expect(utils.isEthereumNumber('a.100')).toBe(false); - expect(utils.isEthereumNumber('abc')).toBe(false); - expect(utils.isEthereumNumber('1abc0')).toBe(false); - }); - - it('hasUppercase', () => { - expect(utils.hasUppercase('0')).toBe(false); - expect(utils.hasUppercase('abc')).toBe(false); - expect(utils.hasUppercase('abcD')).toBe(true); - expect(utils.hasUppercase('Abcd')).toBe(true); - expect(utils.hasUppercase('aBcd')).toBe(true); - expect(utils.hasUppercase('123abc123')).toBe(false); - expect(utils.hasUppercase('0x123abc456')).toBe(false); - expect(utils.hasUppercase('0x123aBc456')).toBe(true); - }); - - it('isEthereumNumber decimals=0', () => { - expect(utils.isEthereumNumber('0', 0)).toBe(true); - expect(utils.isEthereumNumber('0.1', 0)).toBe(false); - expect(utils.isEthereumNumber('0.12345', 0)).toBe(false); - expect(utils.isEthereumNumber('1', 0)).toBe(true); - expect(utils.isEthereumNumber('1.1', 0)).toBe(false); - expect(utils.isEthereumNumber('1000000', 0)).toBe(true); - expect(utils.isEthereumNumber('-1000000', 0)).toBe(false); - expect(utils.isEthereumNumber('.0', 0)).toBe(false); - expect(utils.isEthereumNumber('0.', 0)).toBe(true); - expect(utils.isEthereumNumber('.', 0)).toBe(false); - }); }); diff --git a/src/utils/__tests__/validators.test.js b/src/utils/__tests__/validators.test.js new file mode 100644 index 00000000..a1f5a3b5 --- /dev/null +++ b/src/utils/__tests__/validators.test.js @@ -0,0 +1,63 @@ +import * as utils from '../validators'; + +describe('validators utils', () => { + it('isEthereumNumber', () => { + expect(utils.isEthereumNumber('0')).toBe(true); + expect(utils.isEthereumNumber('0.0')).toBe(true); + expect(utils.isEthereumNumber('0.00000000')).toBe(true); + expect(utils.isEthereumNumber('0.00000001')).toBe(true); + expect(utils.isEthereumNumber('+0.0')).toBe(false); + expect(utils.isEthereumNumber('-0.0')).toBe(false); + expect(utils.isEthereumNumber('1')).toBe(true); + expect(utils.isEthereumNumber('+1')).toBe(false); + expect(utils.isEthereumNumber('+100000')).toBe(false); + expect(utils.isEthereumNumber('.')).toBe(false); + expect(utils.isEthereumNumber('-.1')).toBe(false); + expect(utils.isEthereumNumber('0.1')).toBe(true); + expect(utils.isEthereumNumber('0.12314841')).toBe(true); + expect(utils.isEthereumNumber('0.1381841848184814818391931933')).toBe(false); //28 decimals + expect(utils.isEthereumNumber('0.100000000000000000')).toBe(true); //18s decimals + + expect(utils.isEthereumNumber('100.')).toBe(true); + expect(utils.isEthereumNumber('.1')).toBe(false); + expect(utils.isEthereumNumber('.000000001')).toBe(false); + expect(utils.isEthereumNumber('.13134818481481841')).toBe(false); + + expect(utils.isEthereumNumber('001.12314841')).toBe(false); + expect(utils.isEthereumNumber('83819319391491949941')).toBe(true); + expect(utils.isEthereumNumber('-83819319391491949941')).toBe(false); + expect(utils.isEthereumNumber('+0.131831848184')).toBe(false); + expect(utils.isEthereumNumber('0.127373193981774718318371831731761626162613')).toBe(false); + + expect(utils.isEthereumNumber('0.131831848184a')).toBe(false); + expect(utils.isEthereumNumber('100a')).toBe(false); + expect(utils.isEthereumNumber('.100a')).toBe(false); + expect(utils.isEthereumNumber('a.100')).toBe(false); + expect(utils.isEthereumNumber('abc')).toBe(false); + expect(utils.isEthereumNumber('1abc0')).toBe(false); + }); + + it('hasUppercase', () => { + expect(utils.hasUppercase('0')).toBe(false); + expect(utils.hasUppercase('abc')).toBe(false); + expect(utils.hasUppercase('abcD')).toBe(true); + expect(utils.hasUppercase('Abcd')).toBe(true); + expect(utils.hasUppercase('aBcd')).toBe(true); + expect(utils.hasUppercase('123abc123')).toBe(false); + expect(utils.hasUppercase('0x123abc456')).toBe(false); + expect(utils.hasUppercase('0x123aBc456')).toBe(true); + }); + + it('isNumber decimals=0', () => { + expect(utils.isNumber('0', 0)).toBe(true); + expect(utils.isNumber('0.1', 0)).toBe(false); + expect(utils.isNumber('0.12345', 0)).toBe(false); + expect(utils.isNumber('1', 0)).toBe(true); + expect(utils.isNumber('1.1', 0)).toBe(false); + expect(utils.isNumber('1000000', 0)).toBe(true); + expect(utils.isNumber('-1000000', 0)).toBe(false); + expect(utils.isNumber('.0', 0)).toBe(false); + expect(utils.isNumber('0.', 0)).toBe(false); + expect(utils.isNumber('.', 0)).toBe(false); + }); +}); diff --git a/src/utils/ethUtils.js b/src/utils/ethUtils.js index b997cded..0f8ba01a 100644 --- a/src/utils/ethUtils.js +++ b/src/utils/ethUtils.js @@ -46,15 +46,3 @@ export const isHex = (str: string): boolean => { const regExp = /^(0x|0X)?[0-9A-Fa-f]+$/g; return regExp.test(str); }; - -export const hasUppercase = (value: string) => { - const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); - return UPPERCASE_RE.test(value); -}; - -export const isEthereumNumber = (value: string, decimals: number = 18) => { - const ETH_18_RE = new RegExp( - `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$` - ); - return ETH_18_RE.test(value); -}; diff --git a/src/utils/validators.js b/src/utils/validators.js new file mode 100644 index 00000000..3b960a2e --- /dev/null +++ b/src/utils/validators.js @@ -0,0 +1,30 @@ +/* @flow */ + +export const hasUppercase = (value: string) => { + const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); + return UPPERCASE_RE.test(value); +}; + +export const isNumber = (value: string, decimals: number = 18) => { + if (decimals === 0) { + return isAbs(value); + } + + const ETH_18_RE = new RegExp( + `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$` + ); + return ETH_18_RE.test(value); +}; + +export const isAbs = (value: string) => { + const ABS_RE = new RegExp('^[0-9]+$'); + return ABS_RE.test(value); +}; + +export const isEthereumNumber = (value: string) => { + return isNumber(value, 18); +}; + +export const isRippleNumber = (value: string) => { + return isNumber(value, 6); +}; From 39c683bf0c2e7e2a0e77f6ba3dca96d24b6d6873 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 22 Apr 2019 11:33:20 +0200 Subject: [PATCH 3/6] remove console.log --- src/actions/LocalStorageActions.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/actions/LocalStorageActions.js b/src/actions/LocalStorageActions.js index 54c91c57..84fb9710 100644 --- a/src/actions/LocalStorageActions.js +++ b/src/actions/LocalStorageActions.js @@ -136,10 +136,6 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch) } if (event.key === KEY_ACCOUNTS) { - console.log('omg'); - console.log(event); - console.log(JSON.parse(event.newValue)); - console.log(event.oldValue); dispatch({ type: ACCOUNT.FROM_STORAGE, payload: JSON.parse(event.newValue), From 3efa72988fc5931fe6391451f36c3e92891357ac Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 22 Apr 2019 11:35:49 +0200 Subject: [PATCH 4/6] remove default decimals value --- src/utils/validators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/validators.js b/src/utils/validators.js index 3b960a2e..99157c7f 100644 --- a/src/utils/validators.js +++ b/src/utils/validators.js @@ -5,7 +5,7 @@ export const hasUppercase = (value: string) => { return UPPERCASE_RE.test(value); }; -export const isNumber = (value: string, decimals: number = 18) => { +export const isNumber = (value: string, decimals: number) => { if (decimals === 0) { return isAbs(value); } From 7da8d4124dc22000bf1c55f19b248224db1fb70e Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 22 Apr 2019 13:43:44 +0200 Subject: [PATCH 5/6] fix validation --- .../ethereum/SendFormValidationActions.js | 10 +- .../ripple/SendFormValidationActions.js | 4 +- src/utils/__tests__/validators.test.js | 127 +++++++++++------- src/utils/validators.js | 11 +- 4 files changed, 93 insertions(+), 59 deletions(-) diff --git a/src/actions/ethereum/SendFormValidationActions.js b/src/actions/ethereum/SendFormValidationActions.js index c8a77056..c213710d 100644 --- a/src/actions/ethereum/SendFormValidationActions.js +++ b/src/actions/ethereum/SendFormValidationActions.js @@ -232,7 +232,7 @@ export const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { state.errors.amount = 'Amount is not set'; - } else if (amount.length > 0 && !validators.isEthereumNumber(amount)) { + } else if (amount.length > 0 && !validators.isNumber(amount)) { state.errors.amount = 'Amount is not a number'; } else { const isToken: boolean = state.currency !== state.networkSymbol; @@ -247,7 +247,7 @@ export const amountValidation = ($state: State): PayloadAction => ( ); if (!token) return state; - if (!validators.isNumber(state.amount, parseInt(token.decimals, 0))) { + if (!validators.hasDecimals(state.amount, parseInt(token.decimals, 0))) { state.errors.amount = `Maximum ${token.decimals} decimals allowed`; } else if (new BigNumber(state.total).isGreaterThan(account.balance)) { state.errors.amount = `Not enough ${state.networkSymbol} to cover transaction fee`; @@ -260,7 +260,7 @@ export const amountValidation = ($state: State): PayloadAction => ( } else if (new BigNumber(state.amount).isLessThanOrEqualTo('0')) { state.errors.amount = 'Amount is too low'; } - } else if (!validators.isEthereumNumber(state.amount)) { + } else if (!validators.hasDecimals(state.amount, 18)) { state.errors.amount = 'Maximum 18 decimals allowed'; } else if ( new BigNumber(state.total).isGreaterThan( @@ -289,7 +289,7 @@ export const gasLimitValidation = ($state: State): PayloadAction => ( const { gasLimit } = state; if (gasLimit.length < 1) { state.errors.gasLimit = 'Gas limit is not set'; - } else if (gasLimit.length > 0 && !validators.isEthereumNumber(gasLimit)) { + } else if (gasLimit.length > 0 && !validators.isNumber(gasLimit)) { state.errors.gasLimit = 'Gas limit is not a number'; } else { const gl: BigNumber = new BigNumber(gasLimit); @@ -318,7 +318,7 @@ export const gasPriceValidation = ($state: State): PayloadAction => (): S const { gasPrice } = state; if (gasPrice.length < 1) { state.errors.gasPrice = 'Gas price is not set'; - } else if (gasPrice.length > 0 && !validators.isEthereumNumber(gasPrice)) { + } else if (gasPrice.length > 0 && !validators.isNumber(gasPrice)) { state.errors.gasPrice = 'Gas price is not a number'; } else { const gp: BigNumber = new BigNumber(gasPrice); diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index 3de47265..d24ff34c 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -269,11 +269,11 @@ const amountValidation = ($state: State): PayloadAction => ( const { amount } = state; if (amount.length < 1) { state.errors.amount = 'Amount is not set'; - } else if (amount.length > 0 && !validators.isRippleNumber(amount)) { + } else if (amount.length > 0 && !validators.isNumber(amount)) { state.errors.amount = 'Amount is not a number'; } else { const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol); - if (!validators.isRippleNumber(state.amount)) { + if (!validators.hasDecimals(state.amount, 6)) { state.errors.amount = 'Maximum 6 decimals allowed'; } else if ( new BigNumber(state.total).isGreaterThan( diff --git a/src/utils/__tests__/validators.test.js b/src/utils/__tests__/validators.test.js index a1f5a3b5..71981e6f 100644 --- a/src/utils/__tests__/validators.test.js +++ b/src/utils/__tests__/validators.test.js @@ -1,40 +1,53 @@ import * as utils from '../validators'; describe('validators utils', () => { - it('isEthereumNumber', () => { - expect(utils.isEthereumNumber('0')).toBe(true); - expect(utils.isEthereumNumber('0.0')).toBe(true); - expect(utils.isEthereumNumber('0.00000000')).toBe(true); - expect(utils.isEthereumNumber('0.00000001')).toBe(true); - expect(utils.isEthereumNumber('+0.0')).toBe(false); - expect(utils.isEthereumNumber('-0.0')).toBe(false); - expect(utils.isEthereumNumber('1')).toBe(true); - expect(utils.isEthereumNumber('+1')).toBe(false); - expect(utils.isEthereumNumber('+100000')).toBe(false); - expect(utils.isEthereumNumber('.')).toBe(false); - expect(utils.isEthereumNumber('-.1')).toBe(false); - expect(utils.isEthereumNumber('0.1')).toBe(true); - expect(utils.isEthereumNumber('0.12314841')).toBe(true); - expect(utils.isEthereumNumber('0.1381841848184814818391931933')).toBe(false); //28 decimals - expect(utils.isEthereumNumber('0.100000000000000000')).toBe(true); //18s decimals - - expect(utils.isEthereumNumber('100.')).toBe(true); - expect(utils.isEthereumNumber('.1')).toBe(false); - expect(utils.isEthereumNumber('.000000001')).toBe(false); - expect(utils.isEthereumNumber('.13134818481481841')).toBe(false); - - expect(utils.isEthereumNumber('001.12314841')).toBe(false); - expect(utils.isEthereumNumber('83819319391491949941')).toBe(true); - expect(utils.isEthereumNumber('-83819319391491949941')).toBe(false); - expect(utils.isEthereumNumber('+0.131831848184')).toBe(false); - expect(utils.isEthereumNumber('0.127373193981774718318371831731761626162613')).toBe(false); - - expect(utils.isEthereumNumber('0.131831848184a')).toBe(false); - expect(utils.isEthereumNumber('100a')).toBe(false); - expect(utils.isEthereumNumber('.100a')).toBe(false); - expect(utils.isEthereumNumber('a.100')).toBe(false); - expect(utils.isEthereumNumber('abc')).toBe(false); - expect(utils.isEthereumNumber('1abc0')).toBe(false); + it('hasDecimals', () => { + expect(utils.hasDecimals('0', 18)).toBe(true); + expect(utils.hasDecimals('0.0', 18)).toBe(true); + expect(utils.hasDecimals('0.00000000', 18)).toBe(true); + expect(utils.hasDecimals('0.00000001', 18)).toBe(true); + expect(utils.hasDecimals('+0.0', 18)).toBe(false); + expect(utils.hasDecimals('-0.0', 18)).toBe(false); + expect(utils.hasDecimals('1', 18)).toBe(true); + expect(utils.hasDecimals('+1', 18)).toBe(false); + expect(utils.hasDecimals('+100000', 18)).toBe(false); + expect(utils.hasDecimals('.', 18)).toBe(false); + expect(utils.hasDecimals('-.1', 18)).toBe(false); + expect(utils.hasDecimals('0.1', 18)).toBe(true); + expect(utils.hasDecimals('0.12314841', 18)).toBe(true); + expect(utils.hasDecimals('0.1381841848184814818391931933', 18)).toBe(false); //28 decimals + expect(utils.hasDecimals('0.100000000000000000', 18)).toBe(true); //18s decimals + + expect(utils.hasDecimals('100.', 18)).toBe(true); + expect(utils.hasDecimals('.1', 18)).toBe(false); + expect(utils.hasDecimals('.000000001', 18)).toBe(false); + expect(utils.hasDecimals('.13134818481481841', 18)).toBe(false); + + expect(utils.hasDecimals('001.12314841', 18)).toBe(false); + expect(utils.hasDecimals('83819319391491949941', 18)).toBe(true); + expect(utils.hasDecimals('-83819319391491949941', 18)).toBe(false); + expect(utils.hasDecimals('+0.131831848184', 18)).toBe(false); + expect(utils.hasDecimals('0.127373193981774718318371831731761626162613', 18)).toBe(false); + + expect(utils.hasDecimals('0.131831848184a', 18)).toBe(false); + expect(utils.hasDecimals('100a', 18)).toBe(false); + expect(utils.hasDecimals('.100a', 18)).toBe(false); + expect(utils.hasDecimals('a.100', 18)).toBe(false); + expect(utils.hasDecimals('abc', 18)).toBe(false); + expect(utils.hasDecimals('1abc0', 18)).toBe(false); + }); + + it('hasDecimals decimals=0', () => { + expect(utils.hasDecimals('0', 0)).toBe(true); + expect(utils.hasDecimals('0.1', 0)).toBe(false); + expect(utils.hasDecimals('0.12345', 0)).toBe(false); + expect(utils.hasDecimals('1', 0)).toBe(true); + expect(utils.hasDecimals('1.1', 0)).toBe(false); + expect(utils.hasDecimals('1000000', 0)).toBe(true); + expect(utils.hasDecimals('-1000000', 0)).toBe(false); + expect(utils.hasDecimals('.0', 0)).toBe(false); + expect(utils.hasDecimals('0.', 0)).toBe(false); + expect(utils.hasDecimals('.', 0)).toBe(false); }); it('hasUppercase', () => { @@ -48,16 +61,40 @@ describe('validators utils', () => { expect(utils.hasUppercase('0x123aBc456')).toBe(true); }); - it('isNumber decimals=0', () => { - expect(utils.isNumber('0', 0)).toBe(true); - expect(utils.isNumber('0.1', 0)).toBe(false); - expect(utils.isNumber('0.12345', 0)).toBe(false); - expect(utils.isNumber('1', 0)).toBe(true); - expect(utils.isNumber('1.1', 0)).toBe(false); - expect(utils.isNumber('1000000', 0)).toBe(true); - expect(utils.isNumber('-1000000', 0)).toBe(false); - expect(utils.isNumber('.0', 0)).toBe(false); - expect(utils.isNumber('0.', 0)).toBe(false); - expect(utils.isNumber('.', 0)).toBe(false); + it('isNumber', () => { + expect(utils.isNumber('0')).toBe(true); + expect(utils.isNumber('0.0')).toBe(true); + expect(utils.isNumber('0.00000000')).toBe(true); + expect(utils.isNumber('0.00000001')).toBe(true); + expect(utils.isNumber('+0.0')).toBe(false); + expect(utils.isNumber('-0.0')).toBe(false); + expect(utils.isNumber('1')).toBe(true); + expect(utils.isNumber('+1')).toBe(false); + expect(utils.isNumber('+100000')).toBe(false); + expect(utils.isNumber('.')).toBe(false); + expect(utils.isNumber('')).toBe(false); + expect(utils.isNumber(' ')).toBe(false); + expect(utils.isNumber('-.1')).toBe(false); + expect(utils.isNumber('0.1')).toBe(true); + expect(utils.isNumber('0.12314841')).toBe(true); + expect(utils.isNumber('0.1381841848184814818391931933')).toBe(true); //28 decimals + expect(utils.isNumber('0.100000000000000000')).toBe(true); //18s decimals + + expect(utils.isNumber('100.')).toBe(true); + expect(utils.isNumber('.1')).toBe(false); + expect(utils.isNumber('.000000001')).toBe(false); + expect(utils.isNumber('.13134818481481841')).toBe(false); + + expect(utils.isNumber('001.12314841')).toBe(false); + expect(utils.isNumber('83819319391491949941')).toBe(true); + expect(utils.isNumber('-83819319391491949941')).toBe(false); + expect(utils.isNumber('+0.131831848184')).toBe(false); + + expect(utils.isNumber('0.131831848184a')).toBe(false); + expect(utils.isNumber('100a')).toBe(false); + expect(utils.isNumber('.100a')).toBe(false); + expect(utils.isNumber('a.100')).toBe(false); + expect(utils.isNumber('abc')).toBe(false); + expect(utils.isNumber('1abc0')).toBe(false); }); }); diff --git a/src/utils/validators.js b/src/utils/validators.js index 99157c7f..0ae6c3d1 100644 --- a/src/utils/validators.js +++ b/src/utils/validators.js @@ -5,7 +5,7 @@ export const hasUppercase = (value: string) => { return UPPERCASE_RE.test(value); }; -export const isNumber = (value: string, decimals: number) => { +export const hasDecimals = (value: string, decimals: number) => { if (decimals === 0) { return isAbs(value); } @@ -21,10 +21,7 @@ export const isAbs = (value: string) => { return ABS_RE.test(value); }; -export const isEthereumNumber = (value: string) => { - return isNumber(value, 18); -}; - -export const isRippleNumber = (value: string) => { - return isNumber(value, 6); +export const isNumber = (value: string) => { + const ETH_18_RE = new RegExp(`^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?)$`); + return ETH_18_RE.test(value); }; From 88a005323561d028f34deeaee827e80dcf8836d0 Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Mon, 22 Apr 2019 13:46:19 +0200 Subject: [PATCH 6/6] rename regexps --- src/utils/validators.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/validators.js b/src/utils/validators.js index 0ae6c3d1..e2c0f0d0 100644 --- a/src/utils/validators.js +++ b/src/utils/validators.js @@ -10,10 +10,10 @@ export const hasDecimals = (value: string, decimals: number) => { return isAbs(value); } - const ETH_18_RE = new RegExp( + const ETH_DECIMALS_RE = new RegExp( `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?)$` ); - return ETH_18_RE.test(value); + return ETH_DECIMALS_RE.test(value); }; export const isAbs = (value: string) => { @@ -22,6 +22,6 @@ export const isAbs = (value: string) => { }; export const isNumber = (value: string) => { - const ETH_18_RE = new RegExp(`^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?)$`); - return ETH_18_RE.test(value); + const NUMBER_RE = new RegExp(`^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?)$`); + return NUMBER_RE.test(value); };