From b05b5894b053ef9dec0b90c776957932d4024370 Mon Sep 17 00:00:00 2001 From: shreerang6921 <68148922+shreerang6921@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:25:01 +0530 Subject: [PATCH] Handle incorrect RPC URL error and reject request (#112) * Fix gas limit type for evm chains * Handle incorrect rpc url error * Fix edit network form not working for evm chains --- .eslintrc.js | 8 + src/components/TxErrorDialog.tsx | 2 +- src/screens/ApproveTransaction.tsx | 169 ++++++++++-------- src/screens/EditNetwork.tsx | 43 ++++- .../wallet-connect/WalletConnectRequests.ts | 4 +- 5 files changed, 144 insertions(+), 82 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 187894b..b34003d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,12 @@ module.exports = { root: true, extends: '@react-native', + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + }, + ], + }, }; diff --git a/src/components/TxErrorDialog.tsx b/src/components/TxErrorDialog.tsx index e110760..8b757b4 100644 --- a/src/components/TxErrorDialog.tsx +++ b/src/components/TxErrorDialog.tsx @@ -13,7 +13,7 @@ const TxErrorDialog = ({ return ( - Error sending transaction + Transaction Error {error} diff --git a/src/screens/ApproveTransaction.tsx b/src/screens/ApproveTransaction.tsx index f2082aa..6e839d6 100644 --- a/src/screens/ApproveTransaction.tsx +++ b/src/screens/ApproveTransaction.tsx @@ -133,12 +133,17 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { requestedNetwork?.addressPrefix, ); - const client = await SigningStargateClient.connectWithSigner( - requestedNetwork?.rpcUrl!, - sender, - ); + try { + const client = await SigningStargateClient.connectWithSigner( + requestedNetwork?.rpcUrl!, + sender, + ); - setCosmosStargateClient(client); + setCosmosStargateClient(client); + } catch (error: any) { + setTxError(error.message); + setIsTxErrorDialogOpen(true); + } }; setClient(); @@ -149,8 +154,16 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { if (!requestedNetwork) { throw new Error('Requested chain not supported'); } + try { + const ethProvider = new providers.JsonRpcProvider( + requestedNetwork.rpcUrl, + ); - return new providers.JsonRpcProvider(requestedNetwork.rpcUrl); + return ethProvider; + } catch (error: any) { + setTxError(error.message); + setIsTxErrorDialogOpen(true); + } } }, [requestedNetwork, namespace]); @@ -202,7 +215,6 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { cosmosGasLimit, requestedNetwork, ]); - useEffect(() => { retrieveData(transaction.from!); }, [retrieveData, transaction]); @@ -231,9 +243,10 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { ], gas: cosmosGasLimit!, }, - ethGasLimit: transaction.gasLimit - ? String(transaction.gasLimit) - : ethGasLimit, + ethGasLimit: + namespace === EIP155 + ? BigNumber.from(transaction.gasLimit ?? ethGasLimit) + : undefined, ethGasPrice: transaction.gasPrice ? String(transaction.gasPrice) : ethGasPrice, @@ -245,7 +258,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { await web3wallet!.respondSessionRequest({ topic, response }); navigation.navigate('Laconic'); } catch (error: any) { - setTxError(String(error)); + setTxError(error.message); setIsTxErrorDialogOpen(true); } setIsTxLoading(false); @@ -264,21 +277,27 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { useEffect(() => { const getAccountBalance = async () => { - if (!account) { - return; - } - if (namespace === EIP155) { - const fetchedBalance = - provider && (await provider.getBalance(account.address)); + try { + if (!account) { + return; + } + if (namespace === EIP155) { + if (!provider) { + return; + } + const fetchedBalance = await provider.getBalance(account.address); + setBalance(fetchedBalance ? fetchedBalance.toString() : '0'); + } else { + const cosmosBalance = await cosmosStargateClient?.getBalance( + account.address, + requestedNetwork!.nativeDenom!.toLowerCase(), + ); - setBalance(fetchedBalance ? fetchedBalance.toString() : '0'); - } else { - const cosmosBalance = await cosmosStargateClient?.getBalance( - account.address, - requestedNetwork!.nativeDenom!.toLowerCase(), - ); - - setBalance(cosmosBalance?.amount!); + setBalance(cosmosBalance?.amount!); + } + } catch (error: any) { + setTxError(error.message); + setIsTxErrorDialogOpen(true); } }; @@ -311,61 +330,69 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { useEffect(() => { const getEthGas = async () => { - if (transaction.gasLimit && transaction.gasPrice) { - return; + try { + if (transaction.gasLimit && transaction.gasPrice) { + return; + } + + if (!isSufficientFunds) { + return; + } + + if (!provider) { + return; + } + + const gasPriceVal = await provider.getGasPrice(); + const gasPrice = ethers.utils.hexValue(gasPriceVal); + + setEthGasPrice(String(gasPrice)); + + const transactionObject = { + from: transaction.from!, + to: transaction.to!, + data: transaction.data!, + gasPrice, + value: transaction.value!, + }; + + const gasLimit = await provider.estimateGas(transactionObject); + + setEthGasLimit(String(gasLimit)); + } catch (error: any) { + setTxError(error.message); + setIsTxErrorDialogOpen(true); } - - if (!isSufficientFunds) { - return; - } - - if (!provider) { - return; - } - - const gasPriceVal = await provider.getGasPrice(); - const gasPrice = ethers.utils.hexValue(gasPriceVal); - - setEthGasPrice(String(gasPrice)); - - const transactionObject = { - from: transaction.from!, - to: transaction.to!, - data: transaction.data!, - gasPrice, - value: transaction.value!, - }; - - const gasLimit = await provider.estimateGas(transactionObject); - - setEthGasLimit(String(gasLimit)); }; - getEthGas(); }, [provider, transaction, isSufficientFunds]); useEffect(() => { const getCosmosGas = async () => { - if (!cosmosStargateClient) { - return; - } - if (!isSufficientFunds) { - return; - } + try { + if (!cosmosStargateClient) { + return; + } + if (!isSufficientFunds) { + return; + } - const gasEstimation = await cosmosStargateClient.simulate( - transaction.from!, - [sendMsg], - MEMO, - ); + const gasEstimation = await cosmosStargateClient.simulate( + transaction.from!, + [sendMsg], + MEMO, + ); - setCosmosGasLimit( - String( - Math.round(gasEstimation * Number(Config.DEFAULT_GAS_ADJUSTMENT)), - ), - ); + setCosmosGasLimit( + String( + Math.round(gasEstimation * Number(Config.DEFAULT_GAS_ADJUSTMENT)), + ), + ); + } catch (error: any) { + setTxError(error.message); + setIsTxErrorDialogOpen(true); + } }; - getCosmosGas(); }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]); @@ -479,7 +506,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { visible={isTxErrorDialogOpen} hideDialog={() => { setIsTxErrorDialogOpen(false); - if (!isSufficientFunds) { + if (!isSufficientFunds || !balance || !fees) { rejectRequestHandler(); navigation.navigate('Laconic'); } diff --git a/src/screens/EditNetwork.tsx b/src/screens/EditNetwork.tsx index afebb86..70bd197 100644 --- a/src/screens/EditNetwork.tsx +++ b/src/screens/EditNetwork.tsx @@ -1,6 +1,6 @@ import React, { useCallback } from 'react'; import { ScrollView, View } from 'react-native'; -import { useForm, Controller } from 'react-hook-form'; +import { useForm, Controller, FieldErrors } from 'react-hook-form'; import { TextInput, Button, HelperText, Text } from 'react-native-paper'; import { setInternetCredentials } from 'react-native-keychain'; import { z } from 'zod'; @@ -17,12 +17,25 @@ import styles from '../styles/stylesheet'; import { retrieveNetworksData } from '../utils/accounts'; import { useNetworks } from '../context/NetworksContext'; import { + COSMOS, EIP155, EMPTY_FIELD_ERROR, INVALID_URL_ERROR, } from '../utils/constants'; -const networksFormDataSchema = z.object({ +const ethNetworksFormSchema = z.object({ + // Adding type field for resolving typescript error + type: z.literal(EIP155).optional(), + networkName: z.string().nonempty({ message: EMPTY_FIELD_ERROR }), + rpcUrl: z.string().url({ message: INVALID_URL_ERROR }), + blockExplorerUrl: z + .string() + .url({ message: INVALID_URL_ERROR }) + .or(z.literal('')), +}); + +const cosmosNetworksFormDataSchema = z.object({ + type: z.literal(COSMOS).optional(), networkName: z.string().nonempty({ message: EMPTY_FIELD_ERROR }), rpcUrl: z.string().url({ message: INVALID_URL_ERROR }), blockExplorerUrl: z @@ -42,6 +55,13 @@ const EditNetwork = ({ route }: EditNetworkProps) => { const navigation = useNavigation>(); + const networkData = route.params.selectedNetwork; + + const networksFormDataSchema = + networkData.namespace === COSMOS + ? cosmosNetworksFormDataSchema + : ethNetworksFormSchema; + const { control, formState: { errors, isSubmitting }, @@ -50,13 +70,12 @@ const EditNetwork = ({ route }: EditNetworkProps) => { mode: 'onChange', resolver: zodResolver(networksFormDataSchema), }); - const networkData = route.params.selectedNetwork; const submit = useCallback( async (data: z.infer) => { const retrievedNetworksData = await retrieveNetworksData(); - - const newNetworkData = { ...networkData, ...data }; + const { type, ...dataWithoutType } = data; + const newNetworkData = { ...networkData, ...dataWithoutType }; const index = retrievedNetworksData.findIndex( network => network.networkId === networkData.networkId, ); @@ -137,7 +156,7 @@ const EditNetwork = ({ route }: EditNetworkProps) => { )} /> - {networkData.namespace !== EIP155 ? ( + {networkData.namespace === COSMOS && ( { onBlur={onBlur} onChangeText={onChange} /> - {errors.gasPrice?.message} + + { + ( + errors as FieldErrors< + z.infer + > + ).gasPrice?.message + } + )} /> - ) : null} + )}