forked from cerc-io/laconic-wallet
Take gas limit and fees values from user while approving transaction (#109)
* Take gas limit and fees from user * Update text input ui * Use gasPrice from networks data * Use default gas limit from env * Use default gas price if not found in registry * Remove appended denom in gas price * Use gas limit from env * Show error dialog when transaction fails * Calculate gas limit and gas price if not received from dapp * Update example env * Improve syntax --------- Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
This commit is contained in:
parent
ad202f46fc
commit
b1a0831e78
@ -1 +1,3 @@
|
|||||||
WALLET_CONNECT_PROJECT_ID=
|
WALLET_CONNECT_PROJECT_ID=
|
||||||
|
DEFAULT_GAS_LIMIT=
|
||||||
|
DEFAULT_GAS_PRICE=
|
||||||
|
2
react-native-config.d.ts
vendored
2
react-native-config.d.ts
vendored
@ -2,6 +2,8 @@
|
|||||||
declare module 'react-native-config' {
|
declare module 'react-native-config' {
|
||||||
export interface NativeConfig {
|
export interface NativeConfig {
|
||||||
WALLET_CONNECT_PROJECT_ID: string;
|
WALLET_CONNECT_PROJECT_ID: string;
|
||||||
|
DEFAULT_GAS_LIMIT: string;
|
||||||
|
DEFAULT_GAS_PRICE: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Config: NativeConfig;
|
export const Config: NativeConfig;
|
||||||
|
28
src/components/TxErrorDialog.tsx
Normal file
28
src/components/TxErrorDialog.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Button, Dialog, Portal, Text } from 'react-native-paper';
|
||||||
|
|
||||||
|
const TxErrorDialog = ({
|
||||||
|
error,
|
||||||
|
visible,
|
||||||
|
hideDialog,
|
||||||
|
}: {
|
||||||
|
error: string;
|
||||||
|
visible: boolean;
|
||||||
|
hideDialog: () => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<Dialog visible={visible} onDismiss={hideDialog}>
|
||||||
|
<Dialog.Title>Error sending transaction</Dialog.Title>
|
||||||
|
<Dialog.Content>
|
||||||
|
<Text variant="bodyMedium">{error}</Text>
|
||||||
|
</Dialog.Content>
|
||||||
|
<Dialog.Actions>
|
||||||
|
<Button onPress={hideDialog}>OK</Button>
|
||||||
|
</Dialog.Actions>
|
||||||
|
</Dialog>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TxErrorDialog;
|
@ -53,6 +53,10 @@ const cosmosNetworkDataSchema = z.object({
|
|||||||
coinType: z.string().nonempty({ message: EMPTY_FIELD_ERROR }).regex(/^\d+$/),
|
coinType: z.string().nonempty({ message: EMPTY_FIELD_ERROR }).regex(/^\d+$/),
|
||||||
nativeDenom: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
nativeDenom: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
||||||
addressPrefix: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
addressPrefix: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
||||||
|
gasPrice: z
|
||||||
|
.string()
|
||||||
|
.nonempty({ message: EMPTY_FIELD_ERROR })
|
||||||
|
.regex(/^\d+(\.\d+)?$/),
|
||||||
});
|
});
|
||||||
|
|
||||||
const AddNetwork = () => {
|
const AddNetwork = () => {
|
||||||
@ -113,6 +117,13 @@ const AddNetwork = () => {
|
|||||||
setValue('addressPrefix', cosmosChainDetails.bech32_prefix);
|
setValue('addressPrefix', cosmosChainDetails.bech32_prefix);
|
||||||
setValue('coinType', String(cosmosChainDetails.slip44 ?? '118'));
|
setValue('coinType', String(cosmosChainDetails.slip44 ?? '118'));
|
||||||
setValue('nativeDenom', cosmosChainDetails.fees?.fee_tokens[0].denom || '');
|
setValue('nativeDenom', cosmosChainDetails.fees?.fee_tokens[0].denom || '');
|
||||||
|
setValue(
|
||||||
|
'gasPrice',
|
||||||
|
String(
|
||||||
|
cosmosChainDetails.fees?.fee_tokens[0].average_gas_price ||
|
||||||
|
String(process.env.DEFAULT_GAS_PRICE),
|
||||||
|
),
|
||||||
|
);
|
||||||
}, CHAINID_DEBOUNCE_DELAY);
|
}, CHAINID_DEBOUNCE_DELAY);
|
||||||
|
|
||||||
const submit = useCallback(
|
const submit = useCallback(
|
||||||
@ -359,6 +370,31 @@ const AddNetwork = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="gasPrice"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={value}
|
||||||
|
label="Gas Price"
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChangeText={onChange}
|
||||||
|
/>
|
||||||
|
<HelperText type="error">
|
||||||
|
{
|
||||||
|
(
|
||||||
|
errors as FieldErrors<
|
||||||
|
z.infer<typeof cosmosNetworkDataSchema>
|
||||||
|
>
|
||||||
|
).gasPrice?.message
|
||||||
|
}
|
||||||
|
</HelperText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Image, ScrollView, View } from 'react-native';
|
import { Image, ScrollView, View } from 'react-native';
|
||||||
import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper';
|
import {
|
||||||
import { providers, BigNumber } from 'ethers';
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
Appbar,
|
||||||
|
TextInput,
|
||||||
|
} from 'react-native-paper';
|
||||||
|
import { providers, BigNumber, ethers } from 'ethers';
|
||||||
|
import Config from 'react-native-config';
|
||||||
|
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import {
|
import {
|
||||||
@ -29,6 +36,7 @@ import DataBox from '../components/DataBox';
|
|||||||
import { getPathKey } from '../utils/misc';
|
import { getPathKey } from '../utils/misc';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
import { COSMOS, EIP155 } from '../utils/constants';
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
import TxErrorDialog from '../components/TxErrorDialog';
|
||||||
|
|
||||||
type SignRequestProps = NativeStackScreenProps<
|
type SignRequestProps = NativeStackScreenProps<
|
||||||
StackParamsList,
|
StackParamsList,
|
||||||
@ -52,12 +60,20 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
const [isTxLoading, setIsTxLoading] = useState(false);
|
const [isTxLoading, setIsTxLoading] = useState(false);
|
||||||
const [cosmosStargateClient, setCosmosStargateClient] =
|
const [cosmosStargateClient, setCosmosStargateClient] =
|
||||||
useState<SigningStargateClient>();
|
useState<SigningStargateClient>();
|
||||||
|
const [fees, setFees] = useState('');
|
||||||
|
const [cosmosGasLimit, setCosmosGasLimit] = useState(
|
||||||
|
Config.DEFAULT_GAS_LIMIT,
|
||||||
|
);
|
||||||
|
const [txError, setTxError] = useState<string>('');
|
||||||
|
const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false);
|
||||||
|
const [ethGasPrice, setEthGasPrice] = useState<string>();
|
||||||
|
const [ethGasLimit, setEthGasLimit] = useState<string>();
|
||||||
|
|
||||||
const requestedChain = networksData.find(
|
const requestedNetwork = networksData.find(
|
||||||
networkData =>
|
networkData =>
|
||||||
`${networkData.namespace}:${networkData.chainId}` === chainId,
|
`${networkData.namespace}:${networkData.chainId}` === chainId,
|
||||||
);
|
);
|
||||||
const namespace = requestedChain!.namespace;
|
const namespace = requestedNetwork!.namespace;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (namespace !== COSMOS) {
|
if (namespace !== COSMOS) {
|
||||||
@ -71,18 +87,18 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
const cosmosPrivKey = (
|
const cosmosPrivKey = (
|
||||||
await getPathKey(
|
await getPathKey(
|
||||||
`${requestedChain?.namespace}:${requestedChain?.chainId}`,
|
`${requestedNetwork?.namespace}:${requestedNetwork?.chainId}`,
|
||||||
account.index,
|
account.index,
|
||||||
)
|
)
|
||||||
).privKey;
|
).privKey;
|
||||||
|
|
||||||
const sender = await DirectSecp256k1Wallet.fromKey(
|
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||||
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||||
requestedChain?.addressPrefix,
|
requestedNetwork?.addressPrefix,
|
||||||
);
|
);
|
||||||
|
|
||||||
const client = await SigningStargateClient.connectWithSigner(
|
const client = await SigningStargateClient.connectWithSigner(
|
||||||
requestedChain?.rpcUrl!,
|
requestedNetwork?.rpcUrl!,
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -90,17 +106,17 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setClient();
|
setClient();
|
||||||
}, [account, requestedChain, chainId, namespace]);
|
}, [account, requestedNetwork, chainId, namespace]);
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
if (namespace === EIP155) {
|
if (namespace === EIP155) {
|
||||||
if (!requestedChain) {
|
if (!requestedNetwork) {
|
||||||
throw new Error('Requested chain not supported');
|
throw new Error('Requested chain not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new providers.JsonRpcProvider(requestedChain.rpcUrl);
|
return new providers.JsonRpcProvider(requestedNetwork.rpcUrl);
|
||||||
}
|
}
|
||||||
}, [requestedChain, namespace]);
|
}, [requestedNetwork, namespace]);
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
@ -108,8 +124,8 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
const retrieveData = useCallback(
|
const retrieveData = useCallback(
|
||||||
async (requestAddress: string) => {
|
async (requestAddress: string) => {
|
||||||
const requestAccount = await retrieveSingleAccount(
|
const requestAccount = await retrieveSingleAccount(
|
||||||
requestedChain!.namespace,
|
requestedNetwork!.namespace,
|
||||||
requestedChain!.chainId,
|
requestedNetwork!.chainId,
|
||||||
requestAddress,
|
requestAddress,
|
||||||
);
|
);
|
||||||
if (!requestAccount) {
|
if (!requestAccount) {
|
||||||
@ -120,21 +136,31 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
setAccount(requestAccount);
|
setAccount(requestAccount);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[navigation, requestedChain],
|
[navigation, requestedNetwork],
|
||||||
);
|
);
|
||||||
|
|
||||||
const gasFees = useMemo(() => {
|
useEffect(() => {
|
||||||
if (namespace === EIP155) {
|
if (namespace === EIP155) {
|
||||||
return BigNumber.from(transaction.gasLimit)
|
const ethFees = BigNumber.from(transaction.gasLimit ?? ethGasLimit ?? 0)
|
||||||
.mul(BigNumber.from(transaction.gasPrice))
|
.mul(BigNumber.from(transaction.gasPrice ?? ethGasPrice ?? 0))
|
||||||
.toString();
|
.toString();
|
||||||
|
setFees(ethFees);
|
||||||
} else {
|
} else {
|
||||||
const gasPrice = GasPrice.fromString(transaction.gasPrice!.toString());
|
const gasPrice = GasPrice.fromString(
|
||||||
const cosmosFees = calculateFee(Number(transaction.gasLimit), gasPrice);
|
requestedNetwork?.gasPrice! + requestedNetwork?.nativeDenom,
|
||||||
|
);
|
||||||
|
const cosmosFees = calculateFee(Number(cosmosGasLimit), gasPrice);
|
||||||
|
|
||||||
return cosmosFees.amount[0].amount;
|
setFees(cosmosFees.amount[0].amount);
|
||||||
}
|
}
|
||||||
}, [transaction, namespace]);
|
}, [
|
||||||
|
transaction,
|
||||||
|
namespace,
|
||||||
|
ethGasLimit,
|
||||||
|
ethGasPrice,
|
||||||
|
cosmosGasLimit,
|
||||||
|
requestedNetwork,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
retrieveData(transaction.from!);
|
retrieveData(transaction.from!);
|
||||||
@ -146,21 +172,41 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
throw new Error('account not found');
|
throw new Error('account not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await approveWalletConnectRequest(
|
try {
|
||||||
|
const response = await approveWalletConnectRequest({
|
||||||
networksData,
|
networksData,
|
||||||
requestEvent,
|
requestEvent,
|
||||||
account,
|
account,
|
||||||
namespace!,
|
namespace,
|
||||||
requestedChain!.chainId,
|
chainId: requestedNetwork!.chainId,
|
||||||
'',
|
provider: namespace === EIP155 ? provider : cosmosStargateClient,
|
||||||
namespace === EIP155 ? provider : cosmosStargateClient,
|
// StdFee object
|
||||||
);
|
cosmosFee: {
|
||||||
|
// This amount is total fees required for transaction
|
||||||
|
amount: [
|
||||||
|
{
|
||||||
|
amount: fees,
|
||||||
|
denom: requestedNetwork!.nativeDenom!,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
gas: cosmosGasLimit,
|
||||||
|
},
|
||||||
|
ethGasLimit: transaction.gasLimit
|
||||||
|
? String(transaction.gasLimit)
|
||||||
|
: ethGasLimit,
|
||||||
|
ethGasPrice: transaction.gasPrice
|
||||||
|
? String(transaction.gasPrice)
|
||||||
|
: ethGasPrice,
|
||||||
|
});
|
||||||
|
|
||||||
const { topic } = requestEvent;
|
const { topic } = requestEvent;
|
||||||
await web3wallet!.respondSessionRequest({ topic, response });
|
await web3wallet!.respondSessionRequest({ topic, response });
|
||||||
setIsTxLoading(false);
|
|
||||||
|
|
||||||
navigation.navigate('Laconic');
|
navigation.navigate('Laconic');
|
||||||
|
} catch (error: any) {
|
||||||
|
setTxError(String(error));
|
||||||
|
setIsTxErrorDialogOpen(true);
|
||||||
|
}
|
||||||
|
setIsTxLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const rejectRequestHandler = async () => {
|
const rejectRequestHandler = async () => {
|
||||||
@ -187,7 +233,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
} else {
|
} else {
|
||||||
const cosmosBalance = await cosmosStargateClient?.getBalance(
|
const cosmosBalance = await cosmosStargateClient?.getBalance(
|
||||||
account.address,
|
account.address,
|
||||||
requestedChain!.nativeDenom!.toLowerCase(),
|
requestedNetwork!.nativeDenom!.toLowerCase(),
|
||||||
);
|
);
|
||||||
|
|
||||||
setBalance(cosmosBalance?.amount!);
|
setBalance(cosmosBalance?.amount!);
|
||||||
@ -195,7 +241,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getAccountBalance();
|
getAccountBalance();
|
||||||
}, [account, provider, namespace, cosmosStargateClient, requestedChain]);
|
}, [account, provider, namespace, cosmosStargateClient, requestedNetwork]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
@ -221,6 +267,37 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [navigation, route.name]);
|
}, [navigation, route.name]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getEthGas = async () => {
|
||||||
|
if (transaction.gasLimit && transaction.gasPrice) {
|
||||||
|
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]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -248,7 +325,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Balance (${
|
label={`Balance (${
|
||||||
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
namespace === EIP155 ? 'wei' : requestedNetwork!.nativeDenom
|
||||||
})`}
|
})`}
|
||||||
data={
|
data={
|
||||||
balance === '' || balance === undefined
|
balance === '' || balance === undefined
|
||||||
@ -261,20 +338,43 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
<DataBox label="To" data={transaction.to!} />
|
<DataBox label="To" data={transaction.to!} />
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Amount (${
|
label={`Amount (${
|
||||||
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
namespace === EIP155 ? 'wei' : requestedNetwork!.nativeDenom
|
||||||
})`}
|
})`}
|
||||||
data={BigNumber.from(
|
data={BigNumber.from(
|
||||||
transaction.value?.toString(),
|
transaction.value?.toString(),
|
||||||
).toString()}
|
).toString()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{namespace === EIP155 ? (
|
||||||
|
<>
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Gas Fees (${
|
label={`Gas Fees (${
|
||||||
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
namespace === EIP155
|
||||||
|
? 'wei'
|
||||||
|
: requestedNetwork!.nativeDenom
|
||||||
})`}
|
})`}
|
||||||
data={gasFees!}
|
data={fees!}
|
||||||
/>
|
/>
|
||||||
{namespace === EIP155 && (
|
|
||||||
<DataBox label="Data" data={transaction.data!} />
|
<DataBox label="Data" data={transaction.data!} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Text style={styles.dataBoxLabel}>{`Fees (${
|
||||||
|
requestedNetwork!.nativeDenom
|
||||||
|
})`}</Text>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={fees}
|
||||||
|
onChangeText={value => setFees(value)}
|
||||||
|
style={styles.transactionFeesInput}
|
||||||
|
/>
|
||||||
|
<Text style={styles.dataBoxLabel}>Gas Limit</Text>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={cosmosGasLimit}
|
||||||
|
onChangeText={value => setCosmosGasLimit(value)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@ -296,6 +396,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<TxErrorDialog
|
||||||
|
error={txError}
|
||||||
|
visible={isTxErrorDialogOpen}
|
||||||
|
hideDialog={() => setIsTxErrorDialogOpen(false)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,11 @@ import { StackParamsList } from '../types';
|
|||||||
import styles from '../styles/stylesheet';
|
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 { EMPTY_FIELD_ERROR, INVALID_URL_ERROR } from '../utils/constants';
|
import {
|
||||||
|
EIP155,
|
||||||
|
EMPTY_FIELD_ERROR,
|
||||||
|
INVALID_URL_ERROR,
|
||||||
|
} from '../utils/constants';
|
||||||
|
|
||||||
const networksFormDataSchema = z.object({
|
const networksFormDataSchema = z.object({
|
||||||
networkName: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
networkName: z.string().nonempty({ message: EMPTY_FIELD_ERROR }),
|
||||||
@ -25,6 +29,10 @@ const networksFormDataSchema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.url({ message: INVALID_URL_ERROR })
|
.url({ message: INVALID_URL_ERROR })
|
||||||
.or(z.literal('')),
|
.or(z.literal('')),
|
||||||
|
gasPrice: z
|
||||||
|
.string()
|
||||||
|
.nonempty({ message: EMPTY_FIELD_ERROR })
|
||||||
|
.regex(/^\d+(\.\d+)?$/),
|
||||||
});
|
});
|
||||||
|
|
||||||
type EditNetworkProps = NativeStackScreenProps<StackParamsList, 'EditNetwork'>;
|
type EditNetworkProps = NativeStackScreenProps<StackParamsList, 'EditNetwork'>;
|
||||||
@ -129,6 +137,25 @@ const EditNetwork = ({ route }: EditNetworkProps) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{networkData.namespace !== EIP155 ? (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="gasPrice"
|
||||||
|
defaultValue={networkData.gasPrice}
|
||||||
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={value}
|
||||||
|
label="Gas Price"
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChangeText={onChange}
|
||||||
|
/>
|
||||||
|
<HelperText type="error">{errors.gasPrice?.message}</HelperText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
|
@ -159,14 +159,14 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
throw new Error('Request event not found');
|
throw new Error('Request event not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await approveWalletConnectRequest(
|
const response = await approveWalletConnectRequest({
|
||||||
networksData,
|
networksData,
|
||||||
requestEvent,
|
requestEvent,
|
||||||
account,
|
account,
|
||||||
namespace,
|
namespace,
|
||||||
chainId,
|
chainId,
|
||||||
message,
|
message,
|
||||||
);
|
});
|
||||||
|
|
||||||
const { topic } = requestEvent;
|
const { topic } = requestEvent;
|
||||||
await web3wallet!.respondSessionRequest({ topic, response });
|
await web3wallet!.respondSessionRequest({ topic, response });
|
||||||
|
@ -271,6 +271,7 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginVertical: 10,
|
marginVertical: 10,
|
||||||
},
|
},
|
||||||
|
transactionFeesInput: { marginBottom: 10 },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default styles;
|
export default styles;
|
||||||
|
@ -57,6 +57,7 @@ export type NetworksFormData = {
|
|||||||
nativeDenom?: string;
|
nativeDenom?: string;
|
||||||
addressPrefix?: string;
|
addressPrefix?: string;
|
||||||
coinType?: string;
|
coinType?: string;
|
||||||
|
gasPrice?: string;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -135,7 +135,10 @@ const storeNetworkData = async (
|
|||||||
|
|
||||||
const updatedNetworks: NetworksDataState[] = [
|
const updatedNetworks: NetworksDataState[] = [
|
||||||
...retrievedNetworks,
|
...retrievedNetworks,
|
||||||
{ ...networkData, networkId: String(networkId) },
|
{
|
||||||
|
...networkData,
|
||||||
|
networkId: String(networkId),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
await setInternetCredentials(
|
await setInternetCredentials(
|
||||||
'networks',
|
'networks',
|
||||||
|
@ -23,6 +23,7 @@ export const DEFAULT_NETWORKS = [
|
|||||||
nativeDenom: 'uatom',
|
nativeDenom: 'uatom',
|
||||||
addressPrefix: 'cosmos',
|
addressPrefix: 'cosmos',
|
||||||
coinType: '118',
|
coinType: '118',
|
||||||
|
gasPrice: '0.025',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -4,12 +4,7 @@ import { 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';
|
||||||
import { getSdkError } from '@walletconnect/utils';
|
import { getSdkError } from '@walletconnect/utils';
|
||||||
import {
|
import { SigningStargateClient, coins, StdFee } from '@cosmjs/stargate';
|
||||||
SigningStargateClient,
|
|
||||||
coins,
|
|
||||||
GasPrice,
|
|
||||||
calculateFee,
|
|
||||||
} from '@cosmjs/stargate';
|
|
||||||
|
|
||||||
import { EIP155_SIGNING_METHODS } from './EIP155Data';
|
import { EIP155_SIGNING_METHODS } from './EIP155Data';
|
||||||
import { signDirectMessage, signEthMessage } from '../sign-message';
|
import { signDirectMessage, signEthMessage } from '../sign-message';
|
||||||
@ -17,15 +12,29 @@ import { Account, NetworksDataState } from '../../types';
|
|||||||
import { getMnemonic, getPathKey } from '../misc';
|
import { getMnemonic, getPathKey } from '../misc';
|
||||||
import { getCosmosAccounts } from '../accounts';
|
import { getCosmosAccounts } from '../accounts';
|
||||||
|
|
||||||
export async function approveWalletConnectRequest(
|
export async function approveWalletConnectRequest({
|
||||||
networksData: NetworksDataState[],
|
networksData,
|
||||||
requestEvent: SignClientTypes.EventArguments['session_request'],
|
requestEvent,
|
||||||
account: Account,
|
account,
|
||||||
namespace: string,
|
namespace,
|
||||||
chainId: string,
|
chainId,
|
||||||
message?: string,
|
message,
|
||||||
provider?: providers.JsonRpcProvider | SigningStargateClient,
|
provider,
|
||||||
) {
|
cosmosFee,
|
||||||
|
ethGasLimit,
|
||||||
|
ethGasPrice,
|
||||||
|
}: {
|
||||||
|
networksData: NetworksDataState[];
|
||||||
|
requestEvent: SignClientTypes.EventArguments['session_request'];
|
||||||
|
account: Account;
|
||||||
|
namespace: string;
|
||||||
|
chainId: string;
|
||||||
|
message?: string;
|
||||||
|
provider?: providers.JsonRpcProvider | SigningStargateClient;
|
||||||
|
cosmosFee?: StdFee;
|
||||||
|
ethGasLimit?: string;
|
||||||
|
ethGasPrice?: string;
|
||||||
|
}) {
|
||||||
const { params, id } = requestEvent;
|
const { params, id } = requestEvent;
|
||||||
const { request } = params;
|
const { request } = params;
|
||||||
|
|
||||||
@ -51,13 +60,18 @@ export async function approveWalletConnectRequest(
|
|||||||
).privKey;
|
).privKey;
|
||||||
const wallet = new Wallet(privKey);
|
const wallet = new Wallet(privKey);
|
||||||
const sendTransaction = request.params[0];
|
const sendTransaction = request.params[0];
|
||||||
|
const updatedTransaction = {
|
||||||
|
...sendTransaction,
|
||||||
|
gasLimit: ethGasLimit,
|
||||||
|
gasPrice: ethGasPrice,
|
||||||
|
};
|
||||||
|
|
||||||
if (!(provider instanceof providers.JsonRpcProvider)) {
|
if (!(provider instanceof providers.JsonRpcProvider)) {
|
||||||
throw new Error('Provider not found');
|
throw new Error('Provider not found');
|
||||||
}
|
}
|
||||||
const connectedWallet = wallet.connect(provider);
|
const connectedWallet = wallet.connect(provider);
|
||||||
|
|
||||||
const hash = await connectedWallet.sendTransaction(sendTransaction);
|
const hash = await connectedWallet.sendTransaction(updatedTransaction);
|
||||||
const receipt = typeof hash === 'string' ? hash : hash?.hash;
|
const receipt = typeof hash === 'string' ? hash : hash?.hash;
|
||||||
return formatJsonRpcResult(id, {
|
return formatJsonRpcResult(id, {
|
||||||
signature: receipt,
|
signature: receipt,
|
||||||
@ -118,20 +132,16 @@ export async function approveWalletConnectRequest(
|
|||||||
request.params[0].value,
|
request.params[0].value,
|
||||||
requestedChain!.nativeDenom!,
|
requestedChain!.nativeDenom!,
|
||||||
);
|
);
|
||||||
const gasPrice = GasPrice.fromString(
|
|
||||||
request.params[0].gasPrice.toString(),
|
|
||||||
);
|
|
||||||
const cosmosFee = calculateFee(
|
|
||||||
Number(request.params[0].gasLimit),
|
|
||||||
gasPrice,
|
|
||||||
);
|
|
||||||
|
|
||||||
const receiverAddress = request.params[0].to;
|
const receiverAddress = request.params[0].to;
|
||||||
|
|
||||||
if (!(provider instanceof SigningStargateClient)) {
|
if (!(provider instanceof SigningStargateClient)) {
|
||||||
throw new Error('Cosmos stargate client not found');
|
throw new Error('Cosmos stargate client not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cosmosFee) {
|
||||||
|
throw new Error('Cosmos fee not found');
|
||||||
|
}
|
||||||
|
|
||||||
const result = await provider.sendTokens(
|
const result = await provider.sendTokens(
|
||||||
address,
|
address,
|
||||||
receiverAddress,
|
receiverAddress,
|
||||||
|
Loading…
Reference in New Issue
Block a user