Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REF] ensure that each evm account has all supported wallets attached #1387

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion src/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -565,6 +622,9 @@ export default () => {
);
} else {
await runAddressFix();
if (!accountEvmCreationMigrationComplete) {
await runCompleteEvmWalletsAccountFix();
}
urlHandler();
}

Expand Down
65 changes: 37 additions & 28 deletions src/store/wallet/effects/create/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>(
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<any>(
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}`,
),
);
}
}

Expand Down
22 changes: 18 additions & 4 deletions src/store/wallet/effects/import/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ import {
deleteKey,
failedImport,
setCustomizeNonce,
setEnableReplaceByFee,
setUseUnconfirmedFunds,
setWalletTermsAccepted,
successImport,
updateCacheFeeLevel,
updatePortfolioBalance,
} from '../../wallet.actions';
import {
BitpaySupportedEthereumTokenOptsByAddress,
Expand Down Expand Up @@ -95,14 +93,17 @@ 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,
IsSegwitCoin,
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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions src/store/wallet/utils/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
(
Expand Down
4 changes: 4 additions & 0 deletions src/store/wallet/wallet.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
8 changes: 8 additions & 0 deletions src/store/wallet/wallet.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface WalletState {
initLogs: AddLog[];
customTokensMigrationComplete: boolean;
polygonMigrationComplete: boolean;
accountEvmCreationMigrationComplete: boolean;
}

export const initialState: WalletState = {
Expand Down Expand Up @@ -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 = (
Expand Down Expand Up @@ -606,6 +608,12 @@ export const walletReducer = (
polygonMigrationComplete: true,
};

case WalletActionTypes.SET_ACCOUNT_EVM_CREATION_MIGRATION_COMPLETE:
return {
...state,
accountEvmCreationMigrationComplete: true,
};

default:
return state;
}
Expand Down
8 changes: 7 additions & 1 deletion src/store/wallet/wallet.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -372,4 +377,5 @@ export type WalletActionType =
| toggleHideAccount
| updateCacheFeeLevel
| SetCustomTokensMigrationComplete
| setPolygonMigrationComplete;
| setPolygonMigrationComplete
| setAccountEVMCreationMigrationComplete;
54 changes: 51 additions & 3 deletions src/utils/helper-methods.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -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),
);
};