/* 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 => { 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 => { 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 => { 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 => { 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, };