forked from cerc-io/laconic-wallet
Update keystore data structure (#88)
* Update keystore data structure (#83) * Update create wallet and retrieve accounts functionality for updated data structure * Refactor accounts state * Use constant variable for cosmos * Update add accounts incrementally and with custom HD path for updated data structure (#85) * Change data structure * Reset wallet change * Fix signEthMessage * Fix sign request * Fix pairing with laconic pay dApp and sending tokens * Add accounts to configured networks * Update add account from hd path flow * Handle review changes --------- Co-authored-by: Shreerang Kale <shreerangkale@gmail.com> * Remove network type state * Refactor create wallet code (#89) * Refactor create wallet code * Create cosmos accounts with correct address prefix * Use networks data from state while creating wallet * Refactor code and add network id in types (#91) * Refactor add new networks component * Add selected network state in context * Remove returning account from create wallet --------- Co-authored-by: IshaVenikar <145848618+IshaVenikar@users.noreply.github.com>
This commit is contained in:
parent
94bd8b6480
commit
3809ce88b1
14
src/App.tsx
14
src/App.tsx
@ -27,6 +27,7 @@ import { EIP155_SIGNING_METHODS } from './utils/wallet-connect/EIP155Data';
|
|||||||
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
|
import { getSignParamsMessage } from './utils/wallet-connect/Helpers';
|
||||||
import ApproveTransaction from './screens/ApproveTransaction';
|
import ApproveTransaction from './screens/ApproveTransaction';
|
||||||
import AddNetwork from './screens/AddNetwork';
|
import AddNetwork from './screens/AddNetwork';
|
||||||
|
import { COSMOS, EIP155 } from './utils/constants';
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<StackParamsList>();
|
const Stack = createNativeStackNavigator<StackParamsList>();
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ const App = (): React.JSX.Element => {
|
|||||||
|
|
||||||
const onSessionProposal = useCallback(
|
const onSessionProposal = useCallback(
|
||||||
async (proposal: SignClientTypes.EventArguments['session_proposal']) => {
|
async (proposal: SignClientTypes.EventArguments['session_proposal']) => {
|
||||||
if (!accounts.ethAccounts.length || !accounts.cosmosAccounts.length) {
|
if (!accounts.length || !accounts.length) {
|
||||||
const { id } = proposal;
|
const { id } = proposal;
|
||||||
await web3wallet!.rejectSession({
|
await web3wallet!.rejectSession({
|
||||||
id,
|
id,
|
||||||
@ -55,7 +56,7 @@ const App = (): React.JSX.Element => {
|
|||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
setCurrentProposal(proposal);
|
setCurrentProposal(proposal);
|
||||||
},
|
},
|
||||||
[accounts.ethAccounts, accounts.cosmosAccounts],
|
[accounts],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSessionRequest = useCallback(
|
const onSessionRequest = useCallback(
|
||||||
@ -68,7 +69,6 @@ const App = (): React.JSX.Element => {
|
|||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
|
||||||
navigation.navigate('ApproveTransaction', {
|
navigation.navigate('ApproveTransaction', {
|
||||||
network: 'eth',
|
|
||||||
transaction: request.params[0],
|
transaction: request.params[0],
|
||||||
requestEvent,
|
requestEvent,
|
||||||
requestSessionData,
|
requestSessionData,
|
||||||
@ -77,7 +77,7 @@ const App = (): React.JSX.Element => {
|
|||||||
|
|
||||||
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
|
||||||
navigation.navigate('SignRequest', {
|
navigation.navigate('SignRequest', {
|
||||||
network: 'eth',
|
namespace: EIP155,
|
||||||
address: request.params[1],
|
address: request.params[1],
|
||||||
message: getSignParamsMessage(request.params),
|
message: getSignParamsMessage(request.params),
|
||||||
requestEvent,
|
requestEvent,
|
||||||
@ -103,7 +103,7 @@ const App = (): React.JSX.Element => {
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
navigation.navigate('SignRequest', {
|
navigation.navigate('SignRequest', {
|
||||||
network: 'cosmos',
|
namespace: COSMOS,
|
||||||
address: request.params.signerAddress,
|
address: request.params.signerAddress,
|
||||||
message: JSON.stringify(message, undefined, 2),
|
message: JSON.stringify(message, undefined, 2),
|
||||||
requestEvent,
|
requestEvent,
|
||||||
@ -113,7 +113,7 @@ const App = (): React.JSX.Element => {
|
|||||||
break;
|
break;
|
||||||
case 'cosmos_signAmino':
|
case 'cosmos_signAmino':
|
||||||
navigation.navigate('SignRequest', {
|
navigation.navigate('SignRequest', {
|
||||||
network: 'cosmos',
|
namespace: COSMOS,
|
||||||
address: request.params.signerAddress,
|
address: request.params.signerAddress,
|
||||||
message: request.params.signDoc.memo,
|
message: request.params.signDoc.memo,
|
||||||
requestEvent,
|
requestEvent,
|
||||||
@ -124,7 +124,6 @@ const App = (): React.JSX.Element => {
|
|||||||
|
|
||||||
case 'cosmos_sendTokens':
|
case 'cosmos_sendTokens':
|
||||||
navigation.navigate('ApproveTransaction', {
|
navigation.navigate('ApproveTransaction', {
|
||||||
network: 'cosmos',
|
|
||||||
transaction: request.params[0],
|
transaction: request.params[0],
|
||||||
requestEvent,
|
requestEvent,
|
||||||
requestSessionData,
|
requestSessionData,
|
||||||
@ -173,7 +172,6 @@ const App = (): React.JSX.Element => {
|
|||||||
options={{
|
options={{
|
||||||
headerTitle: () => <Text variant="titleLarge">Sign Message</Text>,
|
headerTitle: () => <Text variant="titleLarge">Sign Message</Text>,
|
||||||
}}
|
}}
|
||||||
initialParams={{ selectedNetwork: 'Ethereum' }}
|
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="SignRequest"
|
name="SignRequest"
|
||||||
|
@ -16,17 +16,14 @@ 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 { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
|
||||||
const Accounts = ({
|
const Accounts = ({ currentIndex, updateIndex }: AccountsProps) => {
|
||||||
network,
|
|
||||||
currentIndex,
|
|
||||||
updateIndex: updateId,
|
|
||||||
}: AccountsProps) => {
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
|
|
||||||
const { accounts, setAccounts } = useAccounts();
|
const { accounts, setAccounts } = useAccounts();
|
||||||
const { networksData, currentChainId } = useNetworks();
|
const { selectedNetwork } = useNetworks();
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
||||||
const [hdDialog, setHdDialog] = useState(false);
|
const [hdDialog, setHdDialog] = useState(false);
|
||||||
@ -36,22 +33,7 @@ const Accounts = ({
|
|||||||
const handlePress = () => setExpanded(!expanded);
|
const handlePress = () => setExpanded(!expanded);
|
||||||
|
|
||||||
const updateAccounts = (account: Account) => {
|
const updateAccounts = (account: Account) => {
|
||||||
switch (network) {
|
setAccounts([...accounts, account]);
|
||||||
case 'eth':
|
|
||||||
setAccounts({
|
|
||||||
...accounts,
|
|
||||||
ethAccounts: [...accounts.ethAccounts, account],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'cosmos':
|
|
||||||
setAccounts({
|
|
||||||
...accounts,
|
|
||||||
cosmosAccounts: [...accounts.cosmosAccounts, account],
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('Select a valid network!');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -70,24 +52,22 @@ const Accounts = ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentNetwork = networksData.find(
|
const namespaceChainId = `${selectedNetwork?.namespace}:${selectedNetwork?.chainId}`;
|
||||||
networkData => networkData.chainId === currentChainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
let updatedNamespaces;
|
let updatedNamespaces;
|
||||||
switch (currentNetwork?.networkType) {
|
switch (selectedNetwork?.namespace) {
|
||||||
case 'eth':
|
case EIP155:
|
||||||
updatedNamespaces = {
|
updatedNamespaces = {
|
||||||
eip155: {
|
eip155: {
|
||||||
chains: [currentNetwork.chainId],
|
chains: [namespaceChainId],
|
||||||
// TODO: Debug optional namespace methods and events being required for approval
|
// TODO: Debug optional namespace methods and events being required for approval
|
||||||
methods: [
|
methods: [
|
||||||
...Object.values(EIP155_SIGNING_METHODS),
|
...Object.values(EIP155_SIGNING_METHODS),
|
||||||
...(combinedNamespaces.eip155?.methods ?? []),
|
...(combinedNamespaces.eip155?.methods ?? []),
|
||||||
],
|
],
|
||||||
events: [...(combinedNamespaces.eip155?.events ?? [])],
|
events: [...(combinedNamespaces.eip155?.events ?? [])],
|
||||||
accounts: accounts.ethAccounts.map(ethAccount => {
|
accounts: accounts.map(ethAccount => {
|
||||||
return `${currentChainId}:${ethAccount.address}`;
|
return `${namespaceChainId}:${ethAccount.address}`;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
cosmos: {
|
cosmos: {
|
||||||
@ -98,17 +78,17 @@ const Accounts = ({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 'cosmos':
|
case COSMOS:
|
||||||
updatedNamespaces = {
|
updatedNamespaces = {
|
||||||
cosmos: {
|
cosmos: {
|
||||||
chains: [currentNetwork.chainId],
|
chains: [namespaceChainId],
|
||||||
methods: [
|
methods: [
|
||||||
...Object.values(COSMOS_METHODS),
|
...Object.values(COSMOS_METHODS),
|
||||||
...(combinedNamespaces.cosmos?.methods ?? []),
|
...(combinedNamespaces.cosmos?.methods ?? []),
|
||||||
],
|
],
|
||||||
events: [...(combinedNamespaces.cosmos?.events ?? [])],
|
events: [...(combinedNamespaces.cosmos?.events ?? [])],
|
||||||
accounts: accounts.cosmosAccounts.map(cosmosAccount => {
|
accounts: accounts.map(cosmosAccount => {
|
||||||
return `${currentChainId}:${cosmosAccount.address}`;
|
return `${namespaceChainId}:${cosmosAccount.address}`;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
eip155: {
|
eip155: {
|
||||||
@ -140,32 +120,21 @@ const Accounts = ({
|
|||||||
|
|
||||||
const addAccountHandler = async () => {
|
const addAccountHandler = async () => {
|
||||||
setIsAccountCreating(true);
|
setIsAccountCreating(true);
|
||||||
const newAccount = await addAccount(network);
|
const newAccount = await addAccount(selectedNetwork!);
|
||||||
setIsAccountCreating(false);
|
setIsAccountCreating(false);
|
||||||
if (newAccount) {
|
if (newAccount) {
|
||||||
updateAccounts(newAccount);
|
updateAccounts(newAccount);
|
||||||
updateId(newAccount.counterId);
|
updateIndex(newAccount.index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedAccounts: Account[] = (() => {
|
|
||||||
switch (network) {
|
|
||||||
case 'eth':
|
|
||||||
return accounts.ethAccounts;
|
|
||||||
case 'cosmos':
|
|
||||||
return accounts.cosmosAccounts;
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const renderAccountItems = () =>
|
const renderAccountItems = () =>
|
||||||
selectedAccounts.map(account => (
|
accounts.map(account => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={account.counterId}
|
key={account.index}
|
||||||
title={`Account ${account.counterId + 1}`}
|
title={`Account ${account.index + 1}`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
updateId(account.counterId);
|
updateIndex(account.index);
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -178,7 +147,7 @@ const Accounts = ({
|
|||||||
visible={hdDialog}
|
visible={hdDialog}
|
||||||
hideDialog={() => setHdDialog(false)}
|
hideDialog={() => setHdDialog(false)}
|
||||||
updateAccounts={updateAccounts}
|
updateAccounts={updateAccounts}
|
||||||
updateIndex={updateId}
|
updateIndex={updateIndex}
|
||||||
pathCode={pathCode}
|
pathCode={pathCode}
|
||||||
/>
|
/>
|
||||||
<List.Accordion
|
<List.Accordion
|
||||||
@ -202,20 +171,26 @@ const Accounts = ({
|
|||||||
mode="contained"
|
mode="contained"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setHdDialog(true);
|
setHdDialog(true);
|
||||||
setPathCode(network === 'eth' ? "m/44'/60'/" : "m/44'/118'/");
|
// TODO: Use coin type while adding from HD path
|
||||||
|
setPathCode(
|
||||||
|
selectedNetwork!.namespace === EIP155
|
||||||
|
? "m/44'/60'/"
|
||||||
|
: "m/44'/118'/",
|
||||||
|
);
|
||||||
}}>
|
}}>
|
||||||
Add Account from HD path
|
Add Account from HD path
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<AccountDetails account={selectedAccounts[currentIndex]} />
|
<AccountDetails account={accounts[currentIndex]} />
|
||||||
|
|
||||||
<View style={styles.signLink}>
|
<View style={styles.signLink}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
navigation.navigate('SignMessage', {
|
navigation.navigate('SignMessage', {
|
||||||
selectedNetwork: network,
|
selectedNamespace: selectedNetwork!.namespace,
|
||||||
accountInfo: selectedAccounts[currentIndex],
|
selectedChainId: selectedNetwork!.chainId,
|
||||||
|
accountInfo: accounts[currentIndex],
|
||||||
});
|
});
|
||||||
}}>
|
}}>
|
||||||
<Text
|
<Text
|
||||||
|
@ -3,7 +3,7 @@ import { ScrollView, View, Text } from 'react-native';
|
|||||||
import { Button, TextInput } from 'react-native-paper';
|
import { Button, TextInput } from 'react-native-paper';
|
||||||
|
|
||||||
import { addAccountFromHDPath } from '../utils/accounts';
|
import { addAccountFromHDPath } from '../utils/accounts';
|
||||||
import { Account, PathState } from '../types';
|
import { Account, NetworksDataState, PathState } from '../types';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
|
|
||||||
const HDPath = ({
|
const HDPath = ({
|
||||||
@ -11,11 +11,13 @@ const HDPath = ({
|
|||||||
updateAccounts,
|
updateAccounts,
|
||||||
updateIndex,
|
updateIndex,
|
||||||
hideDialog,
|
hideDialog,
|
||||||
|
selectedNetwork,
|
||||||
}: {
|
}: {
|
||||||
pathCode: string;
|
pathCode: string;
|
||||||
updateIndex: (index: number) => void;
|
updateIndex: (index: number) => void;
|
||||||
updateAccounts: (account: Account) => void;
|
updateAccounts: (account: Account) => void;
|
||||||
hideDialog: () => void;
|
hideDialog: () => void;
|
||||||
|
selectedNetwork: NetworksDataState;
|
||||||
}) => {
|
}) => {
|
||||||
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
const [isAccountCreating, setIsAccountCreating] = useState(false);
|
||||||
const [path, setPath] = useState<PathState>({
|
const [path, setPath] = useState<PathState>({
|
||||||
@ -41,10 +43,10 @@ const HDPath = ({
|
|||||||
pathCode +
|
pathCode +
|
||||||
`${path.firstNumber}'/${path.secondNumber}/${path.thirdNumber}`;
|
`${path.firstNumber}'/${path.secondNumber}/${path.thirdNumber}`;
|
||||||
try {
|
try {
|
||||||
const newAccount = await addAccountFromHDPath(hdPath);
|
const newAccount = await addAccountFromHDPath(hdPath, selectedNetwork);
|
||||||
if (newAccount) {
|
if (newAccount) {
|
||||||
updateAccounts(newAccount);
|
updateAccounts(newAccount);
|
||||||
updateIndex(newAccount.counterId);
|
updateIndex(newAccount.index);
|
||||||
hideDialog();
|
hideDialog();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Portal, Dialog } from 'react-native-paper';
|
import { Portal, Dialog } from 'react-native-paper';
|
||||||
|
|
||||||
import { HDPathDialogProps } from '../types';
|
import { HDPathDialogProps } from '../types';
|
||||||
import HDPath from './HDPath';
|
import HDPath from './HDPath';
|
||||||
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
|
||||||
const HDPathDialog = ({
|
const HDPathDialog = ({
|
||||||
visible,
|
visible,
|
||||||
@ -10,12 +12,15 @@ const HDPathDialog = ({
|
|||||||
updateAccounts,
|
updateAccounts,
|
||||||
pathCode,
|
pathCode,
|
||||||
}: HDPathDialogProps) => {
|
}: HDPathDialogProps) => {
|
||||||
|
const { selectedNetwork } = useNetworks();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<Dialog visible={visible} onDismiss={hideDialog}>
|
<Dialog visible={visible} onDismiss={hideDialog}>
|
||||||
<Dialog.Title>Add account from HD path</Dialog.Title>
|
<Dialog.Title>Add account from HD path</Dialog.Title>
|
||||||
<Dialog.Content>
|
<Dialog.Content>
|
||||||
<HDPath
|
<HDPath
|
||||||
|
selectedNetwork={selectedNetwork!}
|
||||||
pathCode={pathCode}
|
pathCode={pathCode}
|
||||||
updateIndex={updateIndex}
|
updateIndex={updateIndex}
|
||||||
updateAccounts={updateAccounts}
|
updateAccounts={updateAccounts}
|
||||||
|
@ -6,7 +6,7 @@ import mergeWith from 'lodash/mergeWith';
|
|||||||
|
|
||||||
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils';
|
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils';
|
||||||
|
|
||||||
import { AccountsState, PairingModalProps } from '../types';
|
import { PairingModalProps } from '../types';
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
||||||
import { useAccounts } from '../context/AccountsContext';
|
import { useAccounts } from '../context/AccountsContext';
|
||||||
@ -14,6 +14,7 @@ import { useWalletConnect } from '../context/WalletConnectContext';
|
|||||||
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
import { EIP155_SIGNING_METHODS } from '../utils/wallet-connect/EIP155Data';
|
||||||
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
import { COSMOS_METHODS } from '../utils/wallet-connect/COSMOSData';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
|
||||||
const PairingModal = ({
|
const PairingModal = ({
|
||||||
visible,
|
visible,
|
||||||
@ -23,7 +24,7 @@ const PairingModal = ({
|
|||||||
setToastVisible,
|
setToastVisible,
|
||||||
}: PairingModalProps) => {
|
}: PairingModalProps) => {
|
||||||
const { accounts, currentIndex } = useAccounts();
|
const { accounts, currentIndex } = useAccounts();
|
||||||
const { networksData, currentChainId } = useNetworks();
|
const { selectedNetwork } = useNetworks();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const dappName = currentProposal?.params?.proposer?.metadata.name;
|
const dappName = currentProposal?.params?.proposer?.metadata.name;
|
||||||
@ -83,37 +84,21 @@ const PairingModal = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set selected account as the first account in supported namespaces
|
// Set selected account as the first account in supported namespaces
|
||||||
const sortedAccounts = Object.entries(accounts).reduce(
|
const sortedAccounts = [
|
||||||
(acc: AccountsState, [key, value]) => {
|
accounts[currentIndex],
|
||||||
let newValue = [...value];
|
...accounts.filter((account, index) => index !== currentIndex),
|
||||||
|
];
|
||||||
|
|
||||||
// TODO: Implement selectedAccount instead of currentIndex in AccountsContext
|
const namespaceChainId = `${selectedNetwork?.namespace}:${selectedNetwork?.chainId}`;
|
||||||
if (value.length > currentIndex) {
|
|
||||||
const currentAccount = newValue[currentIndex];
|
|
||||||
const remainingAccounts = newValue.filter(
|
|
||||||
(_, index) => index !== currentIndex,
|
|
||||||
);
|
|
||||||
newValue = [currentAccount, ...remainingAccounts];
|
|
||||||
}
|
|
||||||
|
|
||||||
acc[key as 'ethAccounts' | 'cosmosAccounts'] = newValue;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{ ethAccounts: [], cosmosAccounts: [] },
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentNetwork = networksData.find(
|
|
||||||
networkData => networkData.chainId === currentChainId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { optionalNamespaces, requiredNamespaces } = currentProposal.params;
|
const { optionalNamespaces, requiredNamespaces } = currentProposal.params;
|
||||||
|
|
||||||
// TODO: Check with other dApps
|
// TODO: Check with other dApps
|
||||||
switch (currentNetwork?.networkType) {
|
switch (selectedNetwork?.namespace) {
|
||||||
case 'eth':
|
case EIP155:
|
||||||
return {
|
return {
|
||||||
eip155: {
|
eip155: {
|
||||||
chains: [currentNetwork.chainId],
|
chains: [namespaceChainId],
|
||||||
// TODO: Debug optional namespace methods and events being required for approval
|
// TODO: Debug optional namespace methods and events being required for approval
|
||||||
methods: [
|
methods: [
|
||||||
...Object.values(EIP155_SIGNING_METHODS),
|
...Object.values(EIP155_SIGNING_METHODS),
|
||||||
@ -124,8 +109,8 @@ const PairingModal = ({
|
|||||||
...(optionalNamespaces.eip155?.events ?? []),
|
...(optionalNamespaces.eip155?.events ?? []),
|
||||||
...(requiredNamespaces.eip155?.events ?? []),
|
...(requiredNamespaces.eip155?.events ?? []),
|
||||||
],
|
],
|
||||||
accounts: sortedAccounts.ethAccounts.map(ethAccount => {
|
accounts: sortedAccounts.map(ethAccount => {
|
||||||
return `${currentChainId}:${ethAccount.address}`;
|
return `${namespaceChainId}:${ethAccount.address}`;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
cosmos: {
|
cosmos: {
|
||||||
@ -135,10 +120,10 @@ const PairingModal = ({
|
|||||||
accounts: [],
|
accounts: [],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case 'cosmos':
|
case COSMOS:
|
||||||
return {
|
return {
|
||||||
cosmos: {
|
cosmos: {
|
||||||
chains: [currentNetwork.chainId],
|
chains: [namespaceChainId],
|
||||||
methods: [
|
methods: [
|
||||||
...Object.values(COSMOS_METHODS),
|
...Object.values(COSMOS_METHODS),
|
||||||
...(optionalNamespaces.cosmos?.methods ?? []),
|
...(optionalNamespaces.cosmos?.methods ?? []),
|
||||||
@ -148,8 +133,8 @@ const PairingModal = ({
|
|||||||
...(optionalNamespaces.cosmos?.events ?? []),
|
...(optionalNamespaces.cosmos?.events ?? []),
|
||||||
...(requiredNamespaces.cosmos?.events ?? []),
|
...(requiredNamespaces.cosmos?.events ?? []),
|
||||||
],
|
],
|
||||||
accounts: sortedAccounts.cosmosAccounts.map(cosmosAccount => {
|
accounts: sortedAccounts.map(cosmosAccount => {
|
||||||
return `${currentChainId}:${cosmosAccount.address}`;
|
return `${namespaceChainId}:${cosmosAccount.address}`;
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
eip155: {
|
eip155: {
|
||||||
@ -162,7 +147,7 @@ const PairingModal = ({
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}, [accounts, currentProposal, networksData, currentChainId, currentIndex]);
|
}, [accounts, currentProposal, currentIndex, selectedNetwork]);
|
||||||
|
|
||||||
const namespaces = useMemo(() => {
|
const namespaces = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
|
@ -3,6 +3,7 @@ import { View } from 'react-native';
|
|||||||
import { Text, List } from 'react-native-paper';
|
import { Text, List } from 'react-native-paper';
|
||||||
|
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
|
||||||
const SelectNetworkType = ({
|
const SelectNetworkType = ({
|
||||||
updateNetworkType,
|
updateNetworkType,
|
||||||
@ -16,7 +17,7 @@ const SelectNetworkType = ({
|
|||||||
|
|
||||||
const handleNetworkPress = (network: string) => {
|
const handleNetworkPress = (network: string) => {
|
||||||
setSelectedNetwork(network);
|
setSelectedNetwork(network);
|
||||||
updateNetworkType(network.toLowerCase());
|
updateNetworkType(network === 'ETH' ? EIP155 : COSMOS);
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
import { AccountsState } from '../types';
|
import { Account } from '../types';
|
||||||
|
|
||||||
const AccountsContext = createContext<{
|
const AccountsContext = createContext<{
|
||||||
accounts: AccountsState;
|
accounts: Account[];
|
||||||
setAccounts: (account: AccountsState) => void;
|
setAccounts: (account: Account[]) => void;
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
setCurrentIndex: (index: number) => void;
|
setCurrentIndex: (index: number) => void;
|
||||||
networkType: string;
|
|
||||||
setNetworkType: (networkType: string) => void;
|
|
||||||
}>({
|
}>({
|
||||||
accounts: { ethAccounts: [], cosmosAccounts: [] },
|
accounts: [],
|
||||||
setAccounts: () => {},
|
setAccounts: () => {},
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
setCurrentIndex: () => {},
|
setCurrentIndex: () => {},
|
||||||
networkType: '',
|
|
||||||
setNetworkType: () => {},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const useAccounts = () => {
|
const useAccounts = () => {
|
||||||
@ -24,12 +20,8 @@ const useAccounts = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AccountsProvider = ({ children }: { children: any }) => {
|
const AccountsProvider = ({ children }: { children: any }) => {
|
||||||
const [accounts, setAccounts] = useState<AccountsState>({
|
const [accounts, setAccounts] = useState<Account[]>([]);
|
||||||
ethAccounts: [],
|
|
||||||
cosmosAccounts: [],
|
|
||||||
});
|
|
||||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||||
const [networkType, setNetworkType] = useState<string>('eth');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccountsContext.Provider
|
<AccountsContext.Provider
|
||||||
@ -38,8 +30,6 @@ const AccountsProvider = ({ children }: { children: any }) => {
|
|||||||
setAccounts,
|
setAccounts,
|
||||||
currentIndex,
|
currentIndex,
|
||||||
setCurrentIndex,
|
setCurrentIndex,
|
||||||
networkType,
|
|
||||||
setNetworkType,
|
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</AccountsContext.Provider>
|
</AccountsContext.Provider>
|
||||||
|
@ -2,7 +2,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { NetworksDataState } from '../types';
|
import { NetworksDataState } from '../types';
|
||||||
import { retrieveNetworksData, storeNetworkData } from '../utils/accounts';
|
import { retrieveNetworksData, storeNetworkData } from '../utils/accounts';
|
||||||
import { DEFAULTNETWORKS } from '../utils/constants';
|
import { DEFAULT_NETWORKS, EIP155 } from '../utils/constants';
|
||||||
|
|
||||||
const NetworksContext = createContext<{
|
const NetworksContext = createContext<{
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
@ -11,8 +11,10 @@ const NetworksContext = createContext<{
|
|||||||
setNetworksData: React.Dispatch<React.SetStateAction<NetworksDataState[]>>;
|
setNetworksData: React.Dispatch<React.SetStateAction<NetworksDataState[]>>;
|
||||||
networkType: string;
|
networkType: string;
|
||||||
setNetworkType: (networkType: string) => void;
|
setNetworkType: (networkType: string) => void;
|
||||||
currentChainId?: string;
|
selectedNetwork?: NetworksDataState;
|
||||||
setCurrentChainId: (currentChainId: string) => void;
|
setSelectedNetwork: React.Dispatch<
|
||||||
|
React.SetStateAction<NetworksDataState | undefined>
|
||||||
|
>;
|
||||||
}>({
|
}>({
|
||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
setCurrentIndex: () => {},
|
setCurrentIndex: () => {},
|
||||||
@ -20,8 +22,8 @@ const NetworksContext = createContext<{
|
|||||||
setNetworksData: () => {},
|
setNetworksData: () => {},
|
||||||
networkType: '',
|
networkType: '',
|
||||||
setNetworkType: () => {},
|
setNetworkType: () => {},
|
||||||
currentChainId: undefined,
|
selectedNetwork: {} as NetworksDataState,
|
||||||
setCurrentChainId: () => {},
|
setSelectedNetwork: () => {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const useNetworks = () => {
|
const useNetworks = () => {
|
||||||
@ -32,20 +34,20 @@ const useNetworks = () => {
|
|||||||
const NetworksProvider = ({ children }: { children: any }) => {
|
const NetworksProvider = ({ children }: { children: any }) => {
|
||||||
const [networksData, setNetworksData] = useState<NetworksDataState[]>([]);
|
const [networksData, setNetworksData] = useState<NetworksDataState[]>([]);
|
||||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||||
const [networkType, setNetworkType] = useState<string>('eth');
|
const [networkType, setNetworkType] = useState<string>(EIP155);
|
||||||
const [currentChainId, setCurrentChainId] = useState<string>();
|
const [selectedNetwork, setSelectedNetwork] = useState<NetworksDataState>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const retrievedNetworks = await retrieveNetworksData();
|
const retrievedNetworks = await retrieveNetworksData();
|
||||||
if (retrievedNetworks.length === 0) {
|
if (retrievedNetworks.length === 0) {
|
||||||
for (const defaultNetwork of DEFAULTNETWORKS) {
|
for (const defaultNetwork of DEFAULT_NETWORKS) {
|
||||||
await storeNetworkData(defaultNetwork);
|
await storeNetworkData(defaultNetwork);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const retrievedNewNetworks = await retrieveNetworksData();
|
const retrievedNewNetworks = await retrieveNetworksData();
|
||||||
setNetworksData(retrievedNewNetworks);
|
setNetworksData(retrievedNewNetworks);
|
||||||
setCurrentChainId(retrievedNewNetworks[0].chainId);
|
setSelectedNetwork(retrievedNetworks[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchData();
|
fetchData();
|
||||||
@ -60,8 +62,8 @@ const NetworksProvider = ({ children }: { children: any }) => {
|
|||||||
setNetworksData,
|
setNetworksData,
|
||||||
networkType,
|
networkType,
|
||||||
setNetworkType,
|
setNetworkType,
|
||||||
currentChainId,
|
selectedNetwork,
|
||||||
setCurrentChainId,
|
setSelectedNetwork,
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</NetworksContext.Provider>
|
</NetworksContext.Provider>
|
||||||
|
@ -2,15 +2,22 @@ import React, { useCallback, useState } from 'react';
|
|||||||
import { ScrollView } 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 {
|
||||||
|
getInternetCredentials,
|
||||||
|
setInternetCredentials,
|
||||||
|
} from 'react-native-keychain';
|
||||||
|
import { HDNode } from 'ethers/lib/utils';
|
||||||
|
|
||||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
|
||||||
import styles from '../styles/stylesheet';
|
import styles from '../styles/stylesheet';
|
||||||
import { NetworksDataState, StackParamsList } from '../types';
|
import { NetworksDataState, NetworksFormData, StackParamsList } from '../types';
|
||||||
import { SelectNetworkType } from '../components/SelectNetworkType';
|
import { SelectNetworkType } from '../components/SelectNetworkType';
|
||||||
import { storeNetworkData } from '../utils/accounts';
|
import { storeNetworkData } from '../utils/accounts';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
import { getCosmosAccounts } from '../utils/accounts';
|
||||||
|
|
||||||
// TODO: Add validation to form inputs
|
// TODO: Add validation to form inputs
|
||||||
const AddNetwork = () => {
|
const AddNetwork = () => {
|
||||||
@ -25,29 +32,79 @@ const AddNetwork = () => {
|
|||||||
mode: 'onChange',
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { networksData, setNetworksData } = useNetworks();
|
const { setNetworksData } = useNetworks();
|
||||||
|
|
||||||
const [networkType, setNetworkType] = useState<string>('eth');
|
const [namespace, setNamespace] = useState<string>(EIP155);
|
||||||
|
|
||||||
const updateNetworkType = (newNetworkType: string) => {
|
const updateNetworkType = (newNetworkType: string) => {
|
||||||
setNetworkType(newNetworkType);
|
setNamespace(newNetworkType);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = useCallback(
|
const submit = useCallback(
|
||||||
async (data: NetworksDataState) => {
|
async (data: NetworksFormData) => {
|
||||||
const namespace = networkType === 'eth' ? 'eip155:' : 'cosmos:';
|
|
||||||
const newNetworkData = {
|
const newNetworkData = {
|
||||||
...data,
|
...data,
|
||||||
chainId: `${namespace}${data.chainId}`,
|
namespace,
|
||||||
networkType,
|
|
||||||
isDefault: false,
|
|
||||||
};
|
};
|
||||||
setNetworksData([...networksData, newNetworkData]);
|
|
||||||
await storeNetworkData(newNetworkData);
|
const mnemonicServer = await getInternetCredentials('mnemonicServer');
|
||||||
|
const mnemonic = mnemonicServer && mnemonicServer.password;
|
||||||
|
|
||||||
|
if (!mnemonic) {
|
||||||
|
throw new Error('Mnemonic not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||||
|
|
||||||
|
const hdPath = `m/44'/${newNetworkData.coinType}'/0'/0/0`;
|
||||||
|
const node = hdNode.derivePath(hdPath);
|
||||||
|
let address;
|
||||||
|
|
||||||
|
switch (newNetworkData.namespace) {
|
||||||
|
case EIP155:
|
||||||
|
address = node.address;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COSMOS:
|
||||||
|
address = (
|
||||||
|
await getCosmosAccounts(
|
||||||
|
mnemonic,
|
||||||
|
hdPath,
|
||||||
|
newNetworkData.addressPrefix,
|
||||||
|
)
|
||||||
|
).data.address;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported namespace');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||||
|
|
||||||
|
const retrievedNetworksData = await storeNetworkData(newNetworkData);
|
||||||
|
setNetworksData(retrievedNetworksData);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
setInternetCredentials(
|
||||||
|
`accounts/${newNetworkData.namespace}:${newNetworkData.chainId}/0`,
|
||||||
|
'_',
|
||||||
|
accountInfo,
|
||||||
|
),
|
||||||
|
setInternetCredentials(
|
||||||
|
`addAccountCounter/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||||
|
'_',
|
||||||
|
'1',
|
||||||
|
),
|
||||||
|
setInternetCredentials(
|
||||||
|
`accountIndices/${newNetworkData.namespace}:${newNetworkData.chainId}`,
|
||||||
|
'_',
|
||||||
|
'0',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
navigation.navigate('Laconic');
|
navigation.navigate('Laconic');
|
||||||
},
|
},
|
||||||
[navigation, networkType, networksData, setNetworksData],
|
[navigation, namespace, setNetworksData],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -123,8 +180,25 @@ const AddNetwork = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<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>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<SelectNetworkType updateNetworkType={updateNetworkType} />
|
<SelectNetworkType updateNetworkType={updateNetworkType} />
|
||||||
{networkType === 'eth' ? (
|
{namespace === EIP155 ? (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="currencySymbol"
|
name="currencySymbol"
|
||||||
@ -184,23 +258,6 @@ const AddNetwork = () => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<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
|
||||||
|
@ -28,6 +28,7 @@ import { web3wallet } from '../utils/wallet-connect/WalletConnectUtils';
|
|||||||
import DataBox from '../components/DataBox';
|
import DataBox from '../components/DataBox';
|
||||||
import { getPathKey } from '../utils/misc';
|
import { getPathKey } from '../utils/misc';
|
||||||
import { useNetworks } from '../context/NetworksContext';
|
import { useNetworks } from '../context/NetworksContext';
|
||||||
|
import { COSMOS, EIP155 } from '../utils/constants';
|
||||||
|
|
||||||
type SignRequestProps = NativeStackScreenProps<
|
type SignRequestProps = NativeStackScreenProps<
|
||||||
StackParamsList,
|
StackParamsList,
|
||||||
@ -41,7 +42,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
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];
|
||||||
const requestURL = requestSession.peer.metadata.url;
|
const requestURL = requestSession.peer.metadata.url;
|
||||||
const network = route.params.network;
|
// TODO: Remove and access namespace from requestEvent
|
||||||
const transaction = route.params.transaction;
|
const transaction = route.params.transaction;
|
||||||
const requestEvent = route.params.requestEvent;
|
const requestEvent = route.params.requestEvent;
|
||||||
const chainId = requestEvent.params.chainId;
|
const chainId = requestEvent.params.chainId;
|
||||||
@ -54,18 +55,53 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
useState<SigningStargateClient>();
|
useState<SigningStargateClient>();
|
||||||
|
|
||||||
const requestedChain = networksData.find(
|
const requestedChain = networksData.find(
|
||||||
networkData => networkData.chainId === chainId,
|
networkData =>
|
||||||
|
`${networkData.namespace}:${networkData.chainId}` === chainId,
|
||||||
|
);
|
||||||
|
const namespace = requestedChain!.namespace;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (namespace !== COSMOS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setClient = async () => {
|
||||||
|
if (!account) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cosmosPrivKey = (
|
||||||
|
await getPathKey(
|
||||||
|
`${requestedChain?.namespace}:${requestedChain?.chainId}`,
|
||||||
|
account.index,
|
||||||
|
)
|
||||||
|
).privKey;
|
||||||
|
|
||||||
|
const sender = await DirectSecp256k1Wallet.fromKey(
|
||||||
|
Buffer.from(cosmosPrivKey.split('0x')[1], 'hex'),
|
||||||
|
requestedChain?.addressPrefix,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const client = await SigningStargateClient.connectWithSigner(
|
||||||
|
requestedChain?.rpcUrl!,
|
||||||
|
sender,
|
||||||
|
);
|
||||||
|
|
||||||
|
setCosmosStargateClient(client);
|
||||||
|
};
|
||||||
|
|
||||||
|
setClient();
|
||||||
|
}, [account, requestedChain, chainId, namespace]);
|
||||||
|
|
||||||
const provider = useMemo(() => {
|
const provider = useMemo(() => {
|
||||||
if (network === 'eth') {
|
if (namespace === EIP155) {
|
||||||
if (!requestedChain) {
|
if (!requestedChain) {
|
||||||
throw new Error('Requested chain not supported');
|
throw new Error('Requested chain not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new providers.JsonRpcProvider(requestedChain.rpcUrl);
|
return new providers.JsonRpcProvider(requestedChain.rpcUrl);
|
||||||
}
|
}
|
||||||
}, [requestedChain, network]);
|
}, [requestedChain, namespace]);
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
@ -73,9 +109,9 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
const retrieveData = useCallback(
|
const retrieveData = useCallback(
|
||||||
async (requestAddress: string) => {
|
async (requestAddress: string) => {
|
||||||
const requestAccount = await retrieveSingleAccount(
|
const requestAccount = await retrieveSingleAccount(
|
||||||
network,
|
requestedChain!.namespace,
|
||||||
|
requestedChain!.chainId,
|
||||||
requestAddress,
|
requestAddress,
|
||||||
requestedChain?.addressPrefix,
|
|
||||||
);
|
);
|
||||||
if (!requestAccount) {
|
if (!requestAccount) {
|
||||||
navigation.navigate('InvalidPath');
|
navigation.navigate('InvalidPath');
|
||||||
@ -85,11 +121,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
setAccount(requestAccount);
|
setAccount(requestAccount);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[navigation, network, requestedChain],
|
[navigation, requestedChain],
|
||||||
);
|
);
|
||||||
|
|
||||||
const gasFees = useMemo(() => {
|
const gasFees = useMemo(() => {
|
||||||
if (network === 'eth') {
|
if (namespace === EIP155) {
|
||||||
return BigNumber.from(transaction.gasLimit)
|
return BigNumber.from(transaction.gasLimit)
|
||||||
.mul(BigNumber.from(transaction.gasPrice))
|
.mul(BigNumber.from(transaction.gasPrice))
|
||||||
.toString();
|
.toString();
|
||||||
@ -99,7 +135,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
return cosmosFees.amount[0].amount;
|
return cosmosFees.amount[0].amount;
|
||||||
}
|
}
|
||||||
}, [transaction, network]);
|
}, [transaction, namespace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
retrieveData(transaction.from!);
|
retrieveData(transaction.from!);
|
||||||
@ -115,9 +151,10 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
networksData,
|
networksData,
|
||||||
requestEvent,
|
requestEvent,
|
||||||
account,
|
account,
|
||||||
network,
|
namespace!,
|
||||||
|
requestedChain!.chainId,
|
||||||
'',
|
'',
|
||||||
network === 'eth' ? provider : cosmosStargateClient,
|
namespace === EIP155 ? provider : cosmosStargateClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { topic } = requestEvent;
|
const { topic } = requestEvent;
|
||||||
@ -140,7 +177,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getAccountBalance = async (account: Account) => {
|
const getAccountBalance = async (account: Account) => {
|
||||||
if (network === 'eth') {
|
if (namespace === EIP155) {
|
||||||
const fetchedBalance =
|
const fetchedBalance =
|
||||||
provider && (await provider.getBalance(account.address));
|
provider && (await provider.getBalance(account.address));
|
||||||
|
|
||||||
@ -158,7 +195,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
if (account) {
|
if (account) {
|
||||||
getAccountBalance(account);
|
getAccountBalance(account);
|
||||||
}
|
}
|
||||||
}, [account, provider, network, cosmosStargateClient, requestedChain]);
|
}, [account, provider, namespace, cosmosStargateClient, requestedChain]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
@ -184,35 +221,6 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [navigation, route.name]);
|
}, [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'),
|
|
||||||
requestedChain?.addressPrefix,
|
|
||||||
);
|
|
||||||
|
|
||||||
const client = await SigningStargateClient.connectWithSigner(
|
|
||||||
requestedChain?.rpcUrl!,
|
|
||||||
sender,
|
|
||||||
);
|
|
||||||
|
|
||||||
setCosmosStargateClient(client);
|
|
||||||
};
|
|
||||||
|
|
||||||
setClient();
|
|
||||||
}, [account, requestedChain, chainId, network]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -240,14 +248,12 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
</View>
|
</View>
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Balance (${
|
label={`Balance (${
|
||||||
requestedChain!.networkType === 'eth'
|
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
||||||
? 'wei'
|
})`}
|
||||||
: requestedChain!.nativeDenom
|
|
||||||
}`}
|
|
||||||
data={
|
data={
|
||||||
balance === '' || balance === undefined
|
balance === '' || balance === undefined
|
||||||
? 'Loading balance...'
|
? 'Loading balance...'
|
||||||
: `${balance})`
|
: `${balance}`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{transaction && (
|
{transaction && (
|
||||||
@ -255,9 +261,7 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
<DataBox label="To" data={transaction.to!} />
|
<DataBox label="To" data={transaction.to!} />
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Amount (${
|
label={`Amount (${
|
||||||
requestedChain!.networkType === 'eth'
|
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
||||||
? 'wei'
|
|
||||||
: requestedChain!.nativeDenom
|
|
||||||
})`}
|
})`}
|
||||||
data={BigNumber.from(
|
data={BigNumber.from(
|
||||||
transaction.value?.toString(),
|
transaction.value?.toString(),
|
||||||
@ -265,13 +269,11 @@ const ApproveTransaction = ({ route }: SignRequestProps) => {
|
|||||||
/>
|
/>
|
||||||
<DataBox
|
<DataBox
|
||||||
label={`Gas Fees (${
|
label={`Gas Fees (${
|
||||||
requestedChain!.networkType === 'eth'
|
namespace === EIP155 ? 'wei' : requestedChain!.nativeDenom
|
||||||
? 'wei'
|
|
||||||
: requestedChain!.nativeDenom
|
|
||||||
})`}
|
})`}
|
||||||
data={gasFees!}
|
data={gasFees!}
|
||||||
/>
|
/>
|
||||||
{network === 'eth' && (
|
{namespace === EIP155 && (
|
||||||
<DataBox label="Data" data={transaction.data!} />
|
<DataBox label="Data" data={transaction.data!} />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { View, ActivityIndicator, Image } from 'react-native';
|
import { View, ActivityIndicator, Image } from 'react-native';
|
||||||
import { Button, Text } from 'react-native-paper';
|
import { Button, Text } from 'react-native-paper';
|
||||||
|
|
||||||
@ -29,22 +29,16 @@ const WCLogo = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const HomeScreen = () => {
|
const HomeScreen = () => {
|
||||||
const {
|
const { accounts, setAccounts, currentIndex, setCurrentIndex } =
|
||||||
accounts,
|
useAccounts();
|
||||||
setAccounts,
|
|
||||||
currentIndex,
|
|
||||||
setCurrentIndex,
|
|
||||||
networkType,
|
|
||||||
setNetworkType,
|
|
||||||
} = useAccounts();
|
|
||||||
|
|
||||||
const { networksData, currentChainId, setCurrentChainId } = useNetworks();
|
const { networksData, selectedNetwork, setSelectedNetwork } = useNetworks();
|
||||||
const { setActiveSessions } = useWalletConnect();
|
const { setActiveSessions } = useWalletConnect();
|
||||||
|
|
||||||
const navigation =
|
const navigation =
|
||||||
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
useNavigation<NativeStackNavigationProp<StackParamsList>>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (accounts.ethAccounts.length > 0) {
|
if (accounts.length > 0) {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<Button onPress={() => navigation.navigate('WalletConnect')}>
|
<Button onPress={() => navigation.navigate('WalletConnect')}>
|
||||||
@ -69,16 +63,27 @@ const HomeScreen = () => {
|
|||||||
const hideWalletDialog = () => setWalletDialog(false);
|
const hideWalletDialog = () => setWalletDialog(false);
|
||||||
const hideResetDialog = () => setResetWalletDialog(false);
|
const hideResetDialog = () => setResetWalletDialog(false);
|
||||||
|
|
||||||
|
const fetchAccounts = useCallback(async () => {
|
||||||
|
if (!selectedNetwork) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadedAccounts = await retrieveAccounts(selectedNetwork);
|
||||||
|
|
||||||
|
if (loadedAccounts) {
|
||||||
|
setAccounts(loadedAccounts);
|
||||||
|
setIsWalletCreated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAccountsFetched(true);
|
||||||
|
}, [selectedNetwork, setAccounts]);
|
||||||
|
|
||||||
const createWalletHandler = async () => {
|
const createWalletHandler = async () => {
|
||||||
setIsWalletCreating(true);
|
setIsWalletCreating(true);
|
||||||
const { mnemonic, ethAccounts, cosmosAccounts } = await createWallet();
|
const mnemonic = await createWallet(networksData);
|
||||||
if (ethAccounts && cosmosAccounts) {
|
if (mnemonic) {
|
||||||
setAccounts({
|
fetchAccounts();
|
||||||
ethAccounts: [...accounts.ethAccounts, ethAccounts],
|
|
||||||
cosmosAccounts: [...accounts.cosmosAccounts, cosmosAccounts],
|
|
||||||
});
|
|
||||||
setWalletDialog(true);
|
setWalletDialog(true);
|
||||||
setIsWalletCreated(true);
|
|
||||||
setPhrase(mnemonic);
|
setPhrase(mnemonic);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -87,10 +92,7 @@ const HomeScreen = () => {
|
|||||||
await resetWallet();
|
await resetWallet();
|
||||||
setIsWalletCreated(false);
|
setIsWalletCreated(false);
|
||||||
setIsWalletCreating(false);
|
setIsWalletCreating(false);
|
||||||
setAccounts({
|
setAccounts([]);
|
||||||
ethAccounts: [],
|
|
||||||
cosmosAccounts: [],
|
|
||||||
});
|
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
const sessions = web3wallet!.getActiveSessions();
|
const sessions = web3wallet!.getActiveSessions();
|
||||||
|
|
||||||
@ -103,12 +105,10 @@ const HomeScreen = () => {
|
|||||||
setActiveSessions({});
|
setActiveSessions({});
|
||||||
|
|
||||||
hideResetDialog();
|
hideResetDialog();
|
||||||
setNetworkType('eth');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateNetwork = (networksData: NetworksDataState) => {
|
const updateNetwork = (networksData: NetworksDataState) => {
|
||||||
setNetworkType(networksData.networkType);
|
setSelectedNetwork(networksData);
|
||||||
setCurrentChainId(networksData.chainId);
|
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,34 +117,8 @@ const HomeScreen = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAccounts = async () => {
|
|
||||||
if (!currentChainId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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({
|
|
||||||
ethAccounts: ethLoadedAccounts,
|
|
||||||
cosmosAccounts: cosmosLoadedAccounts,
|
|
||||||
});
|
|
||||||
setIsWalletCreated(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsAccountsFetched(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchAccounts();
|
fetchAccounts();
|
||||||
}, [currentChainId, networksData, setAccounts]);
|
}, [networksData, setAccounts, selectedNetwork, fetchAccounts]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.appContainer}>
|
<View style={styles.appContainer}>
|
||||||
@ -157,11 +131,7 @@ const HomeScreen = () => {
|
|||||||
<>
|
<>
|
||||||
<NetworkDropdown updateNetwork={updateNetwork} />
|
<NetworkDropdown updateNetwork={updateNetwork} />
|
||||||
<View style={styles.accountComponent}>
|
<View style={styles.accountComponent}>
|
||||||
<Accounts
|
<Accounts currentIndex={currentIndex} updateIndex={updateIndex} />
|
||||||
network={networkType}
|
|
||||||
currentIndex={currentIndex}
|
|
||||||
updateIndex={updateIndex}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.resetContainer}>
|
<View style={styles.resetContainer}>
|
||||||
<Button
|
<Button
|
||||||
|
@ -12,7 +12,8 @@ import AccountDetails from '../components/AccountDetails';
|
|||||||
type SignProps = NativeStackScreenProps<StackParamsList, 'SignMessage'>;
|
type SignProps = NativeStackScreenProps<StackParamsList, 'SignMessage'>;
|
||||||
|
|
||||||
const SignMessage = ({ route }: SignProps) => {
|
const SignMessage = ({ route }: SignProps) => {
|
||||||
const network = route.params.selectedNetwork;
|
const namespace = route.params.selectedNamespace;
|
||||||
|
const chainId = route.params.selectedChainId;
|
||||||
const account = route.params.accountInfo;
|
const account = route.params.accountInfo;
|
||||||
|
|
||||||
const [message, setMessage] = useState<string>('');
|
const [message, setMessage] = useState<string>('');
|
||||||
@ -20,8 +21,9 @@ const SignMessage = ({ route }: SignProps) => {
|
|||||||
const signMessageHandler = async () => {
|
const signMessageHandler = async () => {
|
||||||
const signedMessage = await signMessage({
|
const signedMessage = await signMessage({
|
||||||
message,
|
message,
|
||||||
network,
|
namespace,
|
||||||
accountId: account.counterId,
|
chainId,
|
||||||
|
accountId: account.index,
|
||||||
});
|
});
|
||||||
Alert.alert('Signature', signedMessage);
|
Alert.alert('Signature', signedMessage);
|
||||||
};
|
};
|
||||||
@ -31,7 +33,7 @@ const SignMessage = ({ route }: SignProps) => {
|
|||||||
<View style={styles.accountInfo}>
|
<View style={styles.accountInfo}>
|
||||||
<View>
|
<View>
|
||||||
<Text variant="titleMedium">
|
<Text variant="titleMedium">
|
||||||
{account && `Account ${account.counterId + 1}`}
|
{account && `Account ${account.index + 1}`}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.accountContainer}>
|
<View style={styles.accountContainer}>
|
||||||
|
@ -26,7 +26,7 @@ import { useNetworks } from '../context/NetworksContext';
|
|||||||
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
|
type SignRequestProps = NativeStackScreenProps<StackParamsList, 'SignRequest'>;
|
||||||
|
|
||||||
const SignRequest = ({ route }: SignRequestProps) => {
|
const SignRequest = ({ route }: SignRequestProps) => {
|
||||||
const { networksData } = useNetworks();
|
const { networksData, selectedNetwork } = useNetworks();
|
||||||
|
|
||||||
const requestSession = route.params.requestSessionData;
|
const requestSession = route.params.requestSessionData;
|
||||||
const requestName = requestSession?.peer?.metadata?.name;
|
const requestName = requestSession?.peer?.metadata?.name;
|
||||||
@ -35,7 +35,8 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
const [account, setAccount] = useState<Account>();
|
const [account, setAccount] = useState<Account>();
|
||||||
const [message, setMessage] = useState<string>('');
|
const [message, setMessage] = useState<string>('');
|
||||||
const [network, setNetwork] = useState<string>('');
|
const [namespace, setNamespace] = useState<string>('');
|
||||||
|
const [chainId, setChainId] = 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 [isRejecting, setIsRejecting] = useState(false);
|
||||||
@ -68,20 +69,15 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
|
|
||||||
const retrieveData = useCallback(
|
const retrieveData = useCallback(
|
||||||
async (
|
async (
|
||||||
requestNetwork: string,
|
requestNamespace: string,
|
||||||
|
requestChainId: string,
|
||||||
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,
|
requestNamespace,
|
||||||
|
requestChainId,
|
||||||
requestAddress,
|
requestAddress,
|
||||||
currentChain?.addressPrefix,
|
|
||||||
);
|
);
|
||||||
if (!requestAccount) {
|
if (!requestAccount) {
|
||||||
navigation.navigate('InvalidPath');
|
navigation.navigate('InvalidPath');
|
||||||
@ -94,21 +90,23 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
if (requestMessage !== message) {
|
if (requestMessage !== message) {
|
||||||
setMessage(decodeURIComponent(requestMessage));
|
setMessage(decodeURIComponent(requestMessage));
|
||||||
}
|
}
|
||||||
if (requestNetwork !== network) {
|
if (requestNamespace !== namespace) {
|
||||||
setNetwork(requestNetwork);
|
setNamespace(requestNamespace);
|
||||||
|
}
|
||||||
|
if (requestChainId !== chainId) {
|
||||||
|
setChainId(requestChainId);
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
[account, message, navigation, network, networksData],
|
[account, message, navigation, namespace, chainId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sanitizePath = useCallback(
|
const sanitizePath = useCallback(
|
||||||
(path: string) => {
|
(path: string) => {
|
||||||
const regex = /^\/sign\/(eth|cosmos)\/(.+)\/(.+)$/;
|
const regex = /^\/sign\/(eip155|cosmos)\/(.+)\/(.+)$/;
|
||||||
const match = path.match(regex);
|
const match = path.match(regex);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
const [network, address, message] = match;
|
const [, network, address, message] = match;
|
||||||
return {
|
return {
|
||||||
network,
|
network,
|
||||||
address,
|
address,
|
||||||
@ -127,18 +125,28 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
const sanitizedRoute = sanitizePath(route.path);
|
const sanitizedRoute = sanitizePath(route.path);
|
||||||
sanitizedRoute &&
|
sanitizedRoute &&
|
||||||
retrieveData(
|
retrieveData(
|
||||||
route.params.network,
|
sanitizedRoute.network,
|
||||||
route.params.address,
|
// TODO: Take chainId from route.path
|
||||||
route.params.message,
|
selectedNetwork!.chainId!,
|
||||||
|
sanitizedRoute.address,
|
||||||
|
sanitizedRoute.message,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const requestEvent = route.params.requestEvent;
|
||||||
|
const requestChainId = requestEvent?.params.chainId;
|
||||||
|
|
||||||
|
const requestedChain = networksData.find(
|
||||||
|
networkData => networkData.chainId === requestChainId?.split(':')[1],
|
||||||
|
);
|
||||||
|
|
||||||
retrieveData(
|
retrieveData(
|
||||||
route.params.network,
|
requestedChain!.namespace,
|
||||||
|
requestedChain!.chainId,
|
||||||
route.params.address,
|
route.params.address,
|
||||||
route.params.message,
|
route.params.message,
|
||||||
);
|
);
|
||||||
}, [retrieveData, sanitizePath, route]);
|
}, [retrieveData, sanitizePath, route, networksData, selectedNetwork]);
|
||||||
|
|
||||||
const handleWalletConnectRequest = async () => {
|
const handleWalletConnectRequest = async () => {
|
||||||
const { requestEvent } = route.params || {};
|
const { requestEvent } = route.params || {};
|
||||||
@ -155,7 +163,8 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
networksData,
|
networksData,
|
||||||
requestEvent,
|
requestEvent,
|
||||||
account,
|
account,
|
||||||
network,
|
namespace,
|
||||||
|
chainId,
|
||||||
message,
|
message,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -170,8 +179,9 @@ const SignRequest = ({ route }: SignRequestProps) => {
|
|||||||
if (message) {
|
if (message) {
|
||||||
const signedMessage = await signMessage({
|
const signedMessage = await signMessage({
|
||||||
message,
|
message,
|
||||||
network,
|
namespace,
|
||||||
accountId: account.counterId,
|
chainId,
|
||||||
|
accountId: account.index,
|
||||||
});
|
});
|
||||||
Alert.alert('Signature', signedMessage);
|
Alert.alert('Signature', signedMessage);
|
||||||
}
|
}
|
||||||
|
34
src/types.ts
34
src/types.ts
@ -5,16 +5,19 @@ import { Web3WalletTypes } from '@walletconnect/web3wallet';
|
|||||||
|
|
||||||
export type StackParamsList = {
|
export type StackParamsList = {
|
||||||
Laconic: undefined;
|
Laconic: undefined;
|
||||||
SignMessage: { selectedNetwork: string; accountInfo: Account };
|
SignMessage: {
|
||||||
|
selectedNamespace: string;
|
||||||
|
selectedChainId: string;
|
||||||
|
accountInfo: Account;
|
||||||
|
};
|
||||||
SignRequest: {
|
SignRequest: {
|
||||||
network: string;
|
namespace: string;
|
||||||
address: string;
|
address: string;
|
||||||
message: string;
|
message: string;
|
||||||
requestEvent?: Web3WalletTypes.SessionRequest;
|
requestEvent?: Web3WalletTypes.SessionRequest;
|
||||||
requestSessionData?: SessionTypes.Struct;
|
requestSessionData?: SessionTypes.Struct;
|
||||||
};
|
};
|
||||||
ApproveTransaction: {
|
ApproveTransaction: {
|
||||||
network: string;
|
|
||||||
transaction: PopulatedTransaction;
|
transaction: PopulatedTransaction;
|
||||||
requestEvent: Web3WalletTypes.SessionRequest;
|
requestEvent: Web3WalletTypes.SessionRequest;
|
||||||
requestSessionData: SessionTypes.Struct;
|
requestSessionData: SessionTypes.Struct;
|
||||||
@ -26,20 +29,13 @@ export type StackParamsList = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Account = {
|
export type Account = {
|
||||||
counterId: number;
|
index: number;
|
||||||
pubKey: string;
|
pubKey: string;
|
||||||
address: string;
|
address: string;
|
||||||
hdPath: string;
|
hdPath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WalletDetails = {
|
|
||||||
mnemonic: string;
|
|
||||||
ethAccounts: Account | undefined;
|
|
||||||
cosmosAccounts: Account | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AccountsProps = {
|
export type AccountsProps = {
|
||||||
network: string;
|
|
||||||
currentIndex: number;
|
currentIndex: number;
|
||||||
updateIndex: (index: number) => void;
|
updateIndex: (index: number) => void;
|
||||||
};
|
};
|
||||||
@ -48,27 +44,27 @@ export type NetworkDropdownProps = {
|
|||||||
updateNetwork: (networksData: NetworksDataState) => void;
|
updateNetwork: (networksData: NetworksDataState) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AccountsState = {
|
export type NetworksFormData = {
|
||||||
ethAccounts: Account[];
|
|
||||||
cosmosAccounts: Account[];
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
namespace: string;
|
||||||
nativeDenom?: string;
|
nativeDenom?: string;
|
||||||
addressPrefix?: string;
|
addressPrefix?: string;
|
||||||
coinType?: string;
|
coinType?: string;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface NetworksDataState extends NetworksFormData {
|
||||||
|
networkId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type SignMessageParams = {
|
export type SignMessageParams = {
|
||||||
message: string;
|
message: string;
|
||||||
network: string;
|
namespace: string;
|
||||||
|
chainId: string;
|
||||||
accountId: number;
|
accountId: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,78 +16,76 @@ import { Secp256k1HdWallet } from '@cosmjs/amino';
|
|||||||
import { AccountData } from '@cosmjs/proto-signing';
|
import { AccountData } from '@cosmjs/proto-signing';
|
||||||
import { stringToPath } from '@cosmjs/crypto';
|
import { stringToPath } from '@cosmjs/crypto';
|
||||||
|
|
||||||
import { Account, NetworksDataState, WalletDetails } from '../types';
|
import { Account, NetworksDataState, NetworksFormData } from '../types';
|
||||||
import {
|
import {
|
||||||
getHDPath,
|
getHDPath,
|
||||||
getPathKey,
|
getPathKey,
|
||||||
resetKeyServers,
|
resetKeyServers,
|
||||||
updateGlobalCounter,
|
updateAccountIndices,
|
||||||
} from './misc';
|
} from './misc';
|
||||||
|
import { COSMOS, EIP155 } from './constants';
|
||||||
|
|
||||||
const createWallet = async (): Promise<WalletDetails> => {
|
const createWallet = async (
|
||||||
try {
|
networksData: NetworksDataState[],
|
||||||
|
): Promise<string> => {
|
||||||
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16));
|
const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16));
|
||||||
await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic);
|
await setInternetCredentials('mnemonicServer', 'mnemonic', mnemonic);
|
||||||
|
|
||||||
const hdNode = HDNode.fromMnemonic(mnemonic);
|
const hdNode = HDNode.fromMnemonic(mnemonic);
|
||||||
const ethNode = hdNode.derivePath("m/44'/60'/0'/0/0");
|
|
||||||
const cosmosNode = hdNode.derivePath("m/44'/118'/0'/0/0");
|
|
||||||
|
|
||||||
const ethAddress = ethNode.address;
|
for (const network of networksData) {
|
||||||
const cosmosAddress = (await getCosmosAccounts(mnemonic, "0'/0/0")).data
|
const hdPath = `m/44'/${network.coinType}'/0'/0/0`;
|
||||||
.address;
|
const node = hdNode.derivePath(hdPath);
|
||||||
|
let address;
|
||||||
|
|
||||||
const ethAccountInfo = `${"0'/0/0"},${ethNode.privateKey},${
|
switch (network.namespace) {
|
||||||
ethNode.publicKey
|
case EIP155:
|
||||||
},${ethAddress}`;
|
address = node.address;
|
||||||
const cosmosAccountInfo = `${"0'/0/0"},${cosmosNode.privateKey},${
|
break;
|
||||||
cosmosNode.publicKey
|
|
||||||
},${cosmosAddress}`;
|
case COSMOS:
|
||||||
|
address = (
|
||||||
|
await getCosmosAccounts(mnemonic, hdPath, network.addressPrefix)
|
||||||
|
).data.address;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error('Unsupported namespace');
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountInfo = `${hdPath},${node.privateKey},${node.publicKey},${address}`;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
setInternetCredentials(
|
setInternetCredentials(
|
||||||
'eth:keyServer:0',
|
`accounts/${network.namespace}:${network.chainId}/0`,
|
||||||
'eth:pathKey:0',
|
'_',
|
||||||
ethAccountInfo,
|
accountInfo,
|
||||||
),
|
),
|
||||||
setInternetCredentials(
|
setInternetCredentials(
|
||||||
'cosmos:keyServer:0',
|
`addAccountCounter/${network.namespace}:${network.chainId}`,
|
||||||
'cosmos:pathKey:0',
|
'_',
|
||||||
cosmosAccountInfo,
|
'1',
|
||||||
|
),
|
||||||
|
setInternetCredentials(
|
||||||
|
`accountIndices/${network.namespace}:${network.chainId}`,
|
||||||
|
'_',
|
||||||
|
'0',
|
||||||
),
|
),
|
||||||
setInternetCredentials('eth:accountIndices', 'ethCounter', '0'),
|
|
||||||
setInternetCredentials('cosmos:accountIndices', 'cosmosCounter', '0'),
|
|
||||||
setInternetCredentials('eth:globalCounter', 'ethGlobal', '0'),
|
|
||||||
setInternetCredentials('cosmos:globalCounter', 'cosmosGlobal', '0'),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const ethAccounts = {
|
|
||||||
counterId: 0,
|
|
||||||
pubKey: ethNode.publicKey,
|
|
||||||
address: ethAddress,
|
|
||||||
hdPath: "m/44'/60'/0'/0/0",
|
|
||||||
};
|
|
||||||
|
|
||||||
const cosmosAccounts = {
|
|
||||||
counterId: 0,
|
|
||||||
pubKey: cosmosNode.publicKey,
|
|
||||||
address: cosmosAddress,
|
|
||||||
hdPath: "m/44'/118'/0'/0/0",
|
|
||||||
};
|
|
||||||
|
|
||||||
return { mnemonic, ethAccounts, cosmosAccounts };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error creating HD wallet:', error);
|
|
||||||
return { mnemonic: '', ethAccounts: undefined, cosmosAccounts: undefined };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mnemonic;
|
||||||
};
|
};
|
||||||
|
|
||||||
const addAccount = async (network: string): Promise<Account | undefined> => {
|
const addAccount = async (
|
||||||
|
networkData: NetworksDataState,
|
||||||
|
): Promise<Account | undefined> => {
|
||||||
try {
|
try {
|
||||||
const id = await getNextAccountId(network);
|
const namespaceChainId = `${networkData.namespace}:${networkData.chainId}`;
|
||||||
const hdPath = getHDPath(network, `0'/0/${id}`);
|
const id = await getNextAccountId(namespaceChainId);
|
||||||
const accounts = await addAccountFromHDPath(hdPath);
|
const hdPath = getHDPath(namespaceChainId, `0'/0/${id}`);
|
||||||
await updateAccountIndices(network, id);
|
const accounts = await addAccountFromHDPath(hdPath, networkData);
|
||||||
|
await updateAccountCounter(namespaceChainId, id);
|
||||||
return accounts;
|
return accounts;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating account:', error);
|
console.error('Error creating account:', error);
|
||||||
@ -96,37 +94,40 @@ const addAccount = async (network: string): Promise<Account | undefined> => {
|
|||||||
|
|
||||||
const addAccountFromHDPath = async (
|
const addAccountFromHDPath = async (
|
||||||
hdPath: string,
|
hdPath: string,
|
||||||
|
networkData: NetworksDataState,
|
||||||
): Promise<Account | undefined> => {
|
): Promise<Account | undefined> => {
|
||||||
try {
|
try {
|
||||||
const account = await accountInfoFromHDPath(hdPath);
|
const account = await accountInfoFromHDPath(
|
||||||
|
hdPath,
|
||||||
|
networkData.addressPrefix,
|
||||||
|
);
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error('Error while creating account');
|
throw new Error('Error while creating account');
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = hdPath.split('/');
|
const { privKey, pubKey, address } = account;
|
||||||
const path = parts.slice(-3).join('/');
|
|
||||||
|
|
||||||
const { privKey, pubKey, address, network } = account;
|
const namespaceChainId = `${networkData.namespace}:${networkData.chainId}`;
|
||||||
|
|
||||||
const counterId = (await updateGlobalCounter(network)).counterId;
|
const index = (await updateAccountIndices(namespaceChainId)).index;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
setInternetCredentials(
|
setInternetCredentials(
|
||||||
`${network}:keyServer:${counterId}`,
|
`accounts/${namespaceChainId}/${index}`,
|
||||||
`${network}:pathKey:${counterId}`,
|
'_',
|
||||||
`${path},${privKey},${pubKey},${address}`,
|
`${hdPath},${privKey},${pubKey},${address}`,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return { counterId, pubKey, address, hdPath };
|
return { index, pubKey, address, hdPath };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const storeNetworkData = async (
|
const storeNetworkData = async (
|
||||||
networkData: NetworksDataState,
|
networkData: NetworksFormData,
|
||||||
): Promise<void> => {
|
): Promise<NetworksDataState[]> => {
|
||||||
const networks = await getInternetCredentials('networks');
|
const networks = await getInternetCredentials('networks');
|
||||||
const retrievedNetworks =
|
const retrievedNetworks =
|
||||||
networks && networks.password ? JSON.parse(networks.password) : [];
|
networks && networks.password ? JSON.parse(networks.password) : [];
|
||||||
@ -135,7 +136,7 @@ const storeNetworkData = async (
|
|||||||
networkId = retrievedNetworks[retrievedNetworks.length - 1].networkId + 1;
|
networkId = retrievedNetworks[retrievedNetworks.length - 1].networkId + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedNetworks = [
|
const updatedNetworks: NetworksDataState[] = [
|
||||||
...retrievedNetworks,
|
...retrievedNetworks,
|
||||||
{ networkId: networkId, ...networkData },
|
{ networkId: networkId, ...networkData },
|
||||||
];
|
];
|
||||||
@ -144,6 +145,8 @@ const storeNetworkData = async (
|
|||||||
'_',
|
'_',
|
||||||
JSON.stringify(updatedNetworks),
|
JSON.stringify(updatedNetworks),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return updatedNetworks;
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
|
const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
|
||||||
@ -155,24 +158,23 @@ const retrieveNetworksData = async (): Promise<NetworksDataState[]> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const retrieveAccountsForNetwork = async (
|
export const retrieveAccountsForNetwork = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
accountsIndices: string,
|
accountsIndices: string,
|
||||||
prefix: string = 'cosmos',
|
|
||||||
): Promise<Account[]> => {
|
): Promise<Account[]> => {
|
||||||
const accountsIndexArray = accountsIndices.split(',');
|
const accountsIndexArray = accountsIndices.split(',');
|
||||||
|
|
||||||
const loadedAccounts = await Promise.all(
|
const loadedAccounts = await Promise.all(
|
||||||
accountsIndexArray.map(async i => {
|
accountsIndexArray.map(async i => {
|
||||||
const pubKey = (await getPathKey(network, Number(i), prefix)).pubKey;
|
const { address, path, pubKey } = await getPathKey(
|
||||||
const address = (await getPathKey(network, Number(i), prefix)).address;
|
namespaceChainId,
|
||||||
const path = (await getPathKey(network, Number(i))).path;
|
Number(i),
|
||||||
const hdPath = getHDPath(network, path);
|
);
|
||||||
|
|
||||||
const account: Account = {
|
const account: Account = {
|
||||||
counterId: Number(i),
|
index: Number(i),
|
||||||
pubKey: pubKey,
|
pubKey,
|
||||||
address: address,
|
address,
|
||||||
hdPath: hdPath,
|
hdPath: path,
|
||||||
};
|
};
|
||||||
return account;
|
return account;
|
||||||
}),
|
}),
|
||||||
@ -182,77 +184,57 @@ export const retrieveAccountsForNetwork = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const retrieveAccounts = async (
|
const retrieveAccounts = async (
|
||||||
prefix: string = 'cosmos',
|
currentNetworkData: NetworksDataState,
|
||||||
): Promise<{
|
): Promise<Account[] | undefined> => {
|
||||||
ethLoadedAccounts?: Account[];
|
const accountIndicesServer = await getInternetCredentials(
|
||||||
cosmosLoadedAccounts?: Account[];
|
`accountIndices/${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
||||||
}> => {
|
);
|
||||||
const ethServer = await getInternetCredentials('eth:globalCounter');
|
const accountIndices = accountIndicesServer && accountIndicesServer.password;
|
||||||
const ethCounter = ethServer && ethServer.password;
|
|
||||||
const cosmosServer = await getInternetCredentials('cosmos:globalCounter');
|
|
||||||
const cosmosCounter = cosmosServer && cosmosServer.password;
|
|
||||||
|
|
||||||
const ethLoadedAccounts = ethCounter
|
const loadedAccounts = accountIndices
|
||||||
? await retrieveAccountsForNetwork('eth', ethCounter)
|
? await retrieveAccountsForNetwork(
|
||||||
: undefined;
|
`${currentNetworkData.namespace}:${currentNetworkData.chainId}`,
|
||||||
const cosmosLoadedAccounts = cosmosCounter
|
accountIndices,
|
||||||
? await retrieveAccountsForNetwork('cosmos', cosmosCounter, prefix)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return { ethLoadedAccounts, cosmosLoadedAccounts };
|
return loadedAccounts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const retrieveSingleAccount = async (
|
const retrieveSingleAccount = async (
|
||||||
network: string,
|
namespace: string,
|
||||||
|
chainId: string,
|
||||||
address: string,
|
address: string,
|
||||||
prefix: string = 'cosmos',
|
|
||||||
) => {
|
) => {
|
||||||
let loadedAccounts;
|
let loadedAccounts;
|
||||||
|
|
||||||
switch (network) {
|
const accountIndicesServer = await getInternetCredentials(
|
||||||
case 'eth':
|
`accountIndices/${namespace}:${chainId}`,
|
||||||
const ethServer = await getInternetCredentials('eth:globalCounter');
|
|
||||||
const ethCounter = ethServer && ethServer.password;
|
|
||||||
|
|
||||||
if (ethCounter) {
|
|
||||||
loadedAccounts = await retrieveAccountsForNetwork(network, ethCounter);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cosmos':
|
|
||||||
const cosmosServer = await getInternetCredentials('cosmos:globalCounter');
|
|
||||||
const cosmosCounter = cosmosServer && cosmosServer.password;
|
|
||||||
|
|
||||||
if (cosmosCounter) {
|
|
||||||
loadedAccounts = await retrieveAccountsForNetwork(
|
|
||||||
network,
|
|
||||||
cosmosCounter,
|
|
||||||
prefix,
|
|
||||||
);
|
);
|
||||||
}
|
const accountIndices = accountIndicesServer && accountIndicesServer.password;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
if (!accountIndices) {
|
||||||
break;
|
throw new Error('Indices for given chain not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedAccounts = await retrieveAccountsForNetwork(
|
||||||
|
`${namespace}:${chainId}`,
|
||||||
|
accountIndices,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!loadedAccounts) {
|
||||||
|
throw new Error('Accounts for given chain not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadedAccounts) {
|
|
||||||
return loadedAccounts.find(account => account.address === address);
|
return loadedAccounts.find(account => account.address === address);
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetWallet = async () => {
|
const resetWallet = async () => {
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
resetInternetCredentials('mnemonicServer'),
|
resetInternetCredentials('mnemonicServer'),
|
||||||
resetKeyServers('eth'),
|
resetKeyServers(EIP155),
|
||||||
resetKeyServers('cosmos'),
|
resetKeyServers(COSMOS),
|
||||||
resetInternetCredentials('eth:accountIndices'),
|
|
||||||
resetInternetCredentials('cosmos:accountIndices'),
|
|
||||||
resetInternetCredentials('eth:globalCounter'),
|
|
||||||
resetInternetCredentials('cosmos:globalCounter'),
|
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error resetting wallet:', error);
|
console.error('Error resetting wallet:', error);
|
||||||
@ -262,11 +244,10 @@ const resetWallet = async () => {
|
|||||||
|
|
||||||
const accountInfoFromHDPath = async (
|
const accountInfoFromHDPath = async (
|
||||||
hdPath: string,
|
hdPath: string,
|
||||||
|
prefix: string = COSMOS,
|
||||||
): Promise<
|
): Promise<
|
||||||
| { privKey: string; pubKey: string; address: string; network: string }
|
{ privKey: string; pubKey: string; address: 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!');
|
||||||
@ -280,62 +261,65 @@ const accountInfoFromHDPath = async (
|
|||||||
const pubKey = node.publicKey;
|
const pubKey = node.publicKey;
|
||||||
|
|
||||||
const parts = hdPath.split('/');
|
const parts = hdPath.split('/');
|
||||||
const path = parts.slice(-3).join('/');
|
|
||||||
const coinType = parts[2];
|
const coinType = parts[2];
|
||||||
|
|
||||||
let network: string;
|
|
||||||
let address: string;
|
let address: string;
|
||||||
|
|
||||||
switch (coinType) {
|
switch (coinType) {
|
||||||
case "60'":
|
case "60'":
|
||||||
network = 'eth';
|
|
||||||
address = node.address;
|
address = node.address;
|
||||||
break;
|
break;
|
||||||
case "118'":
|
case "118'":
|
||||||
network = 'cosmos';
|
address = (await getCosmosAccounts(mnemonic, hdPath, prefix)).data
|
||||||
address = (await getCosmosAccounts(mnemonic, path)).data.address;
|
.address;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid wallet type');
|
throw new Error('Invalid wallet type');
|
||||||
}
|
}
|
||||||
return { privKey, pubKey, address, network };
|
return { privKey, pubKey, address };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextAccountId = async (network: string): Promise<number> => {
|
const getNextAccountId = async (namespaceChainId: string): Promise<number> => {
|
||||||
const idStore = await getInternetCredentials(`${network}:accountIndices`);
|
const idStore = await getInternetCredentials(
|
||||||
|
`addAccountCounter/${namespaceChainId}`,
|
||||||
|
);
|
||||||
if (!idStore) {
|
if (!idStore) {
|
||||||
throw new Error('Account id not found');
|
throw new Error('Account id not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountIds = idStore.password;
|
const accountCounter = idStore.password;
|
||||||
const ids = accountIds.split(',').map(Number);
|
const nextCounter = Number(accountCounter);
|
||||||
return ids[ids.length - 1] + 1;
|
return nextCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateAccountIndices = async (
|
const updateAccountCounter = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
id: number,
|
id: number,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const idStore = await getInternetCredentials(`${network}:accountIndices`);
|
const idStore = await getInternetCredentials(
|
||||||
|
`addAccountCounter/${namespaceChainId}`,
|
||||||
|
);
|
||||||
if (!idStore) {
|
if (!idStore) {
|
||||||
throw new Error('Account id not found');
|
throw new Error('Account id not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedIndices = `${idStore.password},${id.toString()}`;
|
const updatedCounter = String(id + 1);
|
||||||
await resetInternetCredentials(`${network}:accountIndices`);
|
await resetInternetCredentials(`addAccountCounter/${namespaceChainId}`);
|
||||||
await setInternetCredentials(
|
await setInternetCredentials(
|
||||||
`${network}:accountIndices`,
|
`addAccountCounter/${namespaceChainId}`,
|
||||||
`${network}Counter`,
|
'_',
|
||||||
updatedIndices,
|
updatedCounter,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCosmosAccounts = async (
|
const getCosmosAccounts = async (
|
||||||
mnemonic: string,
|
mnemonic: string,
|
||||||
path: string,
|
path: string,
|
||||||
|
prefix: string = COSMOS,
|
||||||
): Promise<{ cosmosWallet: Secp256k1HdWallet; data: AccountData }> => {
|
): Promise<{ cosmosWallet: Secp256k1HdWallet; data: AccountData }> => {
|
||||||
const cosmosWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
|
const cosmosWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
|
||||||
hdPaths: [stringToPath(`m/44'/118'/${path}`)],
|
hdPaths: [stringToPath(path)],
|
||||||
|
prefix,
|
||||||
});
|
});
|
||||||
|
|
||||||
const accountsData = await cosmosWallet.getAccounts();
|
const accountsData = await cosmosWallet.getAccounts();
|
||||||
@ -355,6 +339,6 @@ export {
|
|||||||
resetWallet,
|
resetWallet,
|
||||||
accountInfoFromHDPath,
|
accountInfoFromHDPath,
|
||||||
getNextAccountId,
|
getNextAccountId,
|
||||||
updateAccountIndices,
|
updateAccountCounter,
|
||||||
getCosmosAccounts,
|
getCosmosAccounts,
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
|
import { COSMOS_TESTNET_CHAINS } from './wallet-connect/COSMOSData';
|
||||||
import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
|
import { EIP155_CHAINS } from './wallet-connect/EIP155Data';
|
||||||
|
|
||||||
export const DEFAULTNETWORKS = [
|
export const EIP155 = 'eip155';
|
||||||
|
export const COSMOS = 'cosmos';
|
||||||
|
export const DEFAULT_NETWORKS = [
|
||||||
{
|
{
|
||||||
chainId: 'eip155:1',
|
chainId: '1',
|
||||||
networkName: EIP155_CHAINS['eip155:1'].name,
|
networkName: EIP155_CHAINS['eip155:1'].name,
|
||||||
networkType: 'eth',
|
namespace: EIP155,
|
||||||
rpcUrl: EIP155_CHAINS['eip155:1'].rpc,
|
rpcUrl: EIP155_CHAINS['eip155:1'].rpc,
|
||||||
currencySymbol: 'ETH',
|
currencySymbol: 'ETH',
|
||||||
|
coinType: '60',
|
||||||
isDefault: true,
|
isDefault: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
chainId: 'cosmos:theta-testnet-001',
|
chainId: 'theta-testnet-001',
|
||||||
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
networkName: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].name,
|
||||||
networkType: 'cosmos',
|
namespace: COSMOS,
|
||||||
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
rpcUrl: COSMOS_TESTNET_CHAINS['cosmos:theta-testnet-001'].rpc,
|
||||||
nativeDenom: 'uatom',
|
nativeDenom: 'uatom',
|
||||||
addressPrefix: 'cosmos',
|
addressPrefix: 'cosmos',
|
||||||
|
@ -10,9 +10,11 @@ import {
|
|||||||
setInternetCredentials,
|
setInternetCredentials,
|
||||||
} from 'react-native-keychain';
|
} from 'react-native-keychain';
|
||||||
|
|
||||||
import { AccountData, Secp256k1Wallet } from '@cosmjs/amino';
|
import { AccountData } 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';
|
||||||
|
import { EIP155 } from './constants';
|
||||||
|
import { NetworksDataState } from '../types';
|
||||||
|
|
||||||
const getMnemonic = async (): Promise<string> => {
|
const getMnemonic = async (): Promise<string> => {
|
||||||
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
||||||
@ -24,8 +26,9 @@ const getMnemonic = async (): Promise<string> => {
|
|||||||
return mnemonic;
|
return mnemonic;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHDPath = (network: string, path: string): string => {
|
const getHDPath = (namespaceChainId: string, path: string): string => {
|
||||||
return network === 'eth' ? `m/44'/60'/${path}` : `m/44'/118'/${path}`;
|
const namespace = namespaceChainId.split(':')[0];
|
||||||
|
return namespace === EIP155 ? `m/44'/60'/${path}` : `m/44'/118'/${path}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDirectWallet = async (
|
export const getDirectWallet = async (
|
||||||
@ -42,9 +45,8 @@ export const getDirectWallet = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPathKey = async (
|
const getPathKey = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
prefix: string = 'cosmos',
|
|
||||||
): Promise<{
|
): Promise<{
|
||||||
path: string;
|
path: string;
|
||||||
privKey: string;
|
privKey: string;
|
||||||
@ -52,7 +54,7 @@ const getPathKey = async (
|
|||||||
address: string;
|
address: string;
|
||||||
}> => {
|
}> => {
|
||||||
const pathKeyStore = await getInternetCredentials(
|
const pathKeyStore = await getInternetCredentials(
|
||||||
`${network}:keyServer:${accountId}`,
|
`accounts/${namespaceChainId}/${accountId}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!pathKeyStore) {
|
if (!pathKeyStore) {
|
||||||
@ -63,84 +65,94 @@ 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];
|
||||||
let pubKey: string;
|
const address = pathkey[3];
|
||||||
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 };
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGlobalCounter = async (
|
const getAccountIndices = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
accountCounter: string;
|
accountIndices: string;
|
||||||
counterIds: number[];
|
indices: number[];
|
||||||
counterId: number;
|
index: number;
|
||||||
}> => {
|
}> => {
|
||||||
const counterStore = await getInternetCredentials(`${network}:globalCounter`);
|
const counterStore = await getInternetCredentials(
|
||||||
|
`accountIndices/${namespaceChainId}`,
|
||||||
|
);
|
||||||
|
|
||||||
if (!counterStore) {
|
if (!counterStore) {
|
||||||
throw new Error('Error while fetching counter');
|
throw new Error('Error while fetching counter');
|
||||||
}
|
}
|
||||||
|
|
||||||
let accountCounter = counterStore.password;
|
let accountIndices = counterStore.password;
|
||||||
const counterIds = accountCounter.split(',').map(Number);
|
const indices = accountIndices.split(',').map(Number);
|
||||||
const counterId = counterIds[counterIds.length - 1] + 1;
|
const index = indices[indices.length - 1] + 1;
|
||||||
|
|
||||||
return { accountCounter, counterIds, counterId };
|
return { accountIndices, indices, index };
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateGlobalCounter = async (
|
const updateAccountIndices = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
): Promise<{ accountCounter: string; counterId: number }> => {
|
): Promise<{ accountIndices: string; index: number }> => {
|
||||||
const globalCounterData = await getGlobalCounter(network);
|
const accountIndicesData = await getAccountIndices(namespaceChainId);
|
||||||
const accountCounter = globalCounterData.accountCounter;
|
const accountIndices = accountIndicesData.accountIndices;
|
||||||
const counterId = globalCounterData.counterId;
|
const index = accountIndicesData.index;
|
||||||
const updatedAccountCounter = `${accountCounter},${counterId.toString()}`;
|
const updatedAccountIndices = `${accountIndices},${index.toString()}`;
|
||||||
|
|
||||||
await resetInternetCredentials(`${network}:globalCounter`);
|
await resetInternetCredentials(`accountIndices/${namespaceChainId}`);
|
||||||
await setInternetCredentials(
|
await setInternetCredentials(
|
||||||
`${network}:globalCounter`,
|
`accountIndices/${namespaceChainId}`,
|
||||||
`${network}Global`,
|
'_',
|
||||||
updatedAccountCounter,
|
updatedAccountIndices,
|
||||||
);
|
);
|
||||||
|
|
||||||
return { accountCounter: updatedAccountCounter, counterId };
|
return { accountIndices: updatedAccountIndices, index };
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetKeyServers = async (prefix: string) => {
|
const resetKeyServers = async (namespace: string) => {
|
||||||
const idStore = await getInternetCredentials(`${prefix}:accountIndices`);
|
const networksServer = await getInternetCredentials('networks');
|
||||||
|
if (!networksServer) {
|
||||||
|
throw new Error('Networks not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const networksData: NetworksDataState[] = JSON.parse(networksServer.password);
|
||||||
|
const filteredNetworks = networksData.filter(
|
||||||
|
network => network.namespace === namespace,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredNetworks.length === 0) {
|
||||||
|
throw new Error(`No networks found for namespace ${namespace}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredNetworks.forEach(async network => {
|
||||||
|
const { chainId } = network;
|
||||||
|
const namespaceChainId = `${namespace}:${chainId}`;
|
||||||
|
|
||||||
|
const idStore = await getInternetCredentials(
|
||||||
|
`accountIndices/${namespaceChainId}`,
|
||||||
|
);
|
||||||
if (!idStore) {
|
if (!idStore) {
|
||||||
throw new Error('Account id not found.');
|
throw new Error(`Account indices not found for ${namespaceChainId}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountIds = idStore.password;
|
const accountIds = idStore.password;
|
||||||
const ids = accountIds.split(',').map(Number);
|
const ids = accountIds.split(',').map(Number);
|
||||||
const id = ids[ids.length - 1];
|
const latestId = Math.max(...ids);
|
||||||
|
|
||||||
for (let i = 0; i <= id; i++) {
|
for (let i = 0; i <= latestId; i++) {
|
||||||
await resetInternetCredentials(`${prefix}:keyServer:${i}`);
|
await resetInternetCredentials(`accounts/${namespaceChainId}/${i}`);
|
||||||
}
|
}
|
||||||
|
await resetInternetCredentials(`addAccountCounter/${namespaceChainId}`);
|
||||||
|
await resetInternetCredentials(`accountIndices/${namespaceChainId}`);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getMnemonic,
|
getMnemonic,
|
||||||
getPathKey,
|
getPathKey,
|
||||||
updateGlobalCounter,
|
updateAccountIndices,
|
||||||
getHDPath,
|
getHDPath,
|
||||||
resetKeyServers,
|
resetKeyServers,
|
||||||
};
|
};
|
||||||
|
@ -10,19 +10,21 @@ import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
|||||||
import { SignMessageParams } from '../types';
|
import { SignMessageParams } from '../types';
|
||||||
import { getDirectWallet, getMnemonic, getPathKey } from './misc';
|
import { getDirectWallet, getMnemonic, getPathKey } from './misc';
|
||||||
import { getCosmosAccounts } from './accounts';
|
import { getCosmosAccounts } from './accounts';
|
||||||
|
import { COSMOS, EIP155 } from './constants';
|
||||||
|
|
||||||
const signMessage = async ({
|
const signMessage = async ({
|
||||||
message,
|
message,
|
||||||
network,
|
namespace,
|
||||||
|
chainId,
|
||||||
accountId,
|
accountId,
|
||||||
}: SignMessageParams): Promise<string | undefined> => {
|
}: SignMessageParams): Promise<string | undefined> => {
|
||||||
const path = (await getPathKey(network, accountId)).path;
|
const path = await getPathKey(`${namespace}:${chainId}`, accountId);
|
||||||
|
|
||||||
switch (network) {
|
switch (namespace) {
|
||||||
case 'eth':
|
case EIP155:
|
||||||
return await signEthMessage(message, accountId);
|
return await signEthMessage(message, accountId, chainId);
|
||||||
case 'cosmos':
|
case COSMOS:
|
||||||
return await signCosmosMessage(message, path);
|
return await signCosmosMessage(message, path.path);
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid wallet type');
|
throw new Error('Invalid wallet type');
|
||||||
}
|
}
|
||||||
@ -31,9 +33,11 @@ const signMessage = async ({
|
|||||||
const signEthMessage = async (
|
const signEthMessage = async (
|
||||||
message: string,
|
message: string,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
|
chainId: string,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
try {
|
try {
|
||||||
const privKey = (await getPathKey('eth', accountId)).privKey;
|
const privKey = (await getPathKey(`${EIP155}:${chainId}`, accountId))
|
||||||
|
.privKey;
|
||||||
const wallet = new Wallet(privKey);
|
const wallet = new Wallet(privKey);
|
||||||
const signature = await wallet.signMessage(message);
|
const signature = await wallet.signMessage(message);
|
||||||
|
|
||||||
@ -83,12 +87,12 @@ const signCosmosMessage = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const signDirectMessage = async (
|
const signDirectMessage = async (
|
||||||
network: string,
|
namespaceChainId: string,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
signDoc: SignDoc,
|
signDoc: SignDoc,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
try {
|
try {
|
||||||
const path = (await getPathKey(network, accountId)).path;
|
const path = (await getPathKey(namespaceChainId, accountId)).path;
|
||||||
const mnemonic = await getMnemonic();
|
const mnemonic = await getMnemonic();
|
||||||
const { directWallet, data } = await getDirectWallet(mnemonic, path);
|
const { directWallet, data } = await getDirectWallet(mnemonic, path);
|
||||||
|
|
||||||
|
@ -21,19 +21,21 @@ export async function approveWalletConnectRequest(
|
|||||||
networksData: NetworksDataState[],
|
networksData: NetworksDataState[],
|
||||||
requestEvent: SignClientTypes.EventArguments['session_request'],
|
requestEvent: SignClientTypes.EventArguments['session_request'],
|
||||||
account: Account,
|
account: Account,
|
||||||
network: string,
|
namespace: string,
|
||||||
|
chainId: string,
|
||||||
message?: string,
|
message?: string,
|
||||||
provider?: providers.JsonRpcProvider | SigningStargateClient,
|
provider?: providers.JsonRpcProvider | SigningStargateClient,
|
||||||
) {
|
) {
|
||||||
const { params, id } = requestEvent;
|
const { params, id } = requestEvent;
|
||||||
const { request } = params;
|
const { request } = params;
|
||||||
|
|
||||||
const chainId = requestEvent.params.chainId;
|
const requestChainId = requestEvent.params.chainId;
|
||||||
const requestedChain = networksData.find(
|
const requestedChain = networksData.find(
|
||||||
networkData => networkData.chainId === chainId,
|
networkData => networkData.chainId === requestChainId.split(':')[1],
|
||||||
);
|
);
|
||||||
|
|
||||||
const path = (await getPathKey(network, account.counterId)).path;
|
const path = (await getPathKey(`${namespace}:${chainId}`, account.index))
|
||||||
|
.path;
|
||||||
const mnemonic = await getMnemonic();
|
const mnemonic = await getMnemonic();
|
||||||
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
const cosmosAccount = await getCosmosAccounts(mnemonic, path);
|
||||||
const address = account.address;
|
const address = account.address;
|
||||||
@ -44,7 +46,9 @@ export async function approveWalletConnectRequest(
|
|||||||
throw new Error('JSON RPC provider not found');
|
throw new Error('JSON RPC provider not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const privKey = (await getPathKey('eth', account.counterId)).privKey;
|
const privKey = (
|
||||||
|
await getPathKey(`${namespace}:${chainId}`, account.index)
|
||||||
|
).privKey;
|
||||||
const wallet = new Wallet(privKey);
|
const wallet = new Wallet(privKey);
|
||||||
const sendTransaction = request.params[0];
|
const sendTransaction = request.params[0];
|
||||||
|
|
||||||
@ -64,7 +68,11 @@ export async function approveWalletConnectRequest(
|
|||||||
throw new Error('Message to be signed not found');
|
throw new Error('Message to be signed not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const ethSignature = await signEthMessage(message, account.counterId);
|
const ethSignature = await signEthMessage(
|
||||||
|
message,
|
||||||
|
account.index,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
return formatJsonRpcResult(id, ethSignature);
|
return formatJsonRpcResult(id, ethSignature);
|
||||||
|
|
||||||
case 'cosmos_signDirect':
|
case 'cosmos_signDirect':
|
||||||
@ -78,8 +86,8 @@ export async function approveWalletConnectRequest(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const cosmosDirectSignature = await signDirectMessage(
|
const cosmosDirectSignature = await signDirectMessage(
|
||||||
network,
|
`${namespace}:${chainId}`,
|
||||||
account.counterId,
|
account.index,
|
||||||
{
|
{
|
||||||
...request.params.signDoc,
|
...request.params.signDoc,
|
||||||
bodyBytes: bodyBytesArray,
|
bodyBytes: bodyBytesArray,
|
||||||
@ -106,7 +114,10 @@ export async function approveWalletConnectRequest(
|
|||||||
});
|
});
|
||||||
|
|
||||||
case 'cosmos_sendTokens':
|
case 'cosmos_sendTokens':
|
||||||
const amount = coins(request.params[0].value, requestedChain!.chainId);
|
const amount = coins(
|
||||||
|
request.params[0].value,
|
||||||
|
requestedChain!.nativeDenom!,
|
||||||
|
);
|
||||||
const gasPrice = GasPrice.fromString(
|
const gasPrice = GasPrice.fromString(
|
||||||
request.params[0].gasPrice.toString(),
|
request.params[0].gasPrice.toString(),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user