forked from cerc-io/laconic-wallet
* Display HD path on sign message page * Create component for displaying account details * Add retrieve accounts function * Load accounts after closing app * Fix the retrieve accounts function * Use hdpath instead of id * Check if keystore is empty while retrieving accounts * Add spinner when accounts are being fetched * Display complete hd paths after reloading the app * Remove any return type * Store public key and address * Modify sign message function to use path * Fix the add accounts functionality
221 lines
5.8 KiB
TypeScript
221 lines
5.8 KiB
TypeScript
/* Importing this library provides react native with a secure random source.
|
|
For more information, "visit https://docs.ethers.org/v5/cookbook/react-native/#cookbook-reactnative-security" */
|
|
import 'react-native-get-random-values';
|
|
import '@ethersproject/shims';
|
|
|
|
import { HDNode } from 'ethers/lib/utils';
|
|
import {
|
|
getInternetCredentials,
|
|
resetInternetCredentials,
|
|
setInternetCredentials,
|
|
} from 'react-native-keychain';
|
|
|
|
import { AccountData, Secp256k1HdWallet } from '@cosmjs/amino';
|
|
import { stringToPath } from '@cosmjs/crypto';
|
|
|
|
const getMnemonic = async (): Promise<string> => {
|
|
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
|
if (!mnemonicStore) {
|
|
throw new Error('Mnemonic not found!');
|
|
}
|
|
|
|
const mnemonic = mnemonicStore.password;
|
|
return mnemonic;
|
|
};
|
|
|
|
const getHDPath = (network: string, path: string): string => {
|
|
return network === 'eth' ? `m/44'/60'/${path}` : `m/44'/118'/${path}`;
|
|
};
|
|
|
|
const getAddress = async (
|
|
network: string,
|
|
mnemonic: string,
|
|
path: string,
|
|
): Promise<string> => {
|
|
switch (network) {
|
|
case 'eth':
|
|
return HDNode.fromMnemonic(mnemonic).derivePath(`m/44'/60'/${path}`)
|
|
.address;
|
|
case 'cosmos':
|
|
return (await getCosmosAccounts(mnemonic, `${path}`)).data.address;
|
|
default:
|
|
throw new Error('Invalid wallet type');
|
|
}
|
|
};
|
|
|
|
const getCosmosAccounts = async (
|
|
mnemonic: string,
|
|
path: string,
|
|
): Promise<{ cosmosWallet: Secp256k1HdWallet; data: AccountData }> => {
|
|
const cosmosWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, {
|
|
hdPaths: [stringToPath(`m/44'/118'/${path}`)],
|
|
});
|
|
|
|
const accountsData = await cosmosWallet.getAccounts();
|
|
const data = accountsData[0];
|
|
|
|
return { cosmosWallet, data };
|
|
};
|
|
|
|
const accountInfoFromHDPath = async (
|
|
hdPath: string,
|
|
): Promise<
|
|
| { privKey: string; pubKey: string; address: string; network: string }
|
|
| undefined
|
|
> => {
|
|
const mnemonicStore = await getInternetCredentials('mnemonicServer');
|
|
if (!mnemonicStore) {
|
|
throw new Error('Mnemonic not found!');
|
|
}
|
|
|
|
const mnemonic = mnemonicStore.password;
|
|
const hdNode = HDNode.fromMnemonic(mnemonic);
|
|
const node = hdNode.derivePath(hdPath);
|
|
|
|
const privKey = node.privateKey;
|
|
const pubKey = node.publicKey;
|
|
|
|
const parts = hdPath.split('/');
|
|
const path = parts.slice(-3).join('/');
|
|
const coinType = parts[2];
|
|
|
|
let network: string;
|
|
let address: string;
|
|
|
|
switch (coinType) {
|
|
case "60'":
|
|
network = 'eth';
|
|
address = node.address;
|
|
break;
|
|
case "118'":
|
|
network = 'cosmos';
|
|
address = (await getCosmosAccounts(mnemonic, path)).data.address;
|
|
break;
|
|
default:
|
|
throw new Error('Invalid wallet type');
|
|
}
|
|
return { privKey, pubKey, address, network };
|
|
};
|
|
|
|
const getPathKey = async (
|
|
network: string,
|
|
accountId: number,
|
|
): Promise<{
|
|
path: string;
|
|
privKey: string;
|
|
pubKey: string;
|
|
address: string;
|
|
}> => {
|
|
const pathKeyStore = await getInternetCredentials(
|
|
`${network}:keyServer:${accountId}`,
|
|
);
|
|
|
|
if (!pathKeyStore) {
|
|
throw new Error('Error while fetching counter');
|
|
}
|
|
|
|
const pathKeyVal = pathKeyStore.password;
|
|
const pathkey = pathKeyVal.split(',');
|
|
const path = pathkey[0];
|
|
const privKey = pathkey[1];
|
|
const pubKey = pathkey[2];
|
|
const address = pathkey[3];
|
|
|
|
return { path, privKey, pubKey, address };
|
|
};
|
|
|
|
const getGlobalCounter = async (
|
|
network: string,
|
|
): Promise<{
|
|
accountCounter: string;
|
|
counterIds: number[];
|
|
counterId: number;
|
|
}> => {
|
|
const counterStore = await getInternetCredentials(`${network}:globalCounter`);
|
|
|
|
if (!counterStore) {
|
|
throw new Error('Error while fetching counter');
|
|
}
|
|
|
|
let accountCounter = counterStore.password;
|
|
const counterIds = accountCounter.split(',').map(Number);
|
|
const counterId = counterIds[counterIds.length - 1] + 1;
|
|
|
|
return { accountCounter, counterIds, counterId };
|
|
};
|
|
|
|
const updateGlobalCounter = async (
|
|
network: string,
|
|
): Promise<{ accountCounter: string; counterId: number }> => {
|
|
const globalCounterData = await getGlobalCounter(network);
|
|
const accountCounter = globalCounterData.accountCounter;
|
|
const counterId = globalCounterData.counterId;
|
|
const updatedAccountCounter = `${accountCounter},${counterId.toString()}`;
|
|
|
|
await resetInternetCredentials(`${network}:globalCounter`);
|
|
await setInternetCredentials(
|
|
`${network}:globalCounter`,
|
|
`${network}Global`,
|
|
updatedAccountCounter,
|
|
);
|
|
|
|
return { accountCounter: updatedAccountCounter, counterId };
|
|
};
|
|
|
|
const getNextAccountId = async (network: string): Promise<number> => {
|
|
const idStore = await getInternetCredentials(`${network}:accountIndices`);
|
|
if (!idStore) {
|
|
throw new Error('Account id not found');
|
|
}
|
|
|
|
const accountIds = idStore.password;
|
|
const ids = accountIds.split(',').map(Number);
|
|
return ids[ids.length - 1] + 1;
|
|
};
|
|
|
|
const updateAccountIndices = async (
|
|
network: string,
|
|
id: number,
|
|
): Promise<void> => {
|
|
const idStore = await getInternetCredentials(`${network}:accountIndices`);
|
|
if (!idStore) {
|
|
throw new Error('Account id not found');
|
|
}
|
|
|
|
const updatedIndices = `${idStore.password},${id.toString()}`;
|
|
await resetInternetCredentials(`${network}:accountIndices`);
|
|
await setInternetCredentials(
|
|
`${network}:accountIndices`,
|
|
`${network}Counter`,
|
|
updatedIndices,
|
|
);
|
|
};
|
|
|
|
const resetKeyServers = async (prefix: string) => {
|
|
const idStore = await getInternetCredentials(`${prefix}:accountIndices`);
|
|
if (!idStore) {
|
|
throw new Error('Account id not found.');
|
|
}
|
|
|
|
const accountIds = idStore.password;
|
|
const ids = accountIds.split(',').map(Number);
|
|
const id = ids[ids.length - 1];
|
|
|
|
for (let i = 0; i <= id; i++) {
|
|
await resetInternetCredentials(`${prefix}:keyServer:${i}`);
|
|
}
|
|
};
|
|
|
|
export {
|
|
accountInfoFromHDPath,
|
|
getCosmosAccounts,
|
|
getMnemonic,
|
|
getPathKey,
|
|
getNextAccountId,
|
|
updateGlobalCounter,
|
|
updateAccountIndices,
|
|
getHDPath,
|
|
getAddress,
|
|
resetKeyServers,
|
|
};
|