Skip to content

Commit

Permalink
Contract interaction - floats in uint256
Browse files Browse the repository at this point in the history
Fixes #2455
  • Loading branch information
tom2drum committed Jan 3, 2025
1 parent bd4c646 commit 7694fba
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 8 deletions.
58 changes: 55 additions & 3 deletions ui/address/contract/methods/form/ContractMethodFieldInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ interface Props {
}

const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDisabled, isOptional: isOptionalProp, level }: Props) => {
const ref = React.useRef<HTMLInputElement>(null);
const ref = React.useRef<HTMLInputElement>();

const [ intPower, setIntPower ] = React.useState<number>(18);

const isNativeCoin = data.fieldType === 'native_coin';
const isOptional = isOptionalProp || isNativeCoin;
Expand All @@ -46,6 +48,8 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi

const hasMultiplyButton = argTypeMatchInt && Number(argTypeMatchInt.power) >= 64;

React.useImperativeHandle(field.ref, () => ref.current);

const handleChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const formattedValue = format(event.target.value);
field.onChange(formattedValue); // data send back to hook form
Expand Down Expand Up @@ -83,6 +87,42 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
setValue(name, newValue, { shouldValidate: true });
}, [ format, name, setValue ]);

const handlePaste = React.useCallback((event: React.ClipboardEvent<HTMLInputElement>) => {
if (!argTypeMatchInt || !hasMultiplyButton) {
return;
}

const value = Number(event.clipboardData.getData('text'));

if (Object.is(value, NaN)) {
return;
}

const isFloat = Number.isFinite(value) && !Number.isInteger(value);

if (!isFloat) {
return;
}

event.preventDefault();

if (field.value) {
return;
}

const newValue = value * 10 ** intPower;
const formattedValue = format(newValue.toString());

field.onChange(formattedValue);
setValue(name, formattedValue, { shouldValidate: true });
window.setTimeout(() => {
// move cursor to the end of the input
// but we have to wait for the input to get the new value
const END_OF_INPUT = 100;
ref.current?.setSelectionRange(END_OF_INPUT, END_OF_INPUT);
}, 100);
}, [ argTypeMatchInt, hasMultiplyButton, intPower, format, field, setValue, name ]);

const error = fieldState.error;

return (
Expand All @@ -107,9 +147,14 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
thousandSeparator: ' ',
decimalScale: 0,
allowNegative: !argTypeMatchInt.isUnsigned,
getInputRef: (element: HTMLInputElement) => {
ref.current = element;
},
} : {}) }
ref={ ref }
// as we use mutable ref, we have to cast it to React.LegacyRef<HTMLInputElement> to trick chakra and typescript
ref={ ref as React.LegacyRef<HTMLInputElement> | undefined }
onChange={ handleChange }
onPaste={ handlePaste }
required={ !isOptional }
isInvalid={ Boolean(error) }
placeholder={ data.type }
Expand Down Expand Up @@ -148,7 +193,14 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
Max
</Button>
)) }
{ hasMultiplyButton && <ContractMethodMultiplyButton onClick={ handleMultiplyButtonClick } isDisabled={ isDisabled }/> }
{ hasMultiplyButton && (
<ContractMethodMultiplyButton
onClick={ handleMultiplyButtonClick }
isDisabled={ isDisabled }
initialValue={ intPower }
onChange={ setIntPower }
/>
) }
</InputRightElement>
</InputGroup>
{ error && <Box color="error" fontSize="sm" lineHeight={ 5 } mt={ 1 }>{ error.message }</Box> }
Expand Down
15 changes: 10 additions & 5 deletions ui/address/contract/methods/form/ContractMethodMultiplyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import IconSvg from 'ui/shared/IconSvg';
interface Props {
onClick: (power: number) => void;
isDisabled?: boolean;
initialValue: number;
onChange: (power: number) => void;
}

const ContractMethodMultiplyButton = ({ onClick, isDisabled }: Props) => {
const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(18);
const ContractMethodMultiplyButton = ({ onClick, isDisabled, initialValue, onChange }: Props) => {
const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(initialValue);
const [ customValue, setCustomValue ] = React.useState<number>();
const { isOpen, onToggle, onClose } = useDisclosure();

Expand All @@ -35,13 +37,16 @@ const ContractMethodMultiplyButton = ({ onClick, isDisabled }: Props) => {
setSelectedOption((prev) => prev === id ? undefined : id);
setCustomValue(undefined);
onClose();
onChange(id);
}
}, [ onClose ]);
}, [ onClose, onChange ]);

const handleInputChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setCustomValue(Number(event.target.value));
const value = Number(event.target.value);
setCustomValue(value);
setSelectedOption(undefined);
}, []);
onChange(value);
}, [ onChange ]);

const value = selectedOption || customValue;

Expand Down

0 comments on commit 7694fba

Please sign in to comment.