Commit 6f7c284f authored by tom goriunov's avatar tom goriunov Committed by GitHub

Show error if contract method has invalid data structure (#2087)

* show error if contract method has invalid data structure

* convert address to checksum when reading from contract method
parent 4bde57e9
<svg viewBox="0 0 241 185" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 241 185" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M58.5 57.705 120.306 22.5l62.743 35.205v72.397l-48.007 26.842v-11.695l37.055-21.373-8.814-5.11.794-1.37 8.681 5.034V70.1l-23.049 13.044c.109-.729.164-1.464.166-2.2v-9.556l17.678-10.174-46.559-26.74v10.17h-1.583V34.605L74.17 61.214l16.33 9.625V81.064c.002.588.037 1.175.107 1.76L68.09 70.1v52.366l8.708-4.994.787 1.373-8.808 5.052 37.262 21.819.234 11.697L58.5 129.576V57.705Zm32.219 51.843c-.11.642-.179 1.29-.206 1.939l-5.774 3.289-.784-1.375 6.764-3.853Zm24.672 28.095h10.139v22.05l-5.147 2.807-4.992-2.806v-22.051Zm10.139-10h-10.139v-4.78h10.139v4.78Zm0-29.66-1.706-1.412 14.301-11.83a4.797 4.797 0 0 0 1.285-1.688 5.169 5.169 0 0 0 .465-2.137v-3.772l-4.375 2.518v1.254l-1.586 1.315h-2.878l-10.73 6.175-10.478-6.175h-3.504l-1.449-1.195V79.31l-4.375-2.578v4.303c.002.741.161 1.472.465 2.136.245.535.578 1.014.982 1.414.097.096.198.187.303.274l4.949 4.063 9.352 7.648-1.16.95v5.968l2.609-2.137v7.17c0 .634.23 1.242.641 1.69.41.448.966.7 1.547.7.58 0 1.136-.252 1.546-.7a2.503 2.503 0 0 0 .641-1.69v-7.141l3.155 2.607v-6.006Zm-4.536-27.703h-1.583v-.032h1.583v.032Zm28.844 40.893 6.353 3.66.79-1.372-7.414-4.27c.134.655.224 1.317.271 1.982ZM120.994 55.5h-1.583v-3.763h1.583V55.5Z" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M58.5 57.705 120.306 22.5l62.743 35.205v72.397l-48.007 26.842v-11.695l37.055-21.373-8.814-5.11.794-1.37 8.681 5.034V70.1l-23.049 13.044c.109-.729.164-1.464.166-2.2v-9.556l17.678-10.174-46.559-26.74v10.17h-1.583V34.605L74.17 61.214l16.33 9.625v10.225c.002.588.037 1.175.107 1.76L68.09 70.1v52.366l8.708-4.994.787 1.373-8.808 5.052 37.262 21.819.234 11.697L58.5 129.576V57.705Zm32.219 51.843c-.11.642-.179 1.29-.206 1.939l-5.774 3.289-.784-1.375 6.764-3.853Zm24.672 28.095h10.139v22.05l-5.147 2.807-4.992-2.806v-22.051Zm10.139-10h-10.139v-4.78h10.139v4.78Zm0-29.66-1.706-1.412 14.301-11.83a4.797 4.797 0 0 0 1.285-1.688 5.169 5.169 0 0 0 .465-2.137v-3.772l-4.375 2.518v1.254l-1.586 1.315h-2.878l-10.73 6.175-10.478-6.175h-3.504l-1.449-1.195V79.31l-4.375-2.578v4.303c.002.741.161 1.472.465 2.136a4.797 4.797 0 0 0 1.285 1.688l4.949 4.063 9.352 7.648-1.16.95v5.968l2.609-2.137v7.17c0 .634.23 1.242.641 1.69.41.448.966.7 1.547.7.58 0 1.136-.252 1.546-.7a2.503 2.503 0 0 0 .641-1.69v-7.141l3.155 2.607v-6.006Zm-4.536-27.703h-1.583v-.032h1.583v.032Zm28.844 40.893 6.353 3.66.79-1.372-7.414-4.27c.134.655.224 1.317.271 1.982ZM120.994 55.5h-1.583v-3.763h1.583V55.5Z" fill="currentColor"/>
<path d="M138.125 84.74a4.728 4.728 0 0 0 1.285-1.688 5.169 5.169 0 0 0 .465-2.136V70.28c0-1.268-.461-2.483-1.281-3.38-.821-.896-1.934-1.4-3.094-1.4h-30.625c-1.16 0-2.273.504-3.094 1.4-.82.897-1.281 2.112-1.281 3.38v10.756c.002.741.161 1.472.465 2.136a4.729 4.729 0 0 0 1.285 1.688l4.949 4.063 9.352 7.648-14.301 11.712a4.725 4.725 0 0 0-1.285 1.688 5.166 5.166 0 0 0-.465 2.136v10.756c0 1.267.461 2.483 1.281 3.38.821.896 1.934 1.4 3.094 1.4H135.5c1.16 0 2.273-.504 3.094-1.4.82-.897 1.281-2.113 1.281-3.38v-10.636a5.174 5.174 0 0 0-.465-2.137 4.733 4.733 0 0 0-1.285-1.688l-14.301-11.83 6.207-5.14 8.094-6.692Zm-33.25-14.46H135.5v10.636l-1.586 1.315h-27.59l-1.449-1.195V70.28Zm30.625 41.947v10.636h-30.625v-10.756L118 101.352v7.17c0 .634.23 1.242.641 1.69.41.448.966.7 1.547.7.58 0 1.136-.252 1.546-.7a2.503 2.503 0 0 0 .641-1.69v-7.141l13.125 10.846Z" fill="currentColor" stroke="currentColor" stroke-width="4"/> <path d="M138.125 84.74a4.728 4.728 0 0 0 1.285-1.688 5.169 5.169 0 0 0 .465-2.136V70.28c0-1.268-.461-2.483-1.281-3.38-.821-.896-1.934-1.4-3.094-1.4h-30.625c-1.16 0-2.273.504-3.094 1.4-.82.897-1.281 2.112-1.281 3.38v10.756c.002.741.161 1.472.465 2.136a4.729 4.729 0 0 0 1.285 1.688l4.949 4.063 9.352 7.648-14.301 11.712a4.725 4.725 0 0 0-1.285 1.688 5.166 5.166 0 0 0-.465 2.136v10.756c0 1.267.461 2.483 1.281 3.38.821.896 1.934 1.4 3.094 1.4H135.5c1.16 0 2.273-.504 3.094-1.4.82-.897 1.281-2.113 1.281-3.38v-10.636a5.174 5.174 0 0 0-.465-2.137 4.733 4.733 0 0 0-1.285-1.688l-14.301-11.83 6.207-5.14 8.094-6.692Zm-33.25-14.46H135.5v10.636l-1.586 1.315h-27.59l-1.449-1.195V70.28Zm30.625 41.947v10.636h-30.625v-10.756L118 101.352v7.17c0 .634.23 1.242.641 1.69.41.448.966.7 1.547.7.58 0 1.136-.252 1.546-.7a2.503 2.503 0 0 0 .641-1.69v-7.141l13.125 10.846Z" fill="currentColor" stroke="currentColor" stroke-width="4"/>
</svg> </svg>
...@@ -129,6 +129,6 @@ export const write: Array<SmartContractMethodWrite> = [ ...@@ -129,6 +129,6 @@ export const write: Array<SmartContractMethodWrite> = [
payable: false, payable: false,
stateMutability: 'nonpayable', stateMutability: 'nonpayable',
type: 'function', type: 'function',
method_id: '0x06', is_invalid: true,
}, },
]; ];
import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react'; import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Alert, Box, Tooltip, useClipboard, useDisclosure } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Element } from 'react-scroll'; import { Element } from 'react-scroll';
...@@ -110,6 +110,9 @@ const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props) ...@@ -110,6 +110,9 @@ const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props)
</AccordionButton> </AccordionButton>
</Element> </Element>
<AccordionPanel pb={ 4 } pr={ 0 } pl="28px" w="calc(100% - 6px)"> <AccordionPanel pb={ 4 } pr={ 0 } pl="28px" w="calc(100% - 6px)">
{ 'is_invalid' in data && data.is_invalid ? (
<Alert status="warning">An error occurred while parsing the method signature.</Alert>
) : (
<ContractMethodForm <ContractMethodForm
key={ id + '_' + index + '_' + attempt } key={ id + '_' + index + '_' + attempt }
data={ data } data={ data }
...@@ -118,6 +121,7 @@ const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props) ...@@ -118,6 +121,7 @@ const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props)
onReset={ handleReset } onReset={ handleReset }
isOpen={ isExpanded } isOpen={ isExpanded }
/> />
) }
</AccordionPanel> </AccordionPanel>
</> </>
) } ) }
......
...@@ -16,7 +16,7 @@ interface Props { ...@@ -16,7 +16,7 @@ interface Props {
const ItemTuple = ({ abiParameter, data, mode, level }: Props) => { const ItemTuple = ({ abiParameter, data, mode, level }: Props) => {
return ( return (
<p> <div>
<p> <p>
<span>{ printRowOffset(level) }</span> <span>{ printRowOffset(level) }</span>
<chakra.span fontWeight={ 500 }>{ abiParameter.name || abiParameter.internalType }</chakra.span> <chakra.span fontWeight={ 500 }>{ abiParameter.name || abiParameter.internalType }</chakra.span>
...@@ -36,7 +36,7 @@ const ItemTuple = ({ abiParameter, data, mode, level }: Props) => { ...@@ -36,7 +36,7 @@ const ItemTuple = ({ abiParameter, data, mode, level }: Props) => {
); );
}) } }) }
<p>{ printRowOffset(level) }{ '}' }</p> <p>{ printRowOffset(level) }{ '}' }</p>
</p> </div>
); );
}; };
......
...@@ -6,8 +6,9 @@ export type MethodType = 'read' | 'write'; ...@@ -6,8 +6,9 @@ export type MethodType = 'read' | 'write';
export type MethodCallStrategy = 'read' | 'write' | 'simulate'; export type MethodCallStrategy = 'read' | 'write' | 'simulate';
export type ResultViewMode = 'preview' | 'result'; export type ResultViewMode = 'preview' | 'result';
export type SmartContractMethodRead = AbiFunction & { method_id: string }; export type SmartContractMethodCustomFields = { method_id: string } | { is_invalid: boolean };
export type SmartContractMethodWrite = AbiFunction & { method_id: string } | AbiFallback | AbiReceive; export type SmartContractMethodRead = AbiFunction & SmartContractMethodCustomFields;
export type SmartContractMethodWrite = AbiFunction & SmartContractMethodCustomFields | AbiFallback | AbiReceive;
export type SmartContractMethod = SmartContractMethodRead | SmartContractMethodWrite; export type SmartContractMethod = SmartContractMethodRead | SmartContractMethodWrite;
export interface FormSubmitResultPublicClient { export interface FormSubmitResultPublicClient {
......
import React from 'react'; import React from 'react';
import { getAddress } from 'viem';
import { usePublicClient } from 'wagmi'; import { usePublicClient } from 'wagmi';
import type { FormSubmitResult, MethodCallStrategy, SmartContractMethod } from './types'; import type { FormSubmitResult, MethodCallStrategy, SmartContractMethod } from './types';
...@@ -15,7 +16,7 @@ interface Params { ...@@ -15,7 +16,7 @@ interface Params {
export default function useCallMethodPublicClient(): (params: Params) => Promise<FormSubmitResult> { export default function useCallMethodPublicClient(): (params: Params) => Promise<FormSubmitResult> {
const publicClient = usePublicClient({ chainId: Number(config.chain.id) }); const publicClient = usePublicClient({ chainId: Number(config.chain.id) });
const { address } = useAccount(); const { address: account } = useAccount();
return React.useCallback(async({ args, item, addressHash, strategy }) => { return React.useCallback(async({ args, item, addressHash, strategy }) => {
if (!('name' in item)) { if (!('name' in item)) {
...@@ -26,12 +27,14 @@ export default function useCallMethodPublicClient(): (params: Params) => Promise ...@@ -26,12 +27,14 @@ export default function useCallMethodPublicClient(): (params: Params) => Promise
throw new Error('Public Client is not defined'); throw new Error('Public Client is not defined');
} }
const address = getAddress(addressHash);
const params = { const params = {
abi: [ item ], abi: [ item ],
functionName: item.name, functionName: item.name,
args: args, args: args,
address: addressHash as `0x${ string }`, address,
account: address, account,
}; };
const result = strategy === 'read' ? await publicClient.readContract(params) : await publicClient.simulateContract(params); const result = strategy === 'read' ? await publicClient.readContract(params) : await publicClient.simulateContract(params);
...@@ -40,5 +43,5 @@ export default function useCallMethodPublicClient(): (params: Params) => Promise ...@@ -40,5 +43,5 @@ export default function useCallMethodPublicClient(): (params: Params) => Promise
data: strategy === 'read' ? result : result.result, data: strategy === 'read' ? result : result.result,
}; };
}, [ address, publicClient ]); }, [ account, publicClient ]);
} }
import type { Abi } from 'abitype'; import type { Abi } from 'abitype';
import type { AbiFunction } from 'viem';
import { toFunctionSelector } from 'viem'; import { toFunctionSelector } from 'viem';
import type { SmartContractMethodRead, SmartContractMethodWrite } from './types'; import type { SmartContractMethodCustomFields, SmartContractMethodRead, SmartContractMethodWrite } from './types';
export const getNativeCoinValue = (value: unknown) => { export const getNativeCoinValue = (value: unknown) => {
if (typeof value !== 'string') { if (typeof value !== 'string') {
...@@ -25,13 +26,25 @@ export const isWriteMethod = (method: Abi[number]): method is SmartContractMetho ...@@ -25,13 +26,25 @@ export const isWriteMethod = (method: Abi[number]): method is SmartContractMetho
(method.type === 'function' || method.type === 'fallback' || method.type === 'receive') && (method.type === 'function' || method.type === 'fallback' || method.type === 'receive') &&
!isReadMethod(method); !isReadMethod(method);
const enrichWithMethodId = (method: AbiFunction): SmartContractMethodCustomFields => {
try {
return {
method_id: toFunctionSelector(method).slice(2),
};
} catch (error) {
return {
is_invalid: true,
};
}
};
export function divideAbiIntoMethodTypes(abi: Abi): DividedAbi { export function divideAbiIntoMethodTypes(abi: Abi): DividedAbi {
return { return {
read: abi read: abi
.filter(isReadMethod) .filter(isReadMethod)
.map((method) => ({ .map((method) => ({
...method, ...method,
method_id: toFunctionSelector(method).slice(2), ...enrichWithMethodId(method),
})), })),
write: abi write: abi
.filter(isWriteMethod) .filter(isWriteMethod)
...@@ -43,7 +56,7 @@ export function divideAbiIntoMethodTypes(abi: Abi): DividedAbi { ...@@ -43,7 +56,7 @@ export function divideAbiIntoMethodTypes(abi: Abi): DividedAbi {
return { return {
...method, ...method,
method_id: toFunctionSelector(method).slice(2), ...enrichWithMethodId(method),
}; };
}), }),
}; };
......
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