forked from cerc-io/laconic-wallet
281 lines
8.3 KiB
TypeScript
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;
|