Commit 03c9e582 authored by tom's avatar tom

contract method form

parent 3a9f3d18
...@@ -22,6 +22,13 @@ const globalCss: SystemConfig['globalCss'] = { ...@@ -22,6 +22,13 @@ const globalCss: SystemConfig['globalCss'] = {
form: { form: {
w: '100%', w: '100%',
}, },
input: {
// hide number input arrows in Google Chrome
'&::-webkit-outer-spin-button, &::-webkit-inner-spin-button': {
WebkitAppearance: 'none',
margin: 0,
},
},
...recaptcha, ...recaptcha,
...scrollbar, ...scrollbar,
...addressEntity, ...addressEntity,
......
...@@ -12,7 +12,7 @@ export const recipe = defineSlotRecipe({ ...@@ -12,7 +12,7 @@ export const recipe = defineSlotRecipe({
display: 'flex', display: 'flex',
width: '100%', width: '100%',
position: 'relative', position: 'relative',
gap: '1.5', gap: '1',
}, },
label: { label: {
display: 'flex', display: 'flex',
...@@ -36,11 +36,11 @@ export const recipe = defineSlotRecipe({ ...@@ -36,11 +36,11 @@ export const recipe = defineSlotRecipe({
fontWeight: 'medium', fontWeight: 'medium',
gap: '1', gap: '1',
color: 'input.fg.error', color: 'input.fg.error',
textStyle: 'xs', textStyle: 'sm',
}, },
helperText: { helperText: {
color: 'fg.muted', color: 'fg.muted',
textStyle: 'xs', textStyle: 'sm',
}, },
}, },
......
...@@ -6,7 +6,7 @@ export const recipe = defineRecipe({ ...@@ -6,7 +6,7 @@ export const recipe = defineRecipe({
minWidth: '0', minWidth: '0',
outline: '0', outline: '0',
position: 'relative', position: 'relative',
appearance: 'none', appearance: 'textfield',
textAlign: 'start', textAlign: 'start',
borderRadius: 'base', borderRadius: 'base',
height: 'var(--input-height)', height: 'var(--input-height)',
...@@ -27,7 +27,7 @@ export const recipe = defineRecipe({ ...@@ -27,7 +27,7 @@ export const recipe = defineRecipe({
variants: { variants: {
size: { size: {
sm: { sm: {
textStyle: 'sm', textStyle: 'md',
px: '2', px: '2',
'--input-height': 'sizes.8', '--input-height': 'sizes.8',
}, },
......
import { Accordion, Box, Flex, Link } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { range } from 'es-toolkit'; import { range } from 'es-toolkit';
import React from 'react'; import React from 'react';
...@@ -7,7 +7,8 @@ import type { SmartContractMethod } from './types'; ...@@ -7,7 +7,8 @@ import type { SmartContractMethod } from './types';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import { apos } from 'lib/html-entities'; import { apos } from 'lib/html-entities';
import LinkInternal from 'ui/shared/links/LinkInternal'; import { AccordionRoot } from 'toolkit/chakra/accordion';
import { Link } from 'toolkit/chakra/link';
import ContractAbiItem from './ContractAbiItem'; import ContractAbiItem from './ContractAbiItem';
import useFormSubmit from './useFormSubmit'; import useFormSubmit from './useFormSubmit';
...@@ -22,15 +23,15 @@ interface Props { ...@@ -22,15 +23,15 @@ interface Props {
} }
const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Props) => { const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Props) => {
const [ expandedSections, setExpandedSections ] = React.useState<Array<number>>(abi.length === 1 ? [ 0 ] : []); const [ expandedSections, setExpandedSections ] = React.useState<Array<string>>(abi.length === 1 ? [ '0' ] : []);
const [ id, setId ] = React.useState(0); const [ id, setId ] = React.useState(0);
useScrollToMethod(abi, setExpandedSections); useScrollToMethod(abi, setExpandedSections);
const handleFormSubmit = useFormSubmit({ addressHash }); const handleFormSubmit = useFormSubmit({ addressHash });
const handleAccordionStateChange = React.useCallback((newValue: Array<number>) => { const handleAccordionStateChange = React.useCallback(({ value }: { value: Array<string> }) => {
setExpandedSections(newValue); setExpandedSections(value);
}, []); }, []);
const handleExpandAll = React.useCallback(() => { const handleExpandAll = React.useCallback(() => {
...@@ -39,7 +40,7 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro ...@@ -39,7 +40,7 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro
} }
if (expandedSections.length < abi.length) { if (expandedSections.length < abi.length) {
setExpandedSections(range(0, abi.length)); setExpandedSections(range(0, abi.length).map(String));
} else { } else {
setExpandedSections([]); setExpandedSections([]);
} }
...@@ -62,13 +63,14 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro ...@@ -62,13 +63,14 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro
) } ) }
<Link onClick={ handleReset } ml={ 3 }>Reset</Link> <Link onClick={ handleReset } ml={ 3 }>Reset</Link>
</Flex> </Flex>
<Accordion allowMultiple position="relative" onChange={ handleAccordionStateChange } index={ expandedSections }> <AccordionRoot multiple lazyMount position="relative" onValueChange={ handleAccordionStateChange } value={ expandedSections }>
{ abi.map((item, index) => ( { abi.map((item, index) => (
<ContractAbiItem <ContractAbiItem
key={ index } key={ index }
id={ id } id={ id }
index={ index } index={ index }
data={ item } data={ item }
isOpen={ expandedSections.includes(String(index)) }
isVisible={ !visibleItems || visibleItems.includes(index) } isVisible={ !visibleItems || visibleItems.includes(index) }
addressHash={ addressHash } addressHash={ addressHash }
sourceAddress={ sourceAddress } sourceAddress={ sourceAddress }
...@@ -76,18 +78,18 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro ...@@ -76,18 +78,18 @@ const ContractAbi = ({ abi, addressHash, sourceAddress, tab, visibleItems }: Pro
onSubmit={ handleFormSubmit } onSubmit={ handleFormSubmit }
/> />
)) } )) }
</Accordion> </AccordionRoot>
{ !hasVisibleItems && ( { !hasVisibleItems && (
<div> <div>
<div>Couldn{ apos }t find any method that matches your query.</div> <div>Couldn{ apos }t find any method that matches your query.</div>
<div> <div>
You can use custom ABI for this contract without verifying the contract in the{ ' ' } You can use custom ABI for this contract without verifying the contract in the{ ' ' }
<LinkInternal <Link
href={ route({ pathname: '/address/[hash]', query: { hash: addressHash, tab: 'read_write_custom_methods' } }) } href={ route({ pathname: '/address/[hash]', query: { hash: addressHash, tab: 'read_write_custom_methods' } }) }
scroll={ false } scroll={ false }
> >
Custom ABI Custom ABI
</LinkInternal> </Link>
{ ' ' }tab. { ' ' }tab.
</div> </div>
</div> </div>
......
import { AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Alert, Box, Tag } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { Element } from 'react-scroll'; import { Element } from 'react-scroll';
...@@ -7,6 +7,9 @@ import type { FormSubmitHandler, SmartContractMethod } from './types'; ...@@ -7,6 +7,9 @@ import type { FormSubmitHandler, SmartContractMethod } from './types';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import { AccordionItem, AccordionItemContent, AccordionItemTrigger } from 'toolkit/chakra/accordion';
import { Alert } from 'toolkit/chakra/alert';
import { Badge } from 'toolkit/chakra/badge';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import Hint from 'ui/shared/Hint'; import Hint from 'ui/shared/Hint';
...@@ -23,9 +26,10 @@ interface Props { ...@@ -23,9 +26,10 @@ interface Props {
tab: string; tab: string;
onSubmit: FormSubmitHandler; onSubmit: FormSubmitHandler;
isVisible?: boolean; isVisible?: boolean;
isOpen: boolean;
} }
const ContractAbiItem = ({ data, index, id, addressHash, sourceAddress, tab, onSubmit, isVisible = true }: Props) => { const ContractAbiItem = ({ data, index, id, addressHash, sourceAddress, tab, onSubmit, isVisible = true, isOpen }: Props) => {
const [ attempt, setAttempt ] = React.useState(0); const [ attempt, setAttempt ] = React.useState(0);
const url = React.useMemo(() => { const url = React.useMemo(() => {
...@@ -47,74 +51,76 @@ const ContractAbiItem = ({ data, index, id, addressHash, sourceAddress, tab, onS ...@@ -47,74 +51,76 @@ const ContractAbiItem = ({ data, index, id, addressHash, sourceAddress, tab, onS
const isRead = isReadMethod(data); const isRead = isReadMethod(data);
return ( return (
<AccordionItem as="section" _first={{ borderTopWidth: 0 }} _last={{ borderBottomWidth: 0 }} display={ isVisible ? 'block' : 'none' }> <AccordionItem
{ ({ isExpanded }) => ( as="section"
<> value={ String(index) }
<Element as="h2" name={ getElementName(data) }> _first={{ borderTopWidth: 0 }}
<AccordionButton _last={{ borderBottomWidth: 0 }}
px={ 0 } display={ isVisible ? 'block' : 'none' }
py={ 3 } >
_hover={{ bgColor: 'inherit' }} <Element as="h2" name={ getElementName(data) }>
wordBreak="break-all" <AccordionItemTrigger
textAlign="left" px={ 0 }
as="div" py={ 3 }
cursor="pointer" _hover={{ bgColor: 'inherit' }}
display="flex" wordBreak="break-all"
alignItems="center" textAlign="left"
columnGap={ 2 } cursor="pointer"
> display="flex"
<CopyToClipboard text={ url } type="link" ml={ 0 } color="text_secondary"/> alignItems="center"
<Box as="div" fontWeight={ 500 } display="flex" alignItems="center"> columnGap={ 2 }
{ index + 1 }. { data.type === 'fallback' || data.type === 'receive' ? data.type : data.name } >
{ data.type === 'fallback' && ( <CopyToClipboard text={ url } type="link" ml={ 0 } color="text_secondary" as="div"/>
<Hint <Box fontWeight={ 500 } display="flex" alignItems="center">
label={ { index + 1 }. { data.type === 'fallback' || data.type === 'receive' ? data.type : data.name }
`The fallback function is executed on a call to the contract if none of the other functions match { data.type === 'fallback' && (
the given function signature, or if no data was supplied at all and there is no receive Ether function. <Hint
label={
`The fallback function is executed on a call to the contract if none of the other functions match
the given function signature, or if no data was supplied at all and there is no receive Ether function.
The fallback function always receives data, but in order to also receive Ether it must be marked payable.` The fallback function always receives data, but in order to also receive Ether it must be marked payable.`
} }
ml={ 1 } ml={ 1 }
/> as="div"
) } />
{ data.type === 'receive' && ( ) }
<Hint { data.type === 'receive' && (
label={ <Hint
`The receive function is executed on a call to the contract with empty calldata. label={
This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). `The receive function is executed on a call to the contract with empty calldata.
If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()).
If neither a receive Ether nor a payable fallback function is present, 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.` the contract cannot receive Ether through regular transactions and throws an exception.`
} }
ml={ 1 } ml={ 1 }
/> as="div"
) }
</Box>
<Tag colorScheme={ isRead ? 'black-purple' : 'black-blue' } flexShrink={ 0 }>{ isRead ? 'read' : 'write' }</Tag>
{ 'method_id' in data && (
<Tag display="inline-flex" alignItems="center" flexShrink={ 0 }>
{ data.method_id }
<CopyToClipboard text={ data.method_id }/>
</Tag>
) }
<AccordionIcon transform={ isExpanded ? 'rotate(0deg)' : 'rotate(-90deg)' } color="gray.500"/>
</AccordionButton>
</Element>
<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
key={ id + '_' + index + '_' + attempt }
data={ data }
attempt={ attempt }
onSubmit={ onSubmit }
onReset={ handleReset }
isOpen={ isExpanded }
/> />
) } ) }
</AccordionPanel> </Box>
</> <Badge colorPalette={ isRead ? 'purple_alt' : 'blue_alt' } flexShrink={ 0 }>{ isRead ? 'read' : 'write' }</Badge>
) } { 'method_id' in data && (
<Badge display="inline-flex" alignItems="center" flexShrink={ 0 }>
{ data.method_id }
<CopyToClipboard text={ data.method_id } as="div"/>
</Badge>
) }
</AccordionItemTrigger>
</Element>
<AccordionItemContent 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
key={ id + '_' + index + '_' + attempt }
data={ data }
attempt={ attempt }
onSubmit={ onSubmit }
onReset={ handleReset }
isOpen={ isOpen }
/>
) }
</AccordionItemContent>
</AccordionItem> </AccordionItem>
); );
}; };
......
...@@ -55,7 +55,7 @@ const ContractConnectWallet = ({ isLoading }: Props) => { ...@@ -55,7 +55,7 @@ const ContractConnectWallet = ({ isLoading }: Props) => {
return ( return (
<Skeleton loading={ isLoading }> <Skeleton loading={ isLoading }>
<Alert status={ web3Wallet.address ? 'success' : 'warning' }> <Alert status={ web3Wallet.address ? 'success' : 'warning' } descriptionProps={{ alignItems: 'center' }}>
{ content } { content }
</Alert> </Alert>
</Skeleton> </Skeleton>
......
...@@ -93,9 +93,9 @@ const ContractMethodsCustom = ({ isLoading: isLoadingProp }: Props) => { ...@@ -93,9 +93,9 @@ const ContractMethodsCustom = ({ isLoading: isLoadingProp }: Props) => {
onChange={ filters.onChange } onChange={ filters.onChange }
isLoading={ isLoading } isLoading={ isLoading }
/> />
{ /* <ContractMethodsContainer isLoading={ isLoading } isEmpty={ abi.length === 0 } type={ filters.methodType }> <ContractMethodsContainer isLoading={ isLoading } isEmpty={ abi.length === 0 } type={ filters.methodType }>
<ContractAbi abi={ abi } tab={ tab } addressHash={ addressHash } visibleItems={ filters.visibleItems }/> <ContractAbi abi={ abi } tab={ tab } addressHash={ addressHash } visibleItems={ filters.visibleItems }/>
</ContractMethodsContainer> */ } </ContractMethodsContainer>
</> </>
) : ( ) : (
<> <>
......
...@@ -58,7 +58,7 @@ const ContractMethodsProxy = ({ implementations, isLoading: isInitialLoading }: ...@@ -58,7 +58,7 @@ const ContractMethodsProxy = ({ implementations, isLoading: isInitialLoading }:
isLoading={ isInitialLoading } isLoading={ isInitialLoading }
/> />
</div> </div>
{ /* <ContractMethodsContainer <ContractMethodsContainer
key={ selectedItem.address } key={ selectedItem.address }
isLoading={ isInitialLoading || contractQuery.isPending } isLoading={ isInitialLoading || contractQuery.isPending }
isEmpty={ abi.length === 0 } isEmpty={ abi.length === 0 }
...@@ -72,7 +72,7 @@ const ContractMethodsProxy = ({ implementations, isLoading: isInitialLoading }: ...@@ -72,7 +72,7 @@ const ContractMethodsProxy = ({ implementations, isLoading: isInitialLoading }:
visibleItems={ filters.visibleItems } visibleItems={ filters.visibleItems }
sourceAddress={ selectedItem.address } sourceAddress={ selectedItem.address }
/> />
</ContractMethodsContainer> */ } </ContractMethodsContainer>
</Flex> </Flex>
); );
}; };
......
...@@ -36,9 +36,9 @@ const ContractMethodsRegular = ({ abi, isLoading }: Props) => { ...@@ -36,9 +36,9 @@ const ContractMethodsRegular = ({ abi, isLoading }: Props) => {
onChange={ filters.onChange } onChange={ filters.onChange }
isLoading={ isLoading } isLoading={ isLoading }
/> />
{ /* <ContractMethodsContainer isLoading={ isLoading } isEmpty={ formattedAbi.length === 0 } type={ filters.methodType }> <ContractMethodsContainer isLoading={ isLoading } isEmpty={ formattedAbi.length === 0 } type={ filters.methodType }>
<ContractAbi abi={ formattedAbi } tab={ tab } addressHash={ addressHash } visibleItems={ filters.visibleItems }/> <ContractAbi abi={ formattedAbi } tab={ tab } addressHash={ addressHash } visibleItems={ filters.visibleItems }/>
</ContractMethodsContainer> */ } </ContractMethodsContainer>
</Flex> </Flex>
); );
}; };
......
import { Button, Tooltip } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
import { Button } from 'toolkit/chakra/button';
import { Tooltip } from 'toolkit/chakra/tooltip';
interface Props { interface Props {
onClick: (address: string) => void; onClick: (address: string) => void;
...@@ -16,16 +17,15 @@ const ContractMethodAddressButton = ({ onClick, isDisabled }: Props) => { ...@@ -16,16 +17,15 @@ const ContractMethodAddressButton = ({ onClick, isDisabled }: Props) => {
}, [ address, onClick ]); }, [ address, onClick ]);
return ( return (
<Tooltip label={ !address ? 'Connect your wallet to enter your address.' : undefined }> <Tooltip content={ !address ? 'Connect your wallet to enter your address.' : undefined }>
<Button <Button
variant="subtle" variant="subtle"
colorScheme="gray"
size="xs" size="xs"
fontSize="normal" textStyle="md"
fontWeight={ 500 } fontWeight={ 500 }
ml={ 1 } ml={ 1 }
onClick={ handleClick } onClick={ handleClick }
isDisabled={ isDisabled || !address } disabled={ isDisabled || !address }
> >
Self Self
</Button> </Button>
......
import { IconButton, chakra } from '@chakra-ui/react'; import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
...@@ -12,19 +13,26 @@ interface Props { ...@@ -12,19 +13,26 @@ interface Props {
} }
const ContractMethodArrayButton = ({ className, type, index, onClick, isDisabled }: Props) => { const ContractMethodArrayButton = ({ className, type, index, onClick, isDisabled }: Props) => {
const handleClick = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
onClick(event);
}, [ onClick ]);
return ( return (
<IconButton <IconButton
as="div"
className={ className } className={ className }
aria-label={ type } aria-label={ type }
data-index={ index } data-index={ index }
variant="outline" variant="outline"
w="20px" boxSize={ 5 }
h="20px"
flexShrink={ 0 } flexShrink={ 0 }
onClick={ onClick } onClick={ handleClick }
icon={ <IconSvg name={ type === 'remove' ? 'minus' : 'plus' } boxSize={ 3 }/> } disabled={ isDisabled }
isDisabled={ isDisabled } >
/> <IconSvg name={ type === 'remove' ? 'minus' : 'plus' } boxSize={ 3 }/>
</IconButton>
); );
}; };
......
import { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel, Box, useColorModeValue } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { AccordionItem, AccordionItemContent, AccordionItemTrigger, AccordionRoot } from 'toolkit/chakra/accordion';
import ContractMethodArrayButton from './ContractMethodArrayButton'; import ContractMethodArrayButton from './ContractMethodArrayButton';
export interface Props { export interface Props {
...@@ -14,37 +16,31 @@ export interface Props { ...@@ -14,37 +16,31 @@ export interface Props {
} }
const ContractMethodFieldAccordion = ({ label, level, children, onAddClick, onRemoveClick, index, isInvalid }: Props) => { const ContractMethodFieldAccordion = ({ label, level, children, onAddClick, onRemoveClick, index, isInvalid }: Props) => {
const bgColorLevel0 = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); const bgColorLevel0 = { _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' };
const bgColor = useColorModeValue('whiteAlpha.700', 'blackAlpha.700'); const bgColor = { _light: 'whiteAlpha.700', _dark: 'blackAlpha.700' };
return ( return (
<Accordion allowToggle w="100%" bgColor={ level === 0 ? bgColorLevel0 : bgColor } borderRadius="base"> <AccordionRoot w="100%" bgColor={ level === 0 ? bgColorLevel0 : bgColor } borderRadius="base" lazyMount>
<AccordionItem _first={{ borderTopWidth: 0 }} _last={{ borderBottomWidth: 0 }}> <AccordionItem value="default" _first={{ borderTopWidth: 0 }} _last={{ borderBottomWidth: 0 }}>
{ ({ isExpanded }) => ( <AccordionItemTrigger
<> indicatorPlacement="start"
<AccordionButton px="6px"
as="div" py="6px"
cursor="pointer" wordBreak="break-all"
px="6px" textAlign="left"
py="6px" _hover={{ bgColor: 'inherit' }}
wordBreak="break-all" >
textAlign="left" <Box textStyle="sm" fontWeight={ 700 } mr="auto" color={ isInvalid ? 'error' : undefined }>
_hover={{ bgColor: 'inherit' }} { label }
> </Box>
<AccordionIcon transform={ isExpanded ? 'rotate(0deg)' : 'rotate(-90deg)' } color="gray.500"/> { onRemoveClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onRemoveClick } type="remove"/> }
<Box fontSize="sm" lineHeight={ 5 } fontWeight={ 700 } mr="auto" ml={ 1 } color={ isInvalid ? 'error' : undefined }> { onAddClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onAddClick } type="add" ml={ 2 }/> }
{ label } </AccordionItemTrigger>
</Box> <AccordionItemContent display="flex" flexDir="column" rowGap={ 1 } pl="18px" pr="6px">
{ onRemoveClick && <ContractMethodArrayButton index={ index } onClick={ onRemoveClick } type="remove"/> } { children }
{ onAddClick && <ContractMethodArrayButton index={ index } onClick={ onAddClick } type="add" ml={ 2 }/> } </AccordionItemContent>
</AccordionButton>
<AccordionPanel display="flex" flexDir="column" rowGap={ 1 } pl="18px" pr="6px">
{ children }
</AccordionPanel>
</>
) }
</AccordionItem> </AccordionItem>
</Accordion> </AccordionRoot>
); );
}; };
......
import { Box, Button, Flex, FormControl, Input, InputGroup, InputRightElement, chakra, useColorModeValue } from '@chakra-ui/react'; import { Flex, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useController, useFormContext } from 'react-hook-form'; import { useController, useFormContext } from 'react-hook-form';
import { NumericFormat } from 'react-number-format'; // import { NumericFormat } from 'react-number-format';
import type { ContractAbiItemInput } from '../types'; import type { ContractAbiItemInput } from '../types';
import { HOUR, SECOND } from 'lib/consts'; import { HOUR, SECOND } from 'lib/consts';
import { Button } from 'toolkit/chakra/button';
import { Field } from 'toolkit/chakra/field';
import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group';
import ClearButton from 'ui/shared/ClearButton'; import ClearButton from 'ui/shared/ClearButton';
import ContractMethodAddressButton from './ContractMethodAddressButton'; import ContractMethodAddressButton from './ContractMethodAddressButton';
...@@ -43,8 +47,8 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -43,8 +47,8 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
const { control, setValue, getValues } = useFormContext(); const { control, setValue, getValues } = useFormContext();
const { field, fieldState } = useController({ control, name, rules: { validate } }); const { field, fieldState } = useController({ control, name, rules: { validate } });
const inputBgColor = useColorModeValue('white', 'black'); const inputBgColor = { _light: 'white', _dark: 'black' };
const nativeCoinRowBgColor = useColorModeValue('gray.100', 'gray.700'); const nativeCoinRowBgColor = { _light: 'gray.100', _dark: 'gray.700' };
const hasMultiplyButton = argTypeMatchInt && Number(argTypeMatchInt.power) >= 64; const hasMultiplyButton = argTypeMatchInt && Number(argTypeMatchInt.power) >= 64;
...@@ -125,6 +129,46 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -125,6 +129,46 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
const error = fieldState.error; const error = fieldState.error;
const inputEndElement = (
<Flex alignItems="center">
{ field.value !== undefined && field.value !== '' && <ClearButton onClick={ handleClear } isDisabled={ isDisabled } boxSize={ 6 }/> }
{ data.type === 'address' && <ContractMethodAddressButton onClick={ handleAddressButtonClick } isDisabled={ isDisabled }/> }
{ argTypeMatchInt && !isNativeCoin && (hasTimestampButton ? (
<Button
variant="subtle"
size="xs"
textStyle="md"
fontWeight={ 500 }
ml={ 1 }
onClick={ handleTimestampButtonClick }
disabled={ isDisabled }
>
Now+1h
</Button>
) : (
<Button
variant="subtle"
size="xs"
textStyle="md"
fontWeight={ 500 }
ml={ 1 }
onClick={ handleMaxIntButtonClick }
disabled={ isDisabled }
>
Max
</Button>
)) }
{ hasMultiplyButton && (
<ContractMethodMultiplyButton
onClick={ handleMultiplyButtonClick }
isDisabled={ isDisabled }
initialValue={ intPower }
onChange={ setIntPower }
/>
) }
</Flex>
);
return ( return (
<Flex <Flex
className={ className } className={ className }
...@@ -138,73 +182,37 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi ...@@ -138,73 +182,37 @@ const ContractMethodFieldInput = ({ data, hideLabel, path: name, className, isDi
py={ isNativeCoin ? 1 : 0 } py={ isNativeCoin ? 1 : 0 }
> >
{ !hideLabel && <ContractMethodFieldLabel data={ data } isOptional={ isOptional } level={ level }/> } { !hideLabel && <ContractMethodFieldLabel data={ data } isOptional={ isOptional } level={ level }/> }
<FormControl isDisabled={ isDisabled }> <Field invalid={ Boolean(error) } errorText={ error?.message } disabled={ isDisabled }>
<InputGroup size="xs"> <InputGroup
endElement={ inputEndElement }
endElementProps={{ pl: 0, pr: 1 }}
>
<Input <Input
{ ...field } { ...field }
{ ...(argTypeMatchInt ? { // TODO @tom2drum fix formatting of numeric input
as: NumericFormat, // { ...(argTypeMatchInt ? {
thousandSeparator: ' ', // as: NumericFormat,
decimalScale: 0, // thousandSeparator: ' ',
allowNegative: !argTypeMatchInt.isUnsigned, // decimalScale: 0,
getInputRef: (element: HTMLInputElement) => { // allowNegative: !argTypeMatchInt.isUnsigned,
ref.current = element; // getInputRef: (element: HTMLInputElement) => {
}, // ref.current = element;
} : {}) } // },
// } : {}) }
// as we use mutable ref, we have to cast it to React.LegacyRef<HTMLInputElement> to trick chakra and typescript // 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 } ref={ ref as React.LegacyRef<HTMLInputElement> | undefined }
size="sm"
onChange={ handleChange } onChange={ handleChange }
onPaste={ handlePaste } onPaste={ handlePaste }
required={ !isOptional } required={ !isOptional }
isInvalid={ Boolean(error) }
placeholder={ data.type } placeholder={ data.type }
autoComplete="off" autoComplete="off"
data-1p-ignore data-1p-ignore
bgColor={ inputBgColor } bgColor={ inputBgColor }
paddingRight={ hasMultiplyButton ? '120px' : '40px' } paddingRight={ hasMultiplyButton ? '120px' : '40px' }
/> />
<InputRightElement w="auto" right={ 1 } bgColor={ inputBgColor } h="calc(100% - 4px)" top="2px" borderRadius="base">
{ field.value !== undefined && field.value !== '' && <ClearButton onClick={ handleClear } isDisabled={ isDisabled }/> }
{ data.type === 'address' && <ContractMethodAddressButton onClick={ handleAddressButtonClick } isDisabled={ isDisabled }/> }
{ argTypeMatchInt && !isNativeCoin && (hasTimestampButton ? (
<Button
variant="subtle"
colorScheme="gray"
size="xs"
fontSize="normal"
fontWeight={ 500 }
ml={ 1 }
onClick={ handleTimestampButtonClick }
isDisabled={ isDisabled }
>
Now+1h
</Button>
) : (
<Button
variant="subtle"
colorScheme="gray"
size="xs"
fontSize="normal"
fontWeight={ 500 }
ml={ 1 }
onClick={ handleMaxIntButtonClick }
isDisabled={ isDisabled }
>
Max
</Button>
)) }
{ hasMultiplyButton && (
<ContractMethodMultiplyButton
onClick={ handleMultiplyButtonClick }
isDisabled={ isDisabled }
initialValue={ intPower }
onChange={ setIntPower }
/>
) }
</InputRightElement>
</InputGroup> </InputGroup>
{ error && <Box color="error" fontSize="sm" lineHeight={ 5 } mt={ 1 }>{ error.message }</Box> } </Field>
</FormControl>
</Flex> </Flex>
); );
}; };
......
import { Box, useColorModeValue } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ContractAbiItemInput } from '../types'; import type { ContractAbiItemInput } from '../types';
...@@ -12,17 +12,14 @@ interface Props { ...@@ -12,17 +12,14 @@ interface Props {
} }
const ContractMethodFieldLabel = ({ data, isOptional, level }: Props) => { const ContractMethodFieldLabel = ({ data, isOptional, level }: Props) => {
const color = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
return ( return (
<Box <Box
w="250px" w="250px"
fontSize="sm" textStyle="sm"
lineHeight={ 5 }
py="6px" py="6px"
flexShrink={ 0 } flexShrink={ 0 }
fontWeight={ 500 } fontWeight={ 500 }
color={ level > 1 ? color : undefined } color={ level > 1 ? { _light: 'blackAlpha.600', _dark: 'whiteAlpha.600' } : undefined }
> >
{ getFieldLabel(data, !isOptional) } { getFieldLabel(data, !isOptional) }
</Box> </Box>
......
import { Box, Button, Flex, Tooltip, chakra, useDisclosure } from '@chakra-ui/react'; import { Box, Flex, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { useForm, FormProvider } from 'react-hook-form'; import { useForm, FormProvider } from 'react-hook-form';
...@@ -9,6 +9,9 @@ import type { FormSubmitHandler, FormSubmitResult, MethodCallStrategy, SmartCont ...@@ -9,6 +9,9 @@ import type { FormSubmitHandler, FormSubmitResult, MethodCallStrategy, SmartCont
import config from 'configs/app'; import config from 'configs/app';
import { SECOND } from 'lib/consts'; import { SECOND } from 'lib/consts';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { Button } from 'toolkit/chakra/button';
import { Tooltip } from 'toolkit/chakra/tooltip';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import { isReadMethod } from '../utils'; import { isReadMethod } from '../utils';
...@@ -139,10 +142,10 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -139,10 +142,10 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
const buttonCallStrategy = methodType === 'write' ? 'write' : 'read'; const buttonCallStrategy = methodType === 'write' ? 'write' : 'read';
return ( return (
<Tooltip label={ isDisabled ? NO_WALLET_CLIENT_TEXT : undefined } maxW="300px"> <Tooltip content={ NO_WALLET_CLIENT_TEXT } disabled={ !isDisabled }>
<Button <Button
isLoading={ callStrategy === buttonCallStrategy && isLoading } loading={ callStrategy === buttonCallStrategy && isLoading }
isDisabled={ isLoading || isDisabled } disabled={ isLoading || isDisabled }
onClick={ handleButtonClick } onClick={ handleButtonClick }
loadingText={ text } loadingText={ text }
variant="outline" variant="outline"
...@@ -174,8 +177,8 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -174,8 +177,8 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
return ( return (
<Button <Button
isLoading={ callStrategy === buttonCallStrategy && isLoading } loading={ callStrategy === buttonCallStrategy && isLoading }
isDisabled={ isLoading } disabled={ isLoading }
onClick={ handleButtonClick } onClick={ handleButtonClick }
loadingText={ text } loadingText={ text }
variant="outline" variant="outline"
...@@ -183,7 +186,6 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -183,7 +186,6 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
flexShrink={ 0 } flexShrink={ 0 }
width="min-content" width="min-content"
px={ 4 } px={ 4 }
mr={ 3 }
type="submit" type="submit"
data-call-strategy={ buttonCallStrategy } data-call-strategy={ buttonCallStrategy }
> >
...@@ -210,15 +212,14 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -210,15 +212,14 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
return ( return (
<Tooltip <Tooltip
isDisabled={ isDisabled } disabled={ isDisabled }
label="Copied" content="Copied"
closeDelay={ SECOND } closeDelay={ SECOND }
isOpen={ calldataButtonTooltip.isOpen } open={ calldataButtonTooltip.open }
onClose={ calldataButtonTooltip.onClose }
> >
<Button <Button
isLoading={ callStrategy === buttonCallStrategy && isLoading } loading={ callStrategy === buttonCallStrategy && isLoading }
isDisabled={ isDisabled } disabled={ isDisabled }
onClick={ handleButtonClick } onClick={ handleButtonClick }
loadingText={ text } loadingText={ text }
variant="outline" variant="outline"
...@@ -226,7 +227,6 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -226,7 +227,6 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
flexShrink={ 0 } flexShrink={ 0 }
width="min-content" width="min-content"
px={ 4 } px={ 4 }
ml={ 3 }
type="submit" type="submit"
data-call-strategy={ buttonCallStrategy } data-call-strategy={ buttonCallStrategy }
> >
...@@ -282,21 +282,22 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props) ...@@ -282,21 +282,22 @@ const ContractMethodForm = ({ data, attempt, onSubmit, onReset, isOpen }: Props)
return <ContractMethodFieldInput key={ index } { ...props } path={ `${ index }` }/>; return <ContractMethodFieldInput key={ index } { ...props } path={ `${ index }` }/>;
}) } }) }
</Flex> </Flex>
{ secondaryButton } <Flex flexDir="row" gap={ 3 }>
{ primaryButton } { secondaryButton }
{ copyCallDataButton } { primaryButton }
{ result && !isLoading && ( { copyCallDataButton }
<Button { result && !isLoading && (
variant="simple" <Button
colorScheme="blue" variant="link"
size="sm" size="sm"
onClick={ onReset } onClick={ onReset }
ml={ 1 } gap={ 1 }
> >
<IconSvg name="repeat" boxSize={ 5 } mr={ 1 }/> <IconSvg name="repeat" boxSize={ 5 }/>
Reset Reset
</Button> </Button>
) } ) }
</Flex>
</chakra.form> </chakra.form>
</FormProvider> </FormProvider>
{ result && result.source === 'wallet_client' && ( { result && result.source === 'wallet_client' && (
......
import { import { chakra, List, Input, ListItem } from '@chakra-ui/react';
chakra,
PopoverBody,
PopoverContent,
PopoverTrigger,
Portal,
Button,
List,
ListItem,
useDisclosure,
Input,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { times } from 'lib/html-entities'; import { times } from 'lib/html-entities';
import Popover from 'ui/shared/chakra/Popover'; import { Button } from 'toolkit/chakra/button';
import { IconButton } from 'toolkit/chakra/icon-button';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
...@@ -27,19 +18,17 @@ interface Props { ...@@ -27,19 +18,17 @@ interface Props {
const ContractMethodMultiplyButton = ({ onClick, isDisabled, initialValue, onChange }: Props) => { const ContractMethodMultiplyButton = ({ onClick, isDisabled, initialValue, onChange }: Props) => {
const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(initialValue); const [ selectedOption, setSelectedOption ] = React.useState<number | undefined>(initialValue);
const [ customValue, setCustomValue ] = React.useState<number>(); const [ customValue, setCustomValue ] = React.useState<number>();
const { isOpen, onToggle, onClose } = useDisclosure(); const { open, onOpenChange } = useDisclosure();
const dividerColor = useColorModeValue('blackAlpha.200', 'whiteAlpha.200');
const handleOptionClick = React.useCallback((event: React.MouseEvent) => { const handleOptionClick = React.useCallback((event: React.MouseEvent) => {
const id = Number((event.currentTarget as HTMLDivElement).getAttribute('data-id')); const id = Number((event.currentTarget as HTMLDivElement).getAttribute('data-id'));
if (!Object.is(id, NaN)) { if (!Object.is(id, NaN)) {
setSelectedOption((prev) => prev === id ? undefined : id); setSelectedOption((prev) => prev === id ? undefined : id);
setCustomValue(undefined); setCustomValue(undefined);
onClose(); onOpenChange({ open: false });
onChange(id); onChange(id);
} }
}, [ onClose, onChange ]); }, [ onOpenChange, onChange ]);
const handleInputChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const value = Number(event.target.value); const value = Number(event.target.value);
...@@ -59,90 +48,84 @@ const ContractMethodMultiplyButton = ({ onClick, isDisabled, initialValue, onCha ...@@ -59,90 +48,84 @@ const ContractMethodMultiplyButton = ({ onClick, isDisabled, initialValue, onCha
{ Boolean(value) && ( { Boolean(value) && (
<Button <Button
px={ 1 } px={ 1 }
lineHeight={ 6 } textStyle="md"
h={ 6 } size="xs"
fontWeight={ 500 } fontWeight={ 500 }
ml={ 1 } ml={ 1 }
variant="subtle" variant="subtle"
colorScheme="gray"
display="inline" display="inline"
onClick={ handleButtonClick } onClick={ handleButtonClick }
isDisabled={ isDisabled } disabled={ isDisabled }
borderBottomRightRadius={ 0 } borderBottomRightRadius={ 0 }
borderTopRightRadius={ 0 } borderTopRightRadius={ 0 }
> >
{ times } { times }
<chakra.span>10</chakra.span> <chakra.span>10</chakra.span>
<chakra.span fontSize="xs" lineHeight={ 4 } verticalAlign="super">{ value }</chakra.span> <chakra.span fontSize="xs" lineHeight="16px" verticalAlign="super">{ value }</chakra.span>
</Button> </Button>
) } ) }
<Popover placement="bottom-end" isLazy isOpen={ isOpen } onClose={ onClose }> <PopoverRoot open={ open } onOpenChange={ onOpenChange } positioning={{ placement: 'bottom-end' }}>
<PopoverTrigger> <PopoverTrigger>
<Button <IconButton
variant="subtle" variant="subtle"
colorScheme="gray"
size="xs" size="xs"
cursor="pointer" cursor="pointer"
p={ 0 } p={ 0 }
onClick={ onToggle } disabled={ isDisabled }
isActive={ isOpen }
isDisabled={ isDisabled }
borderBottomLeftRadius={ 0 } borderBottomLeftRadius={ 0 }
borderTopLeftRadius={ 0 } borderTopLeftRadius={ 0 }
borderLeftWidth="1px" borderLeftWidth="1px"
borderLeftColor={ dividerColor } borderLeftColor="border.divider"
> >
<IconSvg <IconSvg
name="arrows/east-mini" name="arrows/east-mini"
transitionDuration="fast" transitionDuration="fast"
transitionProperty="transform" transitionProperty="transform"
transitionTimingFunction="ease-in-out" transitionTimingFunction="ease-in-out"
transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transform={ open ? 'rotate(90deg)' : 'rotate(-90deg)' }
boxSize={ 6 } boxSize={ 6 }
/> />
</Button> </IconButton>
</PopoverTrigger> </PopoverTrigger>
<Portal> <PopoverContent w="110px">
<PopoverContent w="110px"> <PopoverBody textStyle="md" py={ 2 }>
<PopoverBody py={ 2 }> <List.Root>
<List> { [ 8, 12, 16, 18, 20 ].map((id) => (
{ [ 8, 12, 16, 18, 20 ].map((id) => ( <List.Item
<ListItem key={ id }
key={ id }
py={ 2 }
data-id={ id }
onClick={ handleOptionClick }
display="flex"
justifyContent="space-between"
alignItems="center"
cursor="pointer"
>
<span>10*{ id }</span>
{ selectedOption === id && <IconSvg name="check" boxSize={ 6 } color="blue.600"/> }
</ListItem>
)) }
<ListItem
py={ 2 } py={ 2 }
data-id={ id }
onClick={ handleOptionClick }
display="flex" display="flex"
justifyContent="space-between" justifyContent="space-between"
alignItems="center" alignItems="center"
cursor="pointer"
> >
<span>10*</span> <span>10*{ id }</span>
<Input { selectedOption === id && <IconSvg name="check" boxSize={ 6 } color="blue.600"/> }
type="number" </List.Item>
min={ 0 } )) }
max={ 100 } <ListItem
ml={ 3 } py={ 2 }
size="xs" display="flex"
onChange={ handleInputChange } justifyContent="space-between"
value={ customValue || '' } alignItems="center"
/> >
</ListItem> <span>10*</span>
</List> <Input
</PopoverBody> type="number"
</PopoverContent> min={ 0 }
</Portal> max={ 100 }
</Popover> ml={ 3 }
size="sm"
onChange={ handleInputChange }
value={ customValue || '' }
/>
</ListItem>
</List.Root>
</PopoverBody>
</PopoverContent>
</PopoverRoot>
</> </>
); );
}; };
......
import { Alert, Flex, useColorModeValue } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { AbiFunction } from 'viem'; import type { AbiFunction } from 'viem';
import type { FormSubmitResultPublicClient, ResultViewMode } from '../types'; import type { FormSubmitResultPublicClient, ResultViewMode } from '../types';
import { Alert } from 'toolkit/chakra/alert';
import ResultItem from './resultPublicClient/Item'; import ResultItem from './resultPublicClient/Item';
export interface Props { export interface Props {
...@@ -14,8 +16,6 @@ export interface Props { ...@@ -14,8 +16,6 @@ export interface Props {
} }
const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeProps }: Props) => { const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeProps }: Props) => {
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
React.useEffect(() => { React.useEffect(() => {
if (modeProps === 'result') { if (modeProps === 'result') {
onSettle(); onSettle();
...@@ -32,7 +32,7 @@ const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeP ...@@ -32,7 +32,7 @@ const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeP
return ( return (
<> <>
{ isError && ( { isError && (
<Alert status="error" mt={ 3 } p={ 4 } borderRadius="md" fontSize="sm" wordBreak="break-word" whiteSpace="pre-wrap"> <Alert status="error" mt={ 3 } p={ 4 } borderRadius="md" textStyle="sm" wordBreak="break-word" whiteSpace="pre-wrap">
{ 'shortMessage' in data && typeof data.shortMessage === 'string' ? data.shortMessage : data.message } { 'shortMessage' in data && typeof data.shortMessage === 'string' ? data.shortMessage : data.message }
</Alert> </Alert>
) } ) }
...@@ -42,7 +42,7 @@ const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeP ...@@ -42,7 +42,7 @@ const ContractMethodResultPublicClient = ({ data, abiItem, onSettle, mode: modeP
mt={ 3 } mt={ 3 }
p={ 4 } p={ 4 }
borderRadius="md" borderRadius="md"
bgColor={ bgColor } bgColor={{ _light: 'blackAlpha.50', _dark: 'whiteAlpha.50' }}
color={ mode === 'preview' ? 'gray.500' : undefined } color={ mode === 'preview' ? 'gray.500' : undefined }
fontSize="sm" fontSize="sm"
lineHeight="20px" lineHeight="20px"
......
import { chakra, Spinner, Box, Alert } from '@chakra-ui/react'; import { chakra, Spinner, Box } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { UseWaitForTransactionReceiptReturnType } from 'wagmi'; import type { UseWaitForTransactionReceiptReturnType } from 'wagmi';
import { useWaitForTransactionReceipt } from 'wagmi'; import { useWaitForTransactionReceipt } from 'wagmi';
...@@ -7,7 +7,8 @@ import type { FormSubmitResultWalletClient } from '../types'; ...@@ -7,7 +7,8 @@ import type { FormSubmitResultWalletClient } from '../types';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import LinkInternal from 'ui/shared/links/LinkInternal'; import { Alert } from 'toolkit/chakra/alert';
import { Link } from 'toolkit/chakra/link';
interface Props { interface Props {
data: FormSubmitResultWalletClient['data']; data: FormSubmitResultWalletClient['data'];
...@@ -45,13 +46,13 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }: ...@@ -45,13 +46,13 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }:
const isErrorResult = 'message' in data; const isErrorResult = 'message' in data;
const txLink = txHash ? ( const txLink = txHash ? (
<LinkInternal href={ route({ pathname: '/tx/[hash]', query: { hash: txHash } }) }>View transaction details</LinkInternal> <Link href={ route({ pathname: '/tx/[hash]', query: { hash: txHash } }) }>View transaction details</Link>
) : null; ) : null;
const content = (() => { const content = (() => {
if (isErrorResult) { if (isErrorResult) {
return ( return (
<Alert status="error"> <Alert status="error" textStyle="sm">
{ data.message } { data.message }
</Alert> </Alert>
); );
...@@ -81,7 +82,7 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }: ...@@ -81,7 +82,7 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }:
case 'error': { case 'error': {
return ( return (
<Alert status="error" flexDir="column" alignItems="flex-start" rowGap={ 1 }> <Alert status="error" textStyle="sm" descriptionProps={{ flexDir: 'column', alignItems: 'flex-start', rowGap: 1 }}>
Error: { txInfo.error ? txInfo.error.message : 'Something went wrong' } { txLink } Error: { txInfo.error ? txInfo.error.message : 'Something went wrong' } { txLink }
</Alert> </Alert>
); );
...@@ -91,7 +92,7 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }: ...@@ -91,7 +92,7 @@ export const ContractMethodResultWalletClientDumb = ({ data, onSettle, txInfo }:
return ( return (
<Box <Box
fontSize="sm" textStyle="sm"
mt={ 3 } mt={ 3 }
alignItems="center" alignItems="center"
whiteSpace="pre-wrap" whiteSpace="pre-wrap"
......
import { Tooltip } from '@chakra-ui/react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import React from 'react'; import React from 'react';
import type { AbiParameter } from 'viem'; import type { AbiParameter } from 'viem';
...@@ -6,8 +5,9 @@ import type { AbiParameter } from 'viem'; ...@@ -6,8 +5,9 @@ import type { AbiParameter } from 'viem';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import { WEI } from 'lib/consts'; import { WEI } from 'lib/consts';
import { Link } from 'toolkit/chakra/link';
import { Tooltip } from 'toolkit/chakra/tooltip';
import CopyToClipboard from 'ui/shared/CopyToClipboard'; import CopyToClipboard from 'ui/shared/CopyToClipboard';
import LinkInternal from 'ui/shared/links/LinkInternal';
import { matchInt } from '../utils'; import { matchInt } from '../utils';
import ItemLabel from './ItemLabel'; import ItemLabel from './ItemLabel';
...@@ -44,8 +44,8 @@ const ItemPrimitive = ({ abiParameter, data, level, hideLabel }: Props) => { ...@@ -44,8 +44,8 @@ const ItemPrimitive = ({ abiParameter, data, level, hideLabel }: Props) => {
if (abiParameter.type === 'address' && typeof data === 'string') { if (abiParameter.type === 'address' && typeof data === 'string') {
return ( return (
<> <>
<LinkInternal href={ route({ pathname: '/address/[hash]', query: { hash: data } }) }>{ data }</LinkInternal> <Link href={ route({ pathname: '/address/[hash]', query: { hash: data } }) }>{ data }</Link>
<CopyToClipboard text={ data } size={ 4 } verticalAlign="sub"/> <CopyToClipboard text={ data } boxSize={ 4 } verticalAlign="sub"/>
</> </>
); );
} }
...@@ -54,7 +54,7 @@ const ItemPrimitive = ({ abiParameter, data, level, hideLabel }: Props) => { ...@@ -54,7 +54,7 @@ const ItemPrimitive = ({ abiParameter, data, level, hideLabel }: Props) => {
if (intMatch && typeof data === 'bigint' && intMatch.max > INT_TOOLTIP_THRESHOLD && data > INT_TOOLTIP_THRESHOLD) { if (intMatch && typeof data === 'bigint' && intMatch.max > INT_TOOLTIP_THRESHOLD && data > INT_TOOLTIP_THRESHOLD) {
const dividedValue = BigNumber(data.toString()).div(WEI); const dividedValue = BigNumber(data.toString()).div(WEI);
return ( return (
<Tooltip label={ dividedValue.toLocaleString() + ' ETH' }> <Tooltip content={ dividedValue.toLocaleString() + ' ETH' }>
<span>{ castValueToString(data) }</span> <span>{ castValueToString(data) }</span>
</Tooltip> </Tooltip>
); );
......
...@@ -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 (
<div> <p>
<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>
...@@ -48,7 +48,7 @@ const ItemTuple = ({ abiParameter, data, mode, level }: Props) => { ...@@ -48,7 +48,7 @@ const ItemTuple = ({ abiParameter, data, mode, level }: Props) => {
); );
}) } }) }
<p>{ printRowOffset(level) }{ '}' }</p> <p>{ printRowOffset(level) }{ '}' }</p>
</div> </p>
); );
}; };
......
...@@ -19,7 +19,7 @@ export const getElementName = (data: SmartContractMethod) => { ...@@ -19,7 +19,7 @@ export const getElementName = (data: SmartContractMethod) => {
return `method_${ getElementId(data) }`; return `method_${ getElementId(data) }`;
}; };
export default function useScrollToMethod(data: Array<SmartContractMethod>, onScroll: (indices: Array<number>) => void) { export default function useScrollToMethod(data: Array<SmartContractMethod>, onScroll: (indices: Array<string>) => void) {
React.useEffect(() => { React.useEffect(() => {
const hash = window.location.hash.replace('#', ''); const hash = window.location.hash.replace('#', '');
...@@ -34,7 +34,7 @@ export default function useScrollToMethod(data: Array<SmartContractMethod>, onSc ...@@ -34,7 +34,7 @@ export default function useScrollToMethod(data: Array<SmartContractMethod>, onSc
smooth: true, smooth: true,
offset: -100, offset: -100,
}); });
onScroll([ index ]); onScroll([ String(index) ]);
} }
}, [ data, onScroll ]); }, [ data, onScroll ]);
} }
...@@ -11,9 +11,10 @@ interface Props { ...@@ -11,9 +11,10 @@ interface Props {
className?: string; className?: string;
tooltipProps?: Partial<TooltipProps>; tooltipProps?: Partial<TooltipProps>;
isLoading?: boolean; isLoading?: boolean;
as?: React.ElementType;
} }
const Hint = ({ label, className, tooltipProps, isLoading }: Props) => { const Hint = ({ label, className, tooltipProps, isLoading, as }: Props) => {
return ( return (
<Tooltip <Tooltip
content={ label } content={ label }
...@@ -27,6 +28,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => { ...@@ -27,6 +28,7 @@ const Hint = ({ label, className, tooltipProps, isLoading }: Props) => {
className={ className } className={ className }
loading={ isLoading } loading={ isLoading }
borderRadius="sm" borderRadius="sm"
as={ as }
> >
<IconSvg name="info" w="100%" h="100%" color="icon_info" _hover={{ color: 'link.primary.hover' }}/> <IconSvg name="info" w="100%" h="100%" color="icon_info" _hover={{ color: 'link.primary.hover' }}/>
</IconButton> </IconButton>
......
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