import { cloneDeep } from 'lodash'; const version = 54; function isValidDecimals(decimals) { return ( typeof decimals === 'number' || (typeof decimals === 'string' && decimals.match(/^(0x)?\d+$/u)) ); } /** * Migrates preference tokens with decimals typed as string to number. * It also removes any tokens with corrupted or inconvertible decimal values. */ export default { version, async migrate(originalVersionedData) { const versionedData = cloneDeep(originalVersionedData); versionedData.meta.version = version; const state = versionedData.data; const newState = transformState(state); versionedData.data = newState; return versionedData; }, }; function transformState(state) { const newState = state; if (!newState.PreferencesController) { return newState; } const tokens = newState.PreferencesController.tokens || []; // Filter out any tokens with corrupted decimal values const validTokens = tokens.filter(({ decimals }) => isValidDecimals(decimals), ); for (const token of validTokens) { // In the case of a decimal value type string, convert to a number. if (typeof token.decimals === 'string') { // eslint-disable-next-line radix token.decimals = parseInt(token.decimals); } } newState.PreferencesController.tokens = validTokens; const { accountTokens } = newState.PreferencesController; if (accountTokens && typeof accountTokens === 'object') { for (const address of Object.keys(accountTokens)) { const networkTokens = accountTokens[address]; if (networkTokens && typeof networkTokens === 'object') { for (const network of Object.keys(networkTokens)) { const tokensOnNetwork = networkTokens[network] || []; // Filter out any tokens with corrupted decimal values const validTokensOnNetwork = tokensOnNetwork.filter(({ decimals }) => isValidDecimals(decimals), ); // In the case of a decimal value type string, convert to a number. for (const token of validTokensOnNetwork) { if (typeof token.decimals === 'string') { // eslint-disable-next-line radix token.decimals = parseInt(token.decimals); } } networkTokens[network] = validTokensOnNetwork; } } } } newState.PreferencesController.accountTokens = accountTokens; return newState; }