diff --git a/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch b/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch new file mode 100644 index 000000000000..86499bd20c8d --- /dev/null +++ b/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch @@ -0,0 +1,48 @@ +diff --git a/dist/TokenBalancesController.cjs b/dist/TokenBalancesController.cjs +index 24c2b2d22d3678352fd2ea4932181f3f11ffe41a..e63d3bcd80d68391d713394ec7f340b80fd8b79d 100644 +--- a/dist/TokenBalancesController.cjs ++++ b/dist/TokenBalancesController.cjs +@@ -527,14 +527,16 @@ class TokenBalancesController extends (0, polling_controller_1.StaticIntervalPol + } + // Update with actual fetched balances only if the value has changed + aggregated.forEach(({ success, value, account, token, chainId }) => { +- var _a, _b, _c; ++ var _a, _b; + if (success && value !== undefined) { ++ // Ensure all accounts we add/update are in lower-case ++ const lowerCaseAccount = account.toLowerCase(); + const newBalance = (0, controller_utils_1.toHex)(value); + const tokenAddress = checksum(token); +- const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress]; ++ const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress]; + // Only update if the balance has actually changed + if (currentBalance !== newBalance) { +- ((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance; ++ ((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance; + } + } + }); +diff --git a/dist/TokenBalancesController.mjs b/dist/TokenBalancesController.mjs +index 420934fb16ed6151b5b80fb7be04f81352b4aaf4..d735379adcaf77cbd46b4659f9bfd32046abebdf 100644 +--- a/dist/TokenBalancesController.mjs ++++ b/dist/TokenBalancesController.mjs +@@ -523,14 +523,16 @@ export class TokenBalancesController extends StaticIntervalPollingController() { + } + // Update with actual fetched balances only if the value has changed + aggregated.forEach(({ success, value, account, token, chainId }) => { +- var _a, _b, _c; ++ var _a, _b; + if (success && value !== undefined) { ++ // Ensure all accounts we add/update are in lower-case ++ const lowerCaseAccount = account.toLowerCase(); + const newBalance = toHex(value); + const tokenAddress = checksum(token); +- const currentBalance = d.tokenBalances[account]?.[chainId]?.[tokenAddress]; ++ const currentBalance = d.tokenBalances[lowerCaseAccount]?.[chainId]?.[tokenAddress]; + // Only update if the balance has actually changed + if (currentBalance !== newBalance) { +- ((_c = ((_a = d.tokenBalances)[_b = account] ?? (_a[_b] = {})))[chainId] ?? (_c[chainId] = {}))[tokenAddress] = newBalance; ++ ((_b = ((_a = d.tokenBalances)[lowerCaseAccount] ?? (_a[lowerCaseAccount] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = newBalance; + } + } + }); diff --git a/CHANGELOG.md b/CHANGELOG.md index 734b4ae431e8..04686b304416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [13.10.1] + +### Fixed + +- Prevents token list from fetching balances for all accounts (#38065) +- Fixes dapp-swap comparison fiat rate fetching for native tokens (#37980) +- Fixes dapp-swap fix conversion rate for pol native token (#38102) +- Removes unnecessary extension permission (#38075) +- Fixes missing native token balances in wallet balance (#38126) + ## [13.10.0] ### Added @@ -1217,7 +1227,8 @@ authorized by the user.` error until the user fully revoked dapp - This changelog was split off with 12.22.0 - All older changes can be found in [docs/CHANGELOG_older.md](https://github.com/MetaMask/metamask-extension/blob/main/docs/CHANGELOG_older.md) -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v13.10.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v13.10.1...HEAD +[13.10.1]: https://github.com/MetaMask/metamask-extension/compare/v13.10.0...v13.10.1 [13.10.0]: https://github.com/MetaMask/metamask-extension/compare/v13.9.0...v13.10.0 [13.9.0]: https://github.com/MetaMask/metamask-extension/compare/v13.8.0...v13.9.0 [13.8.0]: https://github.com/MetaMask/metamask-extension/compare/v13.7.0...v13.8.0 diff --git a/app/manifest/v3/_base.json b/app/manifest/v3/_base.json index 775e9e605753..169d30a8cd9a 100644 --- a/app/manifest/v3/_base.json +++ b/app/manifest/v3/_base.json @@ -77,7 +77,6 @@ "notifications", "scripting", "storage", - "tabs", "unlimitedStorage", "webRequest", "offscreen", diff --git a/app/scripts/controller-init/token-balances-controller-init.test.ts b/app/scripts/controller-init/token-balances-controller-init.test.ts index 7a0e007b7743..3b553d02ab74 100644 --- a/app/scripts/controller-init/token-balances-controller-init.test.ts +++ b/app/scripts/controller-init/token-balances-controller-init.test.ts @@ -72,6 +72,7 @@ describe('TokenBalancesControllerInit', () => { queryMultipleAccounts: true, allowExternalServices: expect.any(Function), accountsApiChainIds: expect.any(Function), + platform: 'extension', }); }); }); diff --git a/app/scripts/controller-init/token-balances-controller-init.ts b/app/scripts/controller-init/token-balances-controller-init.ts index f9679acb7148..ef5764bf65cd 100644 --- a/app/scripts/controller-init/token-balances-controller-init.ts +++ b/app/scripts/controller-init/token-balances-controller-init.ts @@ -33,6 +33,7 @@ export const TokenBalancesControllerInit: ControllerInitFunction< ? (featureFlagForAccountApiBalances as `0x${string}`[]) : []; }, + platform: 'extension', }); return { diff --git a/package.json b/package.json index 4c4f343bb28d..0e7393be59e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "13.10.0", + "version": "13.10.1", "private": true, "repository": { "type": "git", @@ -271,7 +271,7 @@ "@metamask/address-book-controller": "^7.0.0", "@metamask/announcement-controller": "^8.0.0", "@metamask/approval-controller": "^8.0.0", - "@metamask/assets-controllers": "^88.0.0", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A88.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch", "@metamask/base-controller": "^9.0.0", "@metamask/bitcoin-wallet-snap": "^1.6.0", "@metamask/bridge-controller": "^60.1.0", diff --git a/ui/hooks/useAssetsUpdateAllAccountBalances.test.ts b/ui/hooks/useAssetsUpdateAllAccountBalances.test.ts index 4f74632e65f4..27c946bda330 100644 --- a/ui/hooks/useAssetsUpdateAllAccountBalances.test.ts +++ b/ui/hooks/useAssetsUpdateAllAccountBalances.test.ts @@ -130,7 +130,7 @@ describe('useAssetsUpdateAllAccountBalances', () => { expect(mockDispatch).toHaveBeenCalledTimes(1); expect(mockUpdateBalancesFoAccounts).toHaveBeenCalledWith( initialChainIds, - true, + false, ); // Update chain IDs and rerender @@ -140,7 +140,7 @@ describe('useAssetsUpdateAllAccountBalances', () => { expect(mockDispatch).toHaveBeenCalledTimes(2); expect(mockUpdateBalancesFoAccounts).toHaveBeenCalledWith( updatedChainIds, - true, + false, ); }); diff --git a/ui/hooks/useAssetsUpdateAllAccountBalances.ts b/ui/hooks/useAssetsUpdateAllAccountBalances.ts index 0d1e68f4ac95..fe4b6fca12fd 100644 --- a/ui/hooks/useAssetsUpdateAllAccountBalances.ts +++ b/ui/hooks/useAssetsUpdateAllAccountBalances.ts @@ -37,7 +37,7 @@ export const useAssetsUpdateAllAccountBalances = (): { // Update balance state for ALL accounts across all enabled EVM chains // TokenBalancesController is configured with queryMultipleAccounts: true // so this will update balances for all accounts, not just the selected one - await dispatch(updateBalancesFoAccounts(enabledChainIds, true)); + await dispatch(updateBalancesFoAccounts(enabledChainIds, false)); } } catch (error) { console.warn('Error updating balances state for all accounts', error); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts index 5872d9900d5f..a1229292f1f4 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.test.ts @@ -1,4 +1,6 @@ import { BigNumber } from 'bignumber.js'; +import { CHAIN_IDS } from '@metamask/transaction-controller'; +import { Hex } from '@metamask/utils'; import { act } from '@testing-library/react'; import { getMockConfirmStateForTransaction } from '../../../../../../test/data/confirmations/helper'; @@ -10,18 +12,23 @@ import * as TokenUtils from '../../../utils/token'; import { Confirmation } from '../../../types/confirm'; import { useDappSwapUSDValues } from './useDappSwapUSDValues'; -async function runHook() { +async function runHook( + tokenAddresses?: Hex[], + mockConfirmation?: Confirmation, +) { const response = renderHookWithConfirmContextProvider( () => useDappSwapUSDValues({ - tokenAddresses: [ + tokenAddresses: tokenAddresses ?? [ '0x0000000000000000000000000000000000000000', '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', ], destTokenAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', }), - getMockConfirmStateForTransaction(mockSwapConfirmation as Confirmation), + getMockConfirmStateForTransaction( + mockConfirmation ?? (mockSwapConfirmation as Confirmation), + ), ); await act(async () => { @@ -98,4 +105,35 @@ describe('useDappSwapUSDValues', () => { '0xfdcc3dd6671eab0709a4c0f3f53de9a333d80798': 1, }); }); + + it('return correct fiat rates token on Polygon', async () => { + jest.spyOn(Utils, 'fetchTokenExchangeRates').mockResolvedValue({ + '0x0000000000000000000000000000000000001010': 4052.27, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 0.999804, + }); + jest.spyOn(TokenUtils, 'fetchAllTokenDetails').mockResolvedValue({ + '0x0000000000000000000000000000000000000000': { + symbol: 'POL', + decimals: '18', + } as TokenStandAndDetails, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': { + symbol: 'USDT', + decimals: '6', + } as TokenStandAndDetails, + }); + + const result = await runHook( + [ + '0x0000000000000000000000000000000000000000', + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', + ], + { ...mockSwapConfirmation, chainId: CHAIN_IDS.POLYGON } as Confirmation, + ); + + expect(result.fiatRates).toEqual({ + '0x0000000000000000000000000000000000000000': 4052.27, + '0x0000000000000000000000000000000000001010': 4052.27, + '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 0.999804, + }); + }); }); diff --git a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts index 1c167d7a9147..2365dcfc3b1d 100644 --- a/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts +++ b/ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapUSDValues.ts @@ -5,7 +5,7 @@ import { getNativeAssetForChainId, isNativeAddress, } from '@metamask/bridge-controller'; -import { TransactionMeta } from '@metamask/transaction-controller'; +import { CHAIN_IDS, TransactionMeta } from '@metamask/transaction-controller'; import { useCallback } from 'react'; import { TokenStandAndDetails } from '../../../../../store/actions'; @@ -15,6 +15,8 @@ import { fetchAllTokenDetails } from '../../../utils/token'; import { getTokenValueFromRecord } from '../../../utils/dapp-swap-comparison-utils'; import { useConfirmContext } from '../../../context/confirm'; +const POLYGON_NATIVE_ASSET = '0x0000000000000000000000000000000000001010'; + export function useDappSwapUSDValues({ tokenAddresses = [], destTokenAddress, @@ -30,10 +32,23 @@ export function useDappSwapUSDValues({ const { value: fiatRates, pending: fiatRatesPending } = useAsyncResult< Record - >( - () => fetchTokenExchangeRates('usd', tokenAddresses as Hex[], chainId), - [chainId, tokenAddresses?.length], - ); + >(async () => { + const addresses = tokenAddresses.filter( + (tokenAddress) => !isNativeAddress(tokenAddress), + ); + const exchangeRates = await fetchTokenExchangeRates( + 'usd', + addresses as Hex[], + chainId, + ); + + if (chainId === CHAIN_IDS.POLYGON) { + const nativeAddress = getNativeAssetForChainId(chainId).address; + exchangeRates[nativeAddress] = exchangeRates[POLYGON_NATIVE_ASSET]; + } + + return exchangeRates; + }, [chainId, tokenAddresses?.length]); const { value: tokenDetails, pending: tokenDetailsPending } = useAsyncResult< Record diff --git a/yarn.lock b/yarn.lock index 3c1e4262c2cd..3e9d985e6a9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5591,7 +5591,7 @@ __metadata: languageName: node linkType: hard -"@metamask/assets-controllers@npm:^88.0.0": +"@metamask/assets-controllers@npm:88.0.0": version: 88.0.0 resolution: "@metamask/assets-controllers@npm:88.0.0" dependencies: @@ -5643,6 +5643,58 @@ __metadata: languageName: node linkType: hard +"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A88.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch": + version: 88.0.0 + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A88.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch::version=88.0.0&hash=ab5ccb" + dependencies: + "@ethereumjs/util": "npm:^9.1.0" + "@ethersproject/abi": "npm:^5.7.0" + "@ethersproject/address": "npm:^5.7.0" + "@ethersproject/bignumber": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@ethersproject/providers": "npm:^5.7.0" + "@metamask/abi-utils": "npm:^2.0.3" + "@metamask/base-controller": "npm:^9.0.0" + "@metamask/contract-metadata": "npm:^2.4.0" + "@metamask/controller-utils": "npm:^11.15.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/messenger": "npm:^0.3.0" + "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/polling-controller": "npm:^15.0.0" + "@metamask/rpc-errors": "npm:^7.0.2" + "@metamask/snaps-sdk": "npm:^9.0.0" + "@metamask/snaps-utils": "npm:^11.0.0" + "@metamask/utils": "npm:^11.8.1" + "@types/bn.js": "npm:^5.1.5" + "@types/uuid": "npm:^8.3.0" + async-mutex: "npm:^0.5.0" + bitcoin-address-validation: "npm:^2.2.3" + bn.js: "npm:^5.2.1" + immer: "npm:^9.0.6" + lodash: "npm:^4.17.21" + multiformats: "npm:^9.9.0" + reselect: "npm:^5.1.1" + single-call-balance-checker-abi: "npm:^1.0.0" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/account-tree-controller": ^3.0.0 + "@metamask/accounts-controller": ^34.0.0 + "@metamask/approval-controller": ^8.0.0 + "@metamask/core-backend": ^4.0.0 + "@metamask/keyring-controller": ^24.0.0 + "@metamask/network-controller": ^25.0.0 + "@metamask/permission-controller": ^12.0.0 + "@metamask/phishing-controller": ^15.0.0 + "@metamask/preferences-controller": ^21.0.0 + "@metamask/providers": ^22.0.0 + "@metamask/snaps-controllers": ^14.0.0 + "@metamask/transaction-controller": ^61.0.0 + webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 + checksum: 10/17587884760c7856d4459c2f60a1acff0d2430b4ae0f9710bff15076e7d21f726d2a1380781d427d32859f1e6e7568eda48e277093d3b85aaffab82021a14c0c + languageName: node + linkType: hard + "@metamask/auth-network-utils@npm:^0.3.0, @metamask/auth-network-utils@npm:^0.3.1": version: 0.3.1 resolution: "@metamask/auth-network-utils@npm:0.3.1" @@ -32492,7 +32544,7 @@ __metadata: "@metamask/announcement-controller": "npm:^8.0.0" "@metamask/api-specs": "npm:^0.13.0" "@metamask/approval-controller": "npm:^8.0.0" - "@metamask/assets-controllers": "npm:^88.0.0" + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A88.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-88.0.0-3dfc0ab8f1.patch" "@metamask/auto-changelog": "npm:^5.1.0" "@metamask/base-controller": "npm:^9.0.0" "@metamask/bitcoin-wallet-snap": "npm:^1.6.0"