Commit 2f62ccc4 authored by tom's avatar tom

web3 signature

parent 2b8f48d8
import { useColorModeValue, useToken } 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';
import appConfig from 'configs/app/config';
import { ContractContextProvider } from 'ui/address/contract/context'; import { ContractContextProvider } from 'ui/address/contract/context';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs'; import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
interface Props { interface Props {
tabs: Array<RoutedSubTab>; tabs: Array<RoutedSubTab>;
addressHash?: string; addressHash?: string;
} }
const { wagmiClient, ethereumClient } = (() => {
try {
const currentChain: Chain = {
id: Number(appConfig.network.id),
name: appConfig.network.name || '',
network: appConfig.network.name || '',
nativeCurrency: {
decimals: appConfig.network.currency.decimals,
name: appConfig.network.currency.name || '',
symbol: appConfig.network.currency.symbol || '',
},
rpcUrls: {
'default': {
http: [ appConfig.network.rpcUrl || '' ],
},
},
blockExplorers: {
'default': {
name: 'Blockscout',
url: appConfig.baseUrl,
},
},
};
const chains = [ currentChain ];
const { provider } = configureChains(chains, [
walletConnectProvider({ projectId: appConfig.walletConnect.projectId || '' }),
]);
const wagmiClient = createClient({
autoConnect: true,
connectors: modalConnectors({ appName: 'web3Modal', chains }),
provider,
});
const ethereumClient = new EthereumClient(wagmiClient, chains);
return { wagmiClient, ethereumClient };
} catch (error) {
return { wagmiClient: undefined, ethereumClient: undefined };
}
})();
const TAB_LIST_PROPS = { const TAB_LIST_PROPS = {
columnGap: 3, columnGap: 3,
}; };
const AddressContract = ({ addressHash, tabs }: Props) => { const AddressContract = ({ addressHash, tabs }: Props) => {
const modalZIndex = useToken<string>('zIndices', 'modal'); const fallback = React.useCallback(() => {
const web3ModalTheme = useColorModeValue('light', 'dark'); const noProviderTabs = tabs.filter(({ id }) => id === 'contact_code');
const noProviderTabs = React.useMemo(() => tabs.filter(({ id }) => id === 'contact_code'), [ tabs ]);
if (!wagmiClient || !ethereumClient) {
return <RoutedTabs tabs={ noProviderTabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/>; return <RoutedTabs tabs={ noProviderTabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/>;
} }, [ tabs ]);
return ( return (
<WagmiConfig client={ wagmiClient }> <Web3ModalProvider fallback={ fallback }>
<ContractContextProvider addressHash={ addressHash }> <ContractContextProvider addressHash={ addressHash }>
<RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/> <RoutedTabs tabs={ tabs } variant="outline" colorScheme="gray" size="sm" tabListProps={ TAB_LIST_PROPS }/>
</ContractContextProvider> </ContractContextProvider>
<Web3Modal </Web3ModalProvider>
projectId={ appConfig.walletConnect.projectId }
ethereumClient={ ethereumClient }
themeZIndex={ Number(modalZIndex) }
themeMode={ web3ModalTheme }
themeBackground="themeColor"
/>
</WagmiConfig>
); );
}; };
......
...@@ -5,6 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form'; ...@@ -5,6 +5,8 @@ import { useForm, FormProvider } from 'react-hook-form';
import type { AddressVerificationFormFields } from './types'; import type { AddressVerificationFormFields } from './types';
import Web3ModalProvider from 'ui/shared/Web3ModalProvider';
import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress'; import AddressVerificationStepAddress from './steps/AddressVerificationStepAddress';
import AddressVerificationStepSignature from './steps/AddressVerificationStepSignature'; import AddressVerificationStepSignature from './steps/AddressVerificationStepSignature';
import AddressVerificationStepSuccess from './steps/AddressVerificationStepSuccess'; import AddressVerificationStepSuccess from './steps/AddressVerificationStepSuccess';
...@@ -32,17 +34,18 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => { ...@@ -32,17 +34,18 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
formApi.reset(); formApi.reset();
}, [ formApi, onClose ]); }, [ formApi, onClose ]);
const steps = [
{ title: 'Verify new address ownership', content: <AddressVerificationStepAddress onContinue={ handleGoToNextStep }/> },
{ title: 'Copy message to sign', content: <AddressVerificationStepSignature onContinue={ handleGoToNextStep }/> },
{ title: 'Congrats! Address is verified.', content: <AddressVerificationStepSuccess onShowListClick={ handleClose } onAddTokenClick={ handleClose }/> },
];
const onFormSubmit: SubmitHandler<AddressVerificationFormFields> = React.useCallback(async(data) => { const onFormSubmit: SubmitHandler<AddressVerificationFormFields> = React.useCallback(async(data) => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('__>__', data); console.log('__>__', data);
}, [ ]); }, [ ]);
const onSubmit = handleSubmit(onFormSubmit);
const steps = [
{ title: 'Verify new address ownership', content: <AddressVerificationStepAddress onContinue={ handleGoToNextStep }/> },
{ title: 'Copy message to sign', content: <AddressVerificationStepSignature onContinue={ handleGoToNextStep } onSubmit={ onSubmit }/> },
{ title: 'Congrats! Address is verified.', content: <AddressVerificationStepSuccess onShowListClick={ handleClose } onAddTokenClick={ handleClose }/> },
];
const step = steps[stepIndex]; const step = steps[stepIndex];
return ( return (
...@@ -52,11 +55,13 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => { ...@@ -52,11 +55,13 @@ const AddressVerificationModal = ({ isOpen, onClose }: Props) => {
<ModalHeader fontWeight="500" textStyle="h3" mb={ 6 }>{ step.title }</ModalHeader> <ModalHeader fontWeight="500" textStyle="h3" mb={ 6 }>{ step.title }</ModalHeader>
<ModalCloseButton/> <ModalCloseButton/>
<ModalBody mb={ 0 }> <ModalBody mb={ 0 }>
<Web3ModalProvider>
<FormProvider { ...formApi }> <FormProvider { ...formApi }>
<form noValidate onSubmit={ handleSubmit(onFormSubmit) }> <form noValidate onSubmit={ onSubmit }>
{ step.content } { step.content }
</form> </form>
</FormProvider> </FormProvider>
</Web3ModalProvider>
</ModalBody> </ModalBody>
</ModalContent> </ModalContent>
</Modal> </Modal>
......
import { Box, Button, Flex, Link } from '@chakra-ui/react'; import { Box, Button, Flex, Link } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import { useSignMessage } from 'wagmi';
import type { AddressVerificationFormFields } from '../types'; import type { AddressVerificationFormFields } from '../types';
import useToast from 'lib/hooks/useToast';
import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress'; import AddressVerificationFieldAddress from '../fields/AddressVerificationFieldAddress';
import AddressVerificationFieldMessage from '../fields/AddressVerificationFieldMessage'; import AddressVerificationFieldMessage from '../fields/AddressVerificationFieldMessage';
import AddressVerificationFieldSignature from '../fields/AddressVerificationFieldSignature'; import AddressVerificationFieldSignature from '../fields/AddressVerificationFieldSignature';
interface Props { interface Props {
onContinue: () => void; onContinue: () => void;
onSubmit: () => void;
} }
const AddressVerificationStepSignature = ({ onContinue }: Props) => { const AddressVerificationStepSignature = ({ onContinue, onSubmit }: Props) => {
const [ isManualSigning, setIsManualSigning ] = React.useState(false); const [ isManualSigning, setIsManualSigning ] = React.useState(false);
const { formState, trigger } = useFormContext<AddressVerificationFormFields>(); const toast = useToast();
const { formState, trigger, getValues, setValue } = useFormContext<AddressVerificationFormFields>();
const { signMessage } = useSignMessage({
onSuccess: (data) => {
setValue('signature', data);
onSubmit();
},
onError: (error) => {
toast({
position: 'top-right',
title: 'Error',
description: (error as Error)?.message || 'Something went wrong',
status: 'error',
variant: 'subtle',
isClosable: true,
});
},
});
const handleVerifyButtonClick = React.useCallback(() => { const handleVerifyButtonClick = React.useCallback(() => {
if (!formState.isValid) { if (!formState.isValid) {
...@@ -27,8 +48,9 @@ const AddressVerificationStepSignature = ({ onContinue }: Props) => { ...@@ -27,8 +48,9 @@ const AddressVerificationStepSignature = ({ onContinue }: Props) => {
}, [ formState, onContinue, trigger ]); }, [ formState, onContinue, trigger ]);
const handleWeb3SignClick = React.useCallback(() => { const handleWeb3SignClick = React.useCallback(() => {
const message = getValues('message');
}, []); signMessage({ message });
}, [ getValues, signMessage ]);
const handleManualSignClick = React.useCallback(() => { const handleManualSignClick = React.useCallback(() => {
setIsManualSigning(true); setIsManualSigning(true);
......
import { useColorModeValue, useToken } from '@chakra-ui/react';
import {
EthereumClient,
modalConnectors,
walletConnectProvider,
} from '@web3modal/ethereum';
import { Web3Modal } from '@web3modal/react';
import React from 'react';
import type { Chain } from 'wagmi';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import appConfig from 'configs/app/config';
const { wagmiClient, ethereumClient } = (() => {
try {
const currentChain: Chain = {
id: Number(appConfig.network.id),
name: appConfig.network.name || '',
network: appConfig.network.name || '',
nativeCurrency: {
decimals: appConfig.network.currency.decimals,
name: appConfig.network.currency.name || '',
symbol: appConfig.network.currency.symbol || '',
},
rpcUrls: {
'default': {
http: [ appConfig.network.rpcUrl || '' ],
},
},
blockExplorers: {
'default': {
name: 'Blockscout',
url: appConfig.baseUrl,
},
},
};
const chains = [ currentChain ];
const { provider } = configureChains(chains, [
walletConnectProvider({ projectId: appConfig.walletConnect.projectId || '' }),
]);
const wagmiClient = createClient({
autoConnect: true,
connectors: modalConnectors({ appName: 'web3Modal', chains }),
provider,
});
const ethereumClient = new EthereumClient(wagmiClient, chains);
return { wagmiClient, ethereumClient };
} catch (error) {
return { wagmiClient: undefined, ethereumClient: undefined };
}
})();
interface Props {
children: React.ReactNode;
fallback?: () => JSX.Element;
}
const Web3ModalProvider = ({ children, fallback }: Props) => {
const modalZIndex = useToken<string>('zIndices', 'modal');
const web3ModalTheme = useColorModeValue('light', 'dark');
if (!wagmiClient || !ethereumClient) {
return fallback?.() || null;
}
return (
<WagmiConfig client={ wagmiClient }>
{ children }
<Web3Modal
projectId={ appConfig.walletConnect.projectId }
ethereumClient={ ethereumClient }
themeZIndex={ Number(modalZIndex) }
themeMode={ web3ModalTheme }
themeBackground="themeColor"
/>
</WagmiConfig>
);
};
export default Web3ModalProvider;
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