Implement functionality to approve tx

This commit is contained in:
Isha 2024-11-07 19:58:28 +05:30
parent 92e7d058b0
commit f82ff49d77

View File

@ -1,9 +1,38 @@
import { useCallback, useEffect } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import { ScrollView, View } from 'react-native';
import {
Button,
Text,
TextInput,
} from 'react-native-paper';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
import {
calculateFee,
GasPrice,
SigningStargateClient,
} from '@cosmjs/stargate';
import { createWallet, retrieveAccounts } from '../utils/accounts';
import AccountDetails from '../components/AccountDetails';
import styles from '../styles/stylesheet';
import { retrieveSingleAccount } from '../utils/accounts';
import DataBox from '../components/DataBox';
import { getPathKey } from '../utils/misc';
import { useNetworks } from '../context/NetworksContext';
import TxErrorDialog from '../components/TxErrorDialog';
import { BigNumber } from 'ethers';
import { MEMO } from '../screens/ApproveTransfer';
export const WalletEmbed = () => {
const [isTxRequested, setIsTxRequested] = useState<boolean>(false);
const [transactionDetails, setTransactionDetails] = useState<any>(null);
const [fees, setFees] = useState('');
const [gasLimit, setGasLimit] = useState('');
const [isTxLoading, setIsTxLoading] = useState(false);
const [txError, setTxError] = useState<string | null>(null);
const { networksData } = useNetworks();
const getAccountsData = useCallback(async (chainId: string) => {
@ -85,5 +114,215 @@ export const WalletEmbed = () => {
};
}, [networksData, getAccountsData]);
return null;
const handleTxRequested = async (event: MessageEvent) => {
if (event.data.type !== 'REQUEST_TX') return;
const { chainId, fromAddress, toAddress, amount } = event.data;
const network = networksData.find(
net => net.chainId === chainId
);
if (!network) {
console.error('Network not found');
return;
}
const account = await retrieveSingleAccount(network.namespace, network.chainId, fromAddress);
if (!account) {
throw new Error('Account not found');
}
const cosmosPrivKey = (
await getPathKey(`${network.namespace}:${chainId}`, account.index)
).privKey;
const sender = await DirectSecp256k1Wallet.fromKey(
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
network.addressPrefix
);
const client = await SigningStargateClient.connectWithSigner(
network.rpcUrl!,
sender
);
const balance = await client?.getBalance(
account.address,
network!.nativeDenom!.toLowerCase(),
);
const gasPrice = GasPrice.fromString(
network.gasPrice! + network.nativeDenom,
);
const sendMsg = {
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: {
fromAddress: fromAddress,
toAddress: toAddress,
amount: [
{
amount: String(amount),
denom: network.nativeDenom!,
},
],
},
};
const gasEstimation = await client.simulate(
fromAddress,
[sendMsg],
MEMO,
);
const gasLimit =
String(
Math.round(gasEstimation * Number(process.env.REACT_APP_GAS_ADJUSTMENT)),
)
const cosmosFees = calculateFee(Number(gasLimit), gasPrice);
setGasLimit(gasLimit);
setFees(cosmosFees.amount[0].amount);
setTransactionDetails({
chainId,
fromAddress,
toAddress,
amount,
account,
balance: balance.amount,
requestedNetwork: network,
});
setIsTxRequested(true);
};
useEffect(() => {
window.addEventListener('message', handleTxRequested);
return () => window.removeEventListener('message', handleTxRequested);
}, [networksData]);
const acceptRequestHandler = async () => {
try {
setIsTxLoading(true);
const { chainId, fromAddress, toAddress, amount, requestedNetwork } =
transactionDetails;
const balanceBigNum = BigNumber.from(transactionDetails.balance);
const amountBigNum = BigNumber.from(String(amount));
if (amountBigNum.gte(balanceBigNum)) {
throw new Error('Insufficient funds');
}
const cosmosPrivKey = (
await getPathKey(`${requestedNetwork.namespace}:${chainId}`, transactionDetails.account.index)
).privKey;
const sender = await DirectSecp256k1Wallet.fromKey(
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
requestedNetwork.addressPrefix
);
const client = await SigningStargateClient.connectWithSigner(
requestedNetwork.rpcUrl!,
sender
);
const fee = calculateFee(
Number(gasLimit),
GasPrice.fromString(`${requestedNetwork.gasPrice}${requestedNetwork.nativeDenom}`)
);
const txResult = await client.sendTokens(
fromAddress,
toAddress,
[{ amount: String(amount), denom: requestedNetwork.nativeDenom }],
fee
);
sendMessage(window, 'TRANSACTION_SUCCESS', txResult.transactionHash, '*');
} catch (error) {
console.error('Transaction error:', error);
setTxError('Transaction failed');
} finally {
setIsTxLoading(false);
}
};
const rejectRequestHandler = () => {
setIsTxRequested(false);
setTransactionDetails(null);
sendMessage(window, 'TRANSACTION_REJECTED', null, '*');
};
return (
<>
{isTxRequested && transactionDetails ? (
<>
<ScrollView contentContainerStyle={styles.appContainer}>
<View style={styles.dataBoxContainer}>
<Text style={styles.dataBoxLabel}>From</Text>
<View style={styles.dataBox}>
<AccountDetails account={transactionDetails.account} />
</View>
</View>
<DataBox
label={`Balance (${transactionDetails.requestedNetwork.nativeDenom})`}
data={
transactionDetails.balance === '' ||
transactionDetails.balance === undefined
? 'Loading balance...'
: `${transactionDetails.balance}`
}
/>
<View style={styles.approveTransfer}>
<DataBox label="To" data={transactionDetails.toAddress} />
<DataBox
label={`Amount (${transactionDetails.requestedNetwork.nativeDenom})`}
data={transactionDetails.amount}
/>
<TextInput
mode="outlined"
label="Fee"
value={fees}
onChangeText={setFees}
style={styles.transactionFeesInput}
/>
<TextInput
mode="outlined"
label="Gas Limit"
value={gasLimit}
onChangeText={value =>
/^\d+$/.test(value) ? setGasLimit(value) : null
}
/>
</View>
</ScrollView>
<View style={styles.buttonContainer}>
<Button
mode="contained"
onPress={acceptRequestHandler}
loading={isTxLoading}
disabled={!transactionDetails.balance || !fees}
>
{isTxLoading ? 'Processing' : 'Yes'}
</Button>
<Button
mode="contained"
onPress={rejectRequestHandler}
buttonColor="#B82B0D"
>
No
</Button>
</View>
</>
) : null}
<TxErrorDialog
error={txError!}
visible={!!txError}
hideDialog={() => setTxError(null)}
/>
</>
);
};