Skip to content

Commit

Permalink
Update token rates controller for multi evm fetching (#4866)
Browse files Browse the repository at this point in the history
## Explanation

This PR Updates the `TokenRatesController` to be used in clients across
multiple chains.

## References

* Related to
[#67890](MetaMask/metamask-extension#28158)


## Changelog

<!--
If you're making any consumer-facing changes, list those changes here as
if you were updating a changelog, using the template below as a guide.

(CATEGORY is one of BREAKING, ADDED, CHANGED, DEPRECATED, REMOVED, or
FIXED. For security-related issues, follow the Security Advisory
process.)

Please take care to name the exact pieces of the API you've added or
changed (e.g. types, interfaces, functions, or methods).

If there are any breaking changes, make sure to offer a solution for
consumers to follow once they upgrade to the changes.

Finally, if you're only making changes to development scripts or tests,
you may replace the template below with "None".
-->

### `@metamask/assets-controllers`

- **CHANGED**: When the `TokensController:stateChange` subscription is
fired, token prices are now updated across all chain IDs whose tokens
changed, instead of just the current chain.

## Checklist

- [ ] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [ ] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
- [ ] I've prepared draft pull requests for clients and consumer
packages to resolve any breaking changes

---------

Co-authored-by: sahar-fehri <[email protected]>
  • Loading branch information
bergeron and sahar-fehri authored Nov 1, 2024
1 parent 674fa0b commit bab1445
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 11 deletions.
4 changes: 2 additions & 2 deletions packages/assets-controllers/src/TokenRatesController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe('TokenRatesController', () => {
},
async ({ controller, triggerTokensStateChange }) => {
const updateExchangeRatesSpy = jest
.spyOn(controller, 'updateExchangeRates')
.spyOn(controller, 'updateExchangeRatesByChainId')
.mockResolvedValue();
await controller.start();
triggerTokensStateChange({
Expand Down Expand Up @@ -198,7 +198,7 @@ describe('TokenRatesController', () => {
},
async ({ controller, triggerTokensStateChange }) => {
const updateExchangeRatesSpy = jest
.spyOn(controller, 'updateExchangeRates')
.spyOn(controller, 'updateExchangeRatesByChainId')
.mockResolvedValue();
await controller.start();
triggerTokensStateChange({
Expand Down
51 changes: 42 additions & 9 deletions packages/assets-controllers/src/TokenRatesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,17 +314,46 @@ export class TokenRatesController extends StaticIntervalPollingController<TokenR
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async ({ allTokens, allDetectedTokens }) => {
const previousTokenAddresses = this.#getTokenAddresses(this.#chainId);
if (this.#disabled) {
return;
}

const chainIds = [
...new Set([
...Object.keys(allTokens),
...Object.keys(allDetectedTokens),
]),
] as Hex[];

const chainIdsToUpdate = chainIds.filter(
(chainId) =>
!isEqual(this.#allTokens[chainId], allTokens[chainId]) ||
!isEqual(
this.#allDetectedTokens[chainId],
allDetectedTokens[chainId],
),
);

this.#allTokens = allTokens;
this.#allDetectedTokens = allDetectedTokens;

const newTokenAddresses = this.#getTokenAddresses(this.#chainId);
if (
!isEqual(previousTokenAddresses, newTokenAddresses) &&
this.#pollState === PollState.Active
) {
await this.updateExchangeRates();
}
const { networkConfigurationsByChainId } = this.messagingSystem.call(
'NetworkController:getState',
);

await Promise.allSettled(
chainIdsToUpdate.map(async (chainId) => {
const nativeCurrency =
networkConfigurationsByChainId[chainId as Hex]?.nativeCurrency;

if (nativeCurrency) {
await this.updateExchangeRatesByChainId({
chainId: chainId as Hex,
nativeCurrency,
});
}
}),
);
},
({ allTokens, allDetectedTokens }) => {
return { allTokens, allDetectedTokens };
Expand Down Expand Up @@ -591,6 +620,7 @@ export class TokenRatesController extends StaticIntervalPollingController<TokenR
}

return await this.#fetchAndMapExchangeRatesForUnsupportedNativeCurrency({
chainId,
tokenAddresses,
nativeCurrency,
});
Expand Down Expand Up @@ -695,16 +725,19 @@ export class TokenRatesController extends StaticIntervalPollingController<TokenR
* API, then convert the prices to our desired native currency.
*
* @param args - The arguments to this function.
* @param args.chainId - The chain id to fetch prices for.
* @param args.tokenAddresses - Addresses for tokens.
* @param args.nativeCurrency - The native currency in which to request
* prices.
* @returns A map of the token addresses (as checksums) to their prices in the
* native currency.
*/
async #fetchAndMapExchangeRatesForUnsupportedNativeCurrency({
chainId,
tokenAddresses,
nativeCurrency,
}: {
chainId: Hex;
tokenAddresses: Hex[];
nativeCurrency: string;
}): Promise<ContractMarketData> {
Expand All @@ -714,7 +747,7 @@ export class TokenRatesController extends StaticIntervalPollingController<TokenR
] = await Promise.all([
this.#fetchAndMapExchangeRatesForSupportedNativeCurrency({
tokenAddresses,
chainId: this.#chainId,
chainId,
nativeCurrency: FALL_BACK_VS_CURRENCY,
}),
getCurrencyConversionRate({
Expand Down

0 comments on commit bab1445

Please sign in to comment.