Skip to content

Commit

Permalink
fix: market data for native tokens with non zero addresses (#28584)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Draft

When querying the price API, the native token is usually represented by
the zero address. But this is not the case on some chains like polygon,
whose native token has a contract
`0x0000000000000000000000000000000000001010`.

Depends on: MetaMask/core#4952

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28584?quickstart=1)

## **Related issues**

Fixes:

## **Manual testing steps**

(pre-req - you may need to enable the `PORTFOLIO_VIEW=true` flag)

1. Onboard with an SRP that have POL tokens.
2. Connect and switch to Polygon
3. View the POL token on the tokens section on the home page.
    - Does it contain percentages and market data.
5. View the POL asset page.
    - Does it contain the market details view; and percentage sections?

## **Screenshots/Recordings**

| Before | After |
|--------|--------|
| ![Screenshot 2024-11-22 at 16 42
25](https://github.com/user-attachments/assets/51e67809-b53f-4a29-a345-ddda516a08b2)
| ![Screenshot 2024-11-22 at 16 29
38](https://github.com/user-attachments/assets/87245972-5a03-4acb-85d0-dfe01660b038)
|
| ![Screenshot 2024-11-22 at 16 42
41](https://github.com/user-attachments/assets/b87a9cc3-dcb5-4479-8406-3a832f9f926f)
| ![Screenshot 2024-11-22 at 16 29
46](https://github.com/user-attachments/assets/20ab36e9-0d55-45d6-a57b-0c05e8c533b9)
|

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **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.

---------

Co-authored-by: Prithpal Sooriya <[email protected]>
  • Loading branch information
bergeron and Prithpal-Sooriya authored Nov 22, 2024
1 parent ad9a748 commit 9bdfd03
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 142 deletions.
Original file line number Diff line number Diff line change
@@ -1,39 +1,3 @@
diff --git a/dist/TokenDetectionController.cjs b/dist/TokenDetectionController.cjs
index 8fd5efde7a3c24080f8a43f79d10300e8c271245..66f656d9a55f1154024a8c18a9fe27b4ed39a21d 100644
--- a/dist/TokenDetectionController.cjs
+++ b/dist/TokenDetectionController.cjs
@@ -250,17 +250,20 @@ _TokenDetectionController_intervalId = new WeakMap(), _TokenDetectionController_
}
});
this.messagingSystem.subscribe('AccountsController:selectedEvmAccountChange',
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
- async (selectedAccount) => {
- const isSelectedAccountIdChanged = __classPrivateFieldGet(this, _TokenDetectionController_selectedAccountId, "f") !== selectedAccount.id;
- if (isSelectedAccountIdChanged) {
- __classPrivateFieldSet(this, _TokenDetectionController_selectedAccountId, selectedAccount.id, "f");
- await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this, {
- selectedAddress: selectedAccount.address,
- });
- }
- });
+ // TODO: Either fix this lint violation or explain why it's necessary to ignore.
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ async (selectedAccount) => {
+ const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
+ const chainIds = Object.keys(networkConfigurationsByChainId);
+ const isSelectedAccountIdChanged = __classPrivateFieldGet(this, _TokenDetectionController_selectedAccountId, "f") !== selectedAccount.id;
+ if (isSelectedAccountIdChanged) {
+ __classPrivateFieldSet(this, _TokenDetectionController_selectedAccountId, selectedAccount.id, "f");
+ await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_restartTokenDetection).call(this, {
+ selectedAddress: selectedAccount.address,
+ chainIds,
+ });
+ }
+ });
}, _TokenDetectionController_stopPolling = function _TokenDetectionController_stopPolling() {
if (__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f")) {
clearInterval(__classPrivateFieldGet(this, _TokenDetectionController_intervalId, "f"));
diff --git a/dist/assetsUtil.cjs b/dist/assetsUtil.cjs
index 48571b8c1b78e94d88e1837e986b5f8735ac651b..61246f51500c8cab48f18296a73629fb73454caa 100644
--- a/dist/assetsUtil.cjs
Expand All @@ -56,7 +20,7 @@ index 48571b8c1b78e94d88e1837e986b5f8735ac651b..61246f51500c8cab48f18296a73629fb
// because most cid v0s appear to be incompatible with IPFS subdomains
return {
diff --git a/dist/token-prices-service/codefi-v2.mjs b/dist/token-prices-service/codefi-v2.mjs
index e7eaad2cfa8b233c4fd42a51f745233a1cc5c387..bf8ec7819f678c2f185d6a85d7e3ea81f055a309 100644
index a13403446a2376d4d905a9ef733941798da89c88..3c8229f9ea40f4c1ee760a22884e1066dac82ec7 100644
--- a/dist/token-prices-service/codefi-v2.mjs
+++ b/dist/token-prices-service/codefi-v2.mjs
@@ -12,8 +12,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
Expand All @@ -65,7 +29,7 @@ index e7eaad2cfa8b233c4fd42a51f745233a1cc5c387..bf8ec7819f678c2f185d6a85d7e3ea81
import { hexToNumber } from "@metamask/utils";
-import $cockatiel from "cockatiel";
-const { circuitBreaker, ConsecutiveBreaker, ExponentialBackoff, handleAll, retry, wrap, CircuitState } = $cockatiel;
+import { circuitBreaker, ConsecutiveBreaker, ExponentialBackoff, handleAll, retry, wrap, CircuitState } from "cockatiel"
+import { circuitBreaker, ConsecutiveBreaker, ExponentialBackoff, handleAll, retry, wrap, CircuitState } from "cockatiel";
/**
* The list of currencies that can be supplied as the `vsCurrency` parameter to
* the `/spot-prices` endpoint, in lowercase form.
62 changes: 0 additions & 62 deletions .yarn/patches/@metamask-assets-controllers-patch-9e00573eb4.patch

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@
"@metamask/address-book-controller": "^6.0.0",
"@metamask/announcement-controller": "^7.0.0",
"@metamask/approval-controller": "^7.0.0",
"@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A45.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.0.0-31810ece32.patch",
"@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A45.1.0#~/.yarn/patches/@metamask-assets-controllers-npm-45.1.0-d914c453f0.patch",
"@metamask/base-controller": "^7.0.0",
"@metamask/bitcoin-wallet-snap": "^0.8.2",
"@metamask/browser-passworder": "^4.3.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';

import { zeroAddress, toChecksumAddress } from 'ethereumjs-util';
import { toChecksumAddress } from 'ethereumjs-util';
import { getNativeTokenAddress } from '@metamask/assets-controllers';
import { Hex } from '@metamask/utils';
import {
getCurrentCurrency,
getSelectedAccount,
Expand Down Expand Up @@ -89,8 +91,9 @@ export const AggregatedPercentageOverviewCrossChains = () => {
item.tokensWithBalances,
);
const nativePricePercentChange1d =
crossChainMarketData?.[item.chainId]?.[zeroAddress()]
?.pricePercentChange1d;
crossChainMarketData?.[item.chainId]?.[
getNativeTokenAddress(item.chainId as Hex)
]?.pricePercentChange1d;

const nativeFiat1dAgo = getCalculatedTokenAmount1dAgo(
item.nativeFiatValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
getShouldHideZeroBalanceTokens,
getTokensMarketData,
getPreferences,
getCurrentChainId,
} from '../../../selectors';
import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance';
import { AggregatedPercentageOverview } from './aggregated-percentage-overview';
Expand All @@ -26,20 +27,22 @@ jest.mock('../../../selectors', () => ({
getPreferences: jest.fn(),
getShouldHideZeroBalanceTokens: jest.fn(),
getTokensMarketData: jest.fn(),
getCurrentChainId: jest.fn(),
}));

jest.mock('../../../hooks/useAccountTotalFiatBalance', () => ({
useAccountTotalFiatBalance: jest.fn(),
}));

const mockGetIntlLocale = getIntlLocale as unknown as jest.Mock;
const mockGetCurrentCurrency = getCurrentCurrency as jest.Mock;
const mockGetPreferences = getPreferences as jest.Mock;
const mockGetSelectedAccount = getSelectedAccount as unknown as jest.Mock;
const mockGetShouldHideZeroBalanceTokens =
getShouldHideZeroBalanceTokens as jest.Mock;

const mockGetIntlLocale = jest.mocked(getIntlLocale);
const mockGetCurrentCurrency = jest.mocked(getCurrentCurrency);
const mockGetPreferences = jest.mocked(getPreferences);
const mockGetSelectedAccount = jest.mocked(getSelectedAccount);
const mockGetShouldHideZeroBalanceTokens = jest.mocked(
getShouldHideZeroBalanceTokens,
);
const mockGetTokensMarketData = getTokensMarketData as jest.Mock;
const mockGetCurrentChainId = jest.mocked(getCurrentChainId);

const selectedAccountMock = {
id: 'd51c0116-de36-4e77-b35b-408d4ea82d01',
Expand Down Expand Up @@ -166,7 +169,7 @@ describe('AggregatedPercentageOverview', () => {
mockGetSelectedAccount.mockReturnValue(selectedAccountMock);
mockGetShouldHideZeroBalanceTokens.mockReturnValue(false);
mockGetTokensMarketData.mockReturnValue(marketDataMock);

mockGetCurrentChainId.mockReturnValue('0x1');
jest.clearAllMocks();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';

import { zeroAddress, toChecksumAddress } from 'ethereumjs-util';
import { toChecksumAddress } from 'ethereumjs-util';
import { getNativeTokenAddress } from '@metamask/assets-controllers';
import {
getCurrentCurrency,
getSelectedAccount,
getShouldHideZeroBalanceTokens,
getTokensMarketData,
getPreferences,
getCurrentChainId,
} from '../../../selectors';

import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance';
Expand Down Expand Up @@ -37,6 +39,7 @@ export const AggregatedPercentageOverview = () => {
const fiatCurrency = useSelector(getCurrentCurrency);
const { privacyMode } = useSelector(getPreferences);
const selectedAccount = useSelector(getSelectedAccount);
const currentChainId = useSelector(getCurrentChainId);
const shouldHideZeroBalanceTokens = useSelector(
getShouldHideZeroBalanceTokens,
);
Expand All @@ -63,7 +66,8 @@ export const AggregatedPercentageOverview = () => {
}
// native token
const nativePricePercentChange1d =
tokensMarketData?.[zeroAddress()]?.pricePercentChange1d;
tokensMarketData?.[getNativeTokenAddress(currentChainId)]
?.pricePercentChange1d;
const nativeFiat1dAgo = getCalculatedTokenAmount1dAgo(
item.fiatBalance,
nativePricePercentChange1d,
Expand Down
7 changes: 5 additions & 2 deletions ui/components/app/wallet-overview/coin-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import React, {
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import classnames from 'classnames';
import { zeroAddress } from 'ethereumjs-util';
import { CaipChainId } from '@metamask/utils';
import type { Hex } from '@metamask/utils';

import { InternalAccount } from '@metamask/keyring-api';
import { getNativeTokenAddress } from '@metamask/assets-controllers';
import {
Box,
ButtonIcon,
Expand Down Expand Up @@ -231,7 +231,10 @@ export const CoinOverview = ({
return (
<Box className="wallet-overview__currency-wrapper">
<PercentageAndAmountChange
value={tokensMarketData?.[zeroAddress()]?.pricePercentChange1d}
value={
tokensMarketData?.[getNativeTokenAddress(chainId as Hex)]
?.pricePercentChange1d
}
/>
{
///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { zeroAddress } from 'ethereumjs-util';
import { MarketDataDetails } from '@metamask/assets-controllers';
import { getIntlLocale } from '../../../../../ducks/locale/locale';
import {
getCurrentCurrency,
getSelectedAccountCachedBalance,
getTokensMarketData,
getCurrentChainId,
} from '../../../../../selectors';
import {
getConversionRate,
Expand All @@ -26,20 +28,23 @@ jest.mock('../../../../../selectors', () => ({
getCurrentCurrency: jest.fn(),
getSelectedAccountCachedBalance: jest.fn(),
getTokensMarketData: jest.fn(),
getCurrentChainId: jest.fn(),
}));

jest.mock('../../../../../ducks/metamask/metamask', () => ({
getConversionRate: jest.fn(),
getNativeCurrency: jest.fn(),
}));

const mockGetIntlLocale = getIntlLocale as unknown as jest.Mock;
const mockGetCurrentCurrency = getCurrentCurrency as jest.Mock;
const mockGetSelectedAccountCachedBalance =
getSelectedAccountCachedBalance as jest.Mock;
const mockGetConversionRate = getConversionRate as jest.Mock;
const mockGetNativeCurrency = getNativeCurrency as jest.Mock;
const mockGetTokensMarketData = getTokensMarketData as jest.Mock;
const mockGetIntlLocale = jest.mocked(getIntlLocale);
const mockGetCurrentCurrency = jest.mocked(getCurrentCurrency);
const mockGetSelectedAccountCachedBalance = jest.mocked(
getSelectedAccountCachedBalance,
);
const mockGetConversionRate = jest.mocked(getConversionRate);
const mockGetNativeCurrency = jest.mocked(getNativeCurrency);
const mockGetTokensMarketData = jest.mocked(getTokensMarketData);
const mockGetCurrentChainId = jest.mocked(getCurrentChainId);

describe('PercentageChange Component', () => {
beforeEach(() => {
Expand All @@ -51,9 +56,9 @@ describe('PercentageChange Component', () => {
mockGetTokensMarketData.mockReturnValue({
[zeroAddress()]: {
pricePercentChange1d: 2,
},
} as MarketDataDetails,
});

mockGetCurrentChainId.mockReturnValue('0x1');
jest.clearAllMocks();
});

Expand Down Expand Up @@ -108,4 +113,19 @@ describe('PercentageChange Component', () => {
expect(percentageElement).toBeInTheDocument();
expect(numberElement).toBeInTheDocument();
});

it('should display percentage for non-zero native tokens (MATIC)', () => {
mockGetTokensMarketData.mockReturnValue({
'0x0000000000000000000000000000000000001010': {
pricePercentChange1d: 2,
} as MarketDataDetails,
});
mockGetCurrentCurrency.mockReturnValue('POL');
mockGetCurrentChainId.mockReturnValue('0x89');
render(<PercentageAndAmountChange value={1} />);
const percentageElement = screen.getByText('(+1.00%)');
const numberElement = screen.getByText('+POL 12.21');
expect(percentageElement).toBeInTheDocument();
expect(numberElement).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { BigNumber } from 'bignumber.js';
import { isHexString, zeroAddress } from 'ethereumjs-util';
import { isHexString } from 'ethereumjs-util';
import { getNativeTokenAddress } from '@metamask/assets-controllers';
import { Text, Box } from '../../../../component-library';
import {
Display,
TextColor,
TextVariant,
} from '../../../../../helpers/constants/design-system';
import {
getCurrentChainId,
getCurrentCurrency,
getSelectedAccountCachedBalance,
getTokensMarketData,
Expand Down Expand Up @@ -66,10 +68,12 @@ export const PercentageAndAmountChange = ({
const conversionRate = useSelector(getConversionRate);
const nativeCurrency = useSelector(getNativeCurrency);
const marketData = useSelector(getTokensMarketData);
const currentChainId = useSelector(getCurrentChainId);

const balanceChange = useMemo(() => {
// Extracts the 1-day percentage change in price from marketData using the zero address as a key.
const percentage1d = marketData?.[zeroAddress()]?.pricePercentChange1d;
const percentage1d =
marketData?.[getNativeTokenAddress(currentChainId)]?.pricePercentChange1d;

// Checks if the balanceValue is in hex format. This is important for cryptocurrency balances which are often represented in hex.
if (isHexString(balanceValue)) {
Expand Down
10 changes: 6 additions & 4 deletions ui/components/multichain/token-list-item/token-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React, { useContext, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import classnames from 'classnames';
import { zeroAddress } from 'ethereumjs-util';
import { getNativeTokenAddress } from '@metamask/assets-controllers';
import { Hex } from '@metamask/utils';
import {
AlignItems,
BackgroundColor,
Expand Down Expand Up @@ -336,13 +337,14 @@ export const TokenListItem = ({
<PercentageChange
value={
isNativeCurrency
? multiChainMarketData?.[chainId]?.[zeroAddress()]
?.pricePercentChange1d
? multiChainMarketData?.[chainId]?.[
getNativeTokenAddress(chainId as Hex)
]?.pricePercentChange1d
: tokenPercentageChange
}
address={
isNativeCurrency
? (zeroAddress() as `0x${string}`)
? getNativeTokenAddress(chainId as Hex)
: (address as `0x${string}`)
}
/>
Expand Down
Loading

0 comments on commit 9bdfd03

Please sign in to comment.