import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Image, ScrollView, View } from 'react-native'; import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp, NativeStackScreenProps, } from '@react-navigation/native-stack'; import { getHeaderTitle } from '@react-navigation/elements'; import { Account, StackParamsList } from '../types'; import AccountDetails from '../components/AccountDetails'; import styles from '../styles/stylesheet'; import { signMessage } from '../utils/sign-message'; import { retrieveSingleAccount } from '../utils/accounts'; import { approveWalletConnectRequest, rejectWalletConnectRequest, WalletConnectRequests, } from '../utils/wallet-connect/wallet-connect-requests'; import { useWalletConnect } from '../context/WalletConnectContext'; import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data'; import { useNetworks } from '../context/NetworksContext'; import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData'; type SignRequestProps = NativeStackScreenProps; const SignRequest = ({ route }: SignRequestProps) => { const { networksData } = useNetworks(); const {web3wallet} = useWalletConnect(); const requestSession = route.params.requestSessionData; const requestName = requestSession?.peer?.metadata?.name; const requestIcon = requestSession?.peer?.metadata?.icons[0]; const requestURL = requestSession?.peer?.metadata?.url; const [account, setAccount] = useState(); const [message, setMessage] = useState(''); const [namespace, setNamespace] = useState(''); const [chainId, setChainId] = useState(''); const [isLoading, setIsLoading] = useState(true); const [isApproving, setIsApproving] = useState(false); const [isRejecting, setIsRejecting] = useState(false); const navigation = useNavigation>(); const isCosmosSignDirect = useMemo(() => { const requestParams = route.params.requestEvent; if (!requestParams?.id) { return false; } return requestParams.params.request.method === 'cosmos_signDirect'; }, [route.params]); const isEthSendTransaction = useMemo(() => { const requestParams = route.params.requestEvent; if (!requestParams?.id) { return false; } return ( requestParams.params.request.method === EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION ); }, [route.params]); const retrieveData = useCallback( async ( requestNamespace: string, requestChainId: string, requestAddress: string, requestMessage: string, ) => { const requestAccount = await retrieveSingleAccount( requestNamespace, requestChainId, requestAddress, ); if (!requestAccount) { navigation.navigate('InvalidPath'); return; } setAccount(requestAccount); setMessage(decodeURIComponent(requestMessage)); setNamespace(requestNamespace); setChainId(requestChainId); setIsLoading(false); }, [navigation], ); const sanitizePath = useCallback( (path: string) => { const regex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)\/(.+)$/; const match = path.match(regex); if (match) { const [, pathNamespace, pathChainId, pathAddress, pathMessage] = match; return { namespace: pathNamespace, chainId: pathChainId, address: pathAddress, message: pathMessage, }; } else { navigation.navigate('InvalidPath'); } return null; }, [navigation], ); useEffect(() => { if (route.path) { const sanitizedRoute = sanitizePath(route.path); sanitizedRoute && retrieveData( sanitizedRoute.namespace, sanitizedRoute.chainId, sanitizedRoute.address, sanitizedRoute.message, ); return; } const requestEvent = route.params.requestEvent; const requestChainId = requestEvent?.params.chainId; const requestedChain = networksData.find( networkData => networkData.chainId === requestChainId?.split(':')[1], ); retrieveData( requestedChain!.namespace, requestedChain!.chainId, route.params.address, route.params.message, ); }, [retrieveData, sanitizePath, route, networksData]); const handleWalletConnectRequest = async () => { const { requestEvent } = route.params || {}; if (!account) { throw new Error('account not found'); } if (!requestEvent) { throw new Error('Request event not found'); } let options: WalletConnectRequests; switch (requestEvent.params.request.method) { case COSMOS_METHODS.COSMOS_SIGN_DIRECT: options = { type: 'cosmos_signDirect', message, }; break; case COSMOS_METHODS.COSMOS_SIGN_AMINO: options = { type: 'cosmos_signAmino', message, }; break; case EIP155_SIGNING_METHODS.PERSONAL_SIGN: options = { type: 'personal_sign', message, }; break; default: throw new Error('Invalid Method'); } const response = await approveWalletConnectRequest( requestEvent, account, namespace, chainId, options, ); const { topic } = requestEvent; await web3wallet!.respondSessionRequest({ topic, response }); }; const handleIntent = async () => { if (!account) { throw new Error('Account is not valid'); } if (message) { const signedMessage = await signMessage({ message, namespace, chainId, accountId: account.index, }); alert(`Signature ${signedMessage}`); } }; const signMessageHandler = async () => { setIsApproving(true); if (route.params.requestEvent) { await handleWalletConnectRequest(); } else { await handleIntent(); } setIsApproving(false); navigation.navigate('Home'); }; const rejectRequestHandler = async () => { setIsRejecting(true); if (route.params?.requestEvent) { const response = rejectWalletConnectRequest(route.params?.requestEvent); const { topic } = route.params?.requestEvent; await web3wallet!.respondSessionRequest({ topic, response, }); } setIsRejecting(false); navigation.navigate('Home'); }; useEffect(() => { navigation.setOptions({ // eslint-disable-next-line react/no-unstable-nested-components header: ({ options, back }) => { const title = getHeaderTitle(options, 'Sign Request'); return ( {back && ( { await rejectRequestHandler(); navigation.navigate('Home'); }} /> )} ); }, }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [navigation, route.name]); return ( <> {isLoading ? ( ) : ( <> {requestIcon && ( <> {requestIcon.endsWith('.svg') ? ( SvgURI requstIcon ) : ( )} )} {requestName} {requestURL} {isCosmosSignDirect || isEthSendTransaction ? ( {message} ) : ( {message} )} )} ); }; export default SignRequest;