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 = {
root: true,
extends: '@react-native',
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
ignoreRestSiblings: true,
},
],
},
};

View File

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

View File

@ -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');
}

View File

@ -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<NativeStackNavigationProp<StackParamsList>>();
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<typeof networksFormDataSchema>) => {
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 && (
<Controller
control={control}
name="gasPrice"
@ -151,11 +170,19 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
onBlur={onBlur}
onChangeText={onChange}
/>
<HelperText type="error">{errors.gasPrice?.message}</HelperText>
<HelperText type="error">
{
(
errors as FieldErrors<
z.infer<typeof cosmosNetworksFormDataSchema>
>
).gasPrice?.message
}
</HelperText>
</>
)}
/>
) : null}
)}
<Button
mode="contained"
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
import { Wallet, providers } from 'ethers';
import { BigNumber, Wallet, providers } from 'ethers';
import { formatJsonRpcError, formatJsonRpcResult } from '@json-rpc-tools/utils';
import { SignClientTypes } from '@walletconnect/types';
@ -36,7 +36,7 @@ export async function approveWalletConnectRequest({
message?: string;
provider?: providers.JsonRpcProvider | SigningStargateClient;
cosmosFee?: StdFee;
ethGasLimit?: string;
ethGasLimit?: BigNumber;
ethGasPrice?: string;
sendMsg?: MsgSendEncodeObject;
memo?: string;