Files
tom goriunovandGitHub 434aae92bb Call contract read methods from the client (#2032)
* display methods accordion

* add method_id to methods

* query read method via public client

* query read methods without inputs at accordion section open

* add custom abi tabs

* add proxy tabs

* custom wagmi config if wc is not configured

* remove ContractAbiItemConstant component

* display tuple properties in method outputs

* display read result

* clean up

* support nested arrays in the read result

* re-write test for contract methods

* change result view for address output

* refactoring

* fix tests

* remove web3 provider fallback

* fix tab loading

* refactoring

* add implementation contract selector

* add "copy to clipboard" for address, remove top-level square brackets

* add preview mode to contract method result

* tests for read result

* fix tests

* change styles for result preview

* review fix
2024-06-24 19:56:06 +02:00

87 lines
2.5 KiB
TypeScript

import React from 'react';
import { getAddress, isAddress, isHex } from 'viem';
import type { MatchInt } from './utils';
import { BYTES_REGEXP } from './utils';
interface Params {
argType: string;
argTypeMatchInt: MatchInt | null;
isOptional: boolean;
}
export default function useValidateField({ isOptional, argType, argTypeMatchInt }: Params) {
const bytesMatch = React.useMemo(() => {
return argType.match(BYTES_REGEXP);
}, [ argType ]);
// some values are formatted before they are sent to the validator
// see ./useFormatFieldValue.tsx hook
return React.useCallback((value: string | boolean | undefined) => {
if (value === undefined || value === '') {
return isOptional ? true : 'Field is required';
}
if (argType === 'address') {
if (typeof value !== 'string' || !isAddress(value)) {
return 'Invalid address format';
}
// all lowercase addresses are valid
const isInLowerCase = value === value.toLowerCase();
if (isInLowerCase) {
return true;
}
// check if address checksum is valid
return getAddress(value) === value ? true : 'Invalid address checksum';
}
if (argTypeMatchInt) {
const valueBi = (() => {
try {
return BigInt(value);
} catch (error) {
return null;
}
})();
if (typeof value !== 'string' || valueBi === null) {
return 'Invalid integer format';
}
if (valueBi > argTypeMatchInt.max || valueBi < argTypeMatchInt.min) {
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`;
return `Value should be in range from "${ lowerBoundary }" to "${ upperBoundary }" inclusively`;
}
return true;
}
if (argType === 'bool') {
if (typeof value !== 'boolean') {
return 'Invalid boolean format. Allowed values: true, false';
}
}
if (bytesMatch) {
const [ , length ] = bytesMatch;
if (!isHex(value)) {
return 'Invalid bytes format';
}
if (length) {
const valueLengthInBytes = value.replace('0x', '').length / 2;
return valueLengthInBytes !== Number(length) ? `Value should be ${ length } bytes in length` : true;
}
return true;
}
return true;
}, [ isOptional, argType, argTypeMatchInt, bytesMatch ]);
}