laconic-wallet/src/screens/ApproveTransaction.tsx
2024-03-28 16:51:58 +05:30

281 lines
8.3 KiB
TypeScript

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Image, ScrollView, View } from 'react-native';
import { ActivityIndicator, Button, Text, Appbar } from 'react-native-paper';
import { providers, BigNumber } from 'ethers';
import { useNavigation } from '@react-navigation/native';
import {
NativeStackNavigationProp,
NativeStackScreenProps,
} from '@react-navigation/native-stack';
import { getHeaderTitle } from '@react-navigation/elements';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
import {
calculateFee,
GasPrice,
SigningStargateClient,
} from '@cosmjs/stargate';
import { Account, StackParamsList } from '../types';
import AccountDetails from '../components/AccountDetails';
import styles from '../styles/stylesheet';
import { retrieveSingleAccount } from '../utils/accounts';
import {
approveWalletConnectRequest,
rejectWalletConnectRequest,
} from '../utils/wallet-connect/WalletConnectRequests';
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
import { EIP155_CHAINS } from '../utils/wallet-connect/EIP155Data';
import DataBox from '../components/DataBox';
import { getPathKey } from '../utils/misc';
import { COSMOS_TESTNET_CHAINS } from '../utils/wallet-connect/COSMOSData';
import { COSMOS_DENOM } from '../utils/constants';
type SignRequestProps = NativeStackScreenProps<
StackParamsList,
'ApproveTransaction'
>;
const ApproveTransaction = ({ route }: SignRequestProps) => {
const requestSession = route.params.requestSessionData;
const requestName = requestSession.peer.metadata.name;
const requestIcon = requestSession.peer.metadata.icons[0];
const requestURL = requestSession.peer.metadata.url;
const network = route.params.network;
const transaction = route.params.transaction;
const requestEvent = route.params.requestEvent;
const chainId = requestEvent.params.chainId;
const [account, setAccount] = useState<Account>();
const [isLoading, setIsLoading] = useState(true);
const [balance, setBalance] = useState<string>('');
const [isTxLoading, setIsTxLoading] = useState(false);
const [cosmosStargateClient, setCosmosStargateClient] =
useState<SigningStargateClient>();
const provider = useMemo(() => {
if (network === 'eth') {
return new providers.JsonRpcProvider(EIP155_CHAINS[chainId].rpc);
}
}, [chainId, network]);
const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>();
const retrieveData = useCallback(
async (requestAddress: string) => {
const requestAccount = await retrieveSingleAccount(
network,
requestAddress,
);
if (!requestAccount) {
navigation.navigate('InvalidPath');
return;
}
setAccount(requestAccount);
setIsLoading(false);
},
[navigation, network],
);
const gasFees = useMemo(() => {
if (network === 'eth') {
return BigNumber.from(transaction.gasLimit)
.mul(BigNumber.from(transaction.gasPrice))
.toString();
} else {
const gasPrice = GasPrice.fromString(transaction.gasPrice!.toString());
const cosmosFees = calculateFee(Number(transaction.gasLimit), gasPrice);
return cosmosFees.amount[0].amount;
}
}, [transaction, network]);
useEffect(() => {
retrieveData(transaction.from!);
}, [retrieveData, transaction]);
const acceptRequestHandler = async () => {
setIsTxLoading(true);
if (!account) {
throw new Error('account not found');
}
const response = await approveWalletConnectRequest(
requestEvent,
account,
network,
'',
network === 'eth' ? provider : cosmosStargateClient,
);
const { topic } = requestEvent;
await web3wallet!.respondSessionRequest({ topic, response });
setIsTxLoading(false);
navigation.navigate('Laconic');
};
const rejectRequestHandler = async () => {
const response = rejectWalletConnectRequest(requestEvent);
const { topic } = requestEvent;
await web3wallet!.respondSessionRequest({
topic,
response,
});
navigation.navigate('Laconic');
};
useEffect(() => {
const getAccountBalance = async (account: Account) => {
if (network === 'eth') {
const fetchedBalance =
provider && (await provider.getBalance(account.address));
setBalance(fetchedBalance ? fetchedBalance.toString() : '0');
} else {
const cosmosBalance = await cosmosStargateClient?.getBalance(
account.address,
COSMOS_DENOM,
);
setBalance(cosmosBalance?.amount!);
}
};
if (account) {
getAccountBalance(account);
}
}, [account, provider, network, cosmosStargateClient]);
useEffect(() => {
navigation.setOptions({
// eslint-disable-next-line react/no-unstable-nested-components
header: ({ options, back }) => {
const title = getHeaderTitle(options, 'Approve Transaction');
return (
<Appbar.Header>
{back && (
<Appbar.BackAction
onPress={async () => {
await rejectRequestHandler();
navigation.navigate('Laconic');
}}
/>
)}
<Appbar.Content title={title} />
</Appbar.Header>
);
},
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigation, route.name]);
useEffect(() => {
if (network !== 'cosmos') {
return;
}
const setClient = async () => {
if (!account) {
return;
}
const cosmosPrivKey = (await getPathKey('cosmos', account.counterId))
.privKey;
const sender = await DirectSecp256k1Wallet.fromKey(
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
);
const client = await SigningStargateClient.connectWithSigner(
COSMOS_TESTNET_CHAINS[chainId as string].rpc,
sender,
);
setCosmosStargateClient(client);
};
setClient();
}, [account, chainId, network]);
return (
<>
{isLoading ? (
<View style={styles.spinnerContainer}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
) : (
<ScrollView contentContainerStyle={styles.appContainer}>
<View style={styles.dappDetails}>
{requestIcon && (
<Image
style={styles.dappLogo}
source={requestIcon ? { uri: requestIcon } : undefined}
/>
)}
<Text>{requestName}</Text>
<Text variant="bodyMedium">{requestURL}</Text>
</View>
<View style={styles.dataBoxContainer}>
<Text style={styles.dataBoxLabel}>From</Text>
<View style={styles.dataBox}>
<AccountDetails account={account} />
</View>
</View>
<DataBox
label={`Balance ${
network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
}`}
data={
balance === '' || balance === undefined
? 'Loading balance...'
: `${balance}`
}
/>
{transaction && (
<View style={styles.approveTransaction}>
<DataBox label="To" data={transaction.to!} />
<DataBox
label={`Amount ${
network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
}`}
data={BigNumber.from(transaction.value?.toString()).toString()}
/>
<DataBox
label={`Gas Fees ${
network === 'eth' ? '(Wei)' : `(${COSMOS_DENOM})`
}`}
data={gasFees!}
/>
{network === 'eth' && (
<DataBox label="Data" data={transaction.data!} />
)}
</View>
)}
<View style={styles.buttonContainer}>
<Button
mode="contained"
onPress={acceptRequestHandler}
loading={isTxLoading}
disabled={!balance && !cosmosStargateClient}>
{isTxLoading ? 'Processing' : 'Yes'}
</Button>
<Button
mode="contained"
onPress={rejectRequestHandler}
buttonColor="#B82B0D">
No
</Button>
</View>
</ScrollView>
)}
</>
);
};
export default ApproveTransaction;