import React, { useCallback, useEffect, useState } from 'react'; import { Button, Snackbar, Text } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import { TxBody, AuthInfo } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; import { SignClientTypes } from '@walletconnect/types'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp, createNativeStackNavigator, } from '@react-navigation/native-stack'; import { getSdkError } from '@walletconnect/utils'; import { Web3WalletTypes } from '@walletconnect/web3wallet'; import { formatJsonRpcResult } from '@json-rpc-tools/utils'; import PairingModal from './components/PairingModal'; import { useWalletConnect } from './context/WalletConnectContext'; import { useAccounts } from './context/AccountsContext'; import InvalidPath from './screens/InvalidPath'; import SignMessage from './screens/SignMessage'; import HomeScreen from './screens/HomeScreen'; import SignRequest from './screens/SignRequest'; import AddSession from './screens/AddSession'; import WalletConnect from './screens/WalletConnect'; import { StackParamsList } from './types'; import { web3wallet } from './utils/wallet-connect/WalletConnectUtils'; import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data'; import { getSignParamsMessage } from './utils/wallet-connect/helpers'; import ApproveTransaction from './screens/ApproveTransaction'; import AddNetwork from './screens/AddNetwork'; import EditNetwork from './screens/EditNetwork'; import { COSMOS, EIP155 } from './utils/constants'; import { useNetworks } from './context/NetworksContext'; import { NETWORK_METHODS } from './utils/wallet-connect/common-data'; const Stack = createNativeStackNavigator(); const App = (): React.JSX.Element => { const navigation = useNavigation>(); const { setActiveSessions } = useWalletConnect(); const { accounts, setCurrentIndex } = useAccounts(); const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks(); const [modalVisible, setModalVisible] = useState(false); const [toastVisible, setToastVisible] = useState(false); const [currentProposal, setCurrentProposal] = useState< SignClientTypes.EventArguments['session_proposal'] | undefined >(); const onSessionProposal = useCallback( async (proposal: SignClientTypes.EventArguments['session_proposal']) => { if (!accounts.length || !accounts.length) { const { id } = proposal; await web3wallet!.rejectSession({ id, reason: getSdkError('UNSUPPORTED_ACCOUNTS'), }); return; } setModalVisible(true); setCurrentProposal(proposal); }, [accounts], ); const onSessionRequest = useCallback( async (requestEvent: Web3WalletTypes.SessionRequest) => { const { topic, params, id } = requestEvent; const { request } = params; const requestSessionData = web3wallet!.engine.signClient.session.get(topic); switch (request.method) { case NETWORK_METHODS.GET_NETWORKS: const currentNetworkId = networksData.find( networkData => networkData.networkId === selectedNetwork!.networkId, )?.networkId; const networkNamesData = networksData.map(networkData => { return { id: networkData.networkId, name: networkData.networkName, }; }); const formattedResponse = formatJsonRpcResult(id, { currentNetworkId, networkNamesData, }); await web3wallet!.respondSessionRequest({ topic, response: formattedResponse, }); break; case NETWORK_METHODS.CHANGE_NETWORK: const networkNameData = request.params[0]; const network = networksData.find( networkData => networkData.networkId === networkNameData.id, ); setCurrentIndex(0); setSelectedNetwork(network); const response = formatJsonRpcResult(id, { response: 'true', }); await web3wallet!.respondSessionRequest({ topic, response: response, }); break; case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION: navigation.navigate('ApproveTransaction', { transaction: request.params[0], requestEvent, requestSessionData, }); break; case EIP155_SIGNING_METHODS.PERSONAL_SIGN: navigation.navigate('SignRequest', { namespace: EIP155, address: request.params[1], message: getSignParamsMessage(request.params), requestEvent, requestSessionData, }); break; case 'cosmos_signDirect': const message = { txbody: TxBody.toJSON( TxBody.decode( Uint8Array.from( Buffer.from(request.params.signDoc.bodyBytes, 'hex'), ), ), ), authInfo: AuthInfo.toJSON( AuthInfo.decode( Uint8Array.from( Buffer.from(request.params.signDoc.authInfoBytes, 'hex'), ), ), ), }; navigation.navigate('SignRequest', { namespace: COSMOS, address: request.params.signerAddress, message: JSON.stringify(message, undefined, 2), requestEvent, requestSessionData, }); break; case 'cosmos_signAmino': navigation.navigate('SignRequest', { namespace: COSMOS, address: request.params.signerAddress, message: request.params.signDoc.memo, requestEvent, requestSessionData, }); break; case 'cosmos_sendTokens': navigation.navigate('ApproveTransaction', { transaction: request.params[0], requestEvent, requestSessionData, }); break; default: throw new Error('Invalid method'); } }, [ navigation, networksData, setSelectedNetwork, setCurrentIndex, selectedNetwork, ], ); const onSessionDelete = useCallback(() => { const sessions = web3wallet!.getActiveSessions(); setActiveSessions(sessions); }, [setActiveSessions]); useEffect(() => { web3wallet?.on('session_proposal', onSessionProposal); web3wallet?.on('session_request', onSessionRequest); web3wallet?.on('session_delete', onSessionDelete); return () => { web3wallet?.off('session_proposal', onSessionProposal); web3wallet?.off('session_request', onSessionRequest); web3wallet?.off('session_delete', onSessionDelete); }; //TODO: Investigate dependencies }); return ( <> Laconic Wallet, headerBackVisible: false, }} /> Sign Message, }} /> Sign Request, }} /> Bad Request, headerBackVisible: false, }} /> WalletConnect, // eslint-disable-next-line react/no-unstable-nested-components headerRight: () => ( ), }} /> setToastVisible(false)} duration={3000}> Session approved ); }; export default App;