Commit dfd590e5 authored by tom's avatar tom

fix contract method form fields value foramatting

parent e8c97e61
...@@ -10,6 +10,7 @@ import ClearButton from 'ui/shared/ClearButton'; ...@@ -10,6 +10,7 @@ import ClearButton from 'ui/shared/ClearButton';
import ContractMethodFieldLabel from './ContractMethodFieldLabel'; import ContractMethodFieldLabel from './ContractMethodFieldLabel';
import ContractMethodMultiplyButton from './ContractMethodMultiplyButton'; import ContractMethodMultiplyButton from './ContractMethodMultiplyButton';
import useArgTypeMatchInt from './useArgTypeMatchInt'; import useArgTypeMatchInt from './useArgTypeMatchInt';
import useFormatFieldValue from './useFormatFieldValue';
import useValidateField from './useValidateField'; import useValidateField from './useValidateField';
interface Props { interface Props {
...@@ -29,15 +30,22 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -29,15 +30,22 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
const argTypeMatchInt = useArgTypeMatchInt({ argType: data.type }); const argTypeMatchInt = useArgTypeMatchInt({ argType: data.type });
const validate = useValidateField({ isOptional, argType: data.type, argTypeMatchInt }); const validate = useValidateField({ isOptional, argType: data.type, argTypeMatchInt });
const format = useFormatFieldValue({ argType: data.type, argTypeMatchInt });
const { control, setValue, getValues } = useFormContext(); const { control, setValue, getValues } = useFormContext();
const { field, fieldState } = useController({ control, name, rules: { validate, required: isOptional ? false : 'Field is required' } }); const { field, fieldState } = useController({ control, name, rules: { validate } });
const inputBgColor = useColorModeValue('white', 'black'); const inputBgColor = useColorModeValue('white', 'black');
const nativeCoinRowBgColor = useColorModeValue('gray.100', 'gray.700'); const nativeCoinRowBgColor = useColorModeValue('gray.100', 'gray.700');
const hasMultiplyButton = argTypeMatchInt && Number(argTypeMatchInt.power) >= 64; const hasMultiplyButton = argTypeMatchInt && Number(argTypeMatchInt.power) >= 64;
const handleChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const formattedValue = format(event.target.value);
field.onChange(formattedValue); // data send back to hook form
setValue(name, formattedValue); // UI state
}, [ field, name, setValue, format ]);
const handleClear = React.useCallback(() => { const handleClear = React.useCallback(() => {
setValue(name, ''); setValue(name, '');
ref.current?.focus(); ref.current?.focus();
...@@ -46,9 +54,9 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -46,9 +54,9 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
const handleMultiplyButtonClick = React.useCallback((power: number) => { const handleMultiplyButtonClick = React.useCallback((power: number) => {
const zeroes = Array(power).fill('0').join(''); const zeroes = Array(power).fill('0').join('');
const value = getValues(name); const value = getValues(name);
const newValue = value ? value + zeroes : '1' + zeroes; const newValue = format(value ? value + zeroes : '1' + zeroes);
setValue(name, newValue); setValue(name, newValue);
}, [ getValues, name, setValue ]); }, [ format, getValues, name, setValue ]);
const error = fieldState.error; const error = fieldState.error;
...@@ -76,6 +84,7 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -76,6 +84,7 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
allowNegative: !argTypeMatchInt.isUnsigned, allowNegative: !argTypeMatchInt.isUnsigned,
} : {}) } } : {}) }
ref={ ref } ref={ ref }
onChange={ handleChange }
required={ !isOptional } required={ !isOptional }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
placeholder={ data.type } placeholder={ data.type }
...@@ -84,7 +93,7 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -84,7 +93,7 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
paddingRight={ hasMultiplyButton ? '120px' : '40px' } paddingRight={ hasMultiplyButton ? '120px' : '40px' }
/> />
<InputRightElement w="auto" right={ 1 }> <InputRightElement w="auto" right={ 1 }>
{ typeof field.value === 'string' && field.value.replace('\n', '') && <ClearButton onClick={ handleClear } isDisabled={ isDisabled }/> } { field.value !== undefined && field.value !== '' && <ClearButton onClick={ handleClear } isDisabled={ isDisabled }/> }
{ hasMultiplyButton && <ContractMethodMultiplyButton onClick={ handleMultiplyButtonClick } isDisabled={ isDisabled }/> } { hasMultiplyButton && <ContractMethodMultiplyButton onClick={ handleMultiplyButtonClick } isDisabled={ isDisabled }/> }
</InputRightElement> </InputRightElement>
</InputGroup> </InputGroup>
......
...@@ -20,11 +20,11 @@ const ContractMethodFormOutputs = ({ data }: Props) => { ...@@ -20,11 +20,11 @@ const ContractMethodFormOutputs = ({ data }: Props) => {
<p> <p>
{ data.map(({ type, name }, index) => { { data.map(({ type, name }, index) => {
return ( return (
<> <React.Fragment key={ index }>
<chakra.span fontWeight={ 500 }>{ name } </chakra.span> <chakra.span fontWeight={ 500 }>{ name } </chakra.span>
<span>{ name ? `(${ type })` : type }</span> <span>{ name ? `(${ type })` : type }</span>
{ index < data.length - 1 && <span>, </span> } { index < data.length - 1 && <span>, </span> }
</> </React.Fragment>
); );
}) } }) }
</p> </p>
......
import React from 'react';
import type { SmartContractMethodArgType } from 'types/api/contract';
import type { MatchInt } from './useArgTypeMatchInt';
interface Params {
argType: SmartContractMethodArgType;
argTypeMatchInt: MatchInt | null;
}
export default function useFormatFieldValue({ argType, argTypeMatchInt }: Params) {
return React.useCallback((value: string | undefined) => {
if (!value) {
return;
}
if (argTypeMatchInt) {
const formattedString = value.replace(/\s/g, '');
return parseInt(formattedString);
}
if (argType === 'bool') {
const formattedValue = value.toLowerCase();
switch (formattedValue) {
case 'true': {
return true;
}
case 'false':{
return false;
}
default:
return value;
}
}
return value;
}, [ argType, argTypeMatchInt ]);
}
...@@ -4,7 +4,7 @@ import { getAddress, isAddress, isHex } from 'viem'; ...@@ -4,7 +4,7 @@ import { getAddress, isAddress, isHex } from 'viem';
import type { SmartContractMethodArgType } from 'types/api/contract'; import type { SmartContractMethodArgType } from 'types/api/contract';
import type { MatchInt } from './useArgTypeMatchInt'; import type { MatchInt } from './useArgTypeMatchInt';
import { BYTES_REGEXP, formatBooleanValue } from './utils'; import { BYTES_REGEXP } from './utils';
interface Params { interface Params {
argType: SmartContractMethodArgType; argType: SmartContractMethodArgType;
...@@ -18,13 +18,15 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt ...@@ -18,13 +18,15 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
return argType.match(BYTES_REGEXP); return argType.match(BYTES_REGEXP);
}, [ argType ]); }, [ argType ]);
return React.useCallback((value: string | undefined) => { // some values are formatted before they are sent to the validator
if (!value) { // see ./useFormatFieldValue.tsx hook
return React.useCallback((value: string | number | boolean | undefined) => {
if (value === undefined || value === '') {
return isOptional ? true : 'Field is required'; return isOptional ? true : 'Field is required';
} }
if (argType === 'address') { if (argType === 'address') {
if (!isAddress(value)) { if (typeof value !== 'string' || !isAddress(value)) {
return 'Invalid address format'; return 'Invalid address format';
} }
...@@ -39,13 +41,11 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt ...@@ -39,13 +41,11 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
} }
if (argTypeMatchInt) { if (argTypeMatchInt) {
const formattedValue = Number(value.replace(/\s/g, '')); if (typeof value !== 'number' || Object.is(value, NaN)) {
if (Object.is(formattedValue, NaN)) {
return 'Invalid integer format'; return 'Invalid integer format';
} }
if (formattedValue > argTypeMatchInt.max || formattedValue < argTypeMatchInt.min) { if (value > argTypeMatchInt.max || value < argTypeMatchInt.min) {
const lowerBoundary = argTypeMatchInt.isUnsigned ? '0' : `-1 * 2 ^ ${ Number(argTypeMatchInt.power) - 1 }`; const lowerBoundary = argTypeMatchInt.isUnsigned ? '0' : `-1 * 2 ^ ${ Number(argTypeMatchInt.power) - 1 }`;
const upperBoundary = argTypeMatchInt.isUnsigned ? `2 ^ ${ argTypeMatchInt.power } - 1` : `2 ^ ${ Number(argTypeMatchInt.power) - 1 } - 1`; const upperBoundary = argTypeMatchInt.isUnsigned ? `2 ^ ${ argTypeMatchInt.power } - 1` : `2 ^ ${ Number(argTypeMatchInt.power) - 1 } - 1`;
return `Value should be in range from "${ lowerBoundary }" to "${ upperBoundary }" inclusively`; return `Value should be in range from "${ lowerBoundary }" to "${ upperBoundary }" inclusively`;
...@@ -55,9 +55,8 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt ...@@ -55,9 +55,8 @@ export default function useValidateField({ isOptional, argType, argTypeMatchInt
} }
if (argType === 'bool') { if (argType === 'bool') {
const formattedValue = formatBooleanValue(value); if (typeof value !== 'boolean') {
if (formattedValue === undefined) { return 'Invalid boolean format. Allowed values: true, false';
return 'Invalid boolean format. Allowed values: 0, 1, true, false';
} }
} }
......
...@@ -17,25 +17,6 @@ export const getIntBoundaries = (power: number, isUnsigned: boolean) => { ...@@ -17,25 +17,6 @@ export const getIntBoundaries = (power: number, isUnsigned: boolean) => {
return [ min, max ]; return [ min, max ];
}; };
export const formatBooleanValue = (value: string) => {
const formattedValue = value.toLowerCase();
switch (formattedValue) {
case 'true':
case '1': {
return 'true';
}
case 'false':
case '0': {
return 'false';
}
default:
return;
}
};
export function transformFormDataToMethodArgs(formData: ContractMethodFormFields) { export function transformFormDataToMethodArgs(formData: ContractMethodFormFields) {
const result: Array<unknown> = []; const result: Array<unknown> = [];
......
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