1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 02:10:12 +01:00

key tokens by chainId (#10510)

This commit is contained in:
Brad Decker 2021-02-26 09:40:25 -06:00 committed by GitHub
parent dedadee346
commit caa32d87fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 596 additions and 30 deletions

View File

@ -8,6 +8,7 @@ import log from 'loglevel';
import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens'; import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens';
import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network'; import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network';
import { isPrefixedFormattedHexString } from '../../../shared/modules/network.utils'; import { isPrefixedFormattedHexString } from '../../../shared/modules/network.utils';
import { NETWORK_EVENTS } from './network';
export default class PreferencesController { export default class PreferencesController {
/** /**
@ -72,7 +73,7 @@ export default class PreferencesController {
this.store.setMaxListeners(12); this.store.setMaxListeners(12);
this.openPopup = opts.openPopup; this.openPopup = opts.openPopup;
this.migrateAddressBookState = opts.migrateAddressBookState; this.migrateAddressBookState = opts.migrateAddressBookState;
this._subscribeProviderType(); this._subscribeToNetworkDidChange();
global.setPreference = (key, value) => { global.setPreference = (key, value) => {
return this.setFeatureFlag(key, value); return this.setFeatureFlag(key, value);
@ -667,12 +668,11 @@ export default class PreferencesController {
// //
/** /**
* Subscription to network provider type. * Handle updating token list to reflect current network by listening for the
* * NETWORK_DID_CHANGE event.
*
*/ */
_subscribeProviderType() { _subscribeToNetworkDidChange() {
this.network.providerStore.subscribe(() => { this.network.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
const { tokens, hiddenTokens } = this._getTokenRelatedStates(); const { tokens, hiddenTokens } = this._getTokenRelatedStates();
this._updateAccountTokens(tokens, this.getAssetImages(), hiddenTokens); this._updateAccountTokens(tokens, this.getAssetImages(), hiddenTokens);
}); });
@ -689,12 +689,12 @@ export default class PreferencesController {
_updateAccountTokens(tokens, assetImages, hiddenTokens) { _updateAccountTokens(tokens, assetImages, hiddenTokens) {
const { const {
accountTokens, accountTokens,
providerType, chainId,
selectedAddress, selectedAddress,
accountHiddenTokens, accountHiddenTokens,
} = this._getTokenRelatedStates(); } = this._getTokenRelatedStates();
accountTokens[selectedAddress][providerType] = tokens; accountTokens[selectedAddress][chainId] = tokens;
accountHiddenTokens[selectedAddress][providerType] = hiddenTokens; accountHiddenTokens[selectedAddress][chainId] = hiddenTokens;
this.store.updateState({ this.store.updateState({
accountTokens, accountTokens,
tokens, tokens,
@ -730,27 +730,27 @@ export default class PreferencesController {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
selectedAddress = this.store.getState().selectedAddress; selectedAddress = this.store.getState().selectedAddress;
} }
const providerType = this.network.providerStore.getState().type; const chainId = this.network.getCurrentChainId();
if (!(selectedAddress in accountTokens)) { if (!(selectedAddress in accountTokens)) {
accountTokens[selectedAddress] = {}; accountTokens[selectedAddress] = {};
} }
if (!(selectedAddress in accountHiddenTokens)) { if (!(selectedAddress in accountHiddenTokens)) {
accountHiddenTokens[selectedAddress] = {}; accountHiddenTokens[selectedAddress] = {};
} }
if (!(providerType in accountTokens[selectedAddress])) { if (!(chainId in accountTokens[selectedAddress])) {
accountTokens[selectedAddress][providerType] = []; accountTokens[selectedAddress][chainId] = [];
} }
if (!(providerType in accountHiddenTokens[selectedAddress])) { if (!(chainId in accountHiddenTokens[selectedAddress])) {
accountHiddenTokens[selectedAddress][providerType] = []; accountHiddenTokens[selectedAddress][chainId] = [];
} }
const tokens = accountTokens[selectedAddress][providerType]; const tokens = accountTokens[selectedAddress][chainId];
const hiddenTokens = accountHiddenTokens[selectedAddress][providerType]; const hiddenTokens = accountHiddenTokens[selectedAddress][chainId];
return { return {
tokens, tokens,
accountTokens, accountTokens,
hiddenTokens, hiddenTokens,
accountHiddenTokens, accountHiddenTokens,
providerType, chainId,
selectedAddress, selectedAddress,
}; };
} }

View File

@ -0,0 +1,125 @@
import { cloneDeep } from 'lodash';
import {
GOERLI,
GOERLI_CHAIN_ID,
KOVAN,
KOVAN_CHAIN_ID,
MAINNET,
MAINNET_CHAIN_ID,
NETWORK_TYPE_RPC,
RINKEBY,
RINKEBY_CHAIN_ID,
ROPSTEN,
ROPSTEN_CHAIN_ID,
} from '../../../shared/constants/network';
const version = 52;
/**
* Migrate tokens in Preferences to be keyed by chainId instead of
* providerType. To prevent breaking user's MetaMask and selected
* tokens, this migration copies the RPC entry into *every* custom RPC
* chainId.
*/
export default {
version,
async migrate(originalVersionedData) {
const versionedData = cloneDeep(originalVersionedData);
versionedData.meta.version = version;
const state = versionedData.data;
versionedData.data = transformState(state);
return versionedData;
},
};
function transformState(state = {}) {
if (state.PreferencesController) {
const {
accountTokens,
accountHiddenTokens,
frequentRpcListDetail,
} = state.PreferencesController;
const newAccountTokens = {};
const newAccountHiddenTokens = {};
if (accountTokens && Object.keys(accountTokens).length > 0) {
for (const address of Object.keys(accountTokens)) {
newAccountTokens[address] = {};
if (accountTokens[address][NETWORK_TYPE_RPC]) {
frequentRpcListDetail.forEach((detail) => {
newAccountTokens[address][detail.chainId] =
accountTokens[address][NETWORK_TYPE_RPC];
});
}
for (const providerType of Object.keys(accountTokens[address])) {
switch (providerType) {
case MAINNET:
newAccountTokens[address][MAINNET_CHAIN_ID] =
accountTokens[address][MAINNET];
break;
case ROPSTEN:
newAccountTokens[address][ROPSTEN_CHAIN_ID] =
accountTokens[address][ROPSTEN];
break;
case RINKEBY:
newAccountTokens[address][RINKEBY_CHAIN_ID] =
accountTokens[address][RINKEBY];
break;
case GOERLI:
newAccountTokens[address][GOERLI_CHAIN_ID] =
accountTokens[address][GOERLI];
break;
case KOVAN:
newAccountTokens[address][KOVAN_CHAIN_ID] =
accountTokens[address][KOVAN];
break;
default:
break;
}
}
}
state.PreferencesController.accountTokens = newAccountTokens;
}
if (accountHiddenTokens && Object.keys(accountHiddenTokens).length > 0) {
for (const address of Object.keys(accountHiddenTokens)) {
newAccountHiddenTokens[address] = {};
if (accountHiddenTokens[address][NETWORK_TYPE_RPC]) {
frequentRpcListDetail.forEach((detail) => {
newAccountHiddenTokens[address][detail.chainId] =
accountHiddenTokens[address][NETWORK_TYPE_RPC];
});
}
for (const providerType of Object.keys(accountHiddenTokens[address])) {
switch (providerType) {
case MAINNET:
newAccountHiddenTokens[address][MAINNET_CHAIN_ID] =
accountHiddenTokens[address][MAINNET];
break;
case ROPSTEN:
newAccountHiddenTokens[address][ROPSTEN_CHAIN_ID] =
accountHiddenTokens[address][ROPSTEN];
break;
case RINKEBY:
newAccountHiddenTokens[address][RINKEBY_CHAIN_ID] =
accountHiddenTokens[address][RINKEBY];
break;
case GOERLI:
newAccountHiddenTokens[address][GOERLI_CHAIN_ID] =
accountHiddenTokens[address][GOERLI];
break;
case KOVAN:
newAccountHiddenTokens[address][KOVAN_CHAIN_ID] =
accountHiddenTokens[address][KOVAN];
break;
default:
break;
}
}
}
state.PreferencesController.accountHiddenTokens = newAccountHiddenTokens;
}
}
return state;
}

View File

@ -56,6 +56,7 @@ const migrations = [
require('./049').default, require('./049').default,
require('./050').default, require('./050').default,
require('./051').default, require('./051').default,
require('./052').default,
]; ];
export default migrations; export default migrations;

View File

@ -1,19 +1,39 @@
import assert from 'assert'; import assert from 'assert';
import { ObservableStore } from '@metamask/obs-store';
import sinon from 'sinon'; import sinon from 'sinon';
import PreferencesController from '../../../../app/scripts/controllers/preferences'; import PreferencesController from '../../../../app/scripts/controllers/preferences';
import {
MAINNET_CHAIN_ID,
RINKEBY_CHAIN_ID,
} from '../../../../shared/constants/network';
describe('preferences controller', function () { describe('preferences controller', function () {
let preferencesController; let preferencesController;
let network; let network;
let currentChainId;
let triggerNetworkChange;
let switchToMainnet;
let switchToRinkeby;
const migrateAddressBookState = sinon.stub(); const migrateAddressBookState = sinon.stub();
beforeEach(function () { beforeEach(function () {
network = { providerStore: new ObservableStore({ type: 'mainnet' }) }; currentChainId = MAINNET_CHAIN_ID;
network = {
getCurrentChainId: () => currentChainId,
on: sinon.spy(),
};
preferencesController = new PreferencesController({ preferencesController = new PreferencesController({
migrateAddressBookState, migrateAddressBookState,
network, network,
}); });
triggerNetworkChange = network.on.firstCall.args[1];
switchToMainnet = () => {
currentChainId = MAINNET_CHAIN_ID;
triggerNetworkChange();
};
switchToRinkeby = () => {
currentChainId = RINKEBY_CHAIN_ID;
triggerNetworkChange();
};
}); });
afterEach(function () { afterEach(function () {
@ -230,12 +250,10 @@ describe('preferences controller', function () {
const symbolFirst = 'ABBR'; const symbolFirst = 'ABBR';
const symbolSecond = 'ABBB'; const symbolSecond = 'ABBB';
const decimals = 5; const decimals = 5;
network.providerStore.updateState({ type: 'mainnet' });
await preferencesController.addToken(addressFirst, symbolFirst, decimals); await preferencesController.addToken(addressFirst, symbolFirst, decimals);
const tokensFirstAddress = preferencesController.getTokens(); const tokensFirstAddress = preferencesController.getTokens();
network.providerStore.updateState({ type: 'rinkeby' }); switchToRinkeby();
await preferencesController.addToken( await preferencesController.addToken(
addressSecond, addressSecond,
symbolSecond, symbolSecond,
@ -304,14 +322,13 @@ describe('preferences controller', function () {
}); });
it('should remove a token from its state on corresponding network', async function () { it('should remove a token from its state on corresponding network', async function () {
network.providerStore.updateState({ type: 'mainnet' });
await preferencesController.addToken('0xa', 'A', 4); await preferencesController.addToken('0xa', 'A', 4);
await preferencesController.addToken('0xb', 'B', 5); await preferencesController.addToken('0xb', 'B', 5);
network.providerStore.updateState({ type: 'rinkeby' }); switchToRinkeby();
await preferencesController.addToken('0xa', 'A', 4); await preferencesController.addToken('0xa', 'A', 4);
await preferencesController.addToken('0xb', 'B', 5); await preferencesController.addToken('0xb', 'B', 5);
const initialTokensSecond = preferencesController.getTokens(); const initialTokensSecond = preferencesController.getTokens();
network.providerStore.updateState({ type: 'mainnet' }); switchToMainnet();
await preferencesController.removeToken('0xa'); await preferencesController.removeToken('0xa');
const tokensFirst = preferencesController.getTokens(); const tokensFirst = preferencesController.getTokens();
@ -320,7 +337,7 @@ describe('preferences controller', function () {
const [token1] = tokensFirst; const [token1] = tokensFirst;
assert.deepEqual(token1, { address: '0xb', symbol: 'B', decimals: 5 }); assert.deepEqual(token1, { address: '0xb', symbol: 'B', decimals: 5 });
network.providerStore.updateState({ type: 'rinkeby' }); switchToRinkeby();
const tokensSecond = preferencesController.getTokens(); const tokensSecond = preferencesController.getTokens();
assert.deepEqual( assert.deepEqual(
tokensSecond, tokensSecond,
@ -371,11 +388,10 @@ describe('preferences controller', function () {
describe('on updateStateNetworkType', function () { describe('on updateStateNetworkType', function () {
it('should remove a token from its state on corresponding network', async function () { it('should remove a token from its state on corresponding network', async function () {
network.providerStore.updateState({ type: 'mainnet' });
await preferencesController.addToken('0xa', 'A', 4); await preferencesController.addToken('0xa', 'A', 4);
await preferencesController.addToken('0xb', 'B', 5); await preferencesController.addToken('0xb', 'B', 5);
const initialTokensFirst = preferencesController.getTokens(); const initialTokensFirst = preferencesController.getTokens();
network.providerStore.updateState({ type: 'rinkeby' }); switchToRinkeby();
await preferencesController.addToken('0xa', 'C', 4); await preferencesController.addToken('0xa', 'C', 4);
await preferencesController.addToken('0xb', 'D', 5); await preferencesController.addToken('0xb', 'D', 5);
const initialTokensSecond = preferencesController.getTokens(); const initialTokensSecond = preferencesController.getTokens();
@ -386,9 +402,9 @@ describe('preferences controller', function () {
'tokens not equal for different networks and tokens', 'tokens not equal for different networks and tokens',
); );
network.providerStore.updateState({ type: 'mainnet' }); switchToMainnet();
const tokensFirst = preferencesController.getTokens(); const tokensFirst = preferencesController.getTokens();
network.providerStore.updateState({ type: 'rinkeby' }); switchToRinkeby();
const tokensSecond = preferencesController.getTokens(); const tokensSecond = preferencesController.getTokens();
assert.deepEqual( assert.deepEqual(
tokensFirst, tokensFirst,

View File

@ -0,0 +1,424 @@
import assert from 'assert';
import migration52 from '../../../app/scripts/migrations/052';
import {
GOERLI,
GOERLI_CHAIN_ID,
KOVAN,
KOVAN_CHAIN_ID,
MAINNET,
MAINNET_CHAIN_ID,
NETWORK_TYPE_RPC,
RINKEBY,
RINKEBY_CHAIN_ID,
ROPSTEN,
ROPSTEN_CHAIN_ID,
} from '../../../shared/constants/network';
const TOKEN1 = { symbol: 'TST', address: '0x10', decimals: 18 };
const TOKEN2 = { symbol: 'TXT', address: '0x11', decimals: 18 };
const TOKEN3 = { symbol: 'TVT', address: '0x12', decimals: 18 };
const TOKEN4 = { symbol: 'TAT', address: '0x13', decimals: 18 };
describe('migration #52', function () {
it('should update the version metadata', async function () {
const oldStorage = {
meta: {
version: 52,
},
data: {},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.meta, {
version: 52,
});
});
it(`should move ${MAINNET} tokens and hidden tokens to be keyed by ${MAINNET_CHAIN_ID} for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[MAINNET]: [TOKEN1],
},
'0x1112': {
[MAINNET]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[MAINNET]: [TOKEN1, TOKEN2],
},
'0x1112': {
[MAINNET]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[MAINNET_CHAIN_ID]: [TOKEN1],
},
'0x1112': {
[MAINNET_CHAIN_ID]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[MAINNET_CHAIN_ID]: [TOKEN1, TOKEN2],
},
'0x1112': {
[MAINNET_CHAIN_ID]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should move ${RINKEBY} tokens and hidden tokens to be keyed by ${RINKEBY_CHAIN_ID} for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[RINKEBY]: [TOKEN1],
},
'0x1112': {
[RINKEBY]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[RINKEBY]: [TOKEN1, TOKEN2],
},
'0x1112': {
[RINKEBY]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[RINKEBY_CHAIN_ID]: [TOKEN1],
},
'0x1112': {
[RINKEBY_CHAIN_ID]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[RINKEBY_CHAIN_ID]: [TOKEN1, TOKEN2],
},
'0x1112': {
[RINKEBY_CHAIN_ID]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should move ${KOVAN} tokens and hidden tokens to be keyed by ${KOVAN_CHAIN_ID} for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[KOVAN]: [TOKEN1],
},
'0x1112': {
[KOVAN]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[KOVAN]: [TOKEN1, TOKEN2],
},
'0x1112': {
[KOVAN]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[KOVAN_CHAIN_ID]: [TOKEN1],
},
'0x1112': {
[KOVAN_CHAIN_ID]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[KOVAN_CHAIN_ID]: [TOKEN1, TOKEN2],
},
'0x1112': {
[KOVAN_CHAIN_ID]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should move ${GOERLI} tokens and hidden tokens to be keyed by ${GOERLI_CHAIN_ID} for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[GOERLI]: [TOKEN1],
},
'0x1112': {
[GOERLI]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[GOERLI]: [TOKEN1, TOKEN2],
},
'0x1112': {
[GOERLI]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[GOERLI_CHAIN_ID]: [TOKEN1],
},
'0x1112': {
[GOERLI_CHAIN_ID]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[GOERLI_CHAIN_ID]: [TOKEN1, TOKEN2],
},
'0x1112': {
[GOERLI_CHAIN_ID]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should move ${ROPSTEN} tokens and hidden tokens to be keyed by ${ROPSTEN_CHAIN_ID} for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[ROPSTEN]: [TOKEN1],
},
'0x1112': {
[ROPSTEN]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[ROPSTEN]: [TOKEN1, TOKEN2],
},
'0x1112': {
[ROPSTEN]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
accountHiddenTokens: {
'0x1111': {
[ROPSTEN_CHAIN_ID]: [TOKEN1],
},
'0x1112': {
[ROPSTEN_CHAIN_ID]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[ROPSTEN_CHAIN_ID]: [TOKEN1, TOKEN2],
},
'0x1112': {
[ROPSTEN_CHAIN_ID]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should duplicate ${NETWORK_TYPE_RPC} tokens and hidden tokens to all custom networks for each address`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
frequentRpcListDetail: [
{ chainId: '0xab' },
{ chainId: '0x12' },
{ chainId: '0xfa' },
],
accountHiddenTokens: {
'0x1111': {
[NETWORK_TYPE_RPC]: [TOKEN1],
},
'0x1112': {
[NETWORK_TYPE_RPC]: [TOKEN3],
},
},
accountTokens: {
'0x1111': {
[NETWORK_TYPE_RPC]: [TOKEN1, TOKEN2],
},
'0x1112': {
[NETWORK_TYPE_RPC]: [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
frequentRpcListDetail: [
{ chainId: '0xab' },
{ chainId: '0x12' },
{ chainId: '0xfa' },
],
accountHiddenTokens: {
'0x1111': {
'0xab': [TOKEN1],
'0x12': [TOKEN1],
'0xfa': [TOKEN1],
},
'0x1112': {
'0xab': [TOKEN3],
'0x12': [TOKEN3],
'0xfa': [TOKEN3],
},
},
accountTokens: {
'0x1111': {
'0xab': [TOKEN1, TOKEN2],
'0x12': [TOKEN1, TOKEN2],
'0xfa': [TOKEN1, TOKEN2],
},
'0x1112': {
'0xab': [TOKEN1, TOKEN3],
'0x12': [TOKEN1, TOKEN3],
'0xfa': [TOKEN1, TOKEN3],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it(`should overwrite ${NETWORK_TYPE_RPC} tokens with built in networks if chainIds match`, async function () {
const oldStorage = {
meta: {},
data: {
PreferencesController: {
frequentRpcListDetail: [{ chainId: '0x1' }],
accountHiddenTokens: {
'0x1111': {
[NETWORK_TYPE_RPC]: [TOKEN3],
[MAINNET]: [TOKEN1],
},
},
accountTokens: {
'0x1111': {
[NETWORK_TYPE_RPC]: [TOKEN1, TOKEN2],
[MAINNET]: [TOKEN3, TOKEN4],
},
},
bar: 'baz',
},
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
PreferencesController: {
frequentRpcListDetail: [{ chainId: '0x1' }],
accountHiddenTokens: {
'0x1111': {
'0x1': [TOKEN1],
},
},
accountTokens: {
'0x1111': {
'0x1': [TOKEN3, TOKEN4],
},
},
bar: 'baz',
},
foo: 'bar',
});
});
it('should do nothing if no PreferencesController key', async function () {
const oldStorage = {
meta: {},
data: {
foo: 'bar',
},
};
const newStorage = await migration52.migrate(oldStorage);
assert.deepStrictEqual(newStorage.data, {
foo: 'bar',
});
});
});