key tokens by chainId (#10510)
parent
dedadee346
commit
caa32d87fb
@ -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; |
||||
} |
@ -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', |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue