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
This commit is contained in:
shreerang6921 2024-04-24 17:25:01 +05:30 committed by Nabarun Gogoi
parent db0b21ddd1
commit b05b5894b0
5 changed files with 144 additions and 82 deletions

View File

@ -1,4 +1,12 @@
module.exports = { module.exports = {
root: true, root: true,
extends: '@react-native', extends: '@react-native',
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
ignoreRestSiblings: true,
},
],
},
}; };

View File

@ -13,7 +13,7 @@ const TxErrorDialog = ({
return ( return (
<Portal> <Portal>
<Dialog visible={visible} onDismiss={hideDialog}> <Dialog visible={visible} onDismiss={hideDialog}>
<Dialog.Title>Error sending transaction</Dialog.Title> <Dialog.Title>Transaction Error</Dialog.Title>
<Dialog.Content> <Dialog.Content>
<Text variant="bodyMedium">{error}</Text> <Text variant="bodyMedium">{error}</Text>
</Dialog.Content> </Dialog.Content>

View File

@ -133,12 +133,17 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
requestedNetwork?.addressPrefix, requestedNetwork?.addressPrefix,
); );
try {
const client = await SigningStargateClient.connectWithSigner( const client = await SigningStargateClient.connectWithSigner(
requestedNetwork?.rpcUrl!, requestedNetwork?.rpcUrl!,
sender, sender,
); );
setCosmosStargateClient(client); setCosmosStargateClient(client);
} catch (error: any) {
setTxError(error.message);
setIsTxErrorDialogOpen(true);
}
}; };
setClient(); setClient();
@ -149,8 +154,16 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
if (!requestedNetwork) { if (!requestedNetwork) {
throw new Error('Requested chain not supported'); 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]); }, [requestedNetwork, namespace]);
@ -202,7 +215,6 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
cosmosGasLimit, cosmosGasLimit,
requestedNetwork, requestedNetwork,
]); ]);
useEffect(() => { useEffect(() => {
retrieveData(transaction.from!); retrieveData(transaction.from!);
}, [retrieveData, transaction]); }, [retrieveData, transaction]);
@ -231,9 +243,10 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
], ],
gas: cosmosGasLimit!, gas: cosmosGasLimit!,
}, },
ethGasLimit: transaction.gasLimit ethGasLimit:
? String(transaction.gasLimit) namespace === EIP155
: ethGasLimit, ? BigNumber.from(transaction.gasLimit ?? ethGasLimit)
: undefined,
ethGasPrice: transaction.gasPrice ethGasPrice: transaction.gasPrice
? String(transaction.gasPrice) ? String(transaction.gasPrice)
: ethGasPrice, : ethGasPrice,
@ -245,7 +258,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
await web3wallet!.respondSessionRequest({ topic, response }); await web3wallet!.respondSessionRequest({ topic, response });
navigation.navigate('Laconic'); navigation.navigate('Laconic');
} catch (error: any) { } catch (error: any) {
setTxError(String(error)); setTxError(error.message);
setIsTxErrorDialogOpen(true); setIsTxErrorDialogOpen(true);
} }
setIsTxLoading(false); setIsTxLoading(false);
@ -264,13 +277,15 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
useEffect(() => { useEffect(() => {
const getAccountBalance = async () => { const getAccountBalance = async () => {
try {
if (!account) { if (!account) {
return; return;
} }
if (namespace === EIP155) { if (namespace === EIP155) {
const fetchedBalance = if (!provider) {
provider && (await provider.getBalance(account.address)); return;
}
const fetchedBalance = await provider.getBalance(account.address);
setBalance(fetchedBalance ? fetchedBalance.toString() : '0'); setBalance(fetchedBalance ? fetchedBalance.toString() : '0');
} else { } else {
const cosmosBalance = await cosmosStargateClient?.getBalance( const cosmosBalance = await cosmosStargateClient?.getBalance(
@ -280,6 +295,10 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
setBalance(cosmosBalance?.amount!); setBalance(cosmosBalance?.amount!);
} }
} catch (error: any) {
setTxError(error.message);
setIsTxErrorDialogOpen(true);
}
}; };
getAccountBalance(); getAccountBalance();
@ -311,6 +330,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
useEffect(() => { useEffect(() => {
const getEthGas = async () => { const getEthGas = async () => {
try {
if (transaction.gasLimit && transaction.gasPrice) { if (transaction.gasLimit && transaction.gasPrice) {
return; return;
} }
@ -339,13 +359,17 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
const gasLimit = await provider.estimateGas(transactionObject); const gasLimit = await provider.estimateGas(transactionObject);
setEthGasLimit(String(gasLimit)); setEthGasLimit(String(gasLimit));
} catch (error: any) {
setTxError(error.message);
setIsTxErrorDialogOpen(true);
}
}; };
getEthGas(); getEthGas();
}, [provider, transaction, isSufficientFunds]); }, [provider, transaction, isSufficientFunds]);
useEffect(() => { useEffect(() => {
const getCosmosGas = async () => { const getCosmosGas = async () => {
try {
if (!cosmosStargateClient) { if (!cosmosStargateClient) {
return; return;
} }
@ -364,8 +388,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
Math.round(gasEstimation * Number(Config.DEFAULT_GAS_ADJUSTMENT)), Math.round(gasEstimation * Number(Config.DEFAULT_GAS_ADJUSTMENT)),
), ),
); );
} catch (error: any) {
setTxError(error.message);
setIsTxErrorDialogOpen(true);
}
}; };
getCosmosGas(); getCosmosGas();
}, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]); }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]);
@ -479,7 +506,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
visible={isTxErrorDialogOpen} visible={isTxErrorDialogOpen}
hideDialog={() => { hideDialog={() => {
setIsTxErrorDialogOpen(false); setIsTxErrorDialogOpen(false);
if (!isSufficientFunds) { if (!isSufficientFunds || !balance || !fees) {
rejectRequestHandler(); rejectRequestHandler();
navigation.navigate('Laconic'); navigation.navigate('Laconic');
} }

View File

@ -1,6 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { ScrollView, View } from 'react-native'; 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 { TextInput, Button, HelperText, Text } from 'react-native-paper';
import { setInternetCredentials } from 'react-native-keychain'; import { setInternetCredentials } from 'react-native-keychain';
import { z } from 'zod'; import { z } from 'zod';
@ -17,12 +17,25 @@ import styles from '../styles/stylesheet';
import { retrieveNetworksData } from '../utils/accounts'; import { retrieveNetworksData } from '../utils/accounts';
import { useNetworks } from '../context/NetworksContext'; import { useNetworks } from '../context/NetworksContext';
import { import {
COSMOS,
EIP155, EIP155,
EMPTY_FIELD_ERROR, EMPTY_FIELD_ERROR,
INVALID_URL_ERROR, INVALID_URL_ERROR,
} from '../utils/constants'; } 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 }), networkName: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
rpcUrl: z.string().url({ message: INVALID_URL_ERROR }), rpcUrl: z.string().url({ message: INVALID_URL_ERROR }),
blockExplorerUrl: z blockExplorerUrl: z
@ -42,6 +55,13 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>(); useNavigation<NativeStackNavigationProp<StackParamsList>>();
const networkData = route.params.selectedNetwork;
const networksFormDataSchema =
networkData.namespace === COSMOS
? cosmosNetworksFormDataSchema
: ethNetworksFormSchema;
const { const {
control, control,
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
@ -50,13 +70,12 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
mode: 'onChange', mode: 'onChange',
resolver: zodResolver(networksFormDataSchema), resolver: zodResolver(networksFormDataSchema),
}); });
const networkData = route.params.selectedNetwork;
const submit = useCallback( const submit = useCallback(
async (data: z.infer<typeof networksFormDataSchema>) => { async (data: z.infer<typeof networksFormDataSchema>) => {
const retrievedNetworksData = await retrieveNetworksData(); const retrievedNetworksData = await retrieveNetworksData();
const { type, ...dataWithoutType } = data;
const newNetworkData = { ...networkData, ...data }; const newNetworkData = { ...networkData, ...dataWithoutType };
const index = retrievedNetworksData.findIndex( const index = retrievedNetworksData.findIndex(
network => network.networkId === networkData.networkId, network => network.networkId === networkData.networkId,
); );
@ -137,7 +156,7 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
</> </>
)} )}
/> />
{networkData.namespace !== EIP155 ? ( {networkData.namespace === COSMOS && (
<Controller <Controller
control={control} control={control}
name="gasPrice" name="gasPrice"
@ -151,11 +170,19 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
onBlur={onBlur} onBlur={onBlur}
onChangeText={onChange} onChangeText={onChange}
/> />
<HelperText type="error">{errors.gasPrice?.message}</HelperText> <HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworksFormDataSchema>
>
).gasPrice?.message
}
</HelperText>
</> </>
)} )}
/> />
) : null} )}
<Button <Button
mode="contained" mode="contained"
loading={isSubmitting} loading={isSubmitting}

View File

@ -1,5 +1,5 @@
// Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a // Taken from https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a
import { Wallet, providers } from 'ethers'; import { BigNumber, Wallet, providers } from 'ethers';
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils'; import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils';
import { SignClientTypes } from '@walletconnect/types'; import { SignClientTypes } from '@walletconnect/types';
@ -36,7 +36,7 @@ export async function approveWalletConnectRequest({
message?: string; message?: string;
provider?: providers.JsonRpcProvider | SigningStargateClient; provider?: providers.JsonRpcProvider | SigningStargateClient;
cosmosFee?: StdFee; cosmosFee?: StdFee;
ethGasLimit?: string; ethGasLimit?: BigNumber;
ethGasPrice?: string; ethGasPrice?: string;
sendMsg?: MsgSendEncodeObject; sendMsg?: MsgSendEncodeObject;
memo?: string; memo?: string;