Skip to content

Commit

Permalink
feat: poll native currency prices across chains (#28196)
Browse files Browse the repository at this point in the history
## **Description**

Leverages MetaMask/core#4852 to fetch native
currency prices across all evm chains, instead of just the current
chain.

Metamask will now hit the `/pricemulti` endpoint of cryptocompare to
fetch an array of currencies in 1 request.

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

## **Related issues**

Fixes:

## **Manual testing steps**

No user facing changes.

1. Add some networks with native currencies other than ETH like polygon,
bnb
2. Verify native tokens have the correct fiat price
3. Export state and verify `currencyRates` has entries for each native
currency

## **Screenshots/Recordings**

<!-- 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**

- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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: MetaMask Bot <[email protected]>
  • Loading branch information
bergeron and metamaskbot authored Oct 31, 2024
1 parent f301604 commit 70ba803
Show file tree
Hide file tree
Showing 17 changed files with 158 additions and 66 deletions.
8 changes: 4 additions & 4 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -877,13 +877,13 @@ export default class MetamaskController extends EventEmitter {
messenger: currencyRateMessenger,
state: initState.CurrencyController,
});
const initialFetchExchangeRate =
this.currencyRateController.fetchExchangeRate.bind(
const initialFetchMultiExchangeRate =
this.currencyRateController.fetchMultiExchangeRate.bind(
this.currencyRateController,
);
this.currencyRateController.fetchExchangeRate = (...args) => {
this.currencyRateController.fetchMultiExchangeRate = (...args) => {
if (this.preferencesController.state.useCurrencyRateCheck) {
return initialFetchExchangeRate(...args);
return initialFetchMultiExchangeRate(...args);
}
return {
conversionRate: null,
Expand Down
17 changes: 16 additions & 1 deletion lavamoat/browserify/beta/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,14 +674,14 @@
"@ethersproject/providers": true,
"@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/polling-controller": true,
"@metamask/assets-controllers>@metamask/utils": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/eth-query": true,
"@metamask/metamask-eth-abis": true,
"@metamask/name-controller>async-mutex": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"bn.js": true,
"cockatiel": true,
"ethers>@ethersproject/address": true,
Expand All @@ -702,6 +702,21 @@
"uuid": true
}
},
"@metamask/assets-controllers>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"@metamask/utils>@metamask/superstruct": true,
"@metamask/utils>@scure/base": true,
"@metamask/utils>pony-cause": true,
"@noble/hashes": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
}
},
"@metamask/base-controller": {
"globals": {
"setTimeout": true
Expand Down
17 changes: 16 additions & 1 deletion lavamoat/browserify/flask/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,14 +674,14 @@
"@ethersproject/providers": true,
"@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/polling-controller": true,
"@metamask/assets-controllers>@metamask/utils": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/eth-query": true,
"@metamask/metamask-eth-abis": true,
"@metamask/name-controller>async-mutex": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"bn.js": true,
"cockatiel": true,
"ethers>@ethersproject/address": true,
Expand All @@ -702,6 +702,21 @@
"uuid": true
}
},
"@metamask/assets-controllers>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"@metamask/utils>@metamask/superstruct": true,
"@metamask/utils>@scure/base": true,
"@metamask/utils>pony-cause": true,
"@noble/hashes": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
}
},
"@metamask/base-controller": {
"globals": {
"setTimeout": true
Expand Down
17 changes: 16 additions & 1 deletion lavamoat/browserify/main/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -674,14 +674,14 @@
"@ethersproject/providers": true,
"@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/polling-controller": true,
"@metamask/assets-controllers>@metamask/utils": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/eth-query": true,
"@metamask/metamask-eth-abis": true,
"@metamask/name-controller>async-mutex": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"bn.js": true,
"cockatiel": true,
"ethers>@ethersproject/address": true,
Expand All @@ -702,6 +702,21 @@
"uuid": true
}
},
"@metamask/assets-controllers>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"@metamask/utils>@metamask/superstruct": true,
"@metamask/utils>@scure/base": true,
"@metamask/utils>pony-cause": true,
"@noble/hashes": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
}
},
"@metamask/base-controller": {
"globals": {
"setTimeout": true
Expand Down
17 changes: 16 additions & 1 deletion lavamoat/browserify/mmi/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -766,14 +766,14 @@
"@ethersproject/providers": true,
"@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/polling-controller": true,
"@metamask/assets-controllers>@metamask/utils": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/eth-query": true,
"@metamask/metamask-eth-abis": true,
"@metamask/name-controller>async-mutex": true,
"@metamask/rpc-errors": true,
"@metamask/utils": true,
"bn.js": true,
"cockatiel": true,
"ethers>@ethersproject/address": true,
Expand All @@ -794,6 +794,21 @@
"uuid": true
}
},
"@metamask/assets-controllers>@metamask/utils": {
"globals": {
"TextDecoder": true,
"TextEncoder": true
},
"packages": {
"@metamask/utils>@metamask/superstruct": true,
"@metamask/utils>@scure/base": true,
"@metamask/utils>pony-cause": true,
"@noble/hashes": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
}
},
"@metamask/base-controller": {
"globals": {
"setTimeout": true
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,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%3A39.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-39.0.0-57b3d695bb.patch",
"@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A41.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-41.0.0-57b3d695bb.patch",
"@metamask/base-controller": "^7.0.0",
"@metamask/bitcoin-wallet-snap": "^0.8.2",
"@metamask/browser-passworder": "^4.3.0",
Expand Down
10 changes: 6 additions & 4 deletions test/e2e/mock-e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const privateHostMatchers = [
async function setupMocking(
server,
testSpecificMock,
{ chainId, ethConversionInUsd = '1700' },
{ chainId, ethConversionInUsd = 1700 },
) {
const privacyReport = new Set();
await server.forAnyRequest().thenPassThrough({
Expand Down Expand Up @@ -616,13 +616,15 @@ async function setupMocking(
});

await server
.forGet('https://min-api.cryptocompare.com/data/price')
.withQuery({ fsym: 'ETH', tsyms: 'USD' })
.forGet('https://min-api.cryptocompare.com/data/pricemulti')
.withQuery({ fsyms: 'ETH', tsyms: 'usd' })
.thenCallback(() => {
return {
statusCode: 200,
json: {
USD: ethConversionInUsd,
ETH: {
USD: ethConversionInUsd,
},
},
};
});
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/tests/metrics/errors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const maskedBackgroundFields = [
'AppStateController.notificationGasPollTokens',
'AppStateController.popupGasPollTokens',
'CurrencyController.currencyRates.ETH.conversionDate',
'CurrencyController.currencyRates.LineaETH.conversionDate',
'CurrencyController.currencyRates.SepoliaETH.conversionDate',
];
const maskedUiFields = maskedBackgroundFields.map(backgroundToUiField);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
},
"LineaETH": {
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
},
"SepoliaETH": {
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
}
},
"currentCurrency": "usd"
Expand Down Expand Up @@ -134,7 +144,7 @@
"MultichainBalancesController": { "balances": "object" },
"MultichainRatesController": {
"fiatCurrency": "usd",
"rates": { "btc": { "conversionDate": 0, "conversionRate": "0" } },
"rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } },
"cryptocurrencies": ["btc"]
},
"NameController": { "names": "object", "nameSources": "object" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
},
"LineaETH": {
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
},
"SepoliaETH": {
"conversionDate": "number",
"conversionRate": 1700,
"usdConversionRate": 1700
}
},
"connectedStatusPopoverHasBeenShown": true,
Expand Down Expand Up @@ -187,7 +197,7 @@
"lastFetchedBlockNumbers": "object",
"submitHistory": "object",
"fiatCurrency": "usd",
"rates": { "btc": { "conversionDate": 0, "conversionRate": "0" } },
"rates": { "btc": { "conversionDate": 0, "conversionRate": 0 } },
"cryptocurrencies": ["btc"],
"snaps": "object",
"jobs": "object",
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/tests/privacy/basic-functionality.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ async function mockApis(mockServer) {
};
}),
await mockServer
.forGet('https://min-api.cryptocompare.com/data/price')
.withQuery({ fsym: 'ETH', tsyms: 'USD' })
.forGet('https://min-api.cryptocompare.com/data/pricemulti')
.withQuery({ fsyms: 'ETH', tsyms: 'usd' })
.thenCallback(() => {
return {
statusCode: 200,
Expand Down
10 changes: 6 additions & 4 deletions test/e2e/tests/settings/localization.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ const FixtureBuilder = require('../../fixture-builder');

async function mockPhpConversion(mockServer) {
return await mockServer
.forGet('https://min-api.cryptocompare.com/data/price')
.withQuery({ fsym: 'ETH', tsyms: 'PHP,USD' })
.forGet('https://min-api.cryptocompare.com/data/pricemulti')
.withQuery({ fsyms: 'ETH', tsyms: 'php,USD' })
.thenCallback(() => {
return {
statusCode: 200,
json: {
PHP: '100000',
USD: '2500',
ETH: {
PHP: '100000',
USD: '2500',
},
},
};
});
Expand Down
19 changes: 12 additions & 7 deletions ui/hooks/useCurrencyRatePolling.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import { useSelector } from 'react-redux';
import {
getSelectedNetworkClientId,
getNetworkConfigurationsByChainId,
getUseCurrencyRateCheck,
} from '../selectors';
import {
currencyRateStartPollingByNetworkClientId,
currencyRateStartPolling,
currencyRateStopPollingByPollingToken,
} from '../store/actions';
import { getCompletedOnboarding } from '../ducks/metamask/metamask';
import usePolling from './usePolling';

const useCurrencyRatePolling = (networkClientId?: string) => {
const useCurrencyRatePolling = () => {
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const completedOnboarding = useSelector(getCompletedOnboarding);
const selectedNetworkClientId = useSelector(getSelectedNetworkClientId);
const networkConfigurations = useSelector(getNetworkConfigurationsByChainId);

const nativeCurrencies = [
...new Set(
Object.values(networkConfigurations).map((n) => n.nativeCurrency),
),
];

usePolling({
startPolling: (input) =>
currencyRateStartPollingByNetworkClientId(input.networkClientId),
startPolling: currencyRateStartPolling,
stopPollingByPollingToken: currencyRateStopPollingByPollingToken,
input: { networkClientId: networkClientId ?? selectedNetworkClientId },
input: nativeCurrencies,
enabled: useCurrencyRateCheck && completedOnboarding,
});
};
Expand Down
2 changes: 1 addition & 1 deletion ui/selectors/multichain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function getEvmState(chainId: Hex = CHAIN_IDS.MAINNET): TestState {
rates: {
btc: {
conversionDate: 0,
conversionRate: '100000',
conversionRate: 100000,
},
},
},
Expand Down
8 changes: 4 additions & 4 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4525,15 +4525,15 @@ export async function removePollingTokenFromAppState(pollingToken: string) {
/**
* Informs the CurrencyRateController that the UI requires currency rate polling
*
* @param networkClientId - unique identifier for the network client
* @param nativeCurrencies - An array of native currency symbols
* @returns polling token that can be used to stop polling
*/
export async function currencyRateStartPollingByNetworkClientId(
networkClientId: string,
export async function currencyRateStartPolling(
nativeCurrencies: string[],
): Promise<string> {
const pollingToken = await submitRequestToBackground(
'currencyRateStartPolling',
[{ networkClientId }],
[{ nativeCurrencies }],
);
await addPollingTokenToAppState(pollingToken);
return pollingToken;
Expand Down
Loading

0 comments on commit 70ba803

Please sign in to comment.