From 7aa051c50e38499752be9dff1d396c40420b6742 Mon Sep 17 00:00:00 2001 From: bry Date: Tue, 1 Oct 2024 09:53:17 -0500 Subject: [PATCH] Rework import logic, make onsignal error nonblocking --- .../onboarding/AccountAssignScreen.tsx | 132 +++++++++--------- src/storage/AccountStorageProvider.tsx | 81 ++++------- src/storage/oneSignalStorage.ts | 22 ++- 3 files changed, 115 insertions(+), 120 deletions(-) diff --git a/src/features/onboarding/AccountAssignScreen.tsx b/src/features/onboarding/AccountAssignScreen.tsx index b1611f2c7..c2793c824 100644 --- a/src/features/onboarding/AccountAssignScreen.tsx +++ b/src/features/onboarding/AccountAssignScreen.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-shadow */ import AccountIcon from '@components/AccountIcon' import Box from '@components/Box' import CircleLoader from '@components/CircleLoader' @@ -19,6 +20,7 @@ import { useTranslation } from 'react-i18next' import { KeyboardAvoidingView, Platform, StyleSheet } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { accountNetType } from '@utils/accountUtils' +import { ResolvedPath } from '@hooks/useDerivationAccounts' import { RootNavigationProp } from '../../navigation/rootTypes' import { useSolana } from '../../solana/SolanaProvider' import { useAccountStorage } from '../../storage/AccountStorageProvider' @@ -79,85 +81,87 @@ const AccountAssignScreen = () => { return paths }, [paths, route.params?.secretKey, route.params?.derivationPath]) - const { execute: handlePress, loading } = useAsyncCallback(async () => { - const getName = (index: number): string => { - const name = `${alias} ${index + 1}` - if (!existingNames?.has(name)) { - return name - } - - return getName(index + 1) - } + const storeAllSecureAccounts = useCallback(async () => { + const secureAccounts = allPaths + .slice() + .reverse() + .map((path) => + toSecureAccount({ + words, + keypair: path.keypair, + derivationPath: path.derivationPath, + }), + ) - try { - let mnemonicHash: string | undefined - if (words) { - mnemonicHash = createHash('sha256') - .update(words.join(' ')) - .digest('hex') - } + await Promise.all(secureAccounts.map(storeSecureAccount)) + }, [words, allPaths]) - const newAccounts = await Promise.all( - allPaths.map(async (p, index) => ({ - alias: index === 0 ? alias : getName(index), - address: heliumAddressFromSolAddress(p.keypair.publicKey.toBase58()), - solanaAddress: p.keypair.publicKey.toBase58(), - derivationPath: p.derivationPath, - mnemonicHash, - version: 'v1' as CSAccountVersion, - balance: await connection?.getBalance(p.keypair.publicKey), - })), - ) + const generateMnemonicHash = (words?: string[]): string | undefined => { + if (!words) return undefined + return createHash('sha256').update(words.join(' ')).digest('hex') + } - const highestBalanceAcc = newAccounts.reduce((acc, curr) => { - if ( - curr.balance !== undefined && - (acc.balance === undefined || curr.balance > acc.balance) - ) { - return curr - } + const generateUniqueName = useCallback( + (baseAlias: string, index: number): string => { + const name = `${baseAlias} ${index + 1}` + return existingNames?.has(name) + ? generateUniqueName(baseAlias, index + 1) + : name + }, + [existingNames], + ) - return acc - }, newAccounts[0]) + const createNewAccounts = async ( + paths: ResolvedPath[], + alias: string, + mnemonicHash?: string, + ) => { + return Promise.all( + paths.map(async (p, index) => ({ + alias: index === 0 ? alias : generateUniqueName(alias, index), + address: heliumAddressFromSolAddress(p.keypair.publicKey.toBase58()), + solanaAddress: p.keypair.publicKey.toBase58(), + derivationPath: p.derivationPath, + mnemonicHash, + version: 'v1' as CSAccountVersion, + balance: (await connection?.getBalance(p.keypair.publicKey)) ?? 0, + })), + ) + } - await Promise.all( - allPaths.reverse().map(async (p) => { - await storeSecureAccount( - toSecureAccount({ - words, - keypair: p.keypair, - derivationPath: p.derivationPath, - }), - ) - }), + const { execute: handlePress, loading } = useAsyncCallback(async () => { + try { + await storeAllSecureAccounts() + const mnemonicHash = generateMnemonicHash(words) + const newAccounts = await createNewAccounts(allPaths, alias, mnemonicHash) + const highestBalanceAcc = newAccounts.reduce((prev, current) => + (current.balance ?? 0) > (prev.balance ?? 0) ? current : prev, ) await upsertAccounts(newAccounts) - setCurrentAccount({ - ...highestBalanceAcc, - netType: accountNetType(highestBalanceAcc.address), - }) - if (setAsDefault) { await updateDefaultAccountAddress(highestBalanceAcc.address) } - reset() - } catch (e) { - console.error(e) - return - } + if (hasAccounts) { + rootNav.reset({ + index: 0, + routes: [{ name: 'TabBarNavigator' }], + }) + reset() + } else { + onboardingNav.navigate('AccountCreatePinScreen', { + pinReset: true, + }) + } - if (hasAccounts) { - rootNav.reset({ - index: 0, - routes: [{ name: 'TabBarNavigator' }], - }) - } else { - onboardingNav.navigate('AccountCreatePinScreen', { - pinReset: true, + setCurrentAccount({ + ...highestBalanceAcc, + netType: accountNetType(highestBalanceAcc.address), }) + } catch (e) { + console.error(e) } }) diff --git a/src/storage/AccountStorageProvider.tsx b/src/storage/AccountStorageProvider.tsx index 3f2fa5536..84b6c2130 100644 --- a/src/storage/AccountStorageProvider.tsx +++ b/src/storage/AccountStorageProvider.tsx @@ -27,9 +27,7 @@ import { useAppStorage } from './AppStorageProvider' import { CSAccount, CSAccounts, - CSAccountVersion, getCloudDefaultAccountAddress, - LedgerDevice, restoreAccounts, setCloudDefaultAccountAddress, signoutCloudStorage, @@ -221,18 +219,14 @@ const useAccountStorageHook = () => { await storeSecureAccount(secureAccount) } - let { mnemonicHash } = csAccount - if (secureAccount?.mnemonic && !mnemonicHash) { - mnemonicHash = createHash('sha256') - .update(secureAccount?.mnemonic.join(' ')) - .digest('hex') - } - - let { solanaAddress } = csAccount + const mnemonicHash = secureAccount?.mnemonic + ? createHash('sha256') + .update(secureAccount.mnemonic.join(' ')) + .digest('hex') + : csAccount.mnemonicHash - if (!solanaAddress) { - solanaAddress = heliumAddressToSolAddress(csAccount.address) - } + const solanaAddress = + csAccount.solanaAddress || heliumAddressToSolAddress(csAccount.address) const nextAccount: CSAccount = { ...csAccount, @@ -246,51 +240,42 @@ const useAccountStorageHook = () => { ...accounts, [csAccount.address]: nextAccount, } + setAccounts(nextAccounts) setCurrentAccount(nextAccount) - await updateCloudAccounts(nextAccounts) - await tagAccount(csAccount.address) + + await Promise.all([ + updateCloudAccounts(nextAccounts), + tagAccount(csAccount.address), + ]) }, [accounts], ) const upsertAccounts = useCallback( async ( - accountBulk: { - alias: string - address: string - ledgerDevice?: LedgerDevice - ledgerIndex?: number - solanaAddress: string - derivationPath?: string - mnemonicHash?: string - version?: CSAccountVersion - }[], + accountBulk: Array< + Partial & { + address: string + solanaAddress: string + ledgerIndex?: number + } + >, ) => { if (!accountBulk.length) return - const bulkAccounts = accountBulk.reduce((prev, curr) => { - const accountIndex = curr.ledgerIndex || 0 - return { - ...prev, - [curr.address]: { - alias: curr.alias, - address: curr.address, - netType: accountNetType(curr.address), - ledgerDevice: curr.ledgerDevice, - accountIndex, - solanaAddress: curr.solanaAddress, - derivationPath: curr.derivationPath, - mnemonicHash: curr.mnemonicHash, - version: curr.version, - }, - } + const bulkAccounts = accountBulk.reduce((acc, curr) => { + const { address, ledgerIndex, ...rest } = curr + acc[address] = { + ...rest, + address, + netType: accountNetType(address), + accountIndex: ledgerIndex ?? 0, + } as CSAccount + return acc }, {}) - const nextAccounts: CSAccounts = { - ...accounts, - ...bulkAccounts, - } + const nextAccounts: CSAccounts = { ...accounts, ...bulkAccounts } setAccounts(nextAccounts) setCurrentAccount( @@ -298,11 +283,7 @@ const useAccountStorageHook = () => { ) await updateCloudAccounts(nextAccounts) - // eslint-disable-next-line no-restricted-syntax - for (const addr of Object.keys(bulkAccounts)) { - // eslint-disable-next-line no-await-in-loop - await tagAccount(addr) - } + await Promise.all(Object.keys(bulkAccounts).map(tagAccount)) }, [accounts], ) diff --git a/src/storage/oneSignalStorage.ts b/src/storage/oneSignalStorage.ts index ac3fb136f..d1b13caf5 100644 --- a/src/storage/oneSignalStorage.ts +++ b/src/storage/oneSignalStorage.ts @@ -4,14 +4,24 @@ import Bcrypt from 'bcrypt-react-native' export const tagAccount = async (address: string) => { if (!Config.ONE_SIGNAL_ACCOUNT_TAG_SALT) return - const salt = Config.ONE_SIGNAL_ACCOUNT_TAG_SALT - const hash = await Bcrypt.hash(salt, address) - OneSignal.OneSignal.User.addTag(hash, ' ') + + try { + const salt = Config.ONE_SIGNAL_ACCOUNT_TAG_SALT + const hash = await Bcrypt.hash(salt, address) + OneSignal.OneSignal.User.addTag(hash, ' ') + } catch (err) { + console.error(err) + } } export const removeAccountTag = async (address: string) => { if (!Config.ONE_SIGNAL_ACCOUNT_TAG_SALT) return - const salt = Config.ONE_SIGNAL_ACCOUNT_TAG_SALT - const hash = await Bcrypt.hash(salt, address) - OneSignal.OneSignal.User.removeTag(hash) + + try { + const salt = Config.ONE_SIGNAL_ACCOUNT_TAG_SALT + const hash = await Bcrypt.hash(salt, address) + OneSignal.OneSignal.User.removeTag(hash) + } catch (err) { + console.error(err) + } }