Skip to content

Commit

Permalink
feat: Update Polygon from MATIC to POL (#26671)
Browse files Browse the repository at this point in the history
## **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
gambinish authored Aug 29, 2024
1 parent be04ca8 commit 1b4417d
Show file tree
Hide file tree
Showing 22 changed files with 404 additions and 28 deletions.
Binary file removed app/images/matic-token.png
Binary file not shown.
File renamed without changes
274 changes: 274 additions & 0 deletions app/scripts/migrations/121.2.test.ts
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);
});
});
93 changes: 93 additions & 0 deletions app/scripts/migrations/121.2.ts
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';
}
}
}
1 change: 1 addition & 0 deletions app/scripts/migrations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const migrations = [
require('./120.6'),
require('./121'),
require('./121.1'),
require('./121.2'),
require('./122'),
require('./123'),
require('./124'),
Expand Down
Loading

0 comments on commit 1b4417d

Please sign in to comment.