forked from cerc-io/laconic-wallet
Add support to configure networks (#76)
* Add support to configure cosmos networks * Fix intents functionality for configured networks * Add address prefix while initializing stargate client * Remove unnecessary functions * Update style for add network page * Handle review changes * Fix eth accounts not showing up on configured eth chain
This commit is contained in:
parent
bdd1b58140
commit
e4fe88939c
@ -1,30 +1,22 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { List } from 'react-native-paper';
|
import { List } from 'react-native-paper';
|
||||||
|
|
||||||
import { NetworkDropdownProps } from '../types';
|
import { NetworkDropdownProps, NetworksDataState } from '../types';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
|
|
||||||
const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||||
const [expanded, setExpanded] = useState<boolean>(false);
|
|
||||||
const [selectedNetwork, setSelectedNetwork] = useState<string>('Ethereum');
|
|
||||||
|
|
||||||
const { networksData } = useAccounts();
|
const { networksData } = useAccounts();
|
||||||
|
|
||||||
const networks = useMemo(() => {
|
const [expanded, setExpanded] = useState<boolean>(false);
|
||||||
return networksData.map(network => {
|
const [selectedNetwork, setSelectedNetwork] = useState<string>(
|
||||||
return {
|
networksData[0].networkName,
|
||||||
value: network.networkType,
|
);
|
||||||
chainId: network.chainId,
|
|
||||||
displayName: network.networkName,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [networksData]);
|
|
||||||
|
|
||||||
const handleNetworkPress = (network: string, displayName: string) => {
|
const handleNetworkPress = (networksData: NetworksDataState) => {
|
||||||
updateNetwork(network);
|
updateNetwork(networksData);
|
||||||
setSelectedNetwork(displayName);
|
setSelectedNetwork(networksData.networkName);
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,13 +26,11 @@ const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
|||||||
title={selectedNetwork}
|
title={selectedNetwork}
|
||||||
expanded={expanded}
|
expanded={expanded}
|
||||||
onPress={() => setExpanded(!expanded)}>
|
onPress={() => setExpanded(!expanded)}>
|
||||||
{networks.map(network => (
|
{networksData.map(networkData => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={network.chainId}
|
key={networkData.chainId}
|
||||||
title={network.displayName}
|
title={networkData.networkName}
|
||||||
onPress={() =>
|
onPress={() => handleNetworkPress(networkData)}
|
||||||
handleNetworkPress(network.value, network.displayName)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</List.Accordion>
|
</List.Accordion>
|
||||||
|
48
src/components/SelectNetworkType.tsx
Normal file
48
src/components/SelectNetworkType.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { Text, List } from 'react-native-paper';
|
||||||
|
|
||||||
|
import styles from '../styles/stylesheet';
|
||||||
|
|
||||||
|
const SelectNetworkType = ({
|
||||||
|
updateNetworkType,
|
||||||
|
}: {
|
||||||
|
updateNetworkType: (networkType: string) => void;
|
||||||
|
}) => {
|
||||||
|
const [expanded, setExpanded] = useState<boolean>(false);
|
||||||
|
const [selectedNetwork, setSelectedNetwork] = useState<string>('ETH');
|
||||||
|
|
||||||
|
const networks = ['ETH', 'COSMOS'];
|
||||||
|
|
||||||
|
const handleNetworkPress = (network: string) => {
|
||||||
|
setSelectedNetwork(network);
|
||||||
|
updateNetworkType(network.toLowerCase());
|
||||||
|
setExpanded(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.networkDropdown}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 10,
|
||||||
|
}}>
|
||||||
|
Select Network Type
|
||||||
|
</Text>
|
||||||
|
<List.Accordion
|
||||||
|
title={selectedNetwork}
|
||||||
|
expanded={expanded}
|
||||||
|
onPress={() => setExpanded(!expanded)}>
|
||||||
|
{networks.map(network => (
|
||||||
|
<List.Item
|
||||||
|
key={network}
|
||||||
|
title={network}
|
||||||
|
onPress={() => handleNetworkPress(network)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</List.Accordion>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { SelectNetworkType };
|
@ -34,6 +34,7 @@ const AccountsProvider = ({ children }: { children: any }) => {
|
|||||||
ethAccounts: [],
|
ethAccounts: [],
|
||||||
cosmosAccounts: [],
|
cosmosAccounts: [],
|
||||||
});
|
});
|
||||||
|
// TODO: Replace chainId values with testnet chainIds
|
||||||
const [networksData, setNetworksData] = useState<NetworksDataState[]>([
|
const [networksData, setNetworksData] = useState<NetworksDataState[]>([
|
||||||
{
|
{
|
||||||
chainId: 'eip155:11155111',
|
chainId: 'eip155:11155111',
|
||||||
@ -47,7 +48,9 @@ const AccountsProvider = ({ children }: { children: any }) => {
|
|||||||
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
||||||
networkType: 'cosmos',
|
networkType: 'cosmos',
|
||||||
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
||||||
currencySymbol: 'ATOM',
|
nativeDenom: 'ATOM',
|
||||||
|
addressPrefix: 'cosmos',
|
||||||
|
coinType: '118',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import React from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { View } from 'react-native';
|
import { ScrollView } from 'react-native';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import { TextInput, Button, HelperText } from 'react-native-paper';
|
import { TextInput, Button, HelperText } from 'react-native-paper';
|
||||||
|
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { NetworksDataState, StackParamsList } from '../types';
|
import { NetworksDataState, StackParamsList } from '../types';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
import { SelectNetworkType } from '../components/SelectNetworkType';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
|
||||||
|
|
||||||
// TODO: Add validation to form inputs
|
// TODO: Add validation to form inputs
|
||||||
const AddNetwork = () => {
|
const AddNetwork = () => {
|
||||||
@ -24,12 +26,28 @@ const AddNetwork = () => {
|
|||||||
|
|
||||||
const { networksData, setNetworksData } = useAccounts();
|
const { networksData, setNetworksData } = useAccounts();
|
||||||
|
|
||||||
const submit = (data: NetworksDataState) => {
|
const [networkType, setNetworkType] = useState<string>('eth');
|
||||||
setNetworksData([...networksData, data]);
|
|
||||||
navigation.navigate('Laconic');
|
// TODO: Update session when new network is added with updated addresses
|
||||||
|
const updateNetworkType = (newNetworkType: string) => {
|
||||||
|
setNetworkType(newNetworkType);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const submit = useCallback(
|
||||||
|
async (data: NetworksDataState) => {
|
||||||
|
const updatedData = {
|
||||||
|
...data,
|
||||||
|
networkType,
|
||||||
|
};
|
||||||
|
setNetworksData([...networksData, updatedData]);
|
||||||
|
|
||||||
|
navigation.navigate('Laconic');
|
||||||
|
},
|
||||||
|
[navigation, networkType, networksData, setNetworksData],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<View style={styles.signPage}>
|
// TODO: get form data from json file
|
||||||
|
<ScrollView contentContainerStyle={styles.addNetwork}>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
@ -81,23 +99,6 @@ const AddNetwork = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="currencySymbol"
|
|
||||||
defaultValue=""
|
|
||||||
render={({ field: { onChange, onBlur, value } }) => (
|
|
||||||
<>
|
|
||||||
<TextInput
|
|
||||||
mode="outlined"
|
|
||||||
value={value}
|
|
||||||
label="Currency Symbol"
|
|
||||||
onBlur={onBlur}
|
|
||||||
onChangeText={value => onChange(value)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<HelperText type="error">{errors.currencySymbol?.message}</HelperText>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
@ -111,35 +112,99 @@ const AddNetwork = () => {
|
|||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onChangeText={value => onChange(value)}
|
onChangeText={value => onChange(value)}
|
||||||
/>
|
/>
|
||||||
|
<HelperText type="error">
|
||||||
|
{errors.blockExplorerUrl?.message}
|
||||||
|
</HelperText>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<HelperText type="error">{errors.blockExplorerUrl?.message}</HelperText>
|
<SelectNetworkType updateNetworkType={updateNetworkType} />
|
||||||
<Controller
|
{networkType === 'eth' ? (
|
||||||
control={control}
|
<Controller
|
||||||
// TODO: Use state to toggle between 'eth' and 'cosmos'
|
control={control}
|
||||||
defaultValue="eth"
|
name="currencySymbol"
|
||||||
name="networkType"
|
defaultValue=""
|
||||||
render={({ field: { onBlur, value } }) => (
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
mode="outlined"
|
mode="outlined"
|
||||||
value={value}
|
value={value}
|
||||||
disabled
|
label="Currency Symbol"
|
||||||
label="Block Explorer URL (Optional)"
|
onBlur={onBlur}
|
||||||
onBlur={onBlur}
|
onChangeText={value => onChange(value)}
|
||||||
/>
|
/>
|
||||||
</>
|
<HelperText type="error">
|
||||||
)}
|
{errors.currencySymbol?.message}
|
||||||
/>
|
</HelperText>
|
||||||
<HelperText type="error">{errors.blockExplorerUrl?.message}</HelperText>
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="nativeDenom"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={value}
|
||||||
|
label="Native Denom"
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChangeText={value => onChange(value)}
|
||||||
|
/>
|
||||||
|
<HelperText type="error">
|
||||||
|
{errors.nativeDenom?.message}
|
||||||
|
</HelperText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="addressPrefix"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={value}
|
||||||
|
label="Address Prefix"
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChangeText={value => onChange(value)}
|
||||||
|
/>
|
||||||
|
<HelperText type="error">
|
||||||
|
{errors.addressPrefix?.message}
|
||||||
|
</HelperText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="coinType"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { onChange, onBlur, value } }) => (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
mode="outlined"
|
||||||
|
value={value}
|
||||||
|
label="Coin Type"
|
||||||
|
onBlur={onBlur}
|
||||||
|
onChangeText={value => onChange(value)}
|
||||||
|
/>
|
||||||
|
<HelperText type="error">{errors.coinType?.message}</HelperText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={handleSubmit(submit)}
|
onPress={handleSubmit(submit)}
|
||||||
disabled={!isValid}>
|
disabled={!isValid}>
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,19 +55,19 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
const [cosmosStargateClient, setCosmosStargateClient] =
|
const [cosmosStargateClient, setCosmosStargateClient] =
|
||||||
useState<SigningStargateClient>();
|
useState<SigningStargateClient>();
|
||||||
|
|
||||||
|
const requestedChain = networksData.find(
|
||||||
|
networkData => networkData.chainId === chainId,
|
||||||
|
);
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
if (network === 'eth') {
|
if (network === 'eth') {
|
||||||
const currentChain = networksData.find(
|
if (!requestedChain) {
|
||||||
networkData => networkData.chainId === chainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!currentChain) {
|
|
||||||
throw new Error('Requested chain not supported');
|
throw new Error('Requested chain not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new providers.JsonRpcProvider(currentChain.rpcUrl);
|
return new providers.JsonRpcProvider(requestedChain.rpcUrl);
|
||||||
}
|
}
|
||||||
}, [chainId, network, networksData]);
|
}, [requestedChain, network]);
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
@ -77,6 +77,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
const requestAccount = await retrieveSingleAccount(
|
const requestAccount = await retrieveSingleAccount(
|
||||||
network,
|
network,
|
||||||
requestAddress,
|
requestAddress,
|
||||||
|
requestedChain?.addressPrefix,
|
||||||
);
|
);
|
||||||
if (!requestAccount) {
|
if (!requestAccount) {
|
||||||
navigation.navigate('InvalidPath');
|
navigation.navigate('InvalidPath');
|
||||||
@ -86,7 +87,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
setAccount(requestAccount);
|
setAccount(requestAccount);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[navigation, network],
|
[navigation, network, requestedChain],
|
||||||
);
|
);
|
||||||
|
|
||||||
const gasFees = useMemo(() => {
|
const gasFees = useMemo(() => {
|
||||||
@ -199,6 +200,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
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,
|
||||||
);
|
);
|
||||||
|
|
||||||
const client = await SigningStargateClient.connectWithSigner(
|
const client = await SigningStargateClient.connectWithSigner(
|
||||||
@ -210,7 +212,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setClient();
|
setClient();
|
||||||
}, [account, chainId, network]);
|
}, [account, requestedChain, chainId, network]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -15,7 +15,7 @@ import ResetWalletDialog from '../components/ResetWalletDialog';
|
|||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
import { useWalletConnect } from '../context/WalletConnectContext';
|
import { useWalletConnect } from '../context/WalletConnectContext';
|
||||||
import { StackParamsList } from '../types';
|
import { NetworksDataState, StackParamsList } from '../types';
|
||||||
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
|
|
||||||
const WCLogo = () => {
|
const WCLogo = () => {
|
||||||
@ -35,6 +35,7 @@ const HomeScreen = () => {
|
|||||||
setCurrentIndex,
|
setCurrentIndex,
|
||||||
networkType,
|
networkType,
|
||||||
setNetworkType,
|
setNetworkType,
|
||||||
|
networksData,
|
||||||
} = useAccounts();
|
} = useAccounts();
|
||||||
const { setActiveSessions } = useWalletConnect();
|
const { setActiveSessions } = useWalletConnect();
|
||||||
|
|
||||||
@ -59,6 +60,9 @@ const HomeScreen = () => {
|
|||||||
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
|
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
|
||||||
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
||||||
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
||||||
|
const [currentChainId, setCurrentChainId] = useState<string>(
|
||||||
|
networksData[0].chainId,
|
||||||
|
);
|
||||||
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
||||||
const [isAccountsFetched, setIsAccountsFetched] = useState<boolean>(false);
|
const [isAccountsFetched, setIsAccountsFetched] = useState<boolean>(false);
|
||||||
const [phrase, setPhrase] = useState('');
|
const [phrase, setPhrase] = useState('');
|
||||||
@ -103,8 +107,9 @@ const HomeScreen = () => {
|
|||||||
setNetworkType('eth');
|
setNetworkType('eth');
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateNetwork = (newNetwork: string) => {
|
const updateNetwork = (networksData: NetworksDataState) => {
|
||||||
setNetworkType(newNetwork);
|
setNetworkType(networksData.networkType);
|
||||||
|
setCurrentChainId(networksData.chainId);
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -114,18 +119,25 @@ const HomeScreen = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAccounts = async () => {
|
const fetchAccounts = async () => {
|
||||||
if (isAccountsFetched) {
|
if (!currentChainId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { ethLoadedAccounts, cosmosLoadedAccounts } =
|
|
||||||
await retrieveAccounts();
|
|
||||||
|
|
||||||
if (ethLoadedAccounts && cosmosLoadedAccounts) {
|
const currentNetwork = networksData.find(
|
||||||
|
networkData => networkData.chainId === currentChainId,
|
||||||
|
);
|
||||||
|
if (!currentNetwork) {
|
||||||
|
throw new Error('Current Network not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ethLoadedAccounts, cosmosLoadedAccounts } =
|
||||||
|
await retrieveAccounts(currentNetwork.addressPrefix);
|
||||||
|
|
||||||
|
if (cosmosLoadedAccounts && ethLoadedAccounts) {
|
||||||
setAccounts({
|
setAccounts({
|
||||||
ethAccounts: ethLoadedAccounts,
|
ethAccounts: ethLoadedAccounts,
|
||||||
cosmosAccounts: cosmosLoadedAccounts,
|
cosmosAccounts: cosmosLoadedAccounts,
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsWalletCreated(true);
|
setIsWalletCreated(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +145,7 @@ const HomeScreen = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchAccounts();
|
fetchAccounts();
|
||||||
}, [isAccountsFetched, setAccounts]);
|
}, [currentChainId, networksData, setAccounts]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.appContainer}>
|
<View style={styles.appContainer}>
|
||||||
@ -144,10 +156,7 @@ const HomeScreen = () => {
|
|||||||
</View>
|
</View>
|
||||||
) : isWalletCreated ? (
|
) : isWalletCreated ? (
|
||||||
<>
|
<>
|
||||||
<NetworkDropdown
|
<NetworkDropdown updateNetwork={updateNetwork} />
|
||||||
selectedNetwork={networkType}
|
|
||||||
updateNetwork={updateNetwork}
|
|
||||||
/>
|
|
||||||
<View style={styles.accountComponent}>
|
<View style={styles.accountComponent}>
|
||||||
<Accounts
|
<Accounts
|
||||||
network={networkType}
|
network={networkType}
|
||||||
|
@ -21,10 +21,13 @@ import {
|
|||||||
} from '../utils/wallet-connect/WalletConnectRequests';
|
} from '../utils/wallet-connect/WalletConnectRequests';
|
||||||
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
||||||
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
|
|
||||||
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
|
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
|
||||||
|
|
||||||
const SignRequest = ({ route }: SignRequestProps) => {
|
const SignRequest = ({ route }: SignRequestProps) => {
|
||||||
|
const { networksData } = useAccounts();
|
||||||
|
|
||||||
const requestSession = route.params.requestSessionData;
|
const requestSession = route.params.requestSessionData;
|
||||||
const requestName = requestSession?.peer?.metadata?.name;
|
const requestName = requestSession?.peer?.metadata?.name;
|
||||||
const requestIcon = requestSession?.peer?.metadata?.icons[0];
|
const requestIcon = requestSession?.peer?.metadata?.icons[0];
|
||||||
@ -35,6 +38,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
const [network, setNetwork] = useState<string>('');
|
const [network, setNetwork] = useState<string>('');
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [isApproving, setIsApproving] = useState(false);
|
const [isApproving, setIsApproving] = useState(false);
|
||||||
|
const [isRejecting, setIsRejecting] = useState(false);
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
@ -68,9 +72,16 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
requestAddress: string,
|
requestAddress: string,
|
||||||
requestMessage: string,
|
requestMessage: string,
|
||||||
) => {
|
) => {
|
||||||
|
const currentChain = networksData.find(networkData => {
|
||||||
|
if (networkData.addressPrefix) {
|
||||||
|
return requestAddress.includes(networkData.addressPrefix);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const requestAccount = await retrieveSingleAccount(
|
const requestAccount = await retrieveSingleAccount(
|
||||||
requestNetwork,
|
requestNetwork,
|
||||||
requestAddress,
|
requestAddress,
|
||||||
|
currentChain?.addressPrefix,
|
||||||
);
|
);
|
||||||
if (!requestAccount) {
|
if (!requestAccount) {
|
||||||
navigation.navigate('InvalidPath');
|
navigation.navigate('InvalidPath');
|
||||||
@ -88,7 +99,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[account, message, navigation, network],
|
[account, message, navigation, network, networksData],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sanitizePath = useCallback(
|
const sanitizePath = useCallback(
|
||||||
@ -178,6 +189,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rejectRequestHandler = async () => {
|
const rejectRequestHandler = async () => {
|
||||||
|
setIsRejecting(true);
|
||||||
if (route.params?.requestEvent) {
|
if (route.params?.requestEvent) {
|
||||||
const response = rejectWalletConnectRequest(route.params?.requestEvent);
|
const response = rejectWalletConnectRequest(route.params?.requestEvent);
|
||||||
const { topic } = route.params?.requestEvent;
|
const { topic } = route.params?.requestEvent;
|
||||||
@ -186,6 +198,8 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
response,
|
response,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsRejecting(false);
|
||||||
navigation.navigate('Laconic');
|
navigation.navigate('Laconic');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -263,6 +277,7 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
<Button
|
<Button
|
||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={rejectRequestHandler}
|
onPress={rejectRequestHandler}
|
||||||
|
loading={isRejecting}
|
||||||
buttonColor="#B82B0D">
|
buttonColor="#B82B0D">
|
||||||
No
|
No
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -48,6 +48,10 @@ const styles = StyleSheet.create({
|
|||||||
signPage: {
|
signPage: {
|
||||||
paddingHorizontal: 24,
|
paddingHorizontal: 24,
|
||||||
},
|
},
|
||||||
|
addNetwork: {
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
marginTop: 30,
|
||||||
|
},
|
||||||
accountInfo: {
|
accountInfo: {
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
marginBottom: 30,
|
marginBottom: 30,
|
||||||
|
12
src/types.ts
12
src/types.ts
@ -45,12 +45,7 @@ export type AccountsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NetworkDropdownProps = {
|
export type NetworkDropdownProps = {
|
||||||
selectedNetwork: string;
|
updateNetwork: (networksData: NetworksDataState) => void;
|
||||||
updateNetwork: (network: string) => void;
|
|
||||||
customNetwork?: {
|
|
||||||
value: string;
|
|
||||||
displayName: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AccountsState = {
|
export type AccountsState = {
|
||||||
@ -62,9 +57,12 @@ export type NetworksDataState = {
|
|||||||
networkName: string;
|
networkName: string;
|
||||||
rpcUrl: string;
|
rpcUrl: string;
|
||||||
chainId: string;
|
chainId: string;
|
||||||
currencySymbol: string;
|
currencySymbol?: string;
|
||||||
blockExplorerUrl?: string;
|
blockExplorerUrl?: string;
|
||||||
networkType: string;
|
networkType: string;
|
||||||
|
nativeDenom?: string;
|
||||||
|
addressPrefix?: string;
|
||||||
|
coinType?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SignMessageParams = {
|
export type SignMessageParams = {
|
||||||
|
@ -124,16 +124,17 @@ const addAccountFromHDPath = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveAccountsForNetwork = async (
|
export const retrieveAccountsForNetwork = async (
|
||||||
network: string,
|
network: string,
|
||||||
count: string,
|
accountsIndices: string,
|
||||||
|
prefix: string = 'cosmos',
|
||||||
): Promise<Account[]> => {
|
): Promise<Account[]> => {
|
||||||
const elementsArray = count.split(',');
|
const accountsIndexArray = accountsIndices.split(',');
|
||||||
|
|
||||||
const loadedAccounts = await Promise.all(
|
const loadedAccounts = await Promise.all(
|
||||||
elementsArray.map(async i => {
|
accountsIndexArray.map(async i => {
|
||||||
const pubKey = (await getPathKey(network, Number(i))).pubKey;
|
const pubKey = (await getPathKey(network, Number(i), prefix)).pubKey;
|
||||||
const address = (await getPathKey(network, Number(i))).address;
|
const address = (await getPathKey(network, Number(i), prefix)).address;
|
||||||
const path = (await getPathKey(network, Number(i))).path;
|
const path = (await getPathKey(network, Number(i))).path;
|
||||||
const hdPath = getHDPath(network, path);
|
const hdPath = getHDPath(network, path);
|
||||||
|
|
||||||
@ -150,7 +151,9 @@ const retrieveAccountsForNetwork = async (
|
|||||||
return loadedAccounts;
|
return loadedAccounts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveAccounts = async (): Promise<{
|
const retrieveAccounts = async (
|
||||||
|
prefix: string = 'cosmos',
|
||||||
|
): Promise<{
|
||||||
ethLoadedAccounts?: Account[];
|
ethLoadedAccounts?: Account[];
|
||||||
cosmosLoadedAccounts?: Account[];
|
cosmosLoadedAccounts?: Account[];
|
||||||
}> => {
|
}> => {
|
||||||
@ -163,13 +166,17 @@ const retrieveAccounts = async (): Promise<{
|
|||||||
? await retrieveAccountsForNetwork('eth', ethCounter)
|
? await retrieveAccountsForNetwork('eth', ethCounter)
|
||||||
: undefined;
|
: undefined;
|
||||||
const cosmosLoadedAccounts = cosmosCounter
|
const cosmosLoadedAccounts = cosmosCounter
|
||||||
? await retrieveAccountsForNetwork('cosmos', cosmosCounter)
|
? await retrieveAccountsForNetwork('cosmos', cosmosCounter, prefix)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return { ethLoadedAccounts, cosmosLoadedAccounts };
|
return { ethLoadedAccounts, cosmosLoadedAccounts };
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveSingleAccount = async (network: string, address: string) => {
|
const retrieveSingleAccount = async (
|
||||||
|
network: string,
|
||||||
|
address: string,
|
||||||
|
prefix: string = 'cosmos',
|
||||||
|
) => {
|
||||||
let loadedAccounts;
|
let loadedAccounts;
|
||||||
|
|
||||||
switch (network) {
|
switch (network) {
|
||||||
@ -190,6 +197,7 @@ const retrieveSingleAccount = async (network: string, address: string) => {
|
|||||||
loadedAccounts = await retrieveAccountsForNetwork(
|
loadedAccounts = await retrieveAccountsForNetwork(
|
||||||
network,
|
network,
|
||||||
cosmosCounter,
|
cosmosCounter,
|
||||||
|
prefix,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -228,6 +236,7 @@ const accountInfoFromHDPath = async (
|
|||||||
| { privKey: string; pubKey: string; address: string; network: string }
|
| { privKey: string; pubKey: string; address: string; network: string }
|
||||||
| undefined
|
| undefined
|
||||||
> => {
|
> => {
|
||||||
|
// TODO: move HDNode inside eth switch case
|
||||||
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
||||||
if (!mnemonicStore) {
|
if (!mnemonicStore) {
|
||||||
throw new Error('Mnemonic not found!');
|
throw new Error('Mnemonic not found!');
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
setInternetCredentials,
|
setInternetCredentials,
|
||||||
} from 'react-native-keychain';
|
} from 'react-native-keychain';
|
||||||
|
|
||||||
import { AccountData } from '@cosmjs/amino';
|
import { AccountData, Secp256k1Wallet } from '@cosmjs/amino';
|
||||||
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
|
import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing';
|
||||||
import { stringToPath } from '@cosmjs/crypto';
|
import { stringToPath } from '@cosmjs/crypto';
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ export const getDirectWallet = async (
|
|||||||
const getPathKey = async (
|
const getPathKey = async (
|
||||||
network: string,
|
network: string,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
|
prefix: string = 'cosmos',
|
||||||
): Promise<{
|
): Promise<{
|
||||||
path: string;
|
path: string;
|
||||||
privKey: string;
|
privKey: string;
|
||||||
@ -62,8 +63,23 @@ const getPathKey = async (
|
|||||||
const pathkey = pathKeyVal.split(',');
|
const pathkey = pathKeyVal.split(',');
|
||||||
const path = pathkey[0];
|
const path = pathkey[0];
|
||||||
const privKey = pathkey[1];
|
const privKey = pathkey[1];
|
||||||
const pubKey = pathkey[2];
|
|
||||||
const address = pathkey[3];
|
let pubKey: string;
|
||||||
|
let address: string;
|
||||||
|
|
||||||
|
if (network === 'eth') {
|
||||||
|
pubKey = pathkey[2];
|
||||||
|
address = pathkey[3];
|
||||||
|
} else {
|
||||||
|
// TODO: Store pubkey and address for cosmos instead of deriving
|
||||||
|
const wallet = await Secp256k1Wallet.fromKey(
|
||||||
|
Uint8Array.from(Buffer.from(privKey.split('0x')[1], 'hex')),
|
||||||
|
prefix,
|
||||||
|
);
|
||||||
|
const currAccount = await wallet.getAccounts();
|
||||||
|
pubKey = '0x' + Buffer.from(currAccount[0].pubkey).toString('hex');
|
||||||
|
address = currAccount[0].address;
|
||||||
|
}
|
||||||
|
|
||||||
return { path, privKey, pubKey, address };
|
return { path, privKey, pubKey, address };
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user