diff --git a/app/components/Views/Login/index.js b/app/components/Views/Login/index.js index 9ea55a275af..4695544d0e2 100644 --- a/app/components/Views/Login/index.js +++ b/app/components/Views/Login/index.js @@ -278,7 +278,8 @@ class Login extends PureComponent { // Restore vault with user entered password await KeyringController.submitPassword(this.state.password); const encryptionLib = await AsyncStorage.getItem(ENCRYPTION_LIB); - if (encryptionLib !== ORIGINAL) { + const existingUser = await AsyncStorage.getItem(EXISTING_USER); + if (encryptionLib !== ORIGINAL && existingUser) { await recreateVaultWithSamePassword(this.state.password, this.props.selectedAddress); await AsyncStorage.setItem(ENCRYPTION_LIB, ORIGINAL); } diff --git a/app/components/Views/ResetPassword/index.js b/app/components/Views/ResetPassword/index.js index 1a7a3a7957a..0a9eb1ce249 100644 --- a/app/components/Views/ResetPassword/index.js +++ b/app/components/Views/ResetPassword/index.js @@ -37,6 +37,7 @@ import { ONBOARDING, PREVIOUS_SCREEN } from '../../../constants/navigation'; import { EXISTING_USER, TRUE, BIOMETRY_CHOICE_DISABLED } from '../../../constants/storage'; import { getPasswordStrengthWord, passwordRequirementsMet } from '../../../util/password'; import NotificationManager from '../../../core/NotificationManager'; +import { syncPrefs } from '../../../util/sync'; const styles = StyleSheet.create({ mainWrapper: { @@ -401,6 +402,7 @@ class ResetPassword extends PureComponent { const { originalPassword, password: newPassword } = this.state; const { KeyringController, PreferencesController } = Engine.context; const seedPhrase = await this.getSeedPhrase(); + const oldPrefs = PreferencesController.state; let importedAccounts = []; try { @@ -427,7 +429,6 @@ class ResetPassword extends PureComponent { const hdKeyring = KeyringController.state.keyrings[0]; const existingAccountCount = hdKeyring.accounts.length; const selectedAddress = this.props.selectedAddress; - let preferencesControllerState = PreferencesController.state; // Create previous accounts again for (let i = 0; i < existingAccountCount - 1; i++) { @@ -443,11 +444,12 @@ class ResetPassword extends PureComponent { Logger.error(e, 'error while trying to import accounts on recreate vault'); } - // Reset preferencesControllerState - preferencesControllerState = PreferencesController.state; + //Persist old account/identities names + const preferencesControllerState = PreferencesController.state; + const prefUpdates = syncPrefs(oldPrefs, preferencesControllerState); // Set preferencesControllerState again - await PreferencesController.update(preferencesControllerState); + await PreferencesController.update(prefUpdates); // Reselect previous selected account if still available if (hdKeyring.accounts.includes(selectedAddress)) { PreferencesController.setSelectedAddress(selectedAddress); diff --git a/app/core/Vault.js b/app/core/Vault.js index 471e6618d5a..6c3ef8684d1 100644 --- a/app/core/Vault.js +++ b/app/core/Vault.js @@ -1,5 +1,6 @@ import Engine from './Engine'; import Logger from '../util/Logger'; +import { syncPrefs, syncAccounts } from '../util/sync'; /** * Returns current vault seed phrase @@ -18,8 +19,10 @@ export const getSeedPhrase = async (password = '') => { * @param password - Password to recreate and set the vault with */ export const recreateVaultWithSamePassword = async (password = '', selectedAddress) => { - const { KeyringController, PreferencesController } = Engine.context; + const { KeyringController, PreferencesController, AccountTrackerController } = Engine.context; const seedPhrase = await getSeedPhrase(password); + const oldPrefs = PreferencesController.state; + const oldAccounts = AccountTrackerController.accounts; let importedAccounts = []; try { @@ -42,7 +45,6 @@ export const recreateVaultWithSamePassword = async (password = '', selectedAddre // Get props to restore vault const hdKeyring = KeyringController.state.keyrings[0]; const existingAccountCount = hdKeyring.accounts.length; - let preferencesControllerState = PreferencesController.state; // Create previous accounts again for (let i = 0; i < existingAccountCount - 1; i++) { @@ -58,11 +60,18 @@ export const recreateVaultWithSamePassword = async (password = '', selectedAddre Logger.error(e, 'error while trying to import accounts on recreate vault'); } - // Reset preferencesControllerState - preferencesControllerState = PreferencesController.state; + //Persist old account/identities names + const preferencesControllerState = PreferencesController.state; + const prefUpdates = syncPrefs(oldPrefs, preferencesControllerState); + + //Persist old account data + const accounts = AccountTrackerController.accounts; + const updateAccounts = syncAccounts(oldAccounts, accounts); // Set preferencesControllerState again - await PreferencesController.update(preferencesControllerState); + await PreferencesController.update(prefUpdates); + await AccountTrackerController.update(updateAccounts); + // Reselect previous selected account if still available if (hdKeyring.accounts.includes(selectedAddress)) { PreferencesController.setSelectedAddress(selectedAddress); diff --git a/app/util/sync.js b/app/util/sync.js new file mode 100644 index 00000000000..571599b377e --- /dev/null +++ b/app/util/sync.js @@ -0,0 +1,37 @@ +/** + * Function to persist the old account name during an new preferences update + * @param {Object} oldPrefs - old preferences object containing the account names + * @param {Object} updatedPref - preferences object that will be updated with oldPrefs + */ +export async function syncPrefs(oldPrefs, updatedPref) { + try { + Object.keys(oldPrefs.identities).forEach(ids => { + if (updatedPref.identities[ids]) { + updatedPref.identities[ids] = oldPrefs.identities[ids]; + } + }); + + return updatedPref; + } catch (err) { + return updatedPref; + } +} + +/** + * Function to persist the old account balance during an vault update + * @param {Object} oldAccounts - old account object containing the account names + * @param {Object} updatedAccounts - accounts object that will be updated with old accout balance + */ +export async function syncAccounts(oldAccounts, updatedAccounts) { + try { + Object.keys(oldAccounts).forEach(account => { + if (updatedAccounts[account]) { + updatedAccounts[account] = oldAccounts[account]; + } + }); + + return updatedAccounts; + } catch (err) { + return updatedAccounts; + } +} diff --git a/app/util/sync.test.js b/app/util/sync.test.js new file mode 100644 index 00000000000..2fd5f8dfaf9 --- /dev/null +++ b/app/util/sync.test.js @@ -0,0 +1,106 @@ +import { syncPrefs, syncAccounts } from '../util/sync'; + +const OLD_PREFS = { + accountTokens: { + '0x0942890c603273059a11a298F81cb137Be9CF704': { '0x1': [Array], '0x3': [Array] }, + '0x120bfFfa4138fD00A8025a223C350b9ffaDAD8F5': { '0x3': [Array] }, + '0x16C6C3079edE914e83B388a52fFD9255E1c3165': { '0x3': [Array] }, + '0x223367C61c38FAcbdd0b92De5aA7B742e1e5a196': { '0x1': [Array], '0x3': [Array] }, + '0x7b8C6B8363B9E7A77d279dDad49BEF2994a3bf28': { '0x3': [Array] }, + '0x9236413AfD369B2aeb5e52C048f6B30e7308f2e3': { '0x1': [Array], '0x3': [Array] }, + '0x9b07Ba86631bdb74eE2DDb5750440986DECB9e11': { '0x1': [Array], '0x3': [Array] }, + '0xE4D7f194b07B85511973f1FAAB31b8C2F1f9F344': { '0x3': [Array] } + }, + currentLocale: 'en', + featureFlags: {}, + frequentRpcList: [], + identities: { + '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539': { + address: '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539', + name: 'Testy Account' + } + }, + ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', + lostIdentities: {}, + selectedAddress: '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539', + tokens: [] +}; +const OLD_ACCOUNTS = { + '0x0942890c603273059a11a298F81cb137Be9CF704': { balance: '0x365369025dd23000' }, + '0x120bfFfa4138fD00A8025a223C350b9ffaDAD8F5': { balance: '0x0' }, + '0x16C6C3079edE914e83B388a52fFD9255E1c3165': { balance: '0x0' }, + '0x223367C61c38FAcbdd0b92De5aA7B742e1e5a196': { balance: '0x1bf5ef59d293408b' }, + '0x7b8C6B8363B9E7A77d279dDad49BEF2994a3bf28': { balance: '0x0' }, + '0x9236413AfD369B2aeb5e52C048f6B30e7308f2e3': { balance: '0x0' }, + '0x9b07Ba86631bdb74eE2DDb5750440986DECB9e11': { balance: '0xe8d4a51000' }, + '0xE4D7f194b07B85511973f1FAAB31b8C2F1f9F344': { balance: '0x0' } +}; +const NEW_PREFS = { + accountTokens: { + '0x0942890c603273059a11a298F81cb137Be9CF704': { '0x1': [Array], '0x3': [Array] }, + '0x120bfFfa4138fD00A8025a223C350b9ffaDAD8F5': { '0x3': [Array] }, + '0x16C6C3079edE914e83B388a52fFD9255E1c3165': { '0x3': [Array] }, + '0x223367C61c38FAcbdd0b92De5aA7B742e1e5a196': { '0x1': [Array], '0x3': [Array] }, + '0x7b8C6B8363B9E7A77d279dDad49BEF2994a3bf28': { '0x3': [Array] }, + '0x9236413AfD369B2aeb5e52C048f6B30e7308f2e3': { '0x1': [Array], '0x3': [Array] }, + '0x9b07Ba86631bdb74eE2DDb5750440986DECB9e11': { '0x1': [Array], '0x3': [Array] }, + '0xE4D7f194b07B85511973f1FAAB31b8C2F1f9F344': { '0x3': [Array] } + }, + currentLocale: 'en', + featureFlags: {}, + frequentRpcList: [], + identities: { + '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539': { + address: '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539', + name: 'Account 1' + }, + '0x7f9f9A0e248Ef58298e911219e5B45D610C4B589': { + address: '0x7f9f9A0e248Ef58298e911219e5B45D610C4B589', + name: 'Account 2' + } + }, + ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', + lostIdentities: {}, + selectedAddress: '0x7f9f9A0e248Ef58298e911219e5B45D610C4B539', + tokens: [] +}; +const NEW_ACCOUNTS = { + '0x0942890c603273059a11a298F81cb137Be9CF704': { balance: '0x0' }, + '0x120bfFfa4138fD00A8025a223C350b9ffaDAD8F5': { balance: '0x0' }, + '0x16C6C3079edE914e83B388a52fFD9255E1c3165': { balance: '0x0' }, + '0x223367C61c38FAcbdd0b92De5aA7B742e1e5a196': { balance: '0x0' }, + '0x7b8C6B8363B9E7A77d279dDad49BEF2994a3bf28': { balance: '0x0' }, + '0x9236413AfD369B2aeb5e52C048f6B30e7308f2e3': { balance: '0x0' }, + '0x9b07Ba86631bdb74eE2DDb5750440986DECB9e11': { balance: '0x0' }, + '0xE4D7f194b07B85511973f1FAAB31b8C2F1f9F344': { balance: '0x0' } +}; + +describe('Success Sync', () => { + it('should succeed sync prefs of varying lengths', async () => { + const syncedPrefs = await syncPrefs(OLD_PREFS, NEW_PREFS); + expect(Object.values(syncedPrefs.identities)[0]).toEqual(Object.values(syncedPrefs.identities)[0]); + expect(Object.values(syncedPrefs.identities)[1]).not.toBeUndefined(); + expect(Object.values(syncedPrefs.identities).length).not.toEqual(Object.values(OLD_PREFS.identities).length); + }); + it('should succeed sync accounts balances', async () => { + const syncedAccounts = await syncAccounts(OLD_ACCOUNTS, NEW_ACCOUNTS); + expect(Object.values(syncedAccounts)[0].balance).toEqual(Object.values(OLD_ACCOUNTS)[0].balance); + expect(Object.values(syncedAccounts)[3].balance).toEqual(Object.values(OLD_ACCOUNTS)[3].balance); + expect(Object.values(syncedAccounts)[6].balance).toEqual(Object.values(OLD_ACCOUNTS)[6].balance); + }); +}); + +describe('Error Syncs', () => { + it('should return undefined sync prefs', async () => { + expect(await syncPrefs(OLD_PREFS, undefined)).toEqual(undefined); + }); + it('should return new sync prefs', async () => { + expect(await syncPrefs(undefined, NEW_PREFS)).toEqual(NEW_PREFS); + }); + it('should return new sync accounts', async () => { + expect(await syncAccounts(undefined, NEW_ACCOUNTS)).toEqual(NEW_ACCOUNTS); + }); + it('should return undefined sync accounts', async () => { + expect(await syncAccounts(OLD_ACCOUNTS, undefined)).toEqual(undefined); + }); +});