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); +};