Commit 88abf613 authored by tom's avatar tom

int validation

parent 7b5101df
...@@ -97,6 +97,8 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ...@@ -97,6 +97,8 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit,
.map(castFieldValue(inputs)) .map(castFieldValue(inputs))
.map(([ , value ]) => value); .map(([ , value ]) => value);
return;
setResult(undefined); setResult(undefined);
setLoading(true); setLoading(true);
...@@ -131,7 +133,7 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ...@@ -131,7 +133,7 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit,
const fieldName = getFieldName(name, index); const fieldName = getFieldName(name, index);
return ( return (
<ContractMethodField <ContractMethodField
key={ fieldName } key={ index }
name={ fieldName } name={ fieldName }
valueType={ type } valueType={ type }
placeholder={ `${ name }(${ type })` } placeholder={ `${ name }(${ type })` }
......
...@@ -8,6 +8,7 @@ import { ...@@ -8,6 +8,7 @@ import {
import React from 'react'; import React from 'react';
import type { Control, ControllerRenderProps, UseFormGetValues, UseFormSetValue, UseFormStateReturn } from 'react-hook-form'; import type { Control, ControllerRenderProps, UseFormGetValues, UseFormSetValue, UseFormStateReturn } from 'react-hook-form';
import { Controller } from 'react-hook-form'; import { Controller } from 'react-hook-form';
import { NumericFormat } from 'react-number-format';
import { isAddress } from 'viem'; import { isAddress } from 'viem';
import type { MethodFormFields } from './types'; import type { MethodFormFields } from './types';
...@@ -16,7 +17,7 @@ import type { SmartContractMethodArgType } from 'types/api/contract'; ...@@ -16,7 +17,7 @@ import type { SmartContractMethodArgType } from 'types/api/contract';
import ClearButton from 'ui/shared/ClearButton'; import ClearButton from 'ui/shared/ClearButton';
import ContractMethodFieldZeroes from './ContractMethodFieldZeroes'; import ContractMethodFieldZeroes from './ContractMethodFieldZeroes';
import { addZeroesAllowed } from './utils'; import { INT_REGEXP, getIntBoundaries } from './utils';
interface Props { interface Props {
control: Control<MethodFormFields>; control: Control<MethodFormFields>;
...@@ -46,12 +47,24 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue, ...@@ -46,12 +47,24 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue,
onChange(); onChange();
}, [ getValues, name, onChange, setValue ]); }, [ getValues, name, onChange, setValue ]);
const hasZerosControl = addZeroesAllowed(valueType); const intMatch = React.useMemo(() => {
const match = valueType.match(INT_REGEXP);
if (!match) {
return null;
}
const [ , isUnsigned, power = '256' ] = match;
const [ min, max ] = getIntBoundaries(Number(power), Boolean(isUnsigned));
return { isUnsigned, power, min, max };
}, [ valueType ]);
const renderInput = React.useCallback(( const renderInput = React.useCallback((
{ field, formState }: { field: ControllerRenderProps<MethodFormFields>; formState: UseFormStateReturn<MethodFormFields> }, { field, formState }: { field: ControllerRenderProps<MethodFormFields>; formState: UseFormStateReturn<MethodFormFields> },
) => { ) => {
const error = formState.errors[name]; const error = formState.errors[name];
// show control for all inputs which allows to insert 10^18 or greater numbers
const hasZerosControl = intMatch && Number(intMatch.power) >= 64;
return ( return (
<Box> <Box>
...@@ -64,6 +77,12 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue, ...@@ -64,6 +77,12 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue,
<InputGroup size="xs"> <InputGroup size="xs">
<Input <Input
{ ...field } { ...field }
{ ...(intMatch ? {
as: NumericFormat,
thousandSeparator: ' ',
decimalScale: 0,
allowNegative: !intMatch.isUnsigned,
} : {}) }
ref={ ref } ref={ ref }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
required required
...@@ -80,18 +99,31 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue, ...@@ -80,18 +99,31 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue,
{ error && <Box color="error" fontSize="sm" mt={ 1 }>{ error.message }</Box> } { error && <Box color="error" fontSize="sm" mt={ 1 }>{ error.message }</Box> }
</Box> </Box>
); );
}, [ name, isDisabled, placeholder, hasZerosControl, handleClear, handleAddZeroesClick ]); }, [ name, intMatch, isDisabled, placeholder, handleClear, handleAddZeroesClick ]);
const validate = React.useCallback((value: string) => { const validate = React.useCallback((value: string) => {
switch (valueType) { if (valueType === 'address') {
case 'address': {
return !isAddress(value) ? 'Invalid address format' : true; return !isAddress(value) ? 'Invalid address format' : true;
} }
default: if (intMatch) {
return; const formattedValue = Number(value.replace(/\s/g, ''));
if (Object.is(formattedValue, NaN)) {
return 'Invalid integer format';
} }
}, [ valueType ]);
if (formattedValue > intMatch.max || formattedValue < intMatch.min) {
const lowerBoundary = intMatch.isUnsigned ? '0' : `-1 * 2 ^ ${ Number(intMatch.power) / 2 }`;
const upperBoundary = intMatch.isUnsigned ? `2 ^ ${ intMatch.power } - 1` : `2^${ Number(intMatch.power) / 2 } - 1`;
return `Value should be in range from "${ lowerBoundary }" to "${ upperBoundary }" inclusively`;
}
return true;
}
return true;
}, [ intMatch, valueType ]);
return ( return (
<> <>
......
...@@ -2,6 +2,15 @@ import type { Abi } from 'abitype'; ...@@ -2,6 +2,15 @@ import type { Abi } from 'abitype';
import type { SmartContractWriteMethod } from 'types/api/contract'; import type { SmartContractWriteMethod } from 'types/api/contract';
export const INT_REGEXP = /^(u)?int(\d+)?$/i;
export const getIntBoundaries = (power: number, isUnsigned: boolean) => {
const maxUnsigned = 2 ** power;
const max = isUnsigned ? maxUnsigned - 1 : maxUnsigned / 2 - 1;
const min = isUnsigned ? 0 : -maxUnsigned / 2;
return [ min, max ];
};
export const getNativeCoinValue = (value: string | Array<unknown>) => { export const getNativeCoinValue = (value: string | Array<unknown>) => {
const _value = Array.isArray(value) ? value[0] : value; const _value = Array.isArray(value) ? value[0] : value;
...@@ -12,24 +21,6 @@ export const getNativeCoinValue = (value: string | Array<unknown>) => { ...@@ -12,24 +21,6 @@ export const getNativeCoinValue = (value: string | Array<unknown>) => {
return BigInt(_value); return BigInt(_value);
}; };
export const addZeroesAllowed = (valueType: string) => {
if (valueType.includes('[')) {
return false;
}
const REGEXP = /^u?int(\d+)/i;
const match = valueType.match(REGEXP);
const power = match?.[1];
if (power) {
// show control for all inputs which allows to insert 10^18 or greater numbers
return Number(power) >= 64;
}
return false;
};
interface ExtendedError extends Error { interface ExtendedError extends Error {
detectedNetwork?: { detectedNetwork?: {
chain: number; chain: number;
......
...@@ -12704,6 +12704,13 @@ react-jazzicon@^1.0.4: ...@@ -12704,6 +12704,13 @@ react-jazzicon@^1.0.4:
dependencies: dependencies:
mersenne-twister "^1.1.0" mersenne-twister "^1.1.0"
react-number-format@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.3.1.tgz#840c257da9cb4b248990d8db46e4d23e8bac67ff"
integrity sha512-qpYcQLauIeEhCZUZY9jXZnnroOtdy3jYaS1zQ3M1Sr6r/KMOBEIGNIb7eKT19g2N1wbYgFgvDzs19hw5TrB8XQ==
dependencies:
prop-types "^15.7.2"
react-redux@^8.1.2: react-redux@^8.1.2:
version "8.1.3" version "8.1.3"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.3.tgz#4fdc0462d0acb59af29a13c27ffef6f49ab4df46"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment