Commit dead2995 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #728 from blockscout/contracts/nested-arrays

fix nested arrays as contract method input
parents bfc959d5 ad9156e6
...@@ -39,5 +39,11 @@ export const withIndexedFields: DecodedInput = { ...@@ -39,5 +39,11 @@ export const withIndexedFields: DecodedInput = {
type: 'uint256', type: 'uint256',
value: '31567373703130350', value: '31567373703130350',
}, },
{
indexed: true,
name: 'inputArray',
type: 'uint256[2][2]',
value: [ [ '1', '1' ], [ '1', '1' ] ],
},
], ],
}; };
...@@ -7,6 +7,6 @@ export interface DecodedInput { ...@@ -7,6 +7,6 @@ export interface DecodedInput {
export interface DecodedInputParams { export interface DecodedInputParams {
name: string; name: string;
type: string; type: string;
value: string; value: string | Array<unknown> | Record<string, unknown>;
indexed?: boolean; indexed?: boolean;
} }
...@@ -20,7 +20,7 @@ interface ResultComponentProps<T extends SmartContractMethod> { ...@@ -20,7 +20,7 @@ interface ResultComponentProps<T extends SmartContractMethod> {
interface Props<T extends SmartContractMethod> { interface Props<T extends SmartContractMethod> {
data: T; data: T;
onSubmit: (data: T, args: Array<string | Array<string>>) => Promise<ContractMethodCallResult<T>>; onSubmit: (data: T, args: Array<string | Array<unknown>>) => Promise<ContractMethodCallResult<T>>;
ResultComponent: (props: ResultComponentProps<T>) => JSX.Element | null; ResultComponent: (props: ResultComponentProps<T>) => JSX.Element | null;
isWrite?: boolean; isWrite?: boolean;
} }
...@@ -44,13 +44,23 @@ const sortFields = (data: Array<SmartContractMethodInput>) => ([ a ]: [string, s ...@@ -44,13 +44,23 @@ const sortFields = (data: Array<SmartContractMethodInput>) => ([ a ]: [string, s
}; };
const castFieldValue = (data: Array<SmartContractMethodInput>) => ([ key, value ]: [ string, string ], index: number) => { const castFieldValue = (data: Array<SmartContractMethodInput>) => ([ key, value ]: [ string, string ], index: number) => {
if (data[index].type.includes('[]')) { if (data[index].type.includes('[')) {
return [ key, parseArrayValue(value) ]; return [ key, parseArrayValue(value) ];
} }
return [ key, value ]; return [ key, value ];
}; };
const parseArrayValue = (value: string) => value.replace(/(\[|\])|\s/g, '').split(','); const parseArrayValue = (value: string) => {
try {
const parsedResult = JSON.parse(value);
if (Array.isArray(parsedResult)) {
return parsedResult;
}
throw new Error('Not an array');
} catch (error) {
return '';
}
};
const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ResultComponent, isWrite }: Props<T>) => { const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ResultComponent, isWrite }: Props<T>) => {
......
...@@ -64,7 +64,7 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue, ...@@ -64,7 +64,7 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue,
/> />
<InputRightElement w="auto" right={ 1 }> <InputRightElement w="auto" right={ 1 }>
{ field.value && <InputClearButton onClick={ handleClear } isDisabled={ isDisabled }/> } { field.value && <InputClearButton onClick={ handleClear } isDisabled={ isDisabled }/> }
{ hasZerosControl && <ContractMethodFieldZeroes onClick={ handleAddZeroesClick }/> } { hasZerosControl && <ContractMethodFieldZeroes onClick={ handleAddZeroesClick } isDisabled={ isDisabled }/> }
</InputRightElement> </InputRightElement>
</InputGroup> </InputGroup>
</FormControl> </FormControl>
......
...@@ -20,9 +20,10 @@ import { times } from 'lib/html-entities'; ...@@ -20,9 +20,10 @@ import { times } from 'lib/html-entities';
interface Props { interface Props {
onClick: (power: number) => void; onClick: (power: number) => void;
isDisabled?: boolean;
} }
const ContractMethodFieldZeroes = ({ onClick }: Props) => { const ContractMethodFieldZeroes = ({ onClick, isDisabled }: Props) => {
const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(18); const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(18);
const [ customValue, setCustomValue ] = React.useState<number>(); const [ customValue, setCustomValue ] = React.useState<number>();
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
...@@ -60,6 +61,7 @@ const ContractMethodFieldZeroes = ({ onClick }: Props) => { ...@@ -60,6 +61,7 @@ const ContractMethodFieldZeroes = ({ onClick }: Props) => {
colorScheme="gray" colorScheme="gray"
display="inline" display="inline"
onClick={ handleButtonClick } onClick={ handleButtonClick }
isDisabled={ isDisabled }
> >
{ times } { times }
<chakra.span>10</chakra.span> <chakra.span>10</chakra.span>
...@@ -76,6 +78,7 @@ const ContractMethodFieldZeroes = ({ onClick }: Props) => { ...@@ -76,6 +78,7 @@ const ContractMethodFieldZeroes = ({ onClick }: Props) => {
ml={ 1 } ml={ 1 }
p={ 0 } p={ 0 }
onClick={ onToggle } onClick={ onToggle }
isDisabled={ isDisabled }
> >
<Icon as={ iconEastMini } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } boxSize={ 6 }/> <Icon as={ iconEastMini } transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } boxSize={ 6 }/>
</Button> </Button>
......
...@@ -38,7 +38,7 @@ const ContractRead = ({ addressHash, isProxy, isCustomAbi }: Props) => { ...@@ -38,7 +38,7 @@ const ContractRead = ({ addressHash, isProxy, isCustomAbi }: Props) => {
}, },
}); });
const handleMethodFormSubmit = React.useCallback(async(item: SmartContractReadMethod, args: Array<string | Array<string>>) => { const handleMethodFormSubmit = React.useCallback(async(item: SmartContractReadMethod, args: Array<string | Array<unknown>>) => {
return apiFetch<'contract_method_query', SmartContractQueryMethodRead>('contract_method_query', { return apiFetch<'contract_method_query', SmartContractQueryMethodRead>('contract_method_query', {
pathParams: { hash: addressHash }, pathParams: { hash: addressHash },
queryParams: { queryParams: {
......
...@@ -51,7 +51,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => { ...@@ -51,7 +51,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => {
return contract; return contract;
})(); })();
const handleMethodFormSubmit = React.useCallback(async(item: SmartContractWriteMethod, args: Array<string | Array<string>>) => { const handleMethodFormSubmit = React.useCallback(async(item: SmartContractWriteMethod, args: Array<string | Array<unknown>>) => {
if (!isConnected) { if (!isConnected) {
throw new Error('Wallet is not connected'); throw new Error('Wallet is not connected');
} }
......
...@@ -2,13 +2,18 @@ import BigNumber from 'bignumber.js'; ...@@ -2,13 +2,18 @@ import BigNumber from 'bignumber.js';
import config from 'configs/app/config'; import config from 'configs/app/config';
export const getNativeCoinValue = (value: string | Array<string>) => { export const getNativeCoinValue = (value: string | Array<unknown>) => {
const _value = Array.isArray(value) ? value[0] : value; const _value = Array.isArray(value) ? value[0] : value;
if (typeof _value !== 'string') {
return '0';
}
return BigNumber(_value).times(10 ** config.network.currency.decimals).toString(); return BigNumber(_value).times(10 ** config.network.currency.decimals).toString();
}; };
export const addZeroesAllowed = (valueType: string) => { export const addZeroesAllowed = (valueType: string) => {
if (valueType.includes('[]')) { if (valueType.includes('[')) {
return false; return false;
} }
......
...@@ -155,19 +155,37 @@ const LogDecodedInputData = ({ data }: Props) => { ...@@ -155,19 +155,37 @@ const LogDecodedInputData = ({ data }: Props) => {
</> </>
) } ) }
{ data.parameters.map(({ name, type, value, indexed }, index) => { { data.parameters.map(({ name, type, value, indexed }, index) => {
return ( const content = (() => {
<TableRow key={ name } name={ name } type={ type } isLast={ index === data.parameters.length - 1 } indexed={ indexed }> if (type === 'address' && typeof value === 'string') {
{ type === 'address' ? ( return (
<Address justifyContent="space-between"> <Address justifyContent="space-between">
<AddressLink type="address" hash={ value }/> <AddressLink type="address" hash={ value }/>
<CopyToClipboard text={ value }/> <CopyToClipboard text={ value }/>
</Address> </Address>
) : ( );
}
if (typeof value === 'object') {
const text = JSON.stringify(value, undefined, 4);
return (
<Flex alignItems="flex-start" justifyContent="space-between" whiteSpace="normal" wordBreak="break-all"> <Flex alignItems="flex-start" justifyContent="space-between" whiteSpace="normal" wordBreak="break-all">
<Text>{ String(value) }</Text> <div>{ text }</div>
<CopyToClipboard text={ value }/> <CopyToClipboard text={ text }/>
</Flex> </Flex>
) } );
}
return (
<Flex alignItems="flex-start" justifyContent="space-between" whiteSpace="normal" wordBreak="break-all">
<Text>{ value }</Text>
<CopyToClipboard text={ value }/>
</Flex>
);
})();
return (
<TableRow key={ name } name={ name } type={ type } isLast={ index === data.parameters.length - 1 } indexed={ indexed }>
{ content }
</TableRow> </TableRow>
); );
}) } }) }
......
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