/* 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 { utils } from 'ethers'; import { HDNode } from 'ethers/lib/utils'; import { setInternetCredentials, resetInternetCredentials, getInternetCredentials, } from 'react-native-keychain'; import { Account, WalletDetails } from '../types'; import { accountInfoFromHDPath, getAddress, getCosmosAccounts, getHDPath, getMnemonic, getNextAccountId, getPathKey, resetKeyServers, updateAccountIndices, updateGlobalCounter, } from './utils'; const createWallet = async (): Promise => { try { const mnemonic = utils.entropyToMnemonic(utils.randomBytes(16)); await setInternetCredentials('mnemonicServer', 'mnemonic', 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; const cosmosAddress = (await getCosmosAccounts(mnemonic, "0'/0/0")).data .address; const ethAccountInfo = `${"0'/0/0"},${ethNode.privateKey},${ ethNode.publicKey },${ethAddress}`; const cosmosAccountInfo = `${"0'/0/0"},${cosmosNode.privateKey},${ cosmosNode.publicKey },${cosmosAddress}`; await Promise.all([ setInternetCredentials( 'eth:keyServer:0', 'eth:pathKey:0', ethAccountInfo, ), setInternetCredentials( 'cosmos:keyServer:0', 'cosmos:pathKey:0', cosmosAccountInfo, ), 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 }; } }; const addAccount = async (network: string): Promise => { try { const mnemonic = await getMnemonic(); const hdNode = HDNode.fromMnemonic(mnemonic); const id = await getNextAccountId(network); const hdPath = getHDPath(network, `0'/0/${id}`); const node = hdNode.derivePath(hdPath); const pubKey = node.publicKey; const address = await getAddress(network, mnemonic, `0'/0/${id}`); await updateAccountIndices(network, id); const { counterId } = await updateGlobalCounter(network); await Promise.all([ setInternetCredentials( `${network}:keyServer:${counterId}`, `${network}:pathKey:${counterId}`, `0'/0/${id},${node.privateKey},${node.publicKey},${address}`, ), ]); return { counterId, pubKey, address, hdPath }; } catch (error) { console.error('Error creating account:', error); } }; const addAccountFromHDPath = async ( hdPath: string, ): Promise => { try { const account = await accountInfoFromHDPath(hdPath); if (!account) { throw new Error('Error while creating account'); } const parts = hdPath.split('/'); const path = parts.slice(-3).join('/'); const { privKey, pubKey, address, network } = account; const counterId = (await updateGlobalCounter(network)).counterId; await Promise.all([ setInternetCredentials( `${network}:keyServer:${counterId}`, `${network}:pathKey:${counterId}`, `${path},${privKey},${pubKey},${address}`, ), ]); return { counterId, pubKey, address, hdPath }; } catch (error) { console.error(error); } }; const retrieveAccountsForNetwork = async ( network: string, count: string, ): Promise => { const elementsArray = count.split(','); const loadedAccounts = await Promise.all( elementsArray.map(async i => { const pubKey = (await getPathKey(network, Number(i))).pubKey; const address = (await getPathKey(network, Number(i))).address; const path = (await getPathKey(network, Number(i))).path; const hdPath = getHDPath(network, path); const account: Account = { counterId: Number(i), pubKey: pubKey, address: address, hdPath: hdPath, }; return account; }), ); return loadedAccounts; }; const retrieveAccounts = async (): Promise<{ ethLoadedAccounts?: Account[]; cosmosLoadedAccounts?: Account[]; }> => { const ethServer = await getInternetCredentials('eth:globalCounter'); const ethCounter = ethServer && ethServer.password; const cosmosServer = await getInternetCredentials('cosmos:globalCounter'); const cosmosCounter = cosmosServer && cosmosServer.password; const ethLoadedAccounts = ethCounter ? await retrieveAccountsForNetwork('eth', ethCounter) : undefined; const cosmosLoadedAccounts = cosmosCounter ? await retrieveAccountsForNetwork('cosmos', cosmosCounter) : undefined; return { ethLoadedAccounts, cosmosLoadedAccounts }; }; const retrieveSingleAccount = async (network: string, address: string) => { let loadedAccounts; switch (network) { case 'eth': 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, ); } break; default: break; } if (loadedAccounts) { return loadedAccounts.find(account => account.address === address); } return undefined; }; const resetWallet = async () => { try { await Promise.all([ resetInternetCredentials('mnemonicServer'), resetKeyServers('eth'), resetKeyServers('cosmos'), resetInternetCredentials('eth:accountIndices'), resetInternetCredentials('cosmos:accountIndices'), resetInternetCredentials('eth:globalCounter'), resetInternetCredentials('cosmos:globalCounter'), ]); } catch (error) { console.error('Error resetting wallet:', error); throw error; } }; export { createWallet, addAccount, addAccountFromHDPath, retrieveAccounts, retrieveSingleAccount, resetWallet, };