-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## **Description** Add `POL` token image, and maps it to Polygon Network config. See [blog post](https://polygon.technology/blog/save-the-date-matic-pol-migration-coming-september-4th-everything-you-need-to-know) for more context. Adds chain collision check to not flag `POL` as scam token Adds migration to overwrite `MATIC` ticker to `POL` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/26671?quickstart=1) ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/MMASSETS-362 ## **Manual testing steps** 1. When adding Polygon as a network from `FEATURED_RPCS`, ticker symbol should be POL 2. When switching to Polygon Mainnet, ticker should be POL (not MATIC) 2. Can import `Polygon Network Token` on Ethereum Mainnet 3. Can import `Matic Network Token` on Ethereum Mainnet (backward compatibility) > Note, that both `MATIC` and `POL` erc20 tokens can already be imported on Ethereum Mainnet, so this behavior should remain unchanged. Additionally, when a user upgrades to this build, migration should run and overwrite ticker from MATIC to POL: 1. Install a previous build (checkout `develop` and `yarn && yarn start` should be fine here) -> Polygon Network should show `MATIC` as ticker. You may need to remove and re-add Polygon Network, as migrations are incremental. 2. checkout to this branch, `yarn && yarn start`, migration 128 should run (Running migration 128... in console) -> Polygon Network should show `POL` as ticker ## **Screenshots/Recordings** Before: <img width="358" alt="Screenshot 2024-08-27 at 2 39 15 PM" src="https://github.com/user-attachments/assets/241b6d60-03a9-49fa-9b1e-61aeeb8b3f9f"> <img width="358" alt="Screenshot 2024-08-27 at 2 39 36 PM" src="https://github.com/user-attachments/assets/fc3f933e-3b27-4dbe-943b-3453a9967687"> After: <img width="356" alt="Screenshot 2024-08-27 at 1 47 40 PM" src="https://github.com/user-attachments/assets/5e849387-3a16-471c-8587-33ea40ca7a81"> <img width="357" alt="Screenshot 2024-08-27 at 1 48 11 PM" src="https://github.com/user-attachments/assets/fd96c3c8-560b-4e06-aa0a-07c3838d5e0b"> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
- Loading branch information
Showing
22 changed files
with
404 additions
and
28 deletions.
There are no files selected for viewing
Binary file not shown.
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
import { cloneDeep } from 'lodash'; | ||
import { migrate, version } from './121.2'; | ||
|
||
const oldVersion = 121.1; | ||
|
||
describe(`migration #${version}`, () => { | ||
it('updates the version metadata', async () => { | ||
const oldStorage = { | ||
meta: { version: oldVersion }, | ||
data: {}, | ||
}; | ||
|
||
const newStorage = await migrate(oldStorage); | ||
|
||
expect(newStorage.meta).toStrictEqual({ version }); | ||
}); | ||
|
||
it('Does nothing if `networkConfigurations` or `providerConfig` are not in the network controller state', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
selectedNetworkClientId: 'mainnet', | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(oldState); | ||
}); | ||
|
||
it('Updates MATIC ticker to POL and updates imageURL in networkConfigurations', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/matic-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'POL', | ||
rpcPrefs: { | ||
imageUrl: './images/pol-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(expectedState); | ||
}); | ||
|
||
it('Does not update ticker to POL if ticker is not MATIC, but still updates imageURL in networkConfigurations', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'NOT_MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/matic-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'NOT_MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/pol-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(expectedState); | ||
}); | ||
|
||
it('Does not update tickers for other network configurations, updates only ticker and imageURL for chain 0x89', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/matic-token.svg', | ||
}, | ||
}, | ||
'0x1': { | ||
chainId: '0x1', | ||
ticker: 'ETH', | ||
rpcPrefs: { | ||
imageUrl: './images/eth-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x89': { | ||
chainId: '0x89', | ||
ticker: 'POL', | ||
rpcPrefs: { | ||
imageUrl: './images/pol-token.svg', | ||
}, | ||
}, | ||
'0x1': { | ||
chainId: '0x1', | ||
ticker: 'ETH', | ||
rpcPrefs: { | ||
imageUrl: './images/eth-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(expectedState); | ||
}); | ||
|
||
it('Does nothing if Polygon ChainId (0x89) is not in networkConfigurations', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
networkConfigurations: { | ||
'0x1': { | ||
chainId: '0x1', | ||
ticker: 'ETH', | ||
rpcPrefs: { | ||
imageUrl: './images/eth-token.svg', | ||
}, | ||
}, | ||
'0x2a': { | ||
chainId: '0x2a', | ||
ticker: 'KOVAN', | ||
rpcPrefs: { | ||
imageUrl: './images/kovan-token.svg', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(oldState); | ||
}); | ||
|
||
it('Updates Polygon ChainId (0x89) in ProviderConfig if exists, and ticker is set to MATIC, and updates imageUrl', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
providerConfig: { | ||
chainId: '0x89', | ||
ticker: 'MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/matic-token.svg', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedState = { | ||
NetworkController: { | ||
providerConfig: { | ||
chainId: '0x89', | ||
ticker: 'POL', | ||
rpcPrefs: { | ||
imageUrl: './images/pol-token.svg', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(expectedState); | ||
}); | ||
|
||
it('Does nothing if Polygon ChainId (0x89) is not in providerConfig', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
providerConfig: { | ||
chainId: '0x1', | ||
ticker: 'ETH', | ||
rpcPrefs: { | ||
imageUrl: './images/eth-token.svg', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(oldState); | ||
}); | ||
|
||
it('Does not update ticker if Polygon ChainId (0x89) is in providerConfig, but ticker is not MATIC, but still updates imageUrl', async () => { | ||
const oldState = { | ||
NetworkController: { | ||
providerConfig: { | ||
chainId: '0x89', | ||
ticker: 'NOT_MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/matic-token.svg', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const expectedState = { | ||
NetworkController: { | ||
providerConfig: { | ||
chainId: '0x89', | ||
ticker: 'NOT_MATIC', | ||
rpcPrefs: { | ||
imageUrl: './images/pol-token.svg', | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const transformedState = await migrate({ | ||
meta: { version: oldVersion }, | ||
data: cloneDeep(oldState), | ||
}); | ||
|
||
expect(transformedState.data).toStrictEqual(expectedState); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { hasProperty, isObject } from '@metamask/utils'; | ||
import { cloneDeep } from 'lodash'; | ||
import { CHAIN_IDS } from '../../../shared/constants/network'; | ||
|
||
export const version = 121.2; | ||
|
||
type VersionedData = { | ||
meta: { version: number }; | ||
data: Record<string, unknown>; | ||
}; | ||
|
||
/** | ||
* Migrates MATIC ticker in Network Configuration to POL ticker as per the direction in https://polygon.technology/blog/save-the-date-matic-pol-migration-coming-september-4th-everything-you-need-to-know | ||
* | ||
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist. | ||
* @param originalVersionedData.meta - State metadata. | ||
* @param originalVersionedData.meta.version - The current state version. | ||
* @param originalVersionedData.data - The persisted MetaMask state, keyed by controller. | ||
* @returns Updated versioned MetaMask extension state. | ||
*/ | ||
export async function migrate( | ||
originalVersionedData: VersionedData, | ||
): Promise<VersionedData> { | ||
const versionedData = cloneDeep(originalVersionedData); | ||
versionedData.meta.version = version; | ||
transformState(versionedData.data); | ||
return versionedData; | ||
} | ||
|
||
function transformState(state: Record<string, unknown>): void { | ||
const networkControllerState = state.NetworkController; | ||
if ( | ||
hasProperty(state, 'NetworkController') && | ||
isObject(networkControllerState) && | ||
hasProperty(networkControllerState, 'networkConfigurations') && | ||
isObject(networkControllerState.networkConfigurations) | ||
) { | ||
for (const networkConfiguration of Object.values( | ||
networkControllerState.networkConfigurations, | ||
)) { | ||
if ( | ||
isObject(networkConfiguration) && | ||
networkConfiguration.chainId === CHAIN_IDS.POLYGON | ||
) { | ||
// update image path regardless of ticker | ||
if ( | ||
hasProperty(networkConfiguration, 'rpcPrefs') && | ||
isObject(networkConfiguration.rpcPrefs) && | ||
hasProperty(networkConfiguration.rpcPrefs, 'imageUrl') && | ||
networkConfiguration.rpcPrefs.imageUrl === './images/matic-token.svg' | ||
) { | ||
networkConfiguration.rpcPrefs.imageUrl = './images/pol-token.svg'; | ||
} | ||
// update ticker only if MATIC | ||
if ( | ||
hasProperty(networkConfiguration, 'ticker') && | ||
networkConfiguration.ticker === 'MATIC' | ||
) { | ||
networkConfiguration.ticker = 'POL'; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// handle legacy NetworkController versions (with providerConfig) | ||
if ( | ||
hasProperty(state, 'NetworkController') && | ||
isObject(networkControllerState) && | ||
hasProperty(networkControllerState, 'providerConfig') && | ||
isObject(networkControllerState.providerConfig) && | ||
hasProperty(networkControllerState.providerConfig, 'chainId') && | ||
networkControllerState.providerConfig.chainId === CHAIN_IDS.POLYGON | ||
) { | ||
// update image path regardless of ticker | ||
if ( | ||
hasProperty(networkControllerState.providerConfig, 'rpcPrefs') && | ||
isObject(networkControllerState.providerConfig.rpcPrefs) && | ||
hasProperty(networkControllerState.providerConfig.rpcPrefs, 'imageUrl') && | ||
networkControllerState.providerConfig.rpcPrefs.imageUrl === | ||
'./images/matic-token.svg' | ||
) { | ||
networkControllerState.providerConfig.rpcPrefs.imageUrl = | ||
'./images/pol-token.svg'; | ||
} | ||
// update ticker only if MATIC | ||
if ( | ||
hasProperty(networkControllerState.providerConfig, 'ticker') && | ||
networkControllerState.providerConfig.ticker === 'MATIC' | ||
) { | ||
networkControllerState.providerConfig.ticker = 'POL'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.