Commit 8b63594b authored by tom's avatar tom

write to contract

parent c159bf91
...@@ -70,6 +70,11 @@ function makePolicyMap() { ...@@ -70,6 +70,11 @@ function makePolicyMap() {
// ad // ad
'request-global.czilladx.com', 'request-global.czilladx.com',
// walletconnect
'*.walletconnect.com',
'wss://*.bridge.walletconnect.org',
'wss://www.walletlink.org',
], ],
'script-src': [ 'script-src': [
...@@ -130,6 +135,9 @@ function makePolicyMap() { ...@@ -130,6 +135,9 @@ function makePolicyMap() {
// ad // ad
'servedbyadbutler.com', 'servedbyadbutler.com',
'cdn.coinzilla.io', 'cdn.coinzilla.io',
// walletconnect
'*.walletconnect.com',
], ],
'font-src': [ 'font-src': [
......
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import {
EthereumClient,
modalConnectors,
walletConnectProvider,
} from '@web3modal/ethereum';
import { Web3Modal } from '@web3modal/react';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { Chain } from 'wagmi';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
...@@ -23,39 +15,6 @@ import theme from 'theme'; ...@@ -23,39 +15,6 @@ import theme from 'theme';
import AppError from 'ui/shared/AppError/AppError'; import AppError from 'ui/shared/AppError/AppError';
import ErrorBoundary from 'ui/shared/ErrorBoundary'; import ErrorBoundary from 'ui/shared/ErrorBoundary';
export const poa: Chain = {
id: 99,
name: 'POA',
network: 'poa',
nativeCurrency: {
decimals: 18,
name: 'POA',
symbol: 'POA',
},
rpcUrls: {
'default': { http: [ 'https://core.poa.network' ] },
},
blockExplorers: {
'default': { name: 'Blockscout', url: 'https://blockscout.com/poa/core' },
},
};
const chains = [ poa ];
const PROJECT_ID = 'b4ed81be141093911032944632465175';
// Wagmi client
const { provider } = configureChains(chains, [
walletConnectProvider({ projectId: PROJECT_ID }),
]);
const wagmiClient = createClient({
autoConnect: true,
connectors: modalConnectors({ appName: 'web3Modal', chains }),
provider,
});
// Web3Modal Ethereum Client
const ethereumClient = new EthereumClient(wagmiClient, chains);
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
useConfigSentry(); useConfigSentry();
const [ queryClient ] = useState(() => new QueryClient({ const [ queryClient ] = useState(() => new QueryClient({
...@@ -102,14 +61,7 @@ function MyApp({ Component, pageProps }: AppProps) { ...@@ -102,14 +61,7 @@ function MyApp({ Component, pageProps }: AppProps) {
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<ScrollDirectionProvider> <ScrollDirectionProvider>
<SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2` }> <SocketProvider url={ `${ appConfig.api.socket }${ appConfig.api.basePath }/socket/v2` }>
<WagmiConfig client={ wagmiClient }> <Component { ...pageProps }/>
<Component { ...pageProps }/>
</WagmiConfig>
<Web3Modal
projectId={ PROJECT_ID }
ethereumClient={ ethereumClient }
themeZIndex={ 1200 }
/>
</SocketProvider> </SocketProvider>
</ScrollDirectionProvider> </ScrollDirectionProvider>
<ReactQueryDevtools/> <ReactQueryDevtools/>
......
import type { Abi } from 'abitype';
export interface SmartContract { export interface SmartContract {
deployed_bytecode: string | null; deployed_bytecode: string | null;
creation_bytecode: string | null; creation_bytecode: string | null;
is_self_destructed: boolean; is_self_destructed: boolean;
abi: Array<Record<string, unknown>> | null; abi: Abi | null;
compiler_version: string | null; compiler_version: string | null;
evm_version: string | null; evm_version: string | null;
optimization_enabled: boolean | null; optimization_enabled: boolean | null;
...@@ -11,6 +13,7 @@ export interface SmartContract { ...@@ -11,6 +13,7 @@ export interface SmartContract {
verified_at: string | null; verified_at: string | null;
is_verified: boolean | null; is_verified: boolean | null;
source_code: string | null; source_code: string | null;
constructor_args: string | null;
can_be_visualized_via_sol2uml: boolean | null; can_be_visualized_via_sol2uml: boolean | null;
} }
...@@ -19,9 +22,10 @@ export interface SmartContractMethodBase { ...@@ -19,9 +22,10 @@ export interface SmartContractMethodBase {
outputs: Array<SmartContractMethodOutput>; outputs: Array<SmartContractMethodOutput>;
constant: boolean; constant: boolean;
name: string; name: string;
stateMutability: string; stateMutability: 'view' | 'nonpayable' | 'payable';
type: 'function'; type: 'function';
payable: boolean; payable: boolean;
error?: string;
} }
export interface SmartContractReadMethod extends SmartContractMethodBase { export interface SmartContractReadMethod extends SmartContractMethodBase {
...@@ -29,19 +33,23 @@ export interface SmartContractReadMethod extends SmartContractMethodBase { ...@@ -29,19 +33,23 @@ export interface SmartContractReadMethod extends SmartContractMethodBase {
} }
export interface SmartContractWriteFallback { export interface SmartContractWriteFallback {
payable: true;
stateMutability: 'payable'; stateMutability: 'payable';
type: 'fallback'; type: 'fallback';
} }
export type SmartContractWriteMethod = SmartContractMethodBase | SmartContractWriteFallback; export interface SmartContractWriteReceive {
stateMutability: 'payable';
type: 'receive';
}
export type SmartContractWriteMethod = SmartContractMethodBase | SmartContractWriteFallback | SmartContractWriteReceive;
export type SmartContractMethod = SmartContractReadMethod | SmartContractWriteMethod; export type SmartContractMethod = SmartContractReadMethod | SmartContractWriteMethod;
export interface SmartContractMethodInput { export interface SmartContractMethodInput {
internalType: string; internalType: string;
name: string; name: string;
type: string; type: 'address' | 'uint256' | 'bool';
} }
export interface SmartContractMethodOutput extends SmartContractMethodInput { export interface SmartContractMethodOutput extends SmartContractMethodInput {
......
import { useColorModeValue } from '@chakra-ui/react';
import {
EthereumClient,
modalConnectors,
walletConnectProvider,
} from '@web3modal/ethereum';
import { Web3Modal } from '@web3modal/react';
import React from 'react'; import React from 'react';
import type { Chain } from 'wagmi';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import type { RoutedSubTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedSubTab } from 'ui/shared/RoutedTabs/types';
...@@ -13,11 +22,53 @@ const TAB_LIST_PROPS = { ...@@ -13,11 +22,53 @@ const TAB_LIST_PROPS = {
columnGap: 3, columnGap: 3,
}; };
export const poa: Chain = {
id: 99,
name: 'POA',
network: 'poa',
nativeCurrency: {
decimals: 18,
name: 'POA',
symbol: 'POA',
},
rpcUrls: {
'default': { http: [ 'https://core.poa.network' ] },
},
blockExplorers: {
'default': { name: 'Blockscout', url: 'https://blockscout.com/poa/core' },
},
};
const chains = [ poa ];
const PROJECT_ID = 'b4ed81be141093911032944632465175';
// Wagmi client
const { provider } = configureChains(chains, [
walletConnectProvider({ projectId: PROJECT_ID }),
]);
const wagmiClient = createClient({
autoConnect: true,
connectors: modalConnectors({ appName: 'web3Modal', chains }),
provider,
});
// Web3Modal Ethereum Client
const ethereumClient = new EthereumClient(wagmiClient, chains);
const AddressContract = ({ tabs }: Props) => { const AddressContract = ({ tabs }: Props) => {
return ( return (
<ContractContextProvider> <WagmiConfig client={ wagmiClient }>
<RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/> <ContractContextProvider>
</ContractContextProvider> <RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/>
</ContractContextProvider>
<Web3Modal
projectId={ PROJECT_ID }
ethereumClient={ ethereumClient }
themeZIndex={ 1200 }
themeMode={ useColorModeValue('light', 'dark') }
themeBackground="themeColor"
/>
</WagmiConfig>
); );
}; };
......
...@@ -77,6 +77,12 @@ const ContractCode = () => { ...@@ -77,6 +77,12 @@ const ContractCode = () => {
</Grid> </Grid>
) } ) }
<Flex flexDir="column" rowGap={ 6 }> <Flex flexDir="column" rowGap={ 6 }>
{ data.constructor_args && (
<RawDataSnippet
data={ data.constructor_args }
title="Constructor Arguments"
/>
) }
{ data.source_code && ( { data.source_code && (
<DynamicContractSourceCode <DynamicContractSourceCode
data={ data.source_code } data={ data.source_code }
......
import { Alert, Button, chakra } from '@chakra-ui/react'; import { Alert, Button, Flex } from '@chakra-ui/react';
import { useWeb3Modal } from '@web3modal/react'; import { useWeb3Modal } from '@web3modal/react';
import React from 'react'; import React from 'react';
import { useAccount, useDisconnect } from 'wagmi'; import { useAccount, useDisconnect } from 'wagmi';
import useIsMobile from 'lib/hooks/useIsMobile';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
const ContractConnectWallet = () => { const ContractConnectWallet = () => {
const { open } = useWeb3Modal(); const { open } = useWeb3Modal();
const { address, isDisconnected } = useAccount(); const { address, isDisconnected } = useAccount();
const { disconnect } = useDisconnect(); const { disconnect } = useDisconnect();
const isMobile = useIsMobile();
const handleConnect = React.useCallback(() => { const handleConnect = React.useCallback(() => {
open(); open();
...@@ -29,12 +32,14 @@ const ContractConnectWallet = () => { ...@@ -29,12 +32,14 @@ const ContractConnectWallet = () => {
} }
return ( return (
<> <Flex columnGap={ 3 } rowGap={ 3 } alignItems={{ base: 'flex-start', lg: 'center' }} flexDir={{ base: 'column', lg: 'row' }}>
<span>Connected to </span> <Flex alignItems="center">
<AddressIcon address={{ hash: address, is_contract: false }} mx={ 2 }/> <span>Connected to </span>
<chakra.span fontWeight={ 600 }>{ address }</chakra.span> <AddressIcon address={{ hash: address, is_contract: false }} mx={ 2 }/>
<Button ml={ 3 } onClick={ handleDisconnect } size="sm" variant="outline">Disconnect</Button> <AddressLink fontWeight={ 600 } hash={ address } truncation={ isMobile ? 'constant' : 'dynamic' }/>
</> </Flex>
<Button onClick={ handleDisconnect } size="sm" variant="outline">Disconnect</Button>
</Flex>
); );
})(); })();
......
...@@ -43,11 +43,14 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ...@@ -43,11 +43,14 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit,
const [ isLoading, setLoading ] = React.useState(false); const [ isLoading, setLoading ] = React.useState(false);
const inputs = React.useMemo(() => { const inputs = React.useMemo(() => {
return data.payable && (!('inputs' in data) || data.inputs.length === 0) ? [ { return [
name: 'value', ...('inputs' in data ? data.inputs : []),
type: appConfig.network.currency.symbol, ...(data.stateMutability === 'payable' ? [ {
internalType: appConfig.network.currency.symbol, name: 'value',
} as SmartContractMethodInput ] : data.inputs; type: appConfig.network.currency.symbol,
internalType: appConfig.network.currency.symbol,
} as SmartContractMethodInput ] : []),
];
}, [ data ]); }, [ data ]);
const { control, handleSubmit, setValue } = useForm<MethodFormFields>({ const { control, handleSubmit, setValue } = useForm<MethodFormFields>({
...@@ -67,7 +70,7 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit, ...@@ -67,7 +70,7 @@ const ContractMethodCallable = <T extends SmartContractMethod>({ data, onSubmit,
setLoading(false); setLoading(false);
}) })
.catch((error) => { .catch((error) => {
setResult(error); setResult('error' in error ? error.error : error);
setLoading(false); setLoading(false);
}); });
}, [ onSubmit, data, inputs ]); }, [ onSubmit, data, inputs ]);
......
...@@ -7,6 +7,7 @@ import type { SmartContractMethodOutput } from 'types/api/contract'; ...@@ -7,6 +7,7 @@ import type { SmartContractMethodOutput } from 'types/api/contract';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import AddressLink from 'ui/shared/address/AddressLink';
interface Props { interface Props {
data: SmartContractMethodOutput; data: SmartContractMethodOutput;
...@@ -31,9 +32,17 @@ const ContractMethodStatic = ({ data }: Props) => { ...@@ -31,9 +32,17 @@ const ContractMethodStatic = ({ data }: Props) => {
} }
}, [ data.value ]); }, [ data.value ]);
const content = (() => {
if (data.type === 'address' && data.value) {
return <AddressLink hash={ data.value }/>;
}
return <chakra.span wordBreak="break-all">({ data.type }): { value }</chakra.span>;
})();
return ( return (
<Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 2 }> <Flex flexDir={{ base: 'column', lg: 'row' }} columnGap={ 2 } rowGap={ 2 }>
<chakra.span wordBreak="break-all">({ data.type }): { value }</chakra.span> { content }
{ isBigInt && <Checkbox onChange={ handleCheckboxChange }>{ label }</Checkbox> } { isBigInt && <Checkbox onChange={ handleCheckboxChange }>{ label }</Checkbox> }
</Flex> </Flex>
); );
......
...@@ -43,7 +43,7 @@ const ContractMethodsAccordion = <T extends SmartContractMethod>({ data, renderC ...@@ -43,7 +43,7 @@ const ContractMethodsAccordion = <T extends SmartContractMethod>({ data, renderC
<h2> <h2>
<AccordionButton px={ 0 } py={ 3 } _hover={{ bgColor: 'inherit' }}> <AccordionButton px={ 0 } py={ 3 } _hover={{ bgColor: 'inherit' }}>
<Box as="span" fontFamily="heading" fontWeight={ 500 } fontSize="lg" mr={ 1 }> <Box as="span" fontFamily="heading" fontWeight={ 500 } fontSize="lg" mr={ 1 }>
{ index + 1 }. { item.type === 'fallback' ? 'fallback' : item.name } { index + 1 }. { item.type === 'fallback' || item.type === 'receive' ? item.type : item.name }
</Box> </Box>
{ item.type === 'fallback' && ( { item.type === 'fallback' && (
<Tooltip <Tooltip
...@@ -58,6 +58,21 @@ const ContractMethodsAccordion = <T extends SmartContractMethod>({ data, renderC ...@@ -58,6 +58,21 @@ const ContractMethodsAccordion = <T extends SmartContractMethod>({ data, renderC
</Box> </Box>
</Tooltip> </Tooltip>
) } ) }
{ item.type === 'receive' && (
<Tooltip
label={ `The receive function is executed on a call to the contract with empty calldata.
This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()).
If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer.
If neither a receive Ether nor a payable fallback function is present,
the contract cannot receive Ether through regular transactions and throws an exception.` }
placement="top"
maxW="320px"
>
<Box cursor="pointer" display="inherit">
<Icon as={ infoIcon } boxSize={ 5 }/>
</Box>
</Tooltip>
) }
<AccordionIcon/> <AccordionIcon/>
</AccordionButton> </AccordionButton>
</h2> </h2>
......
...@@ -76,6 +76,10 @@ const ContractRead = ({ isProxy }: Props) => { ...@@ -76,6 +76,10 @@ const ContractRead = ({ isProxy }: Props) => {
}, [ resultBgColor ]); }, [ resultBgColor ]);
const renderContent = React.useCallback((item: SmartContractReadMethod, index: number, id: number) => { const renderContent = React.useCallback((item: SmartContractReadMethod, index: number, id: number) => {
if (item.error) {
return <Alert status="error" fontSize="sm">{ item.error }</Alert>;
}
if (item.outputs.some(({ value }) => value)) { if (item.outputs.some(({ value }) => value)) {
return ( return (
<Flex flexDir="column" rowGap={ 1 }> <Flex flexDir="column" rowGap={ 1 }>
......
import { Alert } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { ContractMethodWriteResult } from './types';
import type { SmartContractWriteMethod } from 'types/api/contract'; import type { SmartContractWriteMethod } from 'types/api/contract';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
...@@ -11,6 +13,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert'; ...@@ -11,6 +13,7 @@ import DataFetchAlert from 'ui/shared/DataFetchAlert';
import { useContractContext } from './context'; import { useContractContext } from './context';
import ContractConnectWallet from './ContractConnectWallet'; import ContractConnectWallet from './ContractConnectWallet';
import ContractMethodCallable from './ContractMethodCallable'; import ContractMethodCallable from './ContractMethodCallable';
import ContractWriteResult from './ContractWriteResult';
interface Props { interface Props {
isProxy?: boolean; isProxy?: boolean;
...@@ -35,7 +38,7 @@ const ContractWrite = ({ isProxy }: Props) => { ...@@ -35,7 +38,7 @@ const ContractWrite = ({ isProxy }: Props) => {
return; return;
} }
if (item.type === 'fallback') { if (item.type === 'fallback' || item.type === 'receive') {
return; return;
} }
...@@ -45,13 +48,21 @@ const ContractWrite = ({ isProxy }: Props) => { ...@@ -45,13 +48,21 @@ const ContractWrite = ({ isProxy }: Props) => {
}); });
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('__>__', { result }); console.log('__>__', { result, args });
return [ [ 'string', 'this is mock' ] ]; return { hash: result.hash as string };
}, [ contract ]); }, [ contract ]);
const renderResult = React.useCallback(() => { const renderResult = React.useCallback((item: SmartContractWriteMethod, result: ContractMethodWriteResult) => {
return <span>result</span>; if (!result || 'message' in result) {
return (
<Alert status="error" mt={ 3 } p={ 4 } borderRadius="md" fontSize="sm" wordBreak="break-all">
{ result ? result.message : 'No result' }
</Alert>
);
}
return <ContractWriteResult hash={ result.hash as `0x${ string }` }/>;
}, []); }, []);
const renderContent = React.useCallback((item: SmartContractWriteMethod, index: number, id: number) => { const renderContent = React.useCallback((item: SmartContractWriteMethod, index: number, id: number) => {
......
import { Alert, Box, Link } from '@chakra-ui/react';
import React from 'react';
import { useWaitForTransaction } from 'wagmi';
import link from 'lib/link/link';
interface Props {
hash: `0x${ string }`;
}
const ContractWriteResult = ({ hash }: Props) => {
const txInfo = useWaitForTransaction({
hash,
});
// eslint-disable-next-line no-console
console.log('__>__ txInfo', txInfo);
return (
<>
<Alert status="info" mt={ 3 } p={ 4 } borderRadius="md" fontSize="sm" flexDir="column" alignItems="flex-start">
<Box>Tx hash: <Link href={ link('tx', { id: hash }) }>{ hash }</Link></Box>
<Box>Status: { txInfo.status }</Box>
</Alert>
{ txInfo.error && <Alert status="error" mt={ 3 } p={ 4 } borderRadius="md" fontSize="sm" wordBreak="break-all">{ txInfo.error.message }</Alert> }
</>
);
};
export default React.memo(ContractWriteResult);
import type { ExternalProvider } from '@ethersproject/providers';
import type { Contract } from 'ethers'; import type { Contract } from 'ethers';
import { ethers } from 'ethers';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import { useContract, useProvider, useSigner } from 'wagmi';
import useApiQuery from 'lib/api/useApiQuery'; import useApiQuery from 'lib/api/useApiQuery';
...@@ -15,11 +14,12 @@ type TContractContext = Contract; ...@@ -15,11 +14,12 @@ type TContractContext = Contract;
const ContractContext = React.createContext<TContractContext | null>(null); const ContractContext = React.createContext<TContractContext | null>(null);
export function ContractContextProvider({ children }: ProviderProps) { export function ContractContextProvider({ children }: ProviderProps) {
const [ contract, setContract ] = React.useState<TContractContext | null>(null);
const router = useRouter(); const router = useRouter();
const addressHash = router.query.id?.toString(); const provider = useProvider();
const { data: signer } = useSigner();
const { data } = useApiQuery('contract', { const addressHash = router.query.id?.toString();
const { data: contractInfo } = useApiQuery('contract', {
pathParams: { id: addressHash }, pathParams: { id: addressHash },
queryOptions: { queryOptions: {
enabled: Boolean(addressHash), enabled: Boolean(addressHash),
...@@ -27,18 +27,11 @@ export function ContractContextProvider({ children }: ProviderProps) { ...@@ -27,18 +27,11 @@ export function ContractContextProvider({ children }: ProviderProps) {
}, },
}); });
React.useEffect(() => { const contract = useContract({
if (!addressHash || !data?.abi) { address: addressHash,
return; abi: contractInfo?.abi || undefined,
} signerOrProvider: signer || provider,
});
const provider = new ethers.providers.Web3Provider(window.ethereum as unknown as ExternalProvider);
const signer = provider.getSigner();
const contract = new ethers.Contract(addressHash, data.abi, provider);
const contractWithSigner = contract.connect(signer);
setContract(contractWithSigner);
}, [ data, addressHash ]);
return ( return (
<ContractContext.Provider value={ contract }> <ContractContext.Provider value={ contract }>
......
...@@ -5,7 +5,8 @@ import type { ResourceError } from 'lib/api/resources'; ...@@ -5,7 +5,8 @@ import type { ResourceError } from 'lib/api/resources';
export type MethodFormFields = Record<string, string>; export type MethodFormFields = Record<string, string>;
export type ContractMethodReadResult = SmartContractQueryMethodRead | ResourceError; export type ContractMethodReadResult = SmartContractQueryMethodRead | ResourceError;
export type ContractMethodWriteResult = unknown;
export type ContractMethodWriteResult = Error | { hash: string } | undefined;
export type ContractMethodCallResult<T extends SmartContractMethod> = export type ContractMethodCallResult<T extends SmartContractMethod> =
T extends { method_id: string } ? ContractMethodReadResult : ContractMethodWriteResult; T extends { method_id: string } ? ContractMethodReadResult : ContractMethodWriteResult;
...@@ -12,6 +12,7 @@ const TxRevertReason = (props: Props) => { ...@@ -12,6 +12,7 @@ const TxRevertReason = (props: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
if ('raw' in props) { if ('raw' in props) {
const decoded = hexToUtf8(props.raw);
return ( return (
<Grid <Grid
bgColor={ bgColor } bgColor={ bgColor }
...@@ -25,8 +26,12 @@ const TxRevertReason = (props: Props) => { ...@@ -25,8 +26,12 @@ const TxRevertReason = (props: Props) => {
> >
<GridItem fontWeight={ 500 }>Raw:</GridItem> <GridItem fontWeight={ 500 }>Raw:</GridItem>
<GridItem>{ props.raw }</GridItem> <GridItem>{ props.raw }</GridItem>
<GridItem fontWeight={ 500 }>Decoded:</GridItem> { decoded.replace(/\s|\0/g, '') && (
<GridItem>{ hexToUtf8(props.raw) }</GridItem> <>
<GridItem fontWeight={ 500 }>Decoded:</GridItem>
<GridItem>{ decoded }</GridItem>
</>
) }
</Grid> </Grid>
); );
} }
......
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