From db0b21ddd186f769d7fcf30631f569b0dc788c1d Mon Sep 17 00:00:00 2001 From: shreerang6921 <68148922+shreerang6921@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:57:47 +0530 Subject: [PATCH] Simulate gas limit for cosmos transactions (#111) * Simulate gas limit for cosmos transactions * Reject request if funds are not sufficient * Fix submit button disable condition * Dont estimate gas if funds are not sufficient for evm chains * Handle review changes --- .env.example | 3 +- react-native-config.d.ts | 2 +- src/screens/ApproveTransaction.tsx | 108 ++++++++++++++++-- .../wallet-connect/WalletConnectRequests.ts | 36 +++--- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/.env.example b/.env.example index 9687d13..aa54644 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ WALLET_CONNECT_PROJECT_ID= -DEFAULT_GAS_LIMIT= DEFAULT_GAS_PRICE= +# Reference: https://github.com/cosmos/cosmos-sdk/issues/16020 +DEFAULT_GAS_ADJUSTMENT=2 diff --git a/react-native-config.d.ts b/react-native-config.d.ts index 8f78aab..1dd6ba9 100644 --- a/react-native-config.d.ts +++ b/react-native-config.d.ts @@ -2,8 +2,8 @@ declare module 'react-native-config' { export interface NativeConfig { WALLET_CONNECT_PROJECT_ID: string; - DEFAULT_GAS_LIMIT: string; DEFAULT_GAS_PRICE: string; + DEFAULT_GAS_ADJUSTMENT: string; } export const Config: NativeConfig; diff --git a/src/screens/ApproveTransaction.tsx b/src/screens/ApproveTransaction.tsx index 4874f79..f2082aa 100644 --- a/src/screens/ApproveTransaction.tsx +++ b/src/screens/ApproveTransaction.tsx @@ -20,6 +20,7 @@ import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; import { calculateFee, GasPrice, + MsgSendEncodeObject, SigningStargateClient, } from '@cosmjs/stargate'; @@ -38,6 +39,8 @@ import { useNetworks } from '../context/NetworksContext'; import { COSMOS, EIP155 } from '../utils/constants'; import TxErrorDialog from '../components/TxErrorDialog'; +const MEMO = 'Sending signed tx from Laconic Wallet'; + type SignRequestProps = NativeStackScreenProps< StackParamsList, 'ApproveTransaction' @@ -60,21 +63,54 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { const [isTxLoading, setIsTxLoading] = useState(false); const [cosmosStargateClient, setCosmosStargateClient] = useState(); - const [fees, setFees] = useState(''); - const [cosmosGasLimit, setCosmosGasLimit] = useState( - Config.DEFAULT_GAS_LIMIT, - ); - const [txError, setTxError] = useState(''); + const [fees, setFees] = useState(); + const [cosmosGasLimit, setCosmosGasLimit] = useState(); + const [txError, setTxError] = useState(); const [isTxErrorDialogOpen, setIsTxErrorDialogOpen] = useState(false); const [ethGasPrice, setEthGasPrice] = useState(); const [ethGasLimit, setEthGasLimit] = useState(); + const isSufficientFunds = useMemo(() => { + if (!transaction.value) { + return; + } + + if (!balance) { + return; + } + + const amountBigNum = BigNumber.from(String(transaction.value)); + const balanceBigNum = BigNumber.from(balance); + + if (amountBigNum.gte(balanceBigNum)) { + return false; + } else { + return true; + } + }, [balance, transaction]); + const requestedNetwork = networksData.find( networkData => `${networkData.namespace}:${networkData.chainId}` === chainId, ); const namespace = requestedNetwork!.namespace; + const sendMsg: MsgSendEncodeObject = useMemo(() => { + return { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + fromAddress: transaction.from, + toAddress: transaction.to, + amount: [ + { + amount: String(transaction.value), + denom: requestedNetwork!.nativeDenom!, + }, + ], + }, + }; + }, [requestedNetwork, transaction]); + useEffect(() => { if (namespace !== COSMOS) { return; @@ -149,6 +185,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { const gasPrice = GasPrice.fromString( requestedNetwork?.gasPrice! + requestedNetwork?.nativeDenom, ); + + if (!cosmosGasLimit) { + return; + } + const cosmosFees = calculateFee(Number(cosmosGasLimit), gasPrice); setFees(cosmosFees.amount[0].amount); @@ -174,7 +215,6 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { try { const response = await approveWalletConnectRequest({ - networksData, requestEvent, account, namespace, @@ -185,11 +225,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { // This amount is total fees required for transaction amount: [ { - amount: fees, + amount: fees!, denom: requestedNetwork!.nativeDenom!, }, ], - gas: cosmosGasLimit, + gas: cosmosGasLimit!, }, ethGasLimit: transaction.gasLimit ? String(transaction.gasLimit) @@ -197,6 +237,8 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { ethGasPrice: transaction.gasPrice ? String(transaction.gasPrice) : ethGasPrice, + sendMsg, + memo: MEMO, }); const { topic } = requestEvent; @@ -273,6 +315,10 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { return; } + if (!isSufficientFunds) { + return; + } + if (!provider) { return; } @@ -296,7 +342,39 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { }; getEthGas(); - }, [provider, transaction]); + }, [provider, transaction, isSufficientFunds]); + + useEffect(() => { + const getCosmosGas = async () => { + if (!cosmosStargateClient) { + return; + } + if (!isSufficientFunds) { + return; + } + + const gasEstimation = await cosmosStargateClient.simulate( + transaction.from!, + [sendMsg], + MEMO, + ); + + setCosmosGasLimit( + String( + Math.round(gasEstimation * Number(Config.DEFAULT_GAS_ADJUSTMENT)), + ), + ); + }; + + getCosmosGas(); + }, [cosmosStargateClient, isSufficientFunds, sendMsg, transaction]); + + useEffect(() => { + if (balance && !isSufficientFunds) { + setTxError('Insufficient funds'); + setIsTxErrorDialogOpen(true); + } + }, [isSufficientFunds, balance]); return ( <> @@ -384,7 +462,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => { mode="contained" onPress={acceptRequestHandler} loading={isTxLoading} - disabled={!balance && !cosmosStargateClient}> + disabled={!balance || !fees}> {isTxLoading ? 'Processing' : 'Yes'}