Show account data specific to selected network (#11)

* Add state for selected network

* Make review changes

* Add cosmos signature

* Explicit check for cosmos

* Add dummy method for generating wallet

* Remove logic from component

* Add dummy sign method

* Change network state values

* Use separate file for types

* Add default case to switch

* Use consistent method names

---------

Co-authored-by: Adw8 <adwait@deepstacksoft.com>
This commit is contained in:
Adwait Gharpure 2024-02-14 13:45:02 +05:30 committed by GitHub
parent f026d9345f
commit 9ab3148aa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 151 additions and 39 deletions

13
App.tsx
View File

@ -6,14 +6,23 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import SignMessage from './components/SignMessage';
import { HomeScreen } from './components/HomeScreen';
const Stack = createNativeStackNavigator();
import { StackParamsList } from './types';
const Stack = createNativeStackNavigator<StackParamsList>();
const App = (): React.JSX.Element => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Laconic" component={HomeScreen} />
<Stack.Screen name="Sign Message" component={SignMessage} />
<Stack.Screen
name="SignMessage"
component={SignMessage}
options={{
title: 'Sign Message',
}}
initialParams={{ selectedNetwork: 'Ethereum' }}
/>
</Stack.Navigator>
</NavigationContainer>
);

View File

@ -1,35 +1,45 @@
import React, { useState } from 'react';
import { View } from 'react-native';
import { Text, Button, Dialog, Portal, List } from 'react-native-paper';
import { Text, Button, Dialog, Portal } from 'react-native-paper';
import { HDNode } from 'ethers/lib/utils';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { generateWallet, resetWallet } from '../utils';
import { createWallet, resetWallet } from '../utils';
import { DialogComponent } from './Dialog';
import { NetworkDropdown } from './NetworkDropdown';
import { StackParamsList, Account } from '../types';
const HomeScreen = () => {
const navigation = useNavigation();
const navigation =
useNavigation<NativeStackNavigationProp<StackParamsList>>();
const [isWalletCreated, setIsWalletCreated] = useState<boolean>(false);
const [wallet, setWallet] = useState<HDNode | null>();
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 [currentAccount, setCurrentAccount] = useState<Account>();
const [ethAccount, setEthAccount] = useState<Account>();
const [cosmosAccount, setCosmosAccount] = useState<Account>();
const hideWalletDialog = () => setWalletDialog(false);
const hideResetDialog = () => setResetWalletDialog(false);
const createWallet = async () => {
const createWalletHandler = async () => {
setIsWalletCreating(true);
await new Promise(resolve => setTimeout(resolve, 200));
const etherWallet = await generateWallet();
await new Promise(resolve => setTimeout(resolve, 2000));
const { ethAccount, cosmosAccount } = createWallet();
setEthAccount(ethAccount);
setCosmosAccount(cosmosAccount);
setCurrentAccount(ethAccount);
setWalletDialog(true);
if (etherWallet) {
setWallet(etherWallet);
setIsWalletCreated(true);
}
};
const confirmResetWallet = async () => {
@ -40,6 +50,20 @@ const HomeScreen = () => {
hideResetDialog();
};
const updateNetwork = (newNetwork: string) => {
setNetwork(newNetwork);
switch (newNetwork) {
case 'eth':
setCurrentAccount(ethAccount);
break;
case 'cosmos':
setCurrentAccount(cosmosAccount);
break;
default:
console.error('Error updating network');
}
};
return (
<View style={{ marginTop: 24, paddingHorizontal: 24 }}>
<DialogComponent
@ -63,18 +87,22 @@ const HomeScreen = () => {
</Portal>
{isWalletCreated ? (
<View>
<NetworkDropdown />
<NetworkDropdown
selectedNetwork={network}
updateNetwork={updateNetwork}
/>
<Text variant="headlineSmall">Account 1</Text>
<View style={{ marginTop: 15, marginBottom: 15 }}>
<Text variant="bodyLarge">
<Text style={{ fontWeight: '700' }}>Address: </Text>
{wallet && wallet.address.toString()}
{currentAccount && currentAccount.address}
</Text>
<Text variant="bodyLarge">
<Text style={{ fontWeight: '700' }}>Public Key: </Text>
{wallet && wallet.publicKey.toString()}
{currentAccount && currentAccount.publicKey}
</Text>
</View>
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<View
style={{
@ -86,7 +114,9 @@ const HomeScreen = () => {
<Button
mode="contained"
onPress={() => {
navigation.navigate('Sign Message' as never);
navigation.navigate('SignMessage', {
selectedNetwork: network,
});
}}>
Sign Message
</Button>
@ -110,7 +140,7 @@ const HomeScreen = () => {
<Button
mode="contained"
loading={isWalletCreating}
onPress={createWallet}>
onPress={createWalletHandler}>
{isWalletCreating ? 'Creating' : 'Create'}{' '}
</Button>
</View>

View File

@ -2,26 +2,38 @@ import React, { useState } from 'react';
import { View } from 'react-native';
import { List } from 'react-native-paper';
const NetworkDropdown = () => {
type NetworkDropdownProps = {
selectedNetwork: string;
updateNetwork: (network: string) => void;
};
const NetworkDropdown: React.FC<NetworkDropdownProps> = ({
updateNetwork,
}) => {
const [expanded, setExpanded] = useState<boolean>(false);
const [title, setTitle] = useState<string>('Ethereum');
const expandNetworks = () => setExpanded(!expanded);
return (
<View style={{ marginBottom: 20 }}>
<List.Accordion
title="Select Network"
title={title}
expanded={expanded}
onPress={expandNetworks}>
<List.Item
title="Ethereum"
onPress={() => {
updateNetwork('eth');
setTitle('Ethereum');
setExpanded(false);
}}
/>
<List.Item
title="Cosmos"
onPress={() => {
updateNetwork('cosmos');
setTitle('Cosmos');
setExpanded(false);
}}
/>

View File

@ -1,8 +1,8 @@
import { PropsWithChildren } from "react";
import { Text, View, useColorScheme } from "react-native";
import { Colors } from "react-native/Libraries/NewAppScreen";
import { PropsWithChildren } from 'react';
import { Text, View, useColorScheme } from 'react-native';
import { Colors } from 'react-native/Libraries/NewAppScreen';
import styles from "../styles/stylesheet";
import styles from '../styles/stylesheet';
type SectionProps = PropsWithChildren<{
title: string;

View File

@ -2,10 +2,18 @@ import { View } from 'react-native';
import { Button, TextInput } from 'react-native-paper';
import React, { useState } from 'react';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { StackParamsList } from '../types';
import { signMessage } from '../utils';
export default function SignMessage() {
type SignProps = NativeStackScreenProps<StackParamsList, 'SignMessage'>;
const SignMessage = ({ route }: SignProps) => {
const network = route.params?.selectedNetwork;
const [message, setMessage] = useState<string>('');
return (
<View style={{ marginTop: 30, paddingHorizontal: 48 }}>
<TextInput
@ -18,11 +26,13 @@ export default function SignMessage() {
<Button
mode="contained"
onPress={() => {
signMessage(message);
network && signMessage(network, 0, message);
}}>
Sign
</Button>
</View>
</View>
);
}
};
export default SignMessage;

5
constants.ts Normal file
View File

@ -0,0 +1,5 @@
export const COSMOS_SIGNATURE =
'0x56da25d5a9704e0cd685d52ecee5c14bf6637fa2f95653e8499eac4e8285f37b2d9f446c027cac56f3b7840d1b3879ea943415190d7a358cdb3ee05451cdcf7c1c';
export const COSMOS_ADDRESS = 'cosmos1sulk9q5fmagur6m3pctmcnfeeku25gp2ectt75';
export const COSMOS_PUBKEY =
'cosmospub1addwnpepqt9d597c5f6zqqyxy3msrstyc7zl3vyvrl5ku02r4ueuwt5vusw4gmt70dd';

14
types.ts Normal file
View File

@ -0,0 +1,14 @@
export type StackParamsList = {
Laconic: undefined;
SignMessage: { selectedNetwork: string } | undefined;
};
export type Account = {
address: string;
publicKey: string;
};
export type WalletDetails = {
ethAccount: Account;
cosmosAccount: Account;
};

View File

@ -3,16 +3,17 @@ For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#c
import 'react-native-get-random-values';
import '@ethersproject/shims';
import { Wallet, utils } from 'ethers';
import { utils } from 'ethers';
import { HDNode } from 'ethers/lib/utils';
import { Alert } from 'react-native';
import {
setInternetCredentials,
getInternetCredentials,
resetInternetCredentials,
} from 'react-native-keychain';
const generateWallet = async (): Promise<HDNode | undefined> => {
import { Account, WalletDetails } from './types';
const generateEthNode = async (): Promise<HDNode | undefined> => {
try {
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(32));
const hdNode = HDNode.fromMnemonic(mnemonic);
@ -27,16 +28,47 @@ const generateWallet = async (): Promise<HDNode | undefined> => {
}
};
const signMessage = async (message: string) => {
const createWallet = (): WalletDetails => {
try {
const keyCred = await getInternetCredentials('keyServer');
const wallet = keyCred && new Wallet(keyCred.password);
const signature = wallet && (await wallet.signMessage(message));
if (typeof signature === 'string') {
Alert.alert('Message signature: ', signature);
} else {
Alert.alert('Message signing failed. Please try again.');
const ethAccount = {
address: '0x873784c8A011A32C7635C8d8D3D2c83060532A49',
publicKey:
'0x02fd66d3487eb0567c321dac48b5e17c469df8ede7c0c79b74a9d0492249b32f1e',
};
const cosmosAccount = {
address: 'cosmos1sulk9q5fmagur6m3pctmcnfeeku25gp2ectt75',
publicKey:
'cosmospub1addwnpepqt9d597c5f6zqqyxy3msrstyc7zl3vyvrl5ku02r4ueuwt5vusw4gmt70dd',
};
return { ethAccount, cosmosAccount };
} catch (error) {
console.error('Error creating wallet ', error);
throw error;
}
};
const signMessage = async (network: string, index: Number, message: string) => {
try {
let signature: string | false;
switch (network) {
case 'eth':
signature =
'0x43jv95d5a9704z83h85d52ecee5c14bf6637fa2f95653e8499eac4e8285f37b2d9f446c027cac56f3b7840d1b3879ea943415190d7a358cdb3ee05451cdcf7c1c';
break;
case 'cosmos':
signature =
'0x56da25d5a9704e0cd685d52ecee5c14bf6637fa2f95653e8499eac4e8285f37b2d9f446c027cac56f3b7840d1b3879ea943415190d7a358cdb3ee05451cdcf7c1c';
break;
default:
signature = '';
}
Alert.alert('Message signature: ', signature as string);
} catch (error) {
console.error('Error signing transaction ', error);
}
@ -47,4 +79,4 @@ const resetWallet = async () => {
await resetInternetCredentials('mnemonicServer');
};
export { generateWallet, signMessage, resetWallet };
export { createWallet, signMessage, resetWallet };