diff --git a/app/components/UI/Tokens/index.tsx b/app/components/UI/Tokens/index.tsx index dfa305829d3..b439310a21a 100644 --- a/app/components/UI/Tokens/index.tsx +++ b/app/components/UI/Tokens/index.tsx @@ -41,6 +41,7 @@ import { import ButtonBase from '../../../component-library/components/Buttons/Button/foundation/ButtonBase'; import { selectNetworkName } from '../../../selectors/networkInfos'; import ButtonIcon from '../../../component-library/components/Buttons/ButtonIcon'; +import { Hex } from '@metamask/utils'; // this will be imported from TokenRatesController when it is exported from there // PR: https://github.com/MetaMask/core/pull/4622 @@ -180,7 +181,11 @@ const Tokens: React.FC = ({ tokens }) => { TokenRatesController, } = Engine.context; const actions = [ - TokenDetectionController.detectTokens(), + TokenDetectionController.detectTokens({ + chainIds: isPortfolioViewEnabled + ? Object.keys(networkConfigurationsByChainId) as Hex[] + : [chainId] + }), AccountTrackerController.refresh(), CurrencyRateController.updateExchangeRate(nativeCurrencies), ...(isPortfolioViewEnabled diff --git a/app/components/hooks/AssetPolling/AssetPollingProvider.tsx b/app/components/hooks/AssetPolling/AssetPollingProvider.tsx index 005d60f02c9..1d19d82d9fc 100644 --- a/app/components/hooks/AssetPolling/AssetPollingProvider.tsx +++ b/app/components/hooks/AssetPolling/AssetPollingProvider.tsx @@ -1,6 +1,8 @@ import React, { ReactNode } from 'react'; import useCurrencyRatePolling from './useCurrencyRatePolling'; import useTokenRatesPolling from './useTokenRatesPolling'; +import useTokenDetectionPolling from './useTokenDetectionPolling'; +import useTokenListPolling from './useTokenListPolling'; // This provider is a step towards making controller polling fully UI based. // Eventually, individual UI components will call the use*Polling hooks to @@ -8,6 +10,8 @@ import useTokenRatesPolling from './useTokenRatesPolling'; export const AssetPollingProvider = ({ children }: { children: ReactNode }) => { useCurrencyRatePolling(); useTokenRatesPolling(); + useTokenDetectionPolling(); + useTokenListPolling(); return <>{children}; }; diff --git a/app/components/hooks/AssetPolling/useTokenDetectionPolling.test.ts b/app/components/hooks/AssetPolling/useTokenDetectionPolling.test.ts new file mode 100644 index 00000000000..1294d23cad8 --- /dev/null +++ b/app/components/hooks/AssetPolling/useTokenDetectionPolling.test.ts @@ -0,0 +1,92 @@ +import { renderHookWithProvider } from '../../../util/test/renderWithProvider'; +import Engine from '../../../core/Engine'; +import useTokenDetectionPolling from './useTokenDetectionPolling'; + +jest.mock('../../../core/Engine', () => ({ + context: { + TokenDetectionController: { + startPolling: jest.fn(), + stopPollingByPollingToken: jest.fn(), + }, + }, +})); + +describe('useTokenDetectionPolling', () => { + + beforeEach(() => { + jest.resetAllMocks(); + }); + + const selectedAddress = '0x1234567890abcdef'; + const selectedChainId = '0x1' as const; + + const state = { + engine: { + backgroundState: { + AccountsController: { + internalAccounts: { + selectedAccount: '1', + accounts: { + '1': { + address: selectedAddress + } + }, + }, + }, + PreferencesController: { + useTokenDetection: true, + }, + NetworkController: { + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + [selectedChainId]: { + chainId: selectedChainId, + rpcEndpoints: [{ + networkClientId: 'selectedNetworkClientId', + }] + }, + '0x89': {}, + }, + }, + }, + }, + }; + + it('Should poll by current chain ids/address, and stop polling on dismount', async () => { + + const { unmount } = renderHookWithProvider(() => useTokenDetectionPolling(), {state}); + + const mockedTokenDetectionController = jest.mocked(Engine.context.TokenDetectionController); + + expect(mockedTokenDetectionController.startPolling).toHaveBeenCalledTimes(1); + expect( + mockedTokenDetectionController.startPolling + ).toHaveBeenCalledWith({chainIds: [selectedChainId], address: selectedAddress}); + + expect(mockedTokenDetectionController.stopPollingByPollingToken).toHaveBeenCalledTimes(0); + unmount(); + expect(mockedTokenDetectionController.stopPollingByPollingToken).toHaveBeenCalledTimes(1); + + }); + + it('Should not poll when token detection is disabled', async () => { + + renderHookWithProvider(() => useTokenDetectionPolling({chainIds: ['0x1']}), {state:{ + ...state, + engine: { + ...state.engine, + backgroundState: { + ...state.engine.backgroundState, + PreferencesController: { + ...state.engine.backgroundState.PreferencesController, + useTokenDetection: false, + }, + }, + }, + }}); + + const mockedTokenDetectionController = jest.mocked(Engine.context.TokenDetectionController); + expect(mockedTokenDetectionController.startPolling).toHaveBeenCalledTimes(0); + expect(mockedTokenDetectionController.stopPollingByPollingToken).toHaveBeenCalledTimes(0); + }); +}); diff --git a/app/components/hooks/AssetPolling/useTokenDetectionPolling.ts b/app/components/hooks/AssetPolling/useTokenDetectionPolling.ts new file mode 100644 index 00000000000..d07ba5c46f9 --- /dev/null +++ b/app/components/hooks/AssetPolling/useTokenDetectionPolling.ts @@ -0,0 +1,37 @@ +import { useSelector } from 'react-redux'; +import usePolling from '../usePolling'; +import Engine from '../../../core/Engine'; +import { selectChainId, selectNetworkConfigurations } from '../../../selectors/networkController'; +import { Hex } from '@metamask/utils'; +import { isPortfolioViewEnabled } from '../../../util/networks'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; +import { selectUseTokenDetection } from '../../../selectors/preferencesController'; + +const useTokenDetectionPolling = ({ chainIds }: { chainIds?: Hex[] } = {}) => { + + const networkConfigurations = useSelector(selectNetworkConfigurations); + const currentChainId = useSelector(selectChainId); + const selectedAccount = useSelector(selectSelectedInternalAccount); + const useTokenDetection = useSelector(selectUseTokenDetection); + + const chainIdsToPoll = isPortfolioViewEnabled + ? (chainIds ?? Object.keys(networkConfigurations)) + : [currentChainId]; + + const { TokenDetectionController } = Engine.context; + + usePolling({ + startPolling: + TokenDetectionController.startPolling.bind(TokenDetectionController), + stopPollingByPollingToken: + TokenDetectionController.stopPollingByPollingToken.bind(TokenDetectionController), + input: useTokenDetection ? [{ + chainIds: chainIdsToPoll as Hex[], + address: selectedAccount?.address as Hex + }] : [] + }); + + return { }; +}; + +export default useTokenDetectionPolling; diff --git a/app/components/hooks/AssetPolling/useTokenListPolling.test.ts b/app/components/hooks/AssetPolling/useTokenListPolling.test.ts new file mode 100644 index 00000000000..cbf1ff805e5 --- /dev/null +++ b/app/components/hooks/AssetPolling/useTokenListPolling.test.ts @@ -0,0 +1,55 @@ +import { renderHookWithProvider } from '../../../util/test/renderWithProvider'; +import Engine from '../../../core/Engine'; +import useTokenListPolling from './useTokenListPolling'; + +jest.mock('../../../core/Engine', () => ({ + context: { + TokenListController: { + startPolling: jest.fn(), + stopPollingByPollingToken: jest.fn(), + }, + }, +})); + +describe('useTokenListPolling', () => { + + beforeEach(() => { + jest.resetAllMocks(); + }); + + const selectedChainId = '0x1' as const; + const state = { + engine: { + backgroundState: { + NetworkController: { + selectedNetworkClientId: 'selectedNetworkClientId', + networkConfigurationsByChainId: { + [selectedChainId]: { + chainId: selectedChainId, + rpcEndpoints: [{ + networkClientId: 'selectedNetworkClientId', + }] + }, + '0x89': {}, + }, + }, + }, + }, + }; + + it('Should poll by selected chain id, and stop polling on dismount', async () => { + + const { unmount } = renderHookWithProvider(() => useTokenListPolling(), {state}); + + const mockedTokenListController = jest.mocked(Engine.context.TokenListController); + + expect(mockedTokenListController.startPolling).toHaveBeenCalledTimes(1); + expect( + mockedTokenListController.startPolling + ).toHaveBeenCalledWith({chainId: selectedChainId}); + + expect(mockedTokenListController.stopPollingByPollingToken).toHaveBeenCalledTimes(0); + unmount(); + expect(mockedTokenListController.stopPollingByPollingToken).toHaveBeenCalledTimes(1); + }); +}); diff --git a/app/components/hooks/AssetPolling/useTokenListPolling.ts b/app/components/hooks/AssetPolling/useTokenListPolling.ts new file mode 100644 index 00000000000..13bc408efd8 --- /dev/null +++ b/app/components/hooks/AssetPolling/useTokenListPolling.ts @@ -0,0 +1,39 @@ +import { useSelector } from 'react-redux'; +import usePolling from '../usePolling'; +import Engine from '../../../core/Engine'; +import { selectChainId, selectNetworkConfigurations } from '../../../selectors/networkController'; +import { Hex } from '@metamask/utils'; +import { isPortfolioViewEnabled } from '../../../util/networks'; +import { selectERC20TokensByChain, selectTokenList } from '../../../selectors/tokenListController'; + +const useTokenListPolling = ({ chainIds }: { chainIds?: Hex[] } = {}) => { + + // Selectors to determine polling input + const networkConfigurations = useSelector(selectNetworkConfigurations); + const currentChainId = useSelector(selectChainId); + + // Selectors returning state updated by the polling + const tokenList = useSelector(selectTokenList); + const tokenListByChain = useSelector(selectERC20TokensByChain); + + const chainIdsToPoll = isPortfolioViewEnabled + ? (chainIds ?? Object.keys(networkConfigurations)) + : [currentChainId]; + + const { TokenListController } = Engine.context; + + usePolling({ + startPolling: + TokenListController.startPolling.bind(TokenListController), + stopPollingByPollingToken: + TokenListController.stopPollingByPollingToken.bind(TokenListController), + input: chainIdsToPoll.map((chainId) => ({ chainId: chainId as Hex })) + }); + + return { + tokenList, + tokenListByChain, + }; +}; + +export default useTokenListPolling; diff --git a/app/core/Engine.ts b/app/core/Engine.ts index 0ab5be05300..139d2201da2 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -1546,6 +1546,7 @@ export class Engine { ), platform: 'mobile', useAccountsAPI: true, + disabled: false }), new NftDetectionController({ @@ -1919,13 +1920,9 @@ export class Engine { startPolling() { const { - TokenDetectionController, - TokenListController, TransactionController, } = this.context; - TokenListController.start(); - TokenDetectionController.start(); // leaving the reference of TransactionController here, rather than importing it from utils to avoid circular dependency TransactionController.startIncomingTransactionPolling(); } diff --git a/app/core/NotificationManager.js b/app/core/NotificationManager.js index 22af910e519..a021e381677 100644 --- a/app/core/NotificationManager.js +++ b/app/core/NotificationManager.js @@ -238,7 +238,7 @@ class NotificationManager { pollPromises.push( ...[ TokenBalancesController.poll(), - TokenDetectionController.start(), + TokenDetectionController.detectTokens({ chainIds: [transactionMeta.chainId] }), ], ); break; diff --git a/package.json b/package.json index 84db915f9b6..63cd86567f6 100644 --- a/package.json +++ b/package.json @@ -140,7 +140,7 @@ "@metamask/accounts-controller": "^18.2.1", "@metamask/address-book-controller": "^6.0.1", "@metamask/approval-controller": "^7.1.0", - "@metamask/assets-controllers": "^42.0.0", + "@metamask/assets-controllers": "^43.1.1", "@metamask/base-controller": "^7.0.1", "@metamask/composable-controller": "^3.0.0", "@metamask/controller-utils": "^11.3.0", diff --git a/patches/@metamask+assets-controllers+42.0.0.patch b/patches/@metamask+assets-controllers+42.0.0.patch deleted file mode 100644 index 9d224a2b50e..00000000000 --- a/patches/@metamask+assets-controllers+42.0.0.patch +++ /dev/null @@ -1,937 +0,0 @@ -diff --git a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.cjs b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.cjs -index aac84e5..983c3f1 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.cjs -+++ b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.cjs -@@ -1,19 +1,19 @@ - "use strict"; --var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); --}; - var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; - }; -+var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -+}; - var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; - }; --var _AccountTrackerController_instances, _AccountTrackerController_refreshMutex, _AccountTrackerController_handle, _AccountTrackerController_getCurrentChainId, _AccountTrackerController_getCorrectNetworkClient, _AccountTrackerController_getBalanceFromChain; -+var _AccountTrackerController_instances, _AccountTrackerController_refreshMutex, _AccountTrackerController_includeStakedAssets, _AccountTrackerController_getStakedBalanceForChain, _AccountTrackerController_handle, _AccountTrackerController_getCurrentChainId, _AccountTrackerController_getCorrectNetworkClient, _AccountTrackerController_getBalanceFromChain; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.AccountTrackerController = void 0; - const controller_utils_1 = require("@metamask/controller-utils"); -@@ -47,8 +47,10 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo - * @param options.interval - Polling interval used to fetch new account balances. - * @param options.state - Initial state to set on this controller. - * @param options.messenger - The controller messaging system. -+ * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain. -+ * @param options.includeStakedAssets - Whether to include staked assets in the account balances. - */ -- constructor({ interval = 10000, state, messenger, }) { -+ constructor({ interval = 10000, state, messenger, getStakedBalanceForChain, includeStakedAssets = false, }) { - const { selectedNetworkClientId } = messenger.call('NetworkController:getState'); - const { configuration: { chainId }, } = messenger.call('NetworkController:getNetworkClientById', selectedNetworkClientId); - super({ -@@ -65,7 +67,11 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo - }); - _AccountTrackerController_instances.add(this); - _AccountTrackerController_refreshMutex.set(this, new async_mutex_1.Mutex()); -+ _AccountTrackerController_includeStakedAssets.set(this, void 0); -+ _AccountTrackerController_getStakedBalanceForChain.set(this, void 0); - _AccountTrackerController_handle.set(this, void 0); -+ __classPrivateFieldSet(this, _AccountTrackerController_getStakedBalanceForChain, getStakedBalanceForChain, "f"); -+ __classPrivateFieldSet(this, _AccountTrackerController_includeStakedAssets, includeStakedAssets, "f"); - this.setIntervalLength(interval); - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/no-floating-promises -@@ -171,6 +177,15 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo - balance, - }; - } -+ if (__classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")) { -+ const stakedBalance = await __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, address, networkClientId); -+ if (stakedBalance) { -+ accountsForChain[address] = { -+ ...accountsForChain[address], -+ stakedBalance, -+ }; -+ } -+ } - } - this.update((state) => { - if (chainId === __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCurrentChainId).call(this)) { -@@ -196,18 +211,23 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo - return (0, controller_utils_1.safelyExecuteWithTimeout)(async () => { - (0, utils_1.assert)(ethQuery, 'Provider not set.'); - const balance = await (0, controller_utils_1.query)(ethQuery, 'getBalance', [address]); -- return [address, balance]; -+ let stakedBalance; -+ if (__classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")) { -+ stakedBalance = await __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, address, networkClientId); -+ } -+ return [address, balance, stakedBalance]; - }); - })).then((value) => { - return value.reduce((obj, item) => { - if (!item) { - return obj; - } -- const [address, balance] = item; -+ const [address, balance, stakedBalance] = item; - return { - ...obj, - [address]: { - balance, -+ stakedBalance, - }, - }; - }, {}); -@@ -215,7 +235,7 @@ class AccountTrackerController extends (0, polling_controller_1.StaticIntervalPo - } - } - exports.AccountTrackerController = AccountTrackerController; --_AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerController_handle = new WeakMap(), _AccountTrackerController_instances = new WeakSet(), _AccountTrackerController_getCurrentChainId = function _AccountTrackerController_getCurrentChainId() { -+_AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerController_includeStakedAssets = new WeakMap(), _AccountTrackerController_getStakedBalanceForChain = new WeakMap(), _AccountTrackerController_handle = new WeakMap(), _AccountTrackerController_instances = new WeakSet(), _AccountTrackerController_getCurrentChainId = function _AccountTrackerController_getCurrentChainId() { - const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState'); - const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId); - return chainId; -diff --git a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.cts b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.cts -index 144e018..6b7d8cd 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.cts -+++ b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.cts -@@ -2,6 +2,7 @@ import type { AccountsControllerSelectedEvmAccountChangeEvent, AccountsControlle - import type { ControllerStateChangeEvent, ControllerGetStateAction, RestrictedControllerMessenger } from "@metamask/base-controller"; - import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller"; - import type { PreferencesControllerGetStateAction } from "@metamask/preferences-controller"; -+import type { AssetsContractController, StakedBalance } from "./AssetsContractController.cjs"; - /** - * The name of the {@link AccountTrackerController}. - */ -@@ -10,10 +11,12 @@ declare const controllerName = "AccountTrackerController"; - * @type AccountInformation - * - * Account information object -- * @property balance - Hex string of an account balancec in wei -+ * @property balance - Hex string of an account balance in wei -+ * @property stakedBalance - Hex string of an account staked balance in wei - */ - export type AccountInformation = { - balance: string; -+ stakedBalance?: string; - }; - /** - * @type AccountTrackerControllerState -@@ -62,18 +65,15 @@ type AccountTrackerPollingInput = { - networkClientId: NetworkClientId; - }; - declare const AccountTrackerController_base: (abstract new (...args: any[]) => { -- readonly "__#787890@#intervalIds": Record; -- "__#787890@#intervalLength": number | undefined; -+ readonly "__#784968@#intervalIds": Record; -+ "__#784968@#intervalLength": number | undefined; - setIntervalLength(intervalLength: number): void; - getIntervalLength(): number | undefined; - _startPolling(input: AccountTrackerPollingInput): void; - _stopPollingByPollingTokenSetId(key: string): void; -- readonly "__#787882@#pollingTokenSets": Map>; -- "__#787882@#callbacks": Map void>>; -+ readonly "__#784960@#pollingTokenSets": Map>; -+ "__#784960@#callbacks": Map void>>; - _executePoll(input: AccountTrackerPollingInput): Promise; -- /** -- * The action that can be performed to get the state of the {@link AccountTrackerController}. -- */ - startPolling(input: AccountTrackerPollingInput): string; - stopAllPolling(): void; - stopPollingByPollingToken(pollingToken: string): void; -@@ -91,11 +91,15 @@ export declare class AccountTrackerController extends AccountTrackerController_b - * @param options.interval - Polling interval used to fetch new account balances. - * @param options.state - Initial state to set on this controller. - * @param options.messenger - The controller messaging system. -+ * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain. -+ * @param options.includeStakedAssets - Whether to include staked assets in the account balances. - */ -- constructor({ interval, state, messenger, }: { -+ constructor({ interval, state, messenger, getStakedBalanceForChain, includeStakedAssets, }: { - interval?: number; - state?: Partial; - messenger: AccountTrackerControllerMessenger; -+ getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain']; -+ includeStakedAssets?: boolean; - }); - private syncAccounts; - /** -@@ -128,6 +132,7 @@ export declare class AccountTrackerController extends AccountTrackerController_b - */ - syncBalanceWithAddresses(addresses: string[], networkClientId?: NetworkClientId): Promise>; - } - export default AccountTrackerController; -diff --git a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.mts b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.mts -index b25212c..20d566f 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.mts -+++ b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.d.mts -@@ -2,6 +2,7 @@ import type { AccountsControllerSelectedEvmAccountChangeEvent, AccountsControlle - import type { ControllerStateChangeEvent, ControllerGetStateAction, RestrictedControllerMessenger } from "@metamask/base-controller"; - import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction } from "@metamask/network-controller"; - import type { PreferencesControllerGetStateAction } from "@metamask/preferences-controller"; -+import type { AssetsContractController, StakedBalance } from "./AssetsContractController.mjs"; - /** - * The name of the {@link AccountTrackerController}. - */ -@@ -10,10 +11,12 @@ declare const controllerName = "AccountTrackerController"; - * @type AccountInformation - * - * Account information object -- * @property balance - Hex string of an account balancec in wei -+ * @property balance - Hex string of an account balance in wei -+ * @property stakedBalance - Hex string of an account staked balance in wei - */ - export type AccountInformation = { - balance: string; -+ stakedBalance?: string; - }; - /** - * @type AccountTrackerControllerState -@@ -62,18 +65,15 @@ type AccountTrackerPollingInput = { - networkClientId: NetworkClientId; - }; - declare const AccountTrackerController_base: (abstract new (...args: any[]) => { -- readonly "__#787890@#intervalIds": Record; -- "__#787890@#intervalLength": number | undefined; -+ readonly "__#784968@#intervalIds": Record; -+ "__#784968@#intervalLength": number | undefined; - setIntervalLength(intervalLength: number): void; - getIntervalLength(): number | undefined; - _startPolling(input: AccountTrackerPollingInput): void; - _stopPollingByPollingTokenSetId(key: string): void; -- readonly "__#787882@#pollingTokenSets": Map>; -- "__#787882@#callbacks": Map void>>; -+ readonly "__#784960@#pollingTokenSets": Map>; -+ "__#784960@#callbacks": Map void>>; - _executePoll(input: AccountTrackerPollingInput): Promise; -- /** -- * The action that can be performed to get the state of the {@link AccountTrackerController}. -- */ - startPolling(input: AccountTrackerPollingInput): string; - stopAllPolling(): void; - stopPollingByPollingToken(pollingToken: string): void; -@@ -91,11 +91,15 @@ export declare class AccountTrackerController extends AccountTrackerController_b - * @param options.interval - Polling interval used to fetch new account balances. - * @param options.state - Initial state to set on this controller. - * @param options.messenger - The controller messaging system. -+ * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain. -+ * @param options.includeStakedAssets - Whether to include staked assets in the account balances. - */ -- constructor({ interval, state, messenger, }: { -+ constructor({ interval, state, messenger, getStakedBalanceForChain, includeStakedAssets, }: { - interval?: number; - state?: Partial; - messenger: AccountTrackerControllerMessenger; -+ getStakedBalanceForChain: AssetsContractController['getStakedBalanceForChain']; -+ includeStakedAssets?: boolean; - }); - private syncAccounts; - /** -@@ -128,6 +132,7 @@ export declare class AccountTrackerController extends AccountTrackerController_b - */ - syncBalanceWithAddresses(addresses: string[], networkClientId?: NetworkClientId): Promise>; - } - export default AccountTrackerController; -diff --git a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.mjs b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.mjs -index 4c4bf70..48cb006 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.mjs -+++ b/node_modules/@metamask/assets-controllers/dist/AccountTrackerController.mjs -@@ -1,15 +1,15 @@ --var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); --}; - var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { - if (kind === "m") throw new TypeError("Private method is not writable"); - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); - return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; - }; --var _AccountTrackerController_instances, _AccountTrackerController_refreshMutex, _AccountTrackerController_handle, _AccountTrackerController_getCurrentChainId, _AccountTrackerController_getCorrectNetworkClient, _AccountTrackerController_getBalanceFromChain; -+var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { -+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); -+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); -+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -+}; -+var _AccountTrackerController_instances, _AccountTrackerController_refreshMutex, _AccountTrackerController_includeStakedAssets, _AccountTrackerController_getStakedBalanceForChain, _AccountTrackerController_handle, _AccountTrackerController_getCurrentChainId, _AccountTrackerController_getCorrectNetworkClient, _AccountTrackerController_getBalanceFromChain; - function $importDefault(module) { - if (module?.__esModule) { - return module.default; -@@ -49,8 +49,10 @@ export class AccountTrackerController extends StaticIntervalPollingController() - * @param options.interval - Polling interval used to fetch new account balances. - * @param options.state - Initial state to set on this controller. - * @param options.messenger - The controller messaging system. -+ * @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain. -+ * @param options.includeStakedAssets - Whether to include staked assets in the account balances. - */ -- constructor({ interval = 10000, state, messenger, }) { -+ constructor({ interval = 10000, state, messenger, getStakedBalanceForChain, includeStakedAssets = false, }) { - const { selectedNetworkClientId } = messenger.call('NetworkController:getState'); - const { configuration: { chainId }, } = messenger.call('NetworkController:getNetworkClientById', selectedNetworkClientId); - super({ -@@ -67,7 +69,11 @@ export class AccountTrackerController extends StaticIntervalPollingController() - }); - _AccountTrackerController_instances.add(this); - _AccountTrackerController_refreshMutex.set(this, new Mutex()); -+ _AccountTrackerController_includeStakedAssets.set(this, void 0); -+ _AccountTrackerController_getStakedBalanceForChain.set(this, void 0); - _AccountTrackerController_handle.set(this, void 0); -+ __classPrivateFieldSet(this, _AccountTrackerController_getStakedBalanceForChain, getStakedBalanceForChain, "f"); -+ __classPrivateFieldSet(this, _AccountTrackerController_includeStakedAssets, includeStakedAssets, "f"); - this.setIntervalLength(interval); - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/no-floating-promises -@@ -173,6 +179,15 @@ export class AccountTrackerController extends StaticIntervalPollingController() - balance, - }; - } -+ if (__classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")) { -+ const stakedBalance = await __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, address, networkClientId); -+ if (stakedBalance) { -+ accountsForChain[address] = { -+ ...accountsForChain[address], -+ stakedBalance, -+ }; -+ } -+ } - } - this.update((state) => { - if (chainId === __classPrivateFieldGet(this, _AccountTrackerController_instances, "m", _AccountTrackerController_getCurrentChainId).call(this)) { -@@ -198,25 +213,30 @@ export class AccountTrackerController extends StaticIntervalPollingController() - return safelyExecuteWithTimeout(async () => { - assert(ethQuery, 'Provider not set.'); - const balance = await query(ethQuery, 'getBalance', [address]); -- return [address, balance]; -+ let stakedBalance; -+ if (__classPrivateFieldGet(this, _AccountTrackerController_includeStakedAssets, "f")) { -+ stakedBalance = await __classPrivateFieldGet(this, _AccountTrackerController_getStakedBalanceForChain, "f").call(this, address, networkClientId); -+ } -+ return [address, balance, stakedBalance]; - }); - })).then((value) => { - return value.reduce((obj, item) => { - if (!item) { - return obj; - } -- const [address, balance] = item; -+ const [address, balance, stakedBalance] = item; - return { - ...obj, - [address]: { - balance, -+ stakedBalance, - }, - }; - }, {}); - }); - } - } --_AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerController_handle = new WeakMap(), _AccountTrackerController_instances = new WeakSet(), _AccountTrackerController_getCurrentChainId = function _AccountTrackerController_getCurrentChainId() { -+_AccountTrackerController_refreshMutex = new WeakMap(), _AccountTrackerController_includeStakedAssets = new WeakMap(), _AccountTrackerController_getStakedBalanceForChain = new WeakMap(), _AccountTrackerController_handle = new WeakMap(), _AccountTrackerController_instances = new WeakSet(), _AccountTrackerController_getCurrentChainId = function _AccountTrackerController_getCurrentChainId() { - const { selectedNetworkClientId } = this.messagingSystem.call('NetworkController:getState'); - const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId); - return chainId; -diff --git a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.cjs b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.cjs -index a8ba346..6b39491 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.cjs -+++ b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.cjs -@@ -15,7 +15,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - }; - var _AssetsContractController_instances, _AssetsContractController_provider, _AssetsContractController_ipfsGateway, _AssetsContractController_chainId, _AssetsContractController_registerActionHandlers, _AssetsContractController_registerEventSubscriptions, _AssetsContractController_getCorrectProvider, _AssetsContractController_getCorrectChainId; - Object.defineProperty(exports, "__esModule", { value: true }); --exports.AssetsContractController = exports.MISSING_PROVIDER_ERROR = exports.SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID = void 0; -+exports.AssetsContractController = exports.MISSING_PROVIDER_ERROR = exports.STAKING_CONTRACT_ADDRESS_BY_CHAINID = exports.SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID = void 0; -+// import { BigNumber } from '@ethersproject/bignumber'; -+const bignumber_1 = require("@ethersproject/bignumber"); - const contracts_1 = require("@ethersproject/contracts"); - const providers_1 = require("@ethersproject/providers"); - const controller_utils_1 = require("@metamask/controller-utils"); -@@ -51,6 +53,10 @@ exports.SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID = { - [assetsUtil_1.SupportedTokenDetectionNetworks.moonbeam]: '0x6aa75276052d96696134252587894ef5ffa520af', - [assetsUtil_1.SupportedTokenDetectionNetworks.moonriver]: '0x6aa75276052d96696134252587894ef5ffa520af', - }; -+exports.STAKING_CONTRACT_ADDRESS_BY_CHAINID = { -+ [assetsUtil_1.SupportedStakedBalanceNetworks.mainnet]: '0x4fef9d741011476750a243ac70b9789a63dd47df', -+ [assetsUtil_1.SupportedStakedBalanceNetworks.holesky]: '0x37bf0883c27365cffcd0c4202918df930989891f', -+}; - exports.MISSING_PROVIDER_ERROR = 'AssetsContractController failed to set the provider correctly. A provider must be set for this method to be available'; - /** - * The name of the {@link AssetsContractController} -@@ -333,6 +339,60 @@ class AssetsContractController { - } - return nonZeroBalances; - } -+ /** -+ * Get the staked ethereum balance for an address in a single call. -+ * -+ * @param address - The address to check staked ethereum balance for. -+ * @param networkClientId - Network Client ID to fetch the provider with. -+ * @returns The hex staked ethereum balance for address. -+ */ -+ async getStakedBalanceForChain(address, networkClientId) { -+ const chainId = __classPrivateFieldGet(this, _AssetsContractController_instances, "m", _AssetsContractController_getCorrectChainId).call(this, networkClientId); -+ const provider = __classPrivateFieldGet(this, _AssetsContractController_instances, "m", _AssetsContractController_getCorrectProvider).call(this, networkClientId); -+ // balance defaults to zero -+ let balance = bignumber_1.BigNumber.from(0); -+ // Only fetch staked balance on supported networks -+ if (![ -+ assetsUtil_1.SupportedStakedBalanceNetworks.mainnet, -+ assetsUtil_1.SupportedStakedBalanceNetworks.holesky, -+ ].includes(chainId)) { -+ return undefined; -+ } -+ // Only fetch staked balance if contract address exists -+ if (!((id) => id in exports.STAKING_CONTRACT_ADDRESS_BY_CHAINID)(chainId)) { -+ return undefined; -+ } -+ const contractAddress = exports.STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId]; -+ const abi = [ -+ { -+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }], -+ name: 'getShares', -+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], -+ stateMutability: 'view', -+ type: 'function', -+ }, -+ { -+ inputs: [{ internalType: 'uint256', name: 'shares', type: 'uint256' }], -+ name: 'convertToAssets', -+ outputs: [{ internalType: 'uint256', name: 'assets', type: 'uint256' }], -+ stateMutability: 'view', -+ type: 'function', -+ }, -+ ]; -+ try { -+ const contract = new contracts_1.Contract(contractAddress, abi, provider); -+ const userShares = await contract.getShares(address); -+ // convert shares to assets only if address shares > 0 else return default balance -+ if (!userShares.lte(0)) { -+ balance = await contract.convertToAssets(userShares.toString()); -+ } -+ } -+ catch (error) { -+ // if we get an error, log and return the default value -+ console.error(error); -+ } -+ return balance.toHexString(); -+ } - } - exports.AssetsContractController = AssetsContractController; - _AssetsContractController_provider = new WeakMap(), _AssetsContractController_ipfsGateway = new WeakMap(), _AssetsContractController_chainId = new WeakMap(), _AssetsContractController_instances = new WeakSet(), _AssetsContractController_registerActionHandlers = function _AssetsContractController_registerActionHandlers() { -diff --git a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.cts b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.cts -index d7d9b61..b7bf69d 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.cts -+++ b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.cts -@@ -32,6 +32,10 @@ export declare const SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID: { - readonly "0x504": "0x6aa75276052d96696134252587894ef5ffa520af"; - readonly "0x505": "0x6aa75276052d96696134252587894ef5ffa520af"; - }; -+export declare const STAKING_CONTRACT_ADDRESS_BY_CHAINID: { -+ readonly "0x1": "0x4fef9d741011476750a243ac70b9789a63dd47df"; -+ readonly "0x4268": "0x37bf0883c27365cffcd0c4202918df930989891f"; -+}; - export declare const MISSING_PROVIDER_ERROR = "AssetsContractController failed to set the provider correctly. A provider must be set for this method to be available"; - /** - * @type BalanceMap -@@ -98,6 +102,7 @@ export type AllowedEvents = PreferencesControllerStateChangeEvent | NetworkContr - * The messenger of the {@link AssetsContractController}. - */ - export type AssetsContractControllerMessenger = RestrictedControllerMessenger; -+export type StakedBalance = string | undefined; - /** - * Controller that interacts with contracts on mainnet through web3 - */ -@@ -272,6 +277,14 @@ export declare class AssetsContractController { - * @returns The list of non-zero token balances. - */ - getBalancesInSingleCall(selectedAddress: string, tokensToDetect: string[], networkClientId?: NetworkClientId): Promise; -+ /** -+ * Get the staked ethereum balance for an address in a single call. -+ * -+ * @param address - The address to check staked ethereum balance for. -+ * @param networkClientId - Network Client ID to fetch the provider with. -+ * @returns The hex staked ethereum balance for address. -+ */ -+ getStakedBalanceForChain(address: string, networkClientId?: NetworkClientId): Promise; - } - export default AssetsContractController; - //# sourceMappingURL=AssetsContractController.d.cts.map -\ No newline at end of file -diff --git a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.mts b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.mts -index a7916fb..9cbee8c 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.mts -+++ b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.d.mts -@@ -32,6 +32,10 @@ export declare const SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID: { - readonly "0x504": "0x6aa75276052d96696134252587894ef5ffa520af"; - readonly "0x505": "0x6aa75276052d96696134252587894ef5ffa520af"; - }; -+export declare const STAKING_CONTRACT_ADDRESS_BY_CHAINID: { -+ readonly "0x1": "0x4fef9d741011476750a243ac70b9789a63dd47df"; -+ readonly "0x4268": "0x37bf0883c27365cffcd0c4202918df930989891f"; -+}; - export declare const MISSING_PROVIDER_ERROR = "AssetsContractController failed to set the provider correctly. A provider must be set for this method to be available"; - /** - * @type BalanceMap -@@ -98,6 +102,7 @@ export type AllowedEvents = PreferencesControllerStateChangeEvent | NetworkContr - * The messenger of the {@link AssetsContractController}. - */ - export type AssetsContractControllerMessenger = RestrictedControllerMessenger; -+export type StakedBalance = string | undefined; - /** - * Controller that interacts with contracts on mainnet through web3 - */ -@@ -272,6 +277,14 @@ export declare class AssetsContractController { - * @returns The list of non-zero token balances. - */ - getBalancesInSingleCall(selectedAddress: string, tokensToDetect: string[], networkClientId?: NetworkClientId): Promise; -+ /** -+ * Get the staked ethereum balance for an address in a single call. -+ * -+ * @param address - The address to check staked ethereum balance for. -+ * @param networkClientId - Network Client ID to fetch the provider with. -+ * @returns The hex staked ethereum balance for address. -+ */ -+ getStakedBalanceForChain(address: string, networkClientId?: NetworkClientId): Promise; - } - export default AssetsContractController; - //# sourceMappingURL=AssetsContractController.d.mts.map -\ No newline at end of file -diff --git a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.mjs b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.mjs -index 15a0e56..deeb1eb 100644 ---- a/node_modules/@metamask/assets-controllers/dist/AssetsContractController.mjs -+++ b/node_modules/@metamask/assets-controllers/dist/AssetsContractController.mjs -@@ -16,13 +16,15 @@ function $importDefault(module) { - } - return module; - } -+// import { BigNumber } from '@ethersproject/bignumber'; -+import { BigNumber } from "@ethersproject/bignumber"; - import { Contract } from "@ethersproject/contracts"; - import { Web3Provider } from "@ethersproject/providers"; - import { IPFS_DEFAULT_GATEWAY_URL } from "@metamask/controller-utils"; - import { getKnownPropertyNames } from "@metamask/utils"; - import $abiSingleCallBalancesContract from "single-call-balance-checker-abi"; - const abiSingleCallBalancesContract = $importDefault($abiSingleCallBalancesContract); --import { SupportedTokenDetectionNetworks } from "./assetsUtil.mjs"; -+import { SupportedStakedBalanceNetworks, SupportedTokenDetectionNetworks } from "./assetsUtil.mjs"; - import { ERC20Standard } from "./Standards/ERC20Standard.mjs"; - import { ERC1155Standard } from "./Standards/NftStandards/ERC1155/ERC1155Standard.mjs"; - import { ERC721Standard } from "./Standards/NftStandards/ERC721/ERC721Standard.mjs"; -@@ -52,6 +54,10 @@ export const SINGLE_CALL_BALANCES_ADDRESS_BY_CHAINID = { - [SupportedTokenDetectionNetworks.moonbeam]: '0x6aa75276052d96696134252587894ef5ffa520af', - [SupportedTokenDetectionNetworks.moonriver]: '0x6aa75276052d96696134252587894ef5ffa520af', - }; -+export const STAKING_CONTRACT_ADDRESS_BY_CHAINID = { -+ [SupportedStakedBalanceNetworks.mainnet]: '0x4fef9d741011476750a243ac70b9789a63dd47df', -+ [SupportedStakedBalanceNetworks.holesky]: '0x37bf0883c27365cffcd0c4202918df930989891f', -+}; - export const MISSING_PROVIDER_ERROR = 'AssetsContractController failed to set the provider correctly. A provider must be set for this method to be available'; - /** - * The name of the {@link AssetsContractController} -@@ -334,6 +340,60 @@ export class AssetsContractController { - } - return nonZeroBalances; - } -+ /** -+ * Get the staked ethereum balance for an address in a single call. -+ * -+ * @param address - The address to check staked ethereum balance for. -+ * @param networkClientId - Network Client ID to fetch the provider with. -+ * @returns The hex staked ethereum balance for address. -+ */ -+ async getStakedBalanceForChain(address, networkClientId) { -+ const chainId = __classPrivateFieldGet(this, _AssetsContractController_instances, "m", _AssetsContractController_getCorrectChainId).call(this, networkClientId); -+ const provider = __classPrivateFieldGet(this, _AssetsContractController_instances, "m", _AssetsContractController_getCorrectProvider).call(this, networkClientId); -+ // balance defaults to zero -+ let balance = BigNumber.from(0); -+ // Only fetch staked balance on supported networks -+ if (![ -+ SupportedStakedBalanceNetworks.mainnet, -+ SupportedStakedBalanceNetworks.holesky, -+ ].includes(chainId)) { -+ return undefined; -+ } -+ // Only fetch staked balance if contract address exists -+ if (!((id) => id in STAKING_CONTRACT_ADDRESS_BY_CHAINID)(chainId)) { -+ return undefined; -+ } -+ const contractAddress = STAKING_CONTRACT_ADDRESS_BY_CHAINID[chainId]; -+ const abi = [ -+ { -+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }], -+ name: 'getShares', -+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], -+ stateMutability: 'view', -+ type: 'function', -+ }, -+ { -+ inputs: [{ internalType: 'uint256', name: 'shares', type: 'uint256' }], -+ name: 'convertToAssets', -+ outputs: [{ internalType: 'uint256', name: 'assets', type: 'uint256' }], -+ stateMutability: 'view', -+ type: 'function', -+ }, -+ ]; -+ try { -+ const contract = new Contract(contractAddress, abi, provider); -+ const userShares = await contract.getShares(address); -+ // convert shares to assets only if address shares > 0 else return default balance -+ if (!userShares.lte(0)) { -+ balance = await contract.convertToAssets(userShares.toString()); -+ } -+ } -+ catch (error) { -+ // if we get an error, log and return the default value -+ console.error(error); -+ } -+ return balance.toHexString(); -+ } - } - _AssetsContractController_provider = new WeakMap(), _AssetsContractController_ipfsGateway = new WeakMap(), _AssetsContractController_chainId = new WeakMap(), _AssetsContractController_instances = new WeakSet(), _AssetsContractController_registerActionHandlers = function _AssetsContractController_registerActionHandlers() { - const methodsExcludedFromMessenger = [ -diff --git a/node_modules/@metamask/assets-controllers/dist/NftController.cjs b/node_modules/@metamask/assets-controllers/dist/NftController.cjs -index 6ccbe9c..49270d6 100644 ---- a/node_modules/@metamask/assets-controllers/dist/NftController.cjs -+++ b/node_modules/@metamask/assets-controllers/dist/NftController.cjs -@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( - var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; - }; --var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId, _NftController_chainId, _NftController_ipfsGateway, _NftController_openSeaEnabled, _NftController_useIpfsSubdomains, _NftController_isIpfsGatewayEnabled, _NftController_onNftAdded, _NftController_onNetworkControllerNetworkDidChange, _NftController_onPreferencesControllerStateChange, _NftController_onSelectedAccountChange, _NftController_updateNestedNftState, _NftController_getNftCollectionApi, _NftController_getNftInformationFromApi, _NftController_getNftInformationFromTokenURI, _NftController_getNftURIAndStandard, _NftController_getNftInformation, _NftController_getNftContractInformationFromContract, _NftController_getNftContractInformation, _NftController_addIndividualNft, _NftController_addNftContract, _NftController_removeAndIgnoreIndividualNft, _NftController_removeIndividualNft, _NftController_removeNftContract, _NftController_validateWatchNft, _NftController_getCorrectChainId, _NftController_getAddressOrSelectedAddress, _NftController_updateNftUpdateForAccount; -+var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId, _NftController_chainId, _NftController_ipfsGateway, _NftController_displayNftMedia, _NftController_useIpfsSubdomains, _NftController_isIpfsGatewayEnabled, _NftController_onNftAdded, _NftController_onNetworkControllerNetworkDidChange, _NftController_onPreferencesControllerStateChange, _NftController_onSelectedAccountChange, _NftController_updateNestedNftState, _NftController_getNftCollectionApi, _NftController_getNftInformationFromApi, _NftController_getNftInformationFromTokenURI, _NftController_getNftURIAndStandard, _NftController_getNftInformation, _NftController_getNftContractInformationFromContract, _NftController_getNftContractInformation, _NftController_addIndividualNft, _NftController_addNftContract, _NftController_removeAndIgnoreIndividualNft, _NftController_removeIndividualNft, _NftController_removeNftContract, _NftController_validateWatchNft, _NftController_getCorrectChainId, _NftController_getAddressOrSelectedAddress, _NftController_updateNftUpdateForAccount; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.NftController = exports.getDefaultNftControllerState = void 0; - const address_1 = require("@ethersproject/address"); -@@ -53,7 +53,7 @@ class NftController extends base_controller_1.BaseController { - * @param options - The controller options. - * @param options.chainId - The chain ID of the current network. - * @param options.ipfsGateway - The configured IPFS gateway. -- * @param options.openSeaEnabled - Controls whether the OpenSea API is used. -+ * @param options.displayNftMedia - Controls whether the OpenSea API is used. - * @param options.useIpfsSubdomains - Controls whether IPFS subdomains are used. - * @param options.isIpfsGatewayEnabled - Controls whether IPFS is enabled or not. - * @param options.onNftAdded - Callback that is called when an NFT is added. Currently used pass data -@@ -61,7 +61,7 @@ class NftController extends base_controller_1.BaseController { - * @param options.messenger - The controller messenger. - * @param options.state - Initial state to set on this controller. - */ -- constructor({ chainId: initialChainId, ipfsGateway = controller_utils_1.IPFS_DEFAULT_GATEWAY_URL, openSeaEnabled = false, useIpfsSubdomains = true, isIpfsGatewayEnabled = true, onNftAdded, messenger, state = {}, }) { -+ constructor({ chainId: initialChainId, ipfsGateway = controller_utils_1.IPFS_DEFAULT_GATEWAY_URL, displayNftMedia = false, useIpfsSubdomains = true, isIpfsGatewayEnabled = true, onNftAdded, messenger, state = {}, }) { - super({ - name: controllerName, - metadata: nftControllerMetadata, -@@ -76,14 +76,14 @@ class NftController extends base_controller_1.BaseController { - _NftController_selectedAccountId.set(this, void 0); - _NftController_chainId.set(this, void 0); - _NftController_ipfsGateway.set(this, void 0); -- _NftController_openSeaEnabled.set(this, void 0); -+ _NftController_displayNftMedia.set(this, void 0); - _NftController_useIpfsSubdomains.set(this, void 0); - _NftController_isIpfsGatewayEnabled.set(this, void 0); - _NftController_onNftAdded.set(this, void 0); - __classPrivateFieldSet(this, _NftController_selectedAccountId, this.messagingSystem.call('AccountsController:getSelectedAccount').id, "f"); - __classPrivateFieldSet(this, _NftController_chainId, initialChainId, "f"); - __classPrivateFieldSet(this, _NftController_ipfsGateway, ipfsGateway, "f"); -- __classPrivateFieldSet(this, _NftController_openSeaEnabled, openSeaEnabled, "f"); -+ __classPrivateFieldSet(this, _NftController_displayNftMedia, displayNftMedia, "f"); - __classPrivateFieldSet(this, _NftController_useIpfsSubdomains, useIpfsSubdomains, "f"); - __classPrivateFieldSet(this, _NftController_isIpfsGatewayEnabled, isIpfsGatewayEnabled, "f"); - __classPrivateFieldSet(this, _NftController_onNftAdded, onNftAdded, "f"); -@@ -589,7 +589,7 @@ class NftController extends base_controller_1.BaseController { - } - } - exports.NftController = NftController; --_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(), _NftController_chainId = new WeakMap(), _NftController_ipfsGateway = new WeakMap(), _NftController_openSeaEnabled = new WeakMap(), _NftController_useIpfsSubdomains = new WeakMap(), _NftController_isIpfsGatewayEnabled = new WeakMap(), _NftController_onNftAdded = new WeakMap(), _NftController_instances = new WeakSet(), _NftController_onNetworkControllerNetworkDidChange = function _NftController_onNetworkControllerNetworkDidChange({ selectedNetworkClientId, }) { -+_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(), _NftController_chainId = new WeakMap(), _NftController_ipfsGateway = new WeakMap(), _NftController_displayNftMedia = new WeakMap(), _NftController_useIpfsSubdomains = new WeakMap(), _NftController_isIpfsGatewayEnabled = new WeakMap(), _NftController_onNftAdded = new WeakMap(), _NftController_instances = new WeakSet(), _NftController_onNetworkControllerNetworkDidChange = function _NftController_onNetworkControllerNetworkDidChange({ selectedNetworkClientId, }) { - const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId); - __classPrivateFieldSet(this, _NftController_chainId, chainId, "f"); - }, _NftController_onPreferencesControllerStateChange = -@@ -597,16 +597,16 @@ _NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new Wea - * Handles the state change of the preference controller. - * @param preferencesState - The new state of the preference controller. - * @param preferencesState.ipfsGateway - The configured IPFS gateway. -- * @param preferencesState.openSeaEnabled - Controls whether the OpenSea API is used. -+ * @param preferencesState.displayNftMedia - Controls whether the OpenSea API is used. - * @param preferencesState.isIpfsGatewayEnabled - Controls whether IPFS is enabled or not. - */ --async function _NftController_onPreferencesControllerStateChange({ ipfsGateway, openSeaEnabled, isIpfsGatewayEnabled, }) { -+async function _NftController_onPreferencesControllerStateChange({ ipfsGateway, displayNftMedia, isIpfsGatewayEnabled, }) { - const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount'); - __classPrivateFieldSet(this, _NftController_selectedAccountId, selectedAccount.id, "f"); - __classPrivateFieldSet(this, _NftController_ipfsGateway, ipfsGateway, "f"); -- __classPrivateFieldSet(this, _NftController_openSeaEnabled, openSeaEnabled, "f"); -+ __classPrivateFieldSet(this, _NftController_displayNftMedia, displayNftMedia, "f"); - __classPrivateFieldSet(this, _NftController_isIpfsGatewayEnabled, isIpfsGatewayEnabled, "f"); -- const needsUpdateNftMetadata = (isIpfsGatewayEnabled && ipfsGateway !== '') || openSeaEnabled; -+ const needsUpdateNftMetadata = (isIpfsGatewayEnabled && ipfsGateway !== '') || displayNftMedia; - if (needsUpdateNftMetadata && selectedAccount) { - await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_updateNftUpdateForAccount).call(this, selectedAccount); - } -@@ -619,7 +619,7 @@ async function _NftController_onSelectedAccountChange(internalAccount) { - const oldSelectedAccountId = __classPrivateFieldGet(this, _NftController_selectedAccountId, "f"); - __classPrivateFieldSet(this, _NftController_selectedAccountId, internalAccount.id, "f"); - const needsUpdateNftMetadata = ((__classPrivateFieldGet(this, _NftController_isIpfsGatewayEnabled, "f") && __classPrivateFieldGet(this, _NftController_ipfsGateway, "f") !== '') || -- __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f")) && -+ __classPrivateFieldGet(this, _NftController_displayNftMedia, "f")) && - oldSelectedAccountId !== internalAccount.id; - if (needsUpdateNftMetadata) { - await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_updateNftUpdateForAccount).call(this, internalAccount); -@@ -694,6 +694,7 @@ async function _NftController_getNftInformationFromApi(contractAddress, tokenId) - description: null, - image: null, - standard: null, -+ error: 'Opensea import error', - }; - } - // if we've reached this point, we have successfully fetched some data for nftInformation -@@ -738,7 +739,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok - tokenURI: tokenURI ?? null, - }; - } -- const isDisplayNFTMediaToggleEnabled = __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f"); -+ const isDisplayNFTMediaToggleEnabled = __classPrivateFieldGet(this, _NftController_displayNftMedia, "f"); - if (!hasIpfsTokenURI && !isDisplayNFTMediaToggleEnabled) { - return { - image: null, -@@ -747,6 +748,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok - standard: standard || null, - favorite: false, - tokenURI: tokenURI ?? null, -+ error: 'URI import error', - }; - } - if (hasIpfsTokenURI) { -@@ -785,6 +787,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok - standard: standard || null, - favorite: false, - tokenURI: tokenURI ?? null, -+ error: 'URI import error', - }; - } - }, _NftController_getNftURIAndStandard = -@@ -840,10 +843,21 @@ async function _NftController_getNftInformation(contractAddress, tokenId, networ - }); - const [blockchainMetadata, nftApiMetadata] = await Promise.all([ - (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromTokenURI).call(this, contractAddress, tokenId, networkClientId)), -- __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f") && chainId === '0x1' -+ __classPrivateFieldGet(this, _NftController_displayNftMedia, "f") && chainId === '0x1' - ? (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromApi).call(this, contractAddress, tokenId)) - : undefined, - ]); -+ if (blockchainMetadata?.error && nftApiMetadata?.error) { -+ return { -+ image: null, -+ name: null, -+ description: null, -+ standard: blockchainMetadata.standard ?? null, -+ favorite: false, -+ tokenURI: blockchainMetadata.tokenURI ?? null, -+ error: 'Both import failed', -+ }; -+ } - return { - ...nftApiMetadata, - name: blockchainMetadata?.name ?? nftApiMetadata?.name ?? null, -@@ -985,6 +999,7 @@ async function _NftController_addIndividualNft(tokenAddress, tokenId, nftMetadat - tokenId: tokenId.toString(), - standard: nftMetadata.standard, - source, -+ tokenURI: nftMetadata.tokenURI, - }); - } - } -diff --git a/node_modules/@metamask/assets-controllers/dist/NftController.d.cts b/node_modules/@metamask/assets-controllers/dist/NftController.d.cts -index a34725f..21e9d20 100644 ---- a/node_modules/@metamask/assets-controllers/dist/NftController.d.cts -+++ b/node_modules/@metamask/assets-controllers/dist/NftController.d.cts -@@ -108,6 +108,7 @@ export type NftMetadata = { - creator?: string; - transactionId?: string; - tokenURI?: string | null; -+ error?: string; - collection?: Collection; - address?: string; - attributes?: Attributes[]; -diff --git a/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs b/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs -index 6f48d64..f9dc513 100644 ---- a/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs -+++ b/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs -@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); - }; --var _TokenBalancesController_handle, _TokenBalancesController_interval, _TokenBalancesController_tokens, _TokenBalancesController_disabled; -+var _TokenBalancesController_handle, _TokenBalancesController_interval, _TokenBalancesController_tokens, _TokenBalancesController_disabled, _TokenBalancesController_updateInProgress; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.TokenBalancesController = exports.getDefaultTokenBalancesState = void 0; - const base_controller_1 = require("@metamask/base-controller"); -@@ -60,9 +60,11 @@ class TokenBalancesController extends base_controller_1.BaseController { - _TokenBalancesController_interval.set(this, void 0); - _TokenBalancesController_tokens.set(this, void 0); - _TokenBalancesController_disabled.set(this, void 0); -+ _TokenBalancesController_updateInProgress.set(this, void 0); - __classPrivateFieldSet(this, _TokenBalancesController_disabled, disabled, "f"); - __classPrivateFieldSet(this, _TokenBalancesController_interval, interval, "f"); - __classPrivateFieldSet(this, _TokenBalancesController_tokens, tokens, "f"); -+ __classPrivateFieldSet(this, _TokenBalancesController_updateInProgress, false, "f"); - this.messagingSystem.subscribe('TokensController:stateChange', ({ tokens: newTokens, detectedTokens }) => { - __classPrivateFieldSet(this, _TokenBalancesController_tokens, [...newTokens, ...detectedTokens], "f"); - // TODO: Either fix this lint violation or explain why it's necessary to ignore. -@@ -113,21 +115,28 @@ class TokenBalancesController extends base_controller_1.BaseController { - } - const selectedInternalAccount = this.messagingSystem.call('AccountsController:getSelectedAccount'); - const newContractBalances = {}; -- for (const token of __classPrivateFieldGet(this, _TokenBalancesController_tokens, "f")) { -+ const balancePromises = __classPrivateFieldGet(this, _TokenBalancesController_tokens, "f").map((token) => { - const { address } = token; -- try { -- const balance = await this.messagingSystem.call('AssetsContractController:getERC20BalanceOf', address, selectedInternalAccount.address); -+ return this.messagingSystem.call('AssetsContractController:getERC20BalanceOf', address, selectedInternalAccount.address) -+ .then((balance) => { - newContractBalances[address] = (0, controller_utils_1.toHex)(balance); -- token.hasBalanceError = false; -- } -- catch (error) { -+ token = { -+ ...token, -+ hasBalanceError: false -+ } -+ }).catch((error) => { - newContractBalances[address] = (0, controller_utils_1.toHex)(0); -- token.hasBalanceError = true; -- } -- } -+ token = { -+ ...token, -+ hasBalanceError: true -+ } -+ }) -+ }); -+ await Promise.all(balancePromises); - this.update((state) => { - state.contractBalances = newContractBalances; - }); -+ __classPrivateFieldSet(this, _TokenBalancesController_updateInProgress, updateInProgress, "f"); - } - /** - * Reset the controller state to the default state. -@@ -139,6 +148,6 @@ class TokenBalancesController extends base_controller_1.BaseController { - } - } - exports.TokenBalancesController = TokenBalancesController; --_TokenBalancesController_handle = new WeakMap(), _TokenBalancesController_interval = new WeakMap(), _TokenBalancesController_tokens = new WeakMap(), _TokenBalancesController_disabled = new WeakMap(); -+_TokenBalancesController_handle = new WeakMap(), _TokenBalancesController_interval = new WeakMap(), _TokenBalancesController_tokens = new WeakMap(), _TokenBalancesController_disabled = new WeakMap(), _TokenBalancesController_updateInProgress = new WeakMap(); - exports.default = TokenBalancesController; - //# sourceMappingURL=TokenBalancesController.cjs.map -\ No newline at end of file -diff --git a/node_modules/@metamask/assets-controllers/dist/assetsUtil.cjs b/node_modules/@metamask/assets-controllers/dist/assetsUtil.cjs -index e90a1b6..c2e83cf 100644 ---- a/node_modules/@metamask/assets-controllers/dist/assetsUtil.cjs -+++ b/node_modules/@metamask/assets-controllers/dist/assetsUtil.cjs -@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; - }; - Object.defineProperty(exports, "__esModule", { value: true }); --exports.fetchTokenContractExchangeRates = exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.ethersBigNumberToBN = exports.addUrlProtocolPrefix = exports.getFormattedIpfsUrl = exports.getIpfsCIDv1AndPath = exports.removeIpfsProtocolPrefix = exports.isTokenListSupportedForNetwork = exports.isTokenDetectionSupportedForNetwork = exports.SupportedTokenDetectionNetworks = exports.formatIconUrlWithProxy = exports.formatAggregatorNames = exports.hasNewCollectionFields = exports.compareNftMetadata = exports.TOKEN_PRICES_BATCH_SIZE = void 0; -+exports.fetchTokenContractExchangeRates = exports.reduceInBatchesSerially = exports.divideIntoBatches = exports.ethersBigNumberToBN = exports.addUrlProtocolPrefix = exports.getFormattedIpfsUrl = exports.getIpfsCIDv1AndPath = exports.removeIpfsProtocolPrefix = exports.isTokenListSupportedForNetwork = exports.isTokenDetectionSupportedForNetwork = exports.SupportedStakedBalanceNetworks = exports.SupportedTokenDetectionNetworks = exports.formatIconUrlWithProxy = exports.formatAggregatorNames = exports.hasNewCollectionFields = exports.compareNftMetadata = exports.TOKEN_PRICES_BATCH_SIZE = void 0; - const controller_utils_1 = require("@metamask/controller-utils"); - const utils_1 = require("@metamask/utils"); - const bn_js_1 = __importDefault(require("bn.js")); -@@ -168,6 +168,18 @@ var SupportedTokenDetectionNetworks; - // eslint-disable-next-line @typescript-eslint/naming-convention - SupportedTokenDetectionNetworks["moonriver"] = "0x505"; - })(SupportedTokenDetectionNetworks || (exports.SupportedTokenDetectionNetworks = SupportedTokenDetectionNetworks = {})); -+/** -+ * Networks where staked balance is supported - Values are in hex format -+ */ -+var SupportedStakedBalanceNetworks; -+(function (SupportedStakedBalanceNetworks) { -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/naming-convention -+ SupportedStakedBalanceNetworks["mainnet"] = "0x1"; -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/naming-convention -+ SupportedStakedBalanceNetworks["holesky"] = "0x4268"; -+})(SupportedStakedBalanceNetworks || (exports.SupportedStakedBalanceNetworks = SupportedStakedBalanceNetworks = {})); - /** - * Check if token detection is enabled for certain networks. - * -diff --git a/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.cts b/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.cts -index 4007a3b..cca8a6b 100644 ---- a/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.cts -+++ b/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.cts -@@ -68,6 +68,13 @@ export declare enum SupportedTokenDetectionNetworks { - moonbeam = "0x504", - moonriver = "0x505" - } -+/** -+ * Networks where staked balance is supported - Values are in hex format -+ */ -+export declare enum SupportedStakedBalanceNetworks { -+ mainnet = "0x1", -+ holesky = "0x4268" -+} - /** - * Check if token detection is enabled for certain networks. - * -diff --git a/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.mts b/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.mts -index e4d7d0a..0674deb 100644 ---- a/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.mts -+++ b/node_modules/@metamask/assets-controllers/dist/assetsUtil.d.mts -@@ -68,6 +68,13 @@ export declare enum SupportedTokenDetectionNetworks { - moonbeam = "0x504", - moonriver = "0x505" - } -+/** -+ * Networks where staked balance is supported - Values are in hex format -+ */ -+export declare enum SupportedStakedBalanceNetworks { -+ mainnet = "0x1", -+ holesky = "0x4268" -+} - /** - * Check if token detection is enabled for certain networks. - * -diff --git a/node_modules/@metamask/assets-controllers/dist/assetsUtil.mjs b/node_modules/@metamask/assets-controllers/dist/assetsUtil.mjs -index a53a5f0..1ae8b32 100644 ---- a/node_modules/@metamask/assets-controllers/dist/assetsUtil.mjs -+++ b/node_modules/@metamask/assets-controllers/dist/assetsUtil.mjs -@@ -165,6 +165,18 @@ export var SupportedTokenDetectionNetworks; - // eslint-disable-next-line @typescript-eslint/naming-convention - SupportedTokenDetectionNetworks["moonriver"] = "0x505"; - })(SupportedTokenDetectionNetworks || (SupportedTokenDetectionNetworks = {})); -+/** -+ * Networks where staked balance is supported - Values are in hex format -+ */ -+export var SupportedStakedBalanceNetworks; -+(function (SupportedStakedBalanceNetworks) { -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/naming-convention -+ SupportedStakedBalanceNetworks["mainnet"] = "0x1"; -+ // TODO: Either fix this lint violation or explain why it's necessary to ignore. -+ // eslint-disable-next-line @typescript-eslint/naming-convention -+ SupportedStakedBalanceNetworks["holesky"] = "0x4268"; -+})(SupportedStakedBalanceNetworks || (SupportedStakedBalanceNetworks = {})); - /** - * Check if token detection is enabled for certain networks. - * diff --git a/patches/@metamask+assets-controllers+43.1.1.patch b/patches/@metamask+assets-controllers+43.1.1.patch new file mode 100644 index 00000000000..f7e2d65047e --- /dev/null +++ b/patches/@metamask+assets-controllers+43.1.1.patch @@ -0,0 +1,297 @@ +diff --git a/node_modules/@metamask/assets-controllers/dist/NftController.cjs b/node_modules/@metamask/assets-controllers/dist/NftController.cjs +index 6ccbe9c..49270d6 100644 +--- a/node_modules/@metamask/assets-controllers/dist/NftController.cjs ++++ b/node_modules/@metamask/assets-controllers/dist/NftController.cjs +@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( + var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; + }; +-var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId, _NftController_chainId, _NftController_ipfsGateway, _NftController_openSeaEnabled, _NftController_useIpfsSubdomains, _NftController_isIpfsGatewayEnabled, _NftController_onNftAdded, _NftController_onNetworkControllerNetworkDidChange, _NftController_onPreferencesControllerStateChange, _NftController_onSelectedAccountChange, _NftController_updateNestedNftState, _NftController_getNftCollectionApi, _NftController_getNftInformationFromApi, _NftController_getNftInformationFromTokenURI, _NftController_getNftURIAndStandard, _NftController_getNftInformation, _NftController_getNftContractInformationFromContract, _NftController_getNftContractInformation, _NftController_addIndividualNft, _NftController_addNftContract, _NftController_removeAndIgnoreIndividualNft, _NftController_removeIndividualNft, _NftController_removeNftContract, _NftController_validateWatchNft, _NftController_getCorrectChainId, _NftController_getAddressOrSelectedAddress, _NftController_updateNftUpdateForAccount; ++var _NftController_instances, _NftController_mutex, _NftController_selectedAccountId, _NftController_chainId, _NftController_ipfsGateway, _NftController_displayNftMedia, _NftController_useIpfsSubdomains, _NftController_isIpfsGatewayEnabled, _NftController_onNftAdded, _NftController_onNetworkControllerNetworkDidChange, _NftController_onPreferencesControllerStateChange, _NftController_onSelectedAccountChange, _NftController_updateNestedNftState, _NftController_getNftCollectionApi, _NftController_getNftInformationFromApi, _NftController_getNftInformationFromTokenURI, _NftController_getNftURIAndStandard, _NftController_getNftInformation, _NftController_getNftContractInformationFromContract, _NftController_getNftContractInformation, _NftController_addIndividualNft, _NftController_addNftContract, _NftController_removeAndIgnoreIndividualNft, _NftController_removeIndividualNft, _NftController_removeNftContract, _NftController_validateWatchNft, _NftController_getCorrectChainId, _NftController_getAddressOrSelectedAddress, _NftController_updateNftUpdateForAccount; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.NftController = exports.getDefaultNftControllerState = void 0; + const address_1 = require("@ethersproject/address"); +@@ -53,7 +53,7 @@ class NftController extends base_controller_1.BaseController { + * @param options - The controller options. + * @param options.chainId - The chain ID of the current network. + * @param options.ipfsGateway - The configured IPFS gateway. +- * @param options.openSeaEnabled - Controls whether the OpenSea API is used. ++ * @param options.displayNftMedia - Controls whether the OpenSea API is used. + * @param options.useIpfsSubdomains - Controls whether IPFS subdomains are used. + * @param options.isIpfsGatewayEnabled - Controls whether IPFS is enabled or not. + * @param options.onNftAdded - Callback that is called when an NFT is added. Currently used pass data +@@ -61,7 +61,7 @@ class NftController extends base_controller_1.BaseController { + * @param options.messenger - The controller messenger. + * @param options.state - Initial state to set on this controller. + */ +- constructor({ chainId: initialChainId, ipfsGateway = controller_utils_1.IPFS_DEFAULT_GATEWAY_URL, openSeaEnabled = false, useIpfsSubdomains = true, isIpfsGatewayEnabled = true, onNftAdded, messenger, state = {}, }) { ++ constructor({ chainId: initialChainId, ipfsGateway = controller_utils_1.IPFS_DEFAULT_GATEWAY_URL, displayNftMedia = false, useIpfsSubdomains = true, isIpfsGatewayEnabled = true, onNftAdded, messenger, state = {}, }) { + super({ + name: controllerName, + metadata: nftControllerMetadata, +@@ -76,14 +76,14 @@ class NftController extends base_controller_1.BaseController { + _NftController_selectedAccountId.set(this, void 0); + _NftController_chainId.set(this, void 0); + _NftController_ipfsGateway.set(this, void 0); +- _NftController_openSeaEnabled.set(this, void 0); ++ _NftController_displayNftMedia.set(this, void 0); + _NftController_useIpfsSubdomains.set(this, void 0); + _NftController_isIpfsGatewayEnabled.set(this, void 0); + _NftController_onNftAdded.set(this, void 0); + __classPrivateFieldSet(this, _NftController_selectedAccountId, this.messagingSystem.call('AccountsController:getSelectedAccount').id, "f"); + __classPrivateFieldSet(this, _NftController_chainId, initialChainId, "f"); + __classPrivateFieldSet(this, _NftController_ipfsGateway, ipfsGateway, "f"); +- __classPrivateFieldSet(this, _NftController_openSeaEnabled, openSeaEnabled, "f"); ++ __classPrivateFieldSet(this, _NftController_displayNftMedia, displayNftMedia, "f"); + __classPrivateFieldSet(this, _NftController_useIpfsSubdomains, useIpfsSubdomains, "f"); + __classPrivateFieldSet(this, _NftController_isIpfsGatewayEnabled, isIpfsGatewayEnabled, "f"); + __classPrivateFieldSet(this, _NftController_onNftAdded, onNftAdded, "f"); +@@ -589,7 +589,7 @@ class NftController extends base_controller_1.BaseController { + } + } + exports.NftController = NftController; +-_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(), _NftController_chainId = new WeakMap(), _NftController_ipfsGateway = new WeakMap(), _NftController_openSeaEnabled = new WeakMap(), _NftController_useIpfsSubdomains = new WeakMap(), _NftController_isIpfsGatewayEnabled = new WeakMap(), _NftController_onNftAdded = new WeakMap(), _NftController_instances = new WeakSet(), _NftController_onNetworkControllerNetworkDidChange = function _NftController_onNetworkControllerNetworkDidChange({ selectedNetworkClientId, }) { ++_NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new WeakMap(), _NftController_chainId = new WeakMap(), _NftController_ipfsGateway = new WeakMap(), _NftController_displayNftMedia = new WeakMap(), _NftController_useIpfsSubdomains = new WeakMap(), _NftController_isIpfsGatewayEnabled = new WeakMap(), _NftController_onNftAdded = new WeakMap(), _NftController_instances = new WeakSet(), _NftController_onNetworkControllerNetworkDidChange = function _NftController_onNetworkControllerNetworkDidChange({ selectedNetworkClientId, }) { + const { configuration: { chainId }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId); + __classPrivateFieldSet(this, _NftController_chainId, chainId, "f"); + }, _NftController_onPreferencesControllerStateChange = +@@ -597,16 +597,16 @@ _NftController_mutex = new WeakMap(), _NftController_selectedAccountId = new Wea + * Handles the state change of the preference controller. + * @param preferencesState - The new state of the preference controller. + * @param preferencesState.ipfsGateway - The configured IPFS gateway. +- * @param preferencesState.openSeaEnabled - Controls whether the OpenSea API is used. ++ * @param preferencesState.displayNftMedia - Controls whether the OpenSea API is used. + * @param preferencesState.isIpfsGatewayEnabled - Controls whether IPFS is enabled or not. + */ +-async function _NftController_onPreferencesControllerStateChange({ ipfsGateway, openSeaEnabled, isIpfsGatewayEnabled, }) { ++async function _NftController_onPreferencesControllerStateChange({ ipfsGateway, displayNftMedia, isIpfsGatewayEnabled, }) { + const selectedAccount = this.messagingSystem.call('AccountsController:getSelectedAccount'); + __classPrivateFieldSet(this, _NftController_selectedAccountId, selectedAccount.id, "f"); + __classPrivateFieldSet(this, _NftController_ipfsGateway, ipfsGateway, "f"); +- __classPrivateFieldSet(this, _NftController_openSeaEnabled, openSeaEnabled, "f"); ++ __classPrivateFieldSet(this, _NftController_displayNftMedia, displayNftMedia, "f"); + __classPrivateFieldSet(this, _NftController_isIpfsGatewayEnabled, isIpfsGatewayEnabled, "f"); +- const needsUpdateNftMetadata = (isIpfsGatewayEnabled && ipfsGateway !== '') || openSeaEnabled; ++ const needsUpdateNftMetadata = (isIpfsGatewayEnabled && ipfsGateway !== '') || displayNftMedia; + if (needsUpdateNftMetadata && selectedAccount) { + await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_updateNftUpdateForAccount).call(this, selectedAccount); + } +@@ -619,7 +619,7 @@ async function _NftController_onSelectedAccountChange(internalAccount) { + const oldSelectedAccountId = __classPrivateFieldGet(this, _NftController_selectedAccountId, "f"); + __classPrivateFieldSet(this, _NftController_selectedAccountId, internalAccount.id, "f"); + const needsUpdateNftMetadata = ((__classPrivateFieldGet(this, _NftController_isIpfsGatewayEnabled, "f") && __classPrivateFieldGet(this, _NftController_ipfsGateway, "f") !== '') || +- __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f")) && ++ __classPrivateFieldGet(this, _NftController_displayNftMedia, "f")) && + oldSelectedAccountId !== internalAccount.id; + if (needsUpdateNftMetadata) { + await __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_updateNftUpdateForAccount).call(this, internalAccount); +@@ -694,6 +694,7 @@ async function _NftController_getNftInformationFromApi(contractAddress, tokenId) + description: null, + image: null, + standard: null, ++ error: 'Opensea import error', + }; + } + // if we've reached this point, we have successfully fetched some data for nftInformation +@@ -738,7 +739,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok + tokenURI: tokenURI ?? null, + }; + } +- const isDisplayNFTMediaToggleEnabled = __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f"); ++ const isDisplayNFTMediaToggleEnabled = __classPrivateFieldGet(this, _NftController_displayNftMedia, "f"); + if (!hasIpfsTokenURI && !isDisplayNFTMediaToggleEnabled) { + return { + image: null, +@@ -747,6 +748,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok + standard: standard || null, + favorite: false, + tokenURI: tokenURI ?? null, ++ error: 'URI import error', + }; + } + if (hasIpfsTokenURI) { +@@ -785,6 +787,7 @@ async function _NftController_getNftInformationFromTokenURI(contractAddress, tok + standard: standard || null, + favorite: false, + tokenURI: tokenURI ?? null, ++ error: 'URI import error', + }; + } + }, _NftController_getNftURIAndStandard = +@@ -840,10 +843,21 @@ async function _NftController_getNftInformation(contractAddress, tokenId, networ + }); + const [blockchainMetadata, nftApiMetadata] = await Promise.all([ + (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromTokenURI).call(this, contractAddress, tokenId, networkClientId)), +- __classPrivateFieldGet(this, _NftController_openSeaEnabled, "f") && chainId === '0x1' ++ __classPrivateFieldGet(this, _NftController_displayNftMedia, "f") && chainId === '0x1' + ? (0, controller_utils_1.safelyExecute)(() => __classPrivateFieldGet(this, _NftController_instances, "m", _NftController_getNftInformationFromApi).call(this, contractAddress, tokenId)) + : undefined, + ]); ++ if (blockchainMetadata?.error && nftApiMetadata?.error) { ++ return { ++ image: null, ++ name: null, ++ description: null, ++ standard: blockchainMetadata.standard ?? null, ++ favorite: false, ++ tokenURI: blockchainMetadata.tokenURI ?? null, ++ error: 'Both import failed', ++ }; ++ } + return { + ...nftApiMetadata, + name: blockchainMetadata?.name ?? nftApiMetadata?.name ?? null, +@@ -985,6 +999,7 @@ async function _NftController_addIndividualNft(tokenAddress, tokenId, nftMetadat + tokenId: tokenId.toString(), + standard: nftMetadata.standard, + source, ++ tokenURI: nftMetadata.tokenURI, + }); + } + } +diff --git a/node_modules/@metamask/assets-controllers/dist/NftController.d.cts b/node_modules/@metamask/assets-controllers/dist/NftController.d.cts +index a34725f..21e9d20 100644 +--- a/node_modules/@metamask/assets-controllers/dist/NftController.d.cts ++++ b/node_modules/@metamask/assets-controllers/dist/NftController.d.cts +@@ -108,6 +108,7 @@ export type NftMetadata = { + creator?: string; + transactionId?: string; + tokenURI?: string | null; ++ error?: string; + collection?: Collection; + address?: string; + attributes?: Attributes[]; +diff --git a/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs b/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs +index 6f48d64..f9dc513 100644 +--- a/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs ++++ b/node_modules/@metamask/assets-controllers/dist/TokenBalancesController.cjs +@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function ( + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); + }; +-var _TokenBalancesController_handle, _TokenBalancesController_interval, _TokenBalancesController_tokens, _TokenBalancesController_disabled; ++var _TokenBalancesController_handle, _TokenBalancesController_interval, _TokenBalancesController_tokens, _TokenBalancesController_disabled, _TokenBalancesController_updateInProgress; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.TokenBalancesController = exports.getDefaultTokenBalancesState = void 0; + const base_controller_1 = require("@metamask/base-controller"); +@@ -60,9 +60,11 @@ class TokenBalancesController extends base_controller_1.BaseController { + _TokenBalancesController_interval.set(this, void 0); + _TokenBalancesController_tokens.set(this, void 0); + _TokenBalancesController_disabled.set(this, void 0); ++ _TokenBalancesController_updateInProgress.set(this, void 0); + __classPrivateFieldSet(this, _TokenBalancesController_disabled, disabled, "f"); + __classPrivateFieldSet(this, _TokenBalancesController_interval, interval, "f"); + __classPrivateFieldSet(this, _TokenBalancesController_tokens, tokens, "f"); ++ __classPrivateFieldSet(this, _TokenBalancesController_updateInProgress, false, "f"); + this.messagingSystem.subscribe('TokensController:stateChange', ({ tokens: newTokens, detectedTokens }) => { + __classPrivateFieldSet(this, _TokenBalancesController_tokens, [...newTokens, ...detectedTokens], "f"); + // TODO: Either fix this lint violation or explain why it's necessary to ignore. +@@ -113,21 +115,28 @@ class TokenBalancesController extends base_controller_1.BaseController { + } + const selectedInternalAccount = this.messagingSystem.call('AccountsController:getSelectedAccount'); + const newContractBalances = {}; +- for (const token of __classPrivateFieldGet(this, _TokenBalancesController_tokens, "f")) { ++ const balancePromises = __classPrivateFieldGet(this, _TokenBalancesController_tokens, "f").map((token) => { + const { address } = token; +- try { +- const balance = await this.messagingSystem.call('AssetsContractController:getERC20BalanceOf', address, selectedInternalAccount.address); ++ return this.messagingSystem.call('AssetsContractController:getERC20BalanceOf', address, selectedInternalAccount.address) ++ .then((balance) => { + newContractBalances[address] = (0, controller_utils_1.toHex)(balance); +- token.hasBalanceError = false; +- } +- catch (error) { ++ token = { ++ ...token, ++ hasBalanceError: false ++ } ++ }).catch((error) => { + newContractBalances[address] = (0, controller_utils_1.toHex)(0); +- token.hasBalanceError = true; +- } +- } ++ token = { ++ ...token, ++ hasBalanceError: true ++ } ++ }) ++ }); ++ await Promise.all(balancePromises); + this.update((state) => { + state.contractBalances = newContractBalances; + }); ++ __classPrivateFieldSet(this, _TokenBalancesController_updateInProgress, updateInProgress, "f"); + } + /** + * Reset the controller state to the default state. +@@ -139,6 +148,6 @@ class TokenBalancesController extends base_controller_1.BaseController { + } + } + exports.TokenBalancesController = TokenBalancesController; +-_TokenBalancesController_handle = new WeakMap(), _TokenBalancesController_interval = new WeakMap(), _TokenBalancesController_tokens = new WeakMap(), _TokenBalancesController_disabled = new WeakMap(); ++_TokenBalancesController_handle = new WeakMap(), _TokenBalancesController_interval = new WeakMap(), _TokenBalancesController_tokens = new WeakMap(), _TokenBalancesController_disabled = new WeakMap(), _TokenBalancesController_updateInProgress = new WeakMap(); + exports.default = TokenBalancesController; + //# sourceMappingURL=TokenBalancesController.cjs.map +\ No newline at end of file +diff --git a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.cjs b/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.cjs +index ab23c95..8fd5efd 100644 +--- a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.cjs ++++ b/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.cjs +@@ -204,13 +204,10 @@ class TokenDetectionController extends (0, polling_controller_1.StaticIntervalPo + // Try detecting tokens via Account API first if conditions allow + if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { + const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); +- // If API succeeds and no chains are left for RPC detection, we can return early +- if (apiResult?.result === 'success' && +- chainsToDetectUsingRpc.length === 0) { +- return; ++ // If the account API call failed, have those chains fall back to RPC detection ++ if (apiResult?.result === 'failed') { ++ __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } +- // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection +- __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } + // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc + if (chainsToDetectUsingRpc.length > 0) { +@@ -446,8 +443,7 @@ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddre + const tokenBalancesByChain = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f") + .getMultiNetworksBalances(selectedAddress, chainIds, supportedNetworks) + .catch(() => null); +- if (!tokenBalancesByChain || +- Object.keys(tokenBalancesByChain).length === 0) { ++ if (tokenBalancesByChain === null) { + return { result: 'failed' }; + } + // Process each chain ID individually +diff --git a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.mjs b/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.mjs +index f75eb5c..ebc30bb 100644 +--- a/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.mjs ++++ b/node_modules/@metamask/assets-controllers/dist/TokenDetectionController.mjs +@@ -205,13 +205,10 @@ export class TokenDetectionController extends StaticIntervalPollingController() + // Try detecting tokens via Account API first if conditions allow + if (supportedNetworks && chainsToDetectUsingAccountAPI.length > 0) { + const apiResult = await __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_attemptAccountAPIDetection).call(this, chainsToDetectUsingAccountAPI, addressToDetect, supportedNetworks); +- // If API succeeds and no chains are left for RPC detection, we can return early +- if (apiResult?.result === 'success' && +- chainsToDetectUsingRpc.length === 0) { +- return; ++ // If the account API call failed, have those chains fall back to RPC detection ++ if (apiResult?.result === 'failed') { ++ __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } +- // If API fails or chainsToDetectUsingRpc still has items, add chains to RPC detection +- __classPrivateFieldGet(this, _TokenDetectionController_instances, "m", _TokenDetectionController_addChainsToRpcDetection).call(this, chainsToDetectUsingRpc, chainsToDetectUsingAccountAPI, clientNetworks); + } + // Proceed with RPC detection if there are chains remaining in chainsToDetectUsingRpc + if (chainsToDetectUsingRpc.length > 0) { +@@ -446,8 +443,7 @@ async function _TokenDetectionController_addDetectedTokensViaAPI({ selectedAddre + const tokenBalancesByChain = await __classPrivateFieldGet(this, _TokenDetectionController_accountsAPI, "f") + .getMultiNetworksBalances(selectedAddress, chainIds, supportedNetworks) + .catch(() => null); +- if (!tokenBalancesByChain || +- Object.keys(tokenBalancesByChain).length === 0) { ++ if (tokenBalancesByChain === null) { + return { result: 'failed' }; + } + // Process each chain ID individually diff --git a/yarn.lock b/yarn.lock index 1c36adc4e63..c8cf51bb284 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4411,10 +4411,10 @@ "@metamask/utils" "^9.1.0" nanoid "^3.1.31" -"@metamask/assets-controllers@^42.0.0": - version "42.0.0" - resolved "https://registry.yarnpkg.com/@metamask/assets-controllers/-/assets-controllers-42.0.0.tgz#276f856c9a9381b18d2c86102cfa69918dc2628e" - integrity sha512-M3MHtrZFTd8hacHemNirCOmO/wwRX8xTii3n8SDJ5bL5e5BsI8F5HzmOPgBVZ4kj7RDHtKX0s8Z0Yty/5aDVeQ== +"@metamask/assets-controllers@^43.1.1": + version "43.1.1" + resolved "https://registry.yarnpkg.com/@metamask/assets-controllers/-/assets-controllers-43.1.1.tgz#f9c8b97765f06653ca45271ead5b7acea2a169e4" + integrity sha512-dR5XmIGRN9nMW7gPtl83iwhFF5MQzOz+Jv1tN/e5v+uNHZ6f6aYLCvESEL9fDgGOeJn81g2P39RH/fT/QpcbRA== dependencies: "@ethereumjs/util" "^8.1.0" "@ethersproject/address" "^5.7.0" @@ -4424,7 +4424,7 @@ "@metamask/abi-utils" "^2.0.3" "@metamask/base-controller" "^7.0.2" "@metamask/contract-metadata" "^2.4.0" - "@metamask/controller-utils" "^11.4.2" + "@metamask/controller-utils" "^11.4.3" "@metamask/eth-query" "^4.0.0" "@metamask/metamask-eth-abis" "^3.1.1" "@metamask/polling-controller" "^12.0.1" @@ -4522,7 +4522,7 @@ eth-ens-namehash "^2.0.8" fast-deep-equal "^3.1.3" -"@metamask/controller-utils@^11.0.0", "@metamask/controller-utils@^11.0.2", "@metamask/controller-utils@^11.3.0", "@metamask/controller-utils@^11.4.1", "@metamask/controller-utils@^11.4.2": +"@metamask/controller-utils@^11.0.0", "@metamask/controller-utils@^11.0.2", "@metamask/controller-utils@^11.3.0", "@metamask/controller-utils@^11.4.1", "@metamask/controller-utils@^11.4.2", "@metamask/controller-utils@^11.4.3": version "11.4.3" resolved "https://registry.yarnpkg.com/@metamask/controller-utils/-/controller-utils-11.4.3.tgz#5763f0bbee2f3770c1ba42dd4869786afef849bd" integrity sha512-shrVCHFwIbt8qVcKbxe/mp5tOxjz6905/7ZIAnwUJKHYv7iEqfjyO1ibPoOknrZCF2vbXtP21b435g3v9DBNTQ==