forked from cerc-io/laconic-wallet
Add functionality to configure EVM networks (#74)
* Configure EVM networks * Display added EVM networks in network drop down * Add network for configured networks
This commit is contained in:
parent
0dea0082b4
commit
703ea72c1f
@ -31,6 +31,7 @@
|
||||
"patch-package": "^8.0.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"react": "18.2.0",
|
||||
"react-hook-form": "^7.51.2",
|
||||
"react-native": "0.73.3",
|
||||
"react-native-config": "^1.5.1",
|
||||
"react-native-get-random-values": "^1.10.0",
|
||||
|
@ -26,6 +26,7 @@ import { web3wallet } from './utils/wallet-connect/WalletConnectUtils';
|
||||
import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
|
||||
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
|
||||
import ApproveTransaction from './screens/ApproveTransaction';
|
||||
import AddNetwork from './screens/AddNetwork';
|
||||
|
||||
const Stack = createNativeStackNavigator<StackParamsList>();
|
||||
|
||||
@ -220,6 +221,13 @@ const App = (): React.JSX.Element => {
|
||||
title: 'Approve transaction',
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="AddNetwork"
|
||||
component={AddNetwork}
|
||||
options={{
|
||||
title: 'Add Network',
|
||||
}}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
<PairingModal
|
||||
visible={modalVisible}
|
||||
|
@ -187,6 +187,19 @@ const Accounts = ({
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.signLink}>
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
navigation.navigate('AddNetwork');
|
||||
}}>
|
||||
<Text
|
||||
variant="titleSmall"
|
||||
style={[styles.hyperlink, { color: theme.colors.primary }]}>
|
||||
Add Network
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
|
@ -1,14 +1,34 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { List } from 'react-native-paper';
|
||||
|
||||
import { NetworkDropdownProps } from '../types';
|
||||
import styles from '../styles/stylesheet';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
|
||||
const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
const [selectedNetwork, setSelectedNetwork] = useState<string>('Ethereum');
|
||||
|
||||
const { networksData } = useAccounts();
|
||||
|
||||
const networks = useMemo(() => {
|
||||
const defaultNetworks = [
|
||||
{ value: 'eth', chainId: 'eip155:1', displayName: 'Ethereum' },
|
||||
{ value: 'cosmos', chainId: 'cosmos:cosmoshub-4', displayName: 'Cosmos' },
|
||||
];
|
||||
|
||||
networksData.forEach(network => {
|
||||
defaultNetworks.push({
|
||||
value: network.networkType,
|
||||
chainId: network.chainId,
|
||||
displayName: network.networkName,
|
||||
});
|
||||
});
|
||||
|
||||
return defaultNetworks;
|
||||
}, [networksData]);
|
||||
|
||||
const handleNetworkPress = (network: string, displayName: string) => {
|
||||
updateNetwork(network);
|
||||
setSelectedNetwork(displayName);
|
||||
@ -23,7 +43,7 @@ const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||
onPress={() => setExpanded(!expanded)}>
|
||||
{networks.map(network => (
|
||||
<List.Item
|
||||
key={network.value}
|
||||
key={network.chainId}
|
||||
title={network.displayName}
|
||||
onPress={() =>
|
||||
handleNetworkPress(network.value, network.displayName)
|
||||
@ -35,9 +55,4 @@ const NetworkDropdown = ({ updateNetwork }: NetworkDropdownProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const networks = [
|
||||
{ value: 'eth', displayName: 'Ethereum' },
|
||||
{ value: 'cosmos', displayName: 'Cosmos' },
|
||||
];
|
||||
|
||||
export { NetworkDropdown };
|
||||
|
@ -1,17 +1,25 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
|
||||
import { AccountsState } from '../types';
|
||||
import { AccountsState, NetworksDataState } from '../types';
|
||||
|
||||
const AccountsContext = createContext<{
|
||||
accounts: AccountsState;
|
||||
setAccounts: (account: AccountsState) => void;
|
||||
currentIndex: number;
|
||||
setCurrentIndex: (index: number) => void;
|
||||
networksData: NetworksDataState[];
|
||||
setNetworksData: (networksDataArray: NetworksDataState[]) => void;
|
||||
networkType: string;
|
||||
setNetworkType: (networkType: string) => void;
|
||||
}>({
|
||||
accounts: { ethAccounts: [], cosmosAccounts: [] },
|
||||
setAccounts: () => {},
|
||||
currentIndex: 0,
|
||||
setCurrentIndex: () => {},
|
||||
networksData: [],
|
||||
setNetworksData: () => {},
|
||||
networkType: '',
|
||||
setNetworkType: () => {},
|
||||
});
|
||||
|
||||
const useAccounts = () => {
|
||||
@ -24,10 +32,21 @@ const AccountsProvider = ({ children }: { children: any }) => {
|
||||
ethAccounts: [],
|
||||
cosmosAccounts: [],
|
||||
});
|
||||
const [networksData, setNetworksData] = useState<NetworksDataState[]>([]);
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
const [networkType, setNetworkType] = useState<string>('eth');
|
||||
return (
|
||||
<AccountsContext.Provider
|
||||
value={{ accounts, setAccounts, currentIndex, setCurrentIndex }}>
|
||||
value={{
|
||||
accounts,
|
||||
setAccounts,
|
||||
currentIndex,
|
||||
setCurrentIndex,
|
||||
networksData,
|
||||
setNetworksData,
|
||||
networkType,
|
||||
setNetworkType,
|
||||
}}>
|
||||
{children}
|
||||
</AccountsContext.Provider>
|
||||
);
|
||||
|
146
src/screens/AddNetwork.tsx
Normal file
146
src/screens/AddNetwork.tsx
Normal file
@ -0,0 +1,146 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { TextInput, Button, HelperText } from 'react-native-paper';
|
||||
|
||||
import styles from '../styles/stylesheet';
|
||||
import { NetworksDataState, StackParamsList } from '../types';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
// TODO: Add validation to form inputs
|
||||
const AddNetwork = () => {
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
||||
const {
|
||||
control,
|
||||
formState: { errors, isValid },
|
||||
handleSubmit,
|
||||
} = useForm<NetworksDataState>({
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const { networksData, setNetworksData } = useAccounts();
|
||||
|
||||
const submit = (data: NetworksDataState) => {
|
||||
setNetworksData([...networksData, data]);
|
||||
navigation.navigate('Laconic');
|
||||
};
|
||||
return (
|
||||
<View style={styles.signPage}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="networkName"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="Network Name"
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChangeText={value => onChange(value)}
|
||||
/>
|
||||
<HelperText type="error">{errors.networkName?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="rpcUrl"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
label="New RPC URL"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
onChangeText={value => onChange(value)}
|
||||
/>
|
||||
<HelperText type="error">{errors.rpcUrl?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="chainId"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Chain ID"
|
||||
onBlur={onBlur}
|
||||
onChangeText={value => onChange(value)}
|
||||
/>
|
||||
<HelperText type="error">{errors.chainId?.message}</HelperText>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<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
|
||||
control={control}
|
||||
defaultValue=""
|
||||
name="blockExplorerUrl"
|
||||
render={({ field: { onChange, onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
label="Block Explorer URL (Optional)"
|
||||
onBlur={onBlur}
|
||||
onChangeText={value => onChange(value)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<HelperText type="error">{errors.blockExplorerUrl?.message}</HelperText>
|
||||
<Controller
|
||||
control={control}
|
||||
// TODO: Use state to toggle between 'eth' and 'cosmos'
|
||||
defaultValue="eth"
|
||||
name="networkType"
|
||||
render={({ field: { onBlur, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
mode="outlined"
|
||||
value={value}
|
||||
disabled
|
||||
label="Block Explorer URL (Optional)"
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<HelperText type="error">{errors.blockExplorerUrl?.message}</HelperText>
|
||||
<Button
|
||||
mode="contained"
|
||||
onPress={handleSubmit(submit)}
|
||||
disabled={!isValid}>
|
||||
Submit
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNetwork;
|
@ -25,11 +25,11 @@ import {
|
||||
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';
|
||||
import { useAccounts } from '../context/AccountsContext';
|
||||
|
||||
type SignRequestProps = NativeStackScreenProps<
|
||||
StackParamsList,
|
||||
@ -37,6 +37,8 @@ type SignRequestProps = NativeStackScreenProps<
|
||||
>;
|
||||
|
||||
const ApproveTransaction = ({ route }: SignRequestProps) => {
|
||||
const { networksData } = useAccounts();
|
||||
|
||||
const requestSession = route.params.requestSessionData;
|
||||
const requestName = requestSession.peer.metadata.name;
|
||||
const requestIcon = requestSession.peer.metadata.icons[0];
|
||||
@ -55,9 +57,17 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
||||
|
||||
const provider = useMemo(() => {
|
||||
if (network === 'eth') {
|
||||
return new providers.JsonRpcProvider(EIP155_CHAINS[chainId].rpc);
|
||||
const currentChain = networksData.find(
|
||||
networkData => networkData.chainId === chainId,
|
||||
);
|
||||
|
||||
if (!currentChain) {
|
||||
throw new Error('Requested chain not supported');
|
||||
}
|
||||
}, [chainId, network]);
|
||||
|
||||
return new providers.JsonRpcProvider(currentChain.rpcUrl);
|
||||
}
|
||||
}, [chainId, network, networksData]);
|
||||
|
||||
const navigation =
|
||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||
|
@ -28,8 +28,14 @@ const WCLogo = () => {
|
||||
};
|
||||
|
||||
const HomeScreen = () => {
|
||||
const { accounts, setAccounts, currentIndex, setCurrentIndex } =
|
||||
useAccounts();
|
||||
const {
|
||||
accounts,
|
||||
setAccounts,
|
||||
currentIndex,
|
||||
setCurrentIndex,
|
||||
networkType,
|
||||
setNetworkType,
|
||||
} = useAccounts();
|
||||
const { setActiveSessions } = useWalletConnect();
|
||||
|
||||
const navigation =
|
||||
@ -54,7 +60,6 @@ const HomeScreen = () => {
|
||||
const [isWalletCreating, setIsWalletCreating] = useState<boolean>(false);
|
||||
const [walletDialog, setWalletDialog] = useState<boolean>(false);
|
||||
const [resetWalletDialog, setResetWalletDialog] = useState<boolean>(false);
|
||||
const [network, setNetwork] = useState<string>('eth');
|
||||
const [isAccountsFetched, setIsAccountsFetched] = useState<boolean>(false);
|
||||
const [phrase, setPhrase] = useState('');
|
||||
|
||||
@ -95,11 +100,11 @@ const HomeScreen = () => {
|
||||
setActiveSessions({});
|
||||
|
||||
hideResetDialog();
|
||||
setNetwork('eth');
|
||||
setNetworkType('eth');
|
||||
};
|
||||
|
||||
const updateNetwork = (newNetwork: string) => {
|
||||
setNetwork(newNetwork);
|
||||
setNetworkType(newNetwork);
|
||||
setCurrentIndex(0);
|
||||
};
|
||||
|
||||
@ -140,12 +145,12 @@ const HomeScreen = () => {
|
||||
) : isWalletCreated ? (
|
||||
<>
|
||||
<NetworkDropdown
|
||||
selectedNetwork={network}
|
||||
selectedNetwork={networkType}
|
||||
updateNetwork={updateNetwork}
|
||||
/>
|
||||
<View style={styles.accountComponent}>
|
||||
<Accounts
|
||||
network={network}
|
||||
network={networkType}
|
||||
currentIndex={currentIndex}
|
||||
updateIndex={updateIndex}
|
||||
/>
|
||||
|
14
src/types.ts
14
src/types.ts
@ -22,6 +22,7 @@ export type StackParamsList = {
|
||||
InvalidPath: undefined;
|
||||
WalletConnect: undefined;
|
||||
AddSession: undefined;
|
||||
AddNetwork: undefined;
|
||||
};
|
||||
|
||||
export type Account = {
|
||||
@ -46,6 +47,10 @@ export type AccountsProps = {
|
||||
export type NetworkDropdownProps = {
|
||||
selectedNetwork: string;
|
||||
updateNetwork: (network: string) => void;
|
||||
customNetwork?: {
|
||||
value: string;
|
||||
displayName: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type AccountsState = {
|
||||
@ -53,6 +58,15 @@ export type AccountsState = {
|
||||
cosmosAccounts: Account[];
|
||||
};
|
||||
|
||||
export type NetworksDataState = {
|
||||
networkName: string;
|
||||
rpcUrl: string;
|
||||
chainId: string;
|
||||
currencySymbol: string;
|
||||
blockExplorerUrl?: string;
|
||||
networkType: string;
|
||||
};
|
||||
|
||||
export type SignMessageParams = {
|
||||
message: string;
|
||||
network: string;
|
||||
|
@ -7803,6 +7803,11 @@ react-freeze@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.3.tgz#5e3ca90e682fed1d73a7cb50c2c7402b3e85618d"
|
||||
integrity sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==
|
||||
|
||||
react-hook-form@^7.51.2:
|
||||
version "7.51.2"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.2.tgz#79f7f72ee217c5114ff831012d1a7ec344096e7f"
|
||||
integrity sha512-y++lwaWjtzDt/XNnyGDQy6goHskFualmDlf+jzEZvjvz6KWDf7EboL7pUvRCzPTJd0EOPpdekYaQLEvvG6m6HA==
|
||||
|
||||
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
|
Loading…
Reference in New Issue
Block a user