diff --git a/src/Root.tsx b/src/Root.tsx index 7c006ef7d..f56fd913f 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -104,7 +104,12 @@ import DebugScreen, { import CardActivationGroup, { CardActivationGroupParamList, } from './navigation/card-activation/CardActivationGroup'; -import {fixWalletAddresses, sleep} from './utils/helper-methods'; +import { + createWalletsForAccounts, + fixWalletAddresses, + getEvmGasWallets, + sleep, +} from './utils/helper-methods'; import {Analytics} from './store/analytics/analytics.effects'; import { handleBwsEvent, @@ -133,6 +138,11 @@ import SettingsGroup, { } from './navigation/tabs/settings/SettingsGroup'; import {ImportLedgerWalletModal} from './components/modal/import-ledger-wallet/ImportLedgerWalletModal'; import {WalletConnectStartModal} from './components/modal/wallet-connect/WalletConnectStartModal'; +import {KeyMethods} from './store/wallet/wallet.models'; +import { + setAccountEVMCreationMigrationComplete, + successAddWallet, +} from './store/wallet/wallet.actions'; // ROOT NAVIGATION CONFIG export type RootStackParamList = { @@ -266,6 +276,9 @@ export default () => { ); const inAppMessageData = useAppSelector(({APP}) => APP.inAppMessageData); const keys = useAppSelector(({WALLET}) => WALLET.keys); + const accountEvmCreationMigrationComplete = useAppSelector( + ({WALLET}) => WALLET.accountEvmCreationMigrationComplete, + ); const blurScreenList: string[] = [ OnboardingScreens.IMPORT, @@ -553,6 +566,50 @@ export default () => { } }; + // we need to ensure that each evm account has all supported wallets attached. + const runCompleteEvmWalletsAccountFix = async () => { + try { + dispatch(startOnGoingProcessModal('GENERAL_AWAITING')); + await sleep(1000); // give the modal time to show + await Promise.all( + Object.values(keys).map(async key => { + const evmWallets = getEvmGasWallets(key.wallets); + const accountsArray = [ + ...new Set( + evmWallets.map(wallet => wallet.credentials.account), + ), + ]; + const wallets = await createWalletsForAccounts( + dispatch, + accountsArray, + key.methods as KeyMethods, + ); + key.wallets.push(...wallets); + dispatch(successAddWallet({key})); + }), + ); + dispatch( + LogActions.info( + 'success [runCompleteEvmWalletsAccountFix]', + ), + ); + dispatch(setAccountEVMCreationMigrationComplete()); + dispatch(dismissOnGoingProcessModal()); + } catch (error) { + const errMsg = + error instanceof Error + ? error.message + : JSON.stringify(error); + dispatch( + LogActions.error( + `Error in [runCompleteEvmWalletsAccountFix]: ${errMsg}`, + ), + ); + dispatch(setAccountEVMCreationMigrationComplete()); + dispatch(dismissOnGoingProcessModal()); + } + }; + if (pinLockActive || biometricLockActive) { const subscriptionToPinModalDismissed = DeviceEventEmitter.addListener( @@ -565,6 +622,9 @@ export default () => { ); } else { await runAddressFix(); + if (!accountEvmCreationMigrationComplete) { + await runCompleteEvmWalletsAccountFix(); + } urlHandler(); } diff --git a/src/store/wallet/effects/create/create.ts b/src/store/wallet/effects/create/create.ts index 5eab07704..06fff5346 100644 --- a/src/store/wallet/effects/create/create.ts +++ b/src/store/wallet/effects/create/create.ts @@ -368,35 +368,44 @@ export const createMultipleWallets = const tokens = currencies.filter(({isToken}) => isToken); const coins = currencies.filter(({isToken}) => !isToken); for (const coin of coins) { - const wallet = (await dispatch( - createWallet({ - key, - coin: coin.currencyAbbreviation, - chain: coin.chain as SupportedChains, - options: { - ...options, - useNativeSegwit: IsSegwitCoin(coin.currencyAbbreviation), - }, - }), - )) as Wallet; - const receiveAddress = (await dispatch( - createWalletAddress({wallet, newAddress: true}), - )) as string; - dispatch(LogActions.info(`new address generated: ${receiveAddress}`)); - wallet.receiveAddress = receiveAddress; - wallets.push(wallet); - for (const token of tokens) { - if (token.chain === coin.chain) { - const tokenWallet = await dispatch( - createTokenWallet( - wallet, - token.currencyAbbreviation.toLowerCase(), - token.tokenAddress!, - tokenOpts, - ), - ); - wallets.push(tokenWallet); + try { + const wallet = (await dispatch( + createWallet({ + key, + coin: coin.currencyAbbreviation, + chain: coin.chain as SupportedChains, + options: { + ...options, + useNativeSegwit: IsSegwitCoin(coin.currencyAbbreviation), + }, + }), + )) as Wallet; + const receiveAddress = (await dispatch( + createWalletAddress({wallet, newAddress: true}), + )) as string; + dispatch(LogActions.info(`new address generated: ${receiveAddress}`)); + wallet.receiveAddress = receiveAddress; + wallets.push(wallet); + for (const token of tokens) { + if (token.chain === coin.chain) { + const tokenWallet = await dispatch( + createTokenWallet( + wallet, + token.currencyAbbreviation.toLowerCase(), + token.tokenAddress!, + tokenOpts, + ), + ); + wallets.push(tokenWallet); + } } + } catch (err) { + const errMsg = err instanceof Error ? err.message : JSON.stringify(err); + dispatch( + LogActions.debug( + `Error creating wallet - continue anyway: ${errMsg}`, + ), + ); } } diff --git a/src/store/wallet/effects/import/import.ts b/src/store/wallet/effects/import/import.ts index 4db0b90e4..d31c6c1b7 100644 --- a/src/store/wallet/effects/import/import.ts +++ b/src/store/wallet/effects/import/import.ts @@ -26,12 +26,10 @@ import { deleteKey, failedImport, setCustomizeNonce, - setEnableReplaceByFee, setUseUnconfirmedFunds, setWalletTermsAccepted, successImport, updateCacheFeeLevel, - updatePortfolioBalance, } from '../../wallet.actions'; import { BitpaySupportedEthereumTokenOptsByAddress, @@ -95,7 +93,6 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import RNRestart from 'react-native-restart'; import uniqBy from 'lodash.uniqby'; import {credentialsFromExtendedPublicKey} from '../../../../utils/wallet-hardware'; -import {sleep} from '../../../../utils/helper-methods'; import {BitpaySupportedCoins} from '../../../../constants/currencies'; import { GetName, @@ -103,6 +100,10 @@ import { isSingleAddressChain, } from '../../utils/currency'; import {BASE_BWS_URL} from '../../../../constants/config'; +import { + createWalletsForAccounts, + getEvmGasWallets, +} from '../../../../utils/helper-methods'; const BWC = BwcProvider.getInstance(); const BwcConstants = BWC.getConstants(); @@ -849,7 +850,19 @@ export const startImportMnemonic = opts.xPrivKey = xPrivKey; const data = await serverAssistedImport(opts); - + // we need to ensure that each evm account has all supported wallets attached. + const evmWallets = getEvmGasWallets(data.wallets); + const accountsArray = [ + ...new Set(evmWallets.map(wallet => wallet.credentials.account)), + ]; + const _wallets = await createWalletsForAccounts( + dispatch, + accountsArray, + data.key as KeyMethods, + ); + if (_wallets.length > 0) { + data.wallets.push(..._wallets); + } // To Avoid Duplicate wallet import const { key: _key, @@ -1548,6 +1561,7 @@ export const serverAssistedImport = async ( } }); }; + const linkTokenToWallet = (tokens: Wallet[], wallets: Wallet[]) => { tokens.forEach(token => { // find the associated wallet to add tokens too diff --git a/src/store/wallet/utils/wallet.ts b/src/store/wallet/utils/wallet.ts index a28ff24f5..2cfd1e136 100644 --- a/src/store/wallet/utils/wallet.ts +++ b/src/store/wallet/utils/wallet.ts @@ -63,8 +63,6 @@ import { AssetsByChainData, AssetsByChainListProps, } from '../../../navigation/wallet/screens/AccountDetails'; -import {DeviceEventEmitter} from 'react-native'; -import {DeviceEmitterEvents} from '../../../constants/device-emitter-events'; export const mapAbbreviationAndName = ( diff --git a/src/store/wallet/wallet.actions.ts b/src/store/wallet/wallet.actions.ts index 30a2ca59b..b00c970ad 100644 --- a/src/store/wallet/wallet.actions.ts +++ b/src/store/wallet/wallet.actions.ts @@ -279,3 +279,7 @@ export const setCustomTokensMigrationComplete = (): WalletActionType => ({ export const setPolygonMigrationComplete = (): WalletActionType => ({ type: WalletActionTypes.SET_POLYGON_MIGRATION_COMPLETE, }); + +export const setAccountEVMCreationMigrationComplete = (): WalletActionType => ({ + type: WalletActionTypes.SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE, +}); diff --git a/src/store/wallet/wallet.reducer.ts b/src/store/wallet/wallet.reducer.ts index 5666daaed..0915d0bcf 100644 --- a/src/store/wallet/wallet.reducer.ts +++ b/src/store/wallet/wallet.reducer.ts @@ -38,6 +38,7 @@ export interface WalletState { initLogs: AddLog[]; customTokensMigrationComplete: boolean; polygonMigrationComplete: boolean; + accountEvmCreationMigrationComplete: boolean; } export const initialState: WalletState = { @@ -71,6 +72,7 @@ export const initialState: WalletState = { initLogs: [], // keep init logs at the end (order is important) customTokensMigrationComplete: false, polygonMigrationComplete: false, + accountEvmCreationMigrationComplete: false, }; export const walletReducer = ( @@ -606,6 +608,12 @@ export const walletReducer = ( polygonMigrationComplete: true, }; + case WalletActionTypes.SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE: + return { + ...state, + accountEvmCreationMigrationComplete: true, + }; + default: return state; } diff --git a/src/store/wallet/wallet.types.ts b/src/store/wallet/wallet.types.ts index 448c9f640..e14c91fff 100644 --- a/src/store/wallet/wallet.types.ts +++ b/src/store/wallet/wallet.types.ts @@ -54,6 +54,7 @@ export enum WalletActionTypes { CLEAR_DEFERRED_IMPORT = 'WALLET/CLEAR_DEFERRED_IMPORT', SET_CUSTOM_TOKENS_MIGRATION_COMPLETE = 'APP/SET_CUSTOM_TOKENS_MIGRATION_COMPLETE', SET_POLYGON_MIGRATION_COMPLETE = 'APP/SET_POLYGON_MIGRATION_COMPLETE', + SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE = 'APP/SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE', } interface successWalletStoreInit { @@ -330,6 +331,10 @@ interface setPolygonMigrationComplete { type: typeof WalletActionTypes.SET_POLYGON_MIGRATION_COMPLETE; } +interface setAccountEVMCreationMigrationComplete { + type: typeof WalletActionTypes.SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE; +} + export type WalletActionType = | successWalletStoreInit | failedWalletStoreInit @@ -372,4 +377,5 @@ export type WalletActionType = | toggleHideAccount | updateCacheFeeLevel | SetCustomTokensMigrationComplete - | setPolygonMigrationComplete; + | setPolygonMigrationComplete + | setAccountEVMCreationMigrationComplete; diff --git a/src/utils/helper-methods.ts b/src/utils/helper-methods.ts index f45b2a129..c6927fa54 100644 --- a/src/utils/helper-methods.ts +++ b/src/utils/helper-methods.ts @@ -1,17 +1,21 @@ -import {Key, Wallet} from '../store/wallet/wallet.models'; +import {Key, KeyMethods, Wallet} from '../store/wallet/wallet.models'; import {ContactRowProps} from '../components/list/ContactRow'; import {Network} from '../constants'; import {CurrencyListIcons} from '../constants/SupportedCurrencyOptions'; import {ReactElement} from 'react'; -import {IsERCToken} from '../store/wallet/utils/currency'; +import {IsERCToken, IsEVMChain} from '../store/wallet/utils/currency'; import {Rate, Rates} from '../store/rate/rate.models'; import {PROTOCOL_NAME} from '../constants/config'; import _ from 'lodash'; import {NavigationProp, StackActions} from '@react-navigation/native'; import {AppDispatch} from './hooks'; import {createWalletAddress} from '../store/wallet/effects/address/address'; -import {SUPPORTED_EVM_COINS} from '../constants/currencies'; +import { + getBaseAccountCreationCoinsAndTokens, + SUPPORTED_EVM_COINS, +} from '../constants/currencies'; import {LogActions} from '../store/log'; +import {createMultipleWallets} from '../store/wallet/effects'; export const suffixChainMap: {[suffix: string]: string} = { eth: 'e', @@ -581,3 +585,47 @@ export const fixWalletAddresses = async ({ }), ); }; + +export const createWalletsForAccounts = async ( + dispatch: any, + accountsArray: number[], + key: KeyMethods, +) => { + return ( + await Promise.all( + accountsArray.flatMap(async account => { + try { + const newWallets = (await dispatch( + createMultipleWallets({ + key: key as KeyMethods, + currencies: getBaseAccountCreationCoinsAndTokens(), + options: { + account, + customAccount: true, + }, + }), + )) as Wallet[]; + return newWallets; + } catch (err) { + const errMsg = + err instanceof Error ? err.message : JSON.stringify(err); + dispatch( + LogActions.debug( + `Error creating wallet - continue anyway: ${errMsg}`, + ), + ); + } + }), + ) + ) + .flat() + .filter(Boolean) as Wallet[]; +}; + +export const getEvmGasWallets = (wallets: Wallet[]) => { + return wallets.filter( + wallet => + IsEVMChain(wallet.credentials.chain) && + !IsERCToken(wallet.credentials.coin, wallet.credentials.chain), + ); +};