Skip to content

Commit

Permalink
Contract interaction - floats in uint256 (#2499)
Browse files Browse the repository at this point in the history
* Contract interaction - floats in uint256

Fixes #2455

* update screenshots
  • Loading branch information
tom2drum authored Jan 14, 2025
1 parent 8388b41 commit 96f2e11
Show file tree
Hide file tree
Showing 5 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 96f2e11

Please sign in to comment.