From 193da7660527930131cd012294455606f31e958e Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 26 Nov 2024 14:35:05 -0600 Subject: [PATCH 01/22] fix: Add metric trait for token network filter preference (#28336) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds a user trait for network filter preference for PortfolioView™ [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28336?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Open debugger 2. Put a breakpoint in app/scripts/controllers/metametrics.ts's _buildUserTraitsObject function 3. See value being passed ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Nick Gambino <35090461+gambinish@users.noreply.github.com> Co-authored-by: Nicholas Gambino --- .../controllers/metametrics-controller.test.ts | 11 ++++++----- app/scripts/controllers/metametrics-controller.ts | 4 ++++ shared/constants/metametrics.ts | 4 ++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/metametrics-controller.test.ts b/app/scripts/controllers/metametrics-controller.test.ts index 58fad403fdab..99ed4d2e2e48 100644 --- a/app/scripts/controllers/metametrics-controller.test.ts +++ b/app/scripts/controllers/metametrics-controller.test.ts @@ -1453,7 +1453,7 @@ describe('MetaMetricsController', function () { participateInMetaMetrics: true, currentCurrency: 'usd', dataCollectionForMarketing: false, - preferences: { privacyMode: true }, + preferences: { privacyMode: true, tokenNetworkFilter: [] }, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) custodyAccountDetails: {}, ///: END:ONLY_INCLUDE_IF @@ -1494,6 +1494,7 @@ describe('MetaMetricsController', function () { ///: END:ONLY_INCLUDE_IF [MetaMetricsUserTrait.TokenSortPreference]: 'token-sort-key', [MetaMetricsUserTrait.PrivacyModeEnabled]: true, + [MetaMetricsUserTrait.NetworkFilterPreference]: [], }); }); }); @@ -1543,7 +1544,7 @@ describe('MetaMetricsController', function () { allNfts: {}, participateInMetaMetrics: true, dataCollectionForMarketing: false, - preferences: { privacyMode: true }, + preferences: { privacyMode: true, tokenNetworkFilter: [] }, securityAlertsEnabled: true, names: { ethereumAddress: {}, @@ -1607,7 +1608,7 @@ describe('MetaMetricsController', function () { allNfts: {}, participateInMetaMetrics: true, dataCollectionForMarketing: false, - preferences: { privacyMode: true }, + preferences: { privacyMode: true, tokenNetworkFilter: [] }, securityAlertsEnabled: true, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) custodyAccountDetails: {}, @@ -1672,7 +1673,7 @@ describe('MetaMetricsController', function () { }, participateInMetaMetrics: true, dataCollectionForMarketing: false, - preferences: { privacyMode: true }, + preferences: { privacyMode: true, tokenNetworkFilter: [] }, securityAlertsEnabled: true, security_providers: ['blockaid'], currentCurrency: 'usd', @@ -1718,7 +1719,7 @@ describe('MetaMetricsController', function () { allNfts: {}, participateInMetaMetrics: true, dataCollectionForMarketing: false, - preferences: { privacyMode: true }, + preferences: { privacyMode: true, tokenNetworkFilter: [] }, names: { ethereumAddress: {}, }, diff --git a/app/scripts/controllers/metametrics-controller.ts b/app/scripts/controllers/metametrics-controller.ts index d29a2840eb27..b2b78a4e6406 100644 --- a/app/scripts/controllers/metametrics-controller.ts +++ b/app/scripts/controllers/metametrics-controller.ts @@ -166,6 +166,7 @@ export type MetaMaskState = { currentCurrency: string; preferences: { privacyMode: PreferencesControllerState['preferences']['privacyMode']; + tokenNetworkFilter: string[]; }; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) custodyAccountDetails: { @@ -1233,6 +1234,9 @@ export default class MetaMetricsController extends BaseController< metamaskState.tokenSortConfig?.key || '', [MetaMetricsUserTrait.PrivacyModeEnabled]: metamaskState.preferences.privacyMode, + [MetaMetricsUserTrait.NetworkFilterPreference]: Object.keys( + metamaskState.preferences.tokenNetworkFilter || {}, + ), }; if (!previousUserTraits) { diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 760e3c26a31f..c87af10544c3 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -594,6 +594,10 @@ export enum MetaMetricsUserTrait { * Identifies if the Privacy Mode is enabled */ PrivacyModeEnabled = 'privacy_mode_toggle', + /** + * Identified when the user prefers to see all tokens or current network tokens in wallet list + */ + NetworkFilterPreference = 'selected_network_filter', } /** From 4e1d3132511c65fe0bbc7540a68143b6fbdb21d8 Mon Sep 17 00:00:00 2001 From: Jony Bursztyn Date: Tue, 26 Nov 2024 20:37:28 +0000 Subject: [PATCH 02/22] feat: add e2e tests for multichain (#28708) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR adds E2E tests for multichain [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28708?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../page-objects/pages/account-list-page.ts | 20 ++ test/e2e/page-objects/pages/asset-list.ts | 172 +++++++++++++++ test/e2e/page-objects/pages/homepage.ts | 15 +- .../pages/send/send-token-page.ts | 41 ++++ .../pages/settings/settings-page.ts | 29 +++ .../multichain/aggregated-balances.spec.ts | 137 ++++++++++++ test/e2e/tests/multichain/asset-list.spec.ts | 205 ++++++++++++++++++ test/e2e/webdriver/driver.js | 23 ++ 8 files changed, 639 insertions(+), 3 deletions(-) create mode 100644 test/e2e/page-objects/pages/asset-list.ts create mode 100644 test/e2e/tests/multichain/aggregated-balances.spec.ts create mode 100644 test/e2e/tests/multichain/asset-list.spec.ts diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts index 9f96d70f4972..5660dc616279 100644 --- a/test/e2e/page-objects/pages/account-list-page.ts +++ b/test/e2e/page-objects/pages/account-list-page.ts @@ -11,6 +11,9 @@ class AccountListPage { private readonly accountListBalance = '[data-testid="second-currency-display"]'; + private readonly accountValueAndSuffix = + '[data-testid="account-value-and-suffix"]'; + private readonly accountListItem = '.multichain-account-menu-popover__list--menu-item'; @@ -345,6 +348,23 @@ class AccountListPage { ); } + /** + * Checks that the account value and suffix is displayed in the account list. + * + * @param expectedValueAndSuffix - The expected value and suffix to check. + */ + async check_accountValueAndSuffixDisplayed( + expectedValueAndSuffix: string, + ): Promise { + console.log( + `Check that account value and suffix ${expectedValueAndSuffix} is displayed in account list`, + ); + await this.driver.waitForSelector({ + css: this.accountValueAndSuffix, + text: expectedValueAndSuffix, + }); + } + async openAccountOptionsMenu(): Promise { console.log(`Open account option menu`); await this.driver.waitForSelector(this.accountListItem); diff --git a/test/e2e/page-objects/pages/asset-list.ts b/test/e2e/page-objects/pages/asset-list.ts new file mode 100644 index 000000000000..6d6d8af391bd --- /dev/null +++ b/test/e2e/page-objects/pages/asset-list.ts @@ -0,0 +1,172 @@ +import { Driver } from '../../webdriver/driver'; + +class AssetListPage { + private readonly driver: Driver; + + private readonly allNetworksOption = + '[data-testid="network-filter-all__button"]'; + + private readonly currentNetworkOption = + '[data-testid="network-filter-current__button"]'; + + private readonly networksToggle = '[data-testid="sort-by-networks"]'; + + private readonly allNetworksTotal = + '[data-testid="network-filter-all__total"]'; + + private readonly currentNetworksTotal = `${this.currentNetworkOption} [data-testid="account-value-and-suffix"]`; + + constructor(driver: Driver) { + this.driver = driver; + } + + async checkNetworkFilterText(expectedText: string): Promise { + console.log( + `Verify the displayed account label in header is: ${expectedText}`, + ); + await this.driver.waitForSelector({ + css: this.networksToggle, + text: expectedText, + }); + } + + async openNetworksFilter(): Promise { + console.log(`Opening the network filter`); + await this.driver.clickElement(this.networksToggle); + await this.driver.waitUntil( + async () => { + return await this.driver.findElement(this.allNetworksOption); + }, + { + timeout: 5000, + interval: 100, + }, + ); + } + + async getNetworksFilterLabel(): Promise { + console.log(`Retrieving the network filter label`); + const toggle = await this.driver.findElement(this.networksToggle); + const text = await toggle.getText(); + return text; + } + + async clickCurrentNetworkOption(): Promise { + console.log(`Clicking on the current network option`); + await this.driver.clickElement(this.currentNetworkOption); + + await this.driver.waitUntil( + async () => { + const label = await this.getNetworksFilterLabel(); + return label !== 'All networks'; + }, + { timeout: 5000, interval: 100 }, + ); + } + + async waitUntilAssetListHasItems(expectedItemsCount: number): Promise { + console.log(`Waiting until the asset list has ${expectedItemsCount} items`); + await this.driver.waitUntil( + async () => { + const items = await this.getNumberOfAssets(); + return items === expectedItemsCount; + }, + { timeout: 5000, interval: 100 }, + ); + } + + async waitUntilFilterLabelIs(label: string): Promise { + console.log(`Waiting until the filter label is ${label}`); + await this.driver.waitUntil( + async () => { + const currentLabel = await this.getNetworksFilterLabel(); + return currentLabel === label; + }, + { timeout: 5000, interval: 100 }, + ); + } + + async getAllNetworksOptionTotal(): Promise { + console.log(`Retrieving the "All networks" option fiat value`); + const allNetworksValueElement = await this.driver.findElement( + this.allNetworksTotal, + ); + const value = await allNetworksValueElement.getText(); + return value; + } + + async clickOnAsset(assetName: string): Promise { + const buttons = await this.driver.findElements( + '[data-testid="multichain-token-list-button"]', + ); + + for (const button of buttons) { + const text = await button.getText(); + if (text.includes(assetName)) { + await button.click(); + return; + } + } + + throw new Error(`${assetName} button not found`); + } + + async getCurrentNetworksOptionTotal(): Promise { + console.log(`Retrieving the "Current network" option fiat value`); + const allNetworksValueElement = await this.driver.findElement( + this.currentNetworksTotal, + ); + const value = await allNetworksValueElement.getText(); + return value; + } + + async selectNetworkFilterAllNetworks(): Promise { + console.log(`Selecting "All networks" from the network filter`); + await this.driver.clickElement(this.allNetworksOption); + + await this.driver.waitUntil( + async () => { + const label = await this.getNetworksFilterLabel(); + return label === 'All networks'; + }, + { timeout: 5000, interval: 100 }, + ); + } + + async selectNetworkFilterCurrentNetwork(): Promise { + console.log(`Selecting "Current network" from the network filter`); + await this.driver.clickElement(this.currentNetworkOption); + + await this.driver.waitUntil( + async () => { + const label = await this.getNetworksFilterLabel(); + return label !== 'All networks'; + }, + { timeout: 5000, interval: 100 }, + ); + } + + async getNumberOfAssets(): Promise { + console.log(`Returning the total number of asset items in the token list`); + const assets = await this.driver.findElements( + '.multichain-token-list-item', + ); + return assets.length; + } + + // Added method to check if an asset is visible + async isAssetVisible(assetName: string): Promise { + const assets = await this.driver.findElements( + '[data-testid="multichain-token-list-button"]', + ); + for (const asset of assets) { + const text = await asset.getText(); + if (text.includes(assetName)) { + return true; + } + } + return false; + } +} + +export default AssetListPage; diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts index c5c4d5369d4e..889ab344d91a 100644 --- a/test/e2e/page-objects/pages/homepage.ts +++ b/test/e2e/page-objects/pages/homepage.ts @@ -61,6 +61,8 @@ class HomePage { private readonly nftTab = '[data-testid="account-overview__nfts-tab"]'; + private readonly popoverCloseButton = '[data-testid="popover-close"]'; + private readonly successImportNftMessage = { text: 'NFT was successfully added!', tag: 'h6', @@ -85,6 +87,11 @@ class HomePage { console.log('Home page is loaded'); } + async closePopover(): Promise { + console.log('Closing popover'); + await this.driver.clickElement(this.popoverCloseButton); + } + async closeUseNetworkNotificationModal(): Promise { // We need to use clickElementSafe + assertElementNotPresent as sometimes the network dialog doesn't appear, as per this issue (#25788) // TODO: change the 2 actions for clickElementAndWaitToDisappear, once the issue is fixed @@ -240,24 +247,26 @@ class HomePage { * Checks if the expected balance is displayed on homepage. * * @param expectedBalance - The expected balance to be displayed. Defaults to '0'. + * @param symbol - The symbol of the currency or token. Defaults to 'ETH'. */ async check_expectedBalanceIsDisplayed( expectedBalance: string = '0', + symbol: string = 'ETH', ): Promise { try { await this.driver.waitForSelector({ css: this.balance, - text: `${expectedBalance} ETH`, + text: expectedBalance, }); } catch (e) { const balance = await this.driver.waitForSelector(this.balance); const currentBalance = parseFloat(await balance.getText()); - const errorMessage = `Expected balance ${expectedBalance} ETH, got balance ${currentBalance} ETH`; + const errorMessage = `Expected balance ${expectedBalance} ${symbol}, got balance ${currentBalance} ${symbol}`; console.log(errorMessage, e); throw e; } console.log( - `Expected balance ${expectedBalance} ETH is displayed on homepage`, + `Expected balance ${expectedBalance} ${symbol} is displayed on homepage`, ); } diff --git a/test/e2e/page-objects/pages/send/send-token-page.ts b/test/e2e/page-objects/pages/send/send-token-page.ts index 29222ed3d48c..3c1d96618556 100644 --- a/test/e2e/page-objects/pages/send/send-token-page.ts +++ b/test/e2e/page-objects/pages/send/send-token-page.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { WebElement } from 'selenium-webdriver'; import { Driver } from '../../../webdriver/driver'; class SendTokenPage { @@ -11,11 +12,18 @@ class SendTokenPage { tag: 'button', }; + private readonly cancelButton = { + text: 'Cancel', + tag: 'button', + }; + private readonly ensAddressAsRecipient = '[data-testid="ens-input-selected"]'; private readonly ensResolvedName = '[data-testid="multichain-send-page__recipient__item__title"]'; + private readonly assetValue = '[data-testid="account-value-and-suffix"]'; + private readonly inputAmount = '[data-testid="currency-input"]'; private readonly inputNFTAmount = '[data-testid="nft-input"]'; @@ -30,10 +38,17 @@ class SendTokenPage { private readonly tokenListButton = '[data-testid="multichain-token-list-button"]'; + private readonly toastText = '.toast-text'; + constructor(driver: Driver) { this.driver = driver; } + async getAssetPickerItems(): Promise { + console.log('Retrieving asset picker items'); + return this.driver.findElements(this.tokenListButton); + } + async check_pageIsLoaded(): Promise { try { await this.driver.waitForMultipleSelectors([ @@ -59,6 +74,22 @@ class SendTokenPage { await elements[1].click(); } + async checkAccountValueAndSuffix(value: string): Promise { + console.log(`Checking if account value and suffix is ${value}`); + const element = await this.driver.waitForSelector(this.assetValue); + const text = await element.getText(); + assert.equal( + text, + value, + `Expected account value and suffix to be ${value}, got ${text}`, + ); + console.log(`Account value and suffix is ${value}`); + } + + async clickCancelButton(): Promise { + await this.driver.clickElement(this.cancelButton); + } + async fillAmount(amount: string): Promise { console.log(`Fill amount input with ${amount} on send token screen`); const inputAmount = await this.driver.waitForSelector(this.inputAmount); @@ -74,6 +105,16 @@ class SendTokenPage { ); } + async check_networkChange(networkName: string): Promise { + const toastTextElement = await this.driver.findElement(this.toastText); + const toastText = await toastTextElement.getText(); + assert.equal( + toastText, + `You're now using ${networkName}`, + 'Toast text is correct', + ); + } + async fillNFTAmount(amount: string) { await this.driver.pasteIntoField(this.inputNFTAmount, amount); } diff --git a/test/e2e/page-objects/pages/settings/settings-page.ts b/test/e2e/page-objects/pages/settings/settings-page.ts index d103aca83f97..4444556a1b74 100644 --- a/test/e2e/page-objects/pages/settings/settings-page.ts +++ b/test/e2e/page-objects/pages/settings/settings-page.ts @@ -40,6 +40,35 @@ class SettingsPage { console.log('Settings page is loaded'); } + async clickAdvancedTab(): Promise { + console.log('Clicking on Advanced tab'); + await this.driver.clickElement({ + css: '.tab-bar__tab__content__title', + text: 'Advanced', + }); + } + + async toggleShowFiatOnTestnets(): Promise { + console.log('Toggling Show Fiat on Testnets setting'); + await this.driver.clickElement( + '.toggle-button.show-fiat-on-testnets-toggle', + ); + } + + async toggleBalanceSetting(): Promise { + console.log('Toggling balance setting'); + await this.driver.clickElement( + '.toggle-button.show-native-token-as-main-balance', + ); + } + + async exitSettings(): Promise { + console.log('Exiting settings page'); + await this.driver.clickElement( + '.settings-page__header__title-container__close-button', + ); + } + async closeSettingsPage(): Promise { console.log('Closing Settings page'); await this.driver.clickElement(this.closeSettingsPageButton); diff --git a/test/e2e/tests/multichain/aggregated-balances.spec.ts b/test/e2e/tests/multichain/aggregated-balances.spec.ts new file mode 100644 index 000000000000..57df2362b236 --- /dev/null +++ b/test/e2e/tests/multichain/aggregated-balances.spec.ts @@ -0,0 +1,137 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import { Driver } from '../../webdriver/driver'; +import { withFixtures, defaultGanacheOptions } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { Ganache } from '../../seeder/ganache'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; +import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; +import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import SelectNetwork from '../../page-objects/pages/dialog/select-network'; +import HomePage from '../../page-objects/pages/homepage'; +import SettingsPage from '../../page-objects/pages/settings/settings-page'; +import AccountListPage from '../../page-objects/pages/account-list-page'; +import AssetListPage from '../../page-objects/pages/asset-list'; +import SendTokenPage from '../../page-objects/pages/send/send-token-page'; + +const EXPECTED_MAINNET_BALANCE_USD = '$84,985.04'; +const EXPECTED_CURRENT_NETWORK_BALANCE_USD = '$42,492.52'; +const EXPECTED_SEPOLIA_BALANCE_NATIVE = '24.9956'; +const NETWORK_NAME_MAINNET = 'Ethereum Mainnet'; +const NETWORK_NAME_SEPOLIA = 'Sepolia'; +const SEPOLIA_NATIVE_TOKEN = 'SepoliaETH'; + +describe('Multichain Aggregated Balances', function (this: Suite) { + if (!process.env.PORTFOLIO_VIEW) { + return; + } + + it('shows correct aggregated balance when "Current Network" is selected', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withTokensControllerERC20() + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract: SMART_CONTRACTS.HST, + title: this.test?.fullTitle(), + }, + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + // Step 1: Log in and set up page objects + await loginWithBalanceValidation(driver, ganacheServer); + + const homepage = new HomePage(driver); + const headerNavbar = new HeaderNavbar(driver); + const selectNetworkDialog = new SelectNetwork(driver); + const settingsPage = new SettingsPage(driver); + const accountListPage = new AccountListPage(driver); + const assetListPage = new AssetListPage(driver); + const sendTokenPage = new SendTokenPage(driver); + + // Step 2: Switch to Ethereum Mainnet + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(NETWORK_NAME_MAINNET); + + // Step 3: Enable fiat balance display in settings + await headerNavbar.openSettingsPage(); + await settingsPage.toggleBalanceSetting(); + await settingsPage.exitSettings(); + + // Step 4: Verify main balance on homepage and account menu + await homepage.check_expectedBalanceIsDisplayed( + EXPECTED_MAINNET_BALANCE_USD, + 'usd', + ); + await headerNavbar.openAccountMenu(); + await accountListPage.check_accountValueAndSuffixDisplayed( + EXPECTED_MAINNET_BALANCE_USD, + ); + await accountListPage.closeAccountModal(); + + // Step 5: Verify balance in send flow + await homepage.closePopover(); + await homepage.startSendFlow(); + await sendTokenPage.checkAccountValueAndSuffix( + EXPECTED_MAINNET_BALANCE_USD, + ); + await sendTokenPage.clickCancelButton(); + + // Step 6: Check balance for "Current Network" in network filter + await assetListPage.openNetworksFilter(); + const networkFilterTotal = + await assetListPage.getCurrentNetworksOptionTotal(); + assert.equal(networkFilterTotal, EXPECTED_CURRENT_NETWORK_BALANCE_USD); + await assetListPage.clickCurrentNetworkOption(); + + // Step 7: Verify balance after selecting "Current Network" + await homepage.check_expectedBalanceIsDisplayed( + EXPECTED_CURRENT_NETWORK_BALANCE_USD, + 'usd', + ); + await headerNavbar.openAccountMenu(); + await accountListPage.check_accountValueAndSuffixDisplayed( + EXPECTED_CURRENT_NETWORK_BALANCE_USD, + ); + await accountListPage.closeAccountModal(); + + // Step 8: Verify balance in send flow after selecting "Current Network" + await homepage.startSendFlow(); + await sendTokenPage.checkAccountValueAndSuffix( + EXPECTED_CURRENT_NETWORK_BALANCE_USD, + ); + await sendTokenPage.clickCancelButton(); + + // Step 9: Switch to Sepolia test network + await headerNavbar.clickSwitchNetworkDropDown(); + await driver.clickElement('.toggle-button'); + await driver.clickElement({ text: NETWORK_NAME_SEPOLIA, tag: 'p' }); + + // Step 10: Verify native balance on Sepolia network + await homepage.check_expectedBalanceIsDisplayed( + EXPECTED_SEPOLIA_BALANCE_NATIVE, + SEPOLIA_NATIVE_TOKEN, + ); + await assetListPage.checkNetworkFilterText(NETWORK_NAME_SEPOLIA); + + // Step 11: Enable fiat display on testnets in settings + await headerNavbar.openSettingsPage(); + await settingsPage.clickAdvancedTab(); + await settingsPage.toggleShowFiatOnTestnets(); + await settingsPage.closeSettingsPage(); + + // Step 12: Verify USD balance on Sepolia network + await homepage.check_expectedBalanceIsDisplayed( + EXPECTED_CURRENT_NETWORK_BALANCE_USD, + 'usd', + ); + }, + ); + }); +}); diff --git a/test/e2e/tests/multichain/asset-list.spec.ts b/test/e2e/tests/multichain/asset-list.spec.ts new file mode 100644 index 000000000000..5b210730ef36 --- /dev/null +++ b/test/e2e/tests/multichain/asset-list.spec.ts @@ -0,0 +1,205 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import { Driver } from '../../webdriver/driver'; +import { withFixtures, defaultGanacheOptions } from '../../helpers'; +import { Ganache } from '../../seeder/ganache'; +import FixtureBuilder from '../../fixture-builder'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; +import HeaderNavbar from '../../page-objects/pages/header-navbar'; +import SelectNetwork from '../../page-objects/pages/dialog/select-network'; +import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; +import SendTokenPage from '../../page-objects/pages/send/send-token-page'; +import AssetListPage from '../../page-objects/pages/asset-list'; + +const NETWORK_NAME_MAINNET = 'Ethereum Mainnet'; +const LINEA_NAME_MAINNET = 'Linea Mainnet'; +const LOCALHOST = 'Localhost 8545'; +const BALANCE_AMOUNT = '24.9956'; + +function buildFixtures(title: string) { + return { + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withTokensControllerERC20() + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract: SMART_CONTRACTS.HST, + title, + }; +} + +describe('Multichain Asset List', function (this: Suite) { + if (!process.env.PORTFOLIO_VIEW) { + return; + } + + it('persists the preferred asset list preference when changing networks', async function () { + await withFixtures( + buildFixtures(this.test?.fullTitle() as string), + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + await loginWithBalanceValidation(driver, ganacheServer); + const headerNavbar = new HeaderNavbar(driver); + const selectNetworkDialog = new SelectNetwork(driver); + const assetListPage = new AssetListPage(driver); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(NETWORK_NAME_MAINNET); + await assetListPage.waitUntilAssetListHasItems(2); + await assetListPage.openNetworksFilter(); + await assetListPage.clickCurrentNetworkOption(); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(LINEA_NAME_MAINNET); + await assetListPage.waitUntilFilterLabelIs(LINEA_NAME_MAINNET); + await assetListPage.waitUntilAssetListHasItems(1); + assert.equal( + await assetListPage.getNetworksFilterLabel(), + LINEA_NAME_MAINNET, + ); + }, + ); + }); + it('allows clicking into the asset details page of native token on another network', async function () { + await withFixtures( + buildFixtures(this.test?.fullTitle() as string), + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + await loginWithBalanceValidation(driver, ganacheServer); + const headerNavbar = new HeaderNavbar(driver); + const selectNetworkDialog = new SelectNetwork(driver); + const assetListPage = new AssetListPage(driver); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(NETWORK_NAME_MAINNET); + await assetListPage.waitUntilAssetListHasItems(2); + await driver.clickElement('.multichain-token-list-item'); + const coinOverviewElement = await driver.findElement( + '[data-testid="coin-overview-buy"]', + ); + const multichainTokenListButton = await driver.findElement( + '[data-testid="multichain-token-list-button"]', + ); + assert.ok(coinOverviewElement, 'coin-overview-buy is present'); + assert.ok( + multichainTokenListButton, + 'multichain-token-list-button is present', + ); + }, + ); + }); + it('switches networks when clicking on send for a token on another network', async function () { + await withFixtures( + buildFixtures(this.test?.fullTitle() as string), + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + await loginWithBalanceValidation(driver, ganacheServer); + const headerNavbar = new HeaderNavbar(driver); + const selectNetworkDialog = new SelectNetwork(driver); + const assetListPage = new AssetListPage(driver); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(NETWORK_NAME_MAINNET); + const sendPage = new SendTokenPage(driver); + await assetListPage.waitUntilAssetListHasItems(2); + await assetListPage.clickOnAsset('TST'); + await driver.clickElement('[data-testid="eth-overview-send"]'); + await sendPage.check_networkChange(LOCALHOST); + await sendPage.check_pageIsLoaded(); + await sendPage.fillRecipient( + '0x2f318C334780961FB129D2a6c30D0763d9a5C970', + ); + await sendPage.clickAssetPickerButton(); + const assetPickerItems = await sendPage.getAssetPickerItems(); + assert.equal( + assetPickerItems.length, + 2, + 'Two assets should be shown in the asset picker', + ); + }, + ); + }); + it('switches networks when clicking on swap for a token on another network', async function () { + await withFixtures( + buildFixtures(this.test?.fullTitle() as string), + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + await loginWithBalanceValidation(driver, ganacheServer); + const headerNavbar = new HeaderNavbar(driver); + const selectNetworkDialog = new SelectNetwork(driver); + const assetListPage = new AssetListPage(driver); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(NETWORK_NAME_MAINNET); + await assetListPage.waitUntilAssetListHasItems(2); + await assetListPage.clickOnAsset('TST'); + await driver.clickElement('.mm-box > button:nth-of-type(3)'); + const toastTextElement = await driver.findElement('.toast-text'); + const toastText = await toastTextElement.getText(); + assert.equal( + toastText, + `You're now using ${LOCALHOST}`, + 'Toast text is correct', + ); + }, + ); + }); + it('shows correct asset and balance when swapping on a different chain', async function () { + await withFixtures( + buildFixtures(this.test?.fullTitle() as string), + async ({ + driver, + ganacheServer, + }: { + driver: Driver; + ganacheServer?: Ganache; + }) => { + await loginWithBalanceValidation(driver, ganacheServer); + const headerNavbar = new HeaderNavbar(driver); + const assetListPage = new AssetListPage(driver); + const selectNetworkDialog = new SelectNetwork(driver); + await headerNavbar.clickSwitchNetworkDropDown(); + await selectNetworkDialog.selectNetworkName(LINEA_NAME_MAINNET); + await assetListPage.waitUntilAssetListHasItems(2); + + await assetListPage.clickOnAsset('Ethereum'); + + const swapButton = await driver.findElement( + '[data-testid="token-overview-button-swap"]', + ); + await swapButton.click(); + const toastTextElement = await driver.findElement('.toast-text'); + const toastText = await toastTextElement.getText(); + assert.equal( + toastText, + `You're now using ${LOCALHOST}`, + 'Toast text is correct', + ); + const balanceMessageElement = await driver.findElement( + '.prepare-swap-page__balance-message', + ); + const balanceMessage = await balanceMessageElement.getText(); + assert.equal( + balanceMessage.replace('Max', '').trim(), + `Balance: ${BALANCE_AMOUNT}`, + 'Balance message is correct', + ); + }, + ); + }); +}); diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 42fa0f018f6d..b0a335eb0d64 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -767,6 +767,29 @@ class Driver { ); } + /** + * Waits for a condition to be met within a given timeout period. + * + * @param {Function} condition - The condition to wait for. This function should return a boolean indicating whether the condition is met. + * @param {object} options - Options for the wait. + * @param {number} options.timeout - The maximum amount of time (in milliseconds) to wait for the condition to be met. + * @param {number} options.interval - The interval (in milliseconds) between checks for the condition. + * @returns {Promise} A promise that resolves when the condition is met or the timeout is reached. + * @throws {Error} Throws an error if the condition is not met within the timeout period. + */ + async waitUntil(condition, options) { + const { timeout, interval } = options; + const endTime = Date.now() + timeout; + + while (Date.now() < endTime) { + if (await condition()) { + return true; + } + await new Promise((resolve) => setTimeout(resolve, interval)); + } + throw new Error('Condition not met within timeout'); + } + /** * Checks if an element that matches the given locator is present on the page. * From 86d6c05c58af26e62826f625bd3c21ee89b5edce Mon Sep 17 00:00:00 2001 From: jiexi Date: Tue, 26 Nov 2024 12:46:15 -0800 Subject: [PATCH 03/22] feat: Bump `@metamask/permission-controller` to `^11.0.0` (#28743) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Bumps `@metamask/permission-controller` to `^11.0.0` and adopt breaking changes [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28743?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** No changes in behavior. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: MetaMask Bot --- .../permissions/caveat-mutators.js | 12 +- .../permissions/caveat-mutators.test.js | 14 +- lavamoat/browserify/beta/policy.json | 135 +----------------- lavamoat/browserify/flask/policy.json | 135 +----------------- lavamoat/browserify/main/policy.json | 135 +----------------- lavamoat/browserify/mmi/policy.json | 135 +----------------- package.json | 2 +- yarn.lock | 21 +-- 8 files changed, 43 insertions(+), 546 deletions(-) diff --git a/app/scripts/controllers/permissions/caveat-mutators.js b/app/scripts/controllers/permissions/caveat-mutators.js index 551d1f7b37b2..047341e34770 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.js +++ b/app/scripts/controllers/permissions/caveat-mutators.js @@ -33,14 +33,14 @@ function removeAccount(targetAccount, existingAccounts) { ); if (newAccounts.length === existingAccounts.length) { - return { operation: CaveatMutatorOperation.noop }; + return { operation: CaveatMutatorOperation.Noop }; } else if (newAccounts.length > 0) { return { - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: newAccounts, }; } - return { operation: CaveatMutatorOperation.revokePermission }; + return { operation: CaveatMutatorOperation.RevokePermission }; } /** @@ -60,12 +60,12 @@ function removeChainId(targetChainId, existingChainIds) { ); if (newChainIds.length === existingChainIds.length) { - return { operation: CaveatMutatorOperation.noop }; + return { operation: CaveatMutatorOperation.Noop }; } else if (newChainIds.length > 0) { return { - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: newChainIds, }; } - return { operation: CaveatMutatorOperation.revokePermission }; + return { operation: CaveatMutatorOperation.RevokePermission }; } diff --git a/app/scripts/controllers/permissions/caveat-mutators.test.js b/app/scripts/controllers/permissions/caveat-mutators.test.js index a87115dc744b..8c16924514f4 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.test.js +++ b/app/scripts/controllers/permissions/caveat-mutators.test.js @@ -14,20 +14,20 @@ describe('caveat mutators', () => { describe('removeAccount', () => { it('returns the no-op operation if the target account is not permitted', () => { expect(removeAccount(address2, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.noop, + operation: CaveatMutatorOperation.Noop, }); }); it('returns the update operation and a new value if the target account is permitted', () => { expect(removeAccount(address2, [address1, address2])).toStrictEqual({ - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: [address1], }); }); it('returns the revoke permission operation the target account is the only permitted account', () => { expect(removeAccount(address1, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); @@ -36,20 +36,20 @@ describe('caveat mutators', () => { const checksummedAddress3 = '0x95222290dd7278AA3DDd389cc1E1d165Cc4BaeE5'; expect(removeAccount(checksummedAddress3, [address3])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); describe('Multichain behaviour', () => { it('returns the no-op operation if the target account is not permitted', () => { expect(removeAccount(address2, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.noop, + operation: CaveatMutatorOperation.Noop, }); }); it('can revoke permission for non-EVM addresses', () => { expect(removeAccount(nonEvmAddress, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); @@ -57,7 +57,7 @@ describe('caveat mutators', () => { expect( removeAccount(nonEvmAddress, [address1, nonEvmAddress]), ).toStrictEqual({ - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: [address1], }); }); diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 30456a0bd61d..93308bf0af4a 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2173,82 +2173,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2822,9 +2756,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2848,21 +2782,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2920,8 +2839,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2930,26 +2849,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3000,10 +2899,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3019,26 +2918,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 30456a0bd61d..93308bf0af4a 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2173,82 +2173,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2822,9 +2756,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2848,21 +2782,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2920,8 +2839,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2930,26 +2849,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3000,10 +2899,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3019,26 +2918,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 30456a0bd61d..93308bf0af4a 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2173,82 +2173,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2822,9 +2756,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2848,21 +2782,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2920,8 +2839,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2930,26 +2849,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3000,10 +2899,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3019,26 +2918,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index a604526f155d..b135a959d359 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2265,82 +2265,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2914,9 +2848,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2940,21 +2874,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -3012,8 +2931,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -3022,26 +2941,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3092,10 +2991,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3111,26 +3010,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/package.json b/package.json index b7adb1fcdcee..df700ec1b415 100644 --- a/package.json +++ b/package.json @@ -327,7 +327,7 @@ "@metamask/notification-services-controller": "^0.14.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", - "@metamask/permission-controller": "^10.0.0", + "@metamask/permission-controller": "^11.0.0", "@metamask/permission-log-controller": "^2.0.1", "@metamask/phishing-controller": "^12.3.0", "@metamask/polling-controller": "^10.0.1", diff --git a/yarn.lock b/yarn.lock index 968594154f06..1f5433e3d780 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6107,25 +6107,6 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-controller@npm:^10.0.0": - version: 10.0.0 - resolution: "@metamask/permission-controller@npm:10.0.0" - dependencies: - "@metamask/base-controller": "npm:^6.0.0" - "@metamask/controller-utils": "npm:^11.0.0" - "@metamask/json-rpc-engine": "npm:^9.0.0" - "@metamask/rpc-errors": "npm:^6.2.1" - "@metamask/utils": "npm:^8.3.0" - "@types/deep-freeze-strict": "npm:^1.1.0" - deep-freeze-strict: "npm:^1.1.1" - immer: "npm:^9.0.6" - nanoid: "npm:^3.1.31" - peerDependencies: - "@metamask/approval-controller": ^7.0.0 - checksum: 10/0c72e205be760fc471b2a6892a9ad52d5c6a40b4cf1757464e992a5ada2dec57efbb24b09351ce8c29990b59f1d731cd2b338caaef37ce7690ea2d1919afe061 - languageName: node - linkType: hard - "@metamask/permission-controller@npm:^11.0.0, @metamask/permission-controller@npm:^11.0.3": version: 11.0.3 resolution: "@metamask/permission-controller@npm:11.0.3" @@ -26864,7 +26845,7 @@ __metadata: "@metamask/notification-services-controller": "npm:^0.14.0" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/obs-store": "npm:^9.0.0" - "@metamask/permission-controller": "npm:^10.0.0" + "@metamask/permission-controller": "npm:^11.0.0" "@metamask/permission-log-controller": "npm:^2.0.1" "@metamask/phishing-controller": "npm:^12.3.0" "@metamask/phishing-warning": "npm:^4.1.0" From 3f574c49cc976e4520cf5f35d9a3dc6d51ebd3e7 Mon Sep 17 00:00:00 2001 From: micaelae <100321200+micaelae@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:44:31 -0800 Subject: [PATCH 04/22] fix: content dialog styling is being applied to all dialogs (#28739) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Problem: A scss change for preventing modal scrolling in the bridge experience was added and got unintentionally applied to all modals. Solution: Nest the styling within the `quotes-modal` className [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28739?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/28722 ## **Manual testing steps** 1. Visually inspect Swap token picker 2. Visually inspect tx "Speed up" and "Cancel" layout ## **Screenshots/Recordings** ### **Before** See bug report screenshots ### **After** ![Screenshot 2024-11-26 at 9 48 13 AM](https://github.com/user-attachments/assets/f5bd07d5-f062-4489-8c11-5e64f88eed75) ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ui/pages/bridge/quotes/index.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/pages/bridge/quotes/index.scss b/ui/pages/bridge/quotes/index.scss index 6407309220c2..0d9eafa9ad69 100644 --- a/ui/pages/bridge/quotes/index.scss +++ b/ui/pages/bridge/quotes/index.scss @@ -63,7 +63,9 @@ } } -.mm-modal-content__dialog { - display: flex; - height: 100%; +.quotes-modal { + .mm-modal-content__dialog { + display: flex; + height: 100%; + } } From 27763f5585c272dc10be6bd0e0b8399456df3d43 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 26 Nov 2024 19:40:29 -0330 Subject: [PATCH 05/22] chore: Update `@metamask/gas-fee-controller` and peer deps (#28745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Update the `@metamask/gas-fee-controller` package to v21 to satisfy the peer dependency on `@metamask/network-controller@^21.0.0`, and update the `@metamask/user-operation-controller` to satisfy its peer dependency upon `@metamask/gas-fee-controller`. Note that an older version of `@metamask/gas-fee-controller` (v18) remains in the dependency tree, but only because it's imported by `@metamask/smart-transaction-controller` for type reasons. It has no runtime impact on the application, so the associated peer dependency warnings from this older release can be ignored. This will be eliminated soon, in an upcoming PR. The updated `@metamask/user-operation-controller` still does not have its peer dependencies satisfied, but the problems are pre-existing. The `@metamask/keyring-controller` and `@metamask/transaction-controller` packages are head of where this package expects them to be. This is not made worse by this PR though, and will be addressed in a future PR. Changelogs: - `@metamask/gas-fee-controller@21`: https://github.com/MetaMask/core/blob/main/packages/gas-fee-controller/CHANGELOG.md#2100 - One breaking change with an impact: >The inherited AbstractPollingController method startPollingByNetworkClientId has been renamed to startPolling (https://github.com/MetaMask/core/pull/4752) - `@metamask/user-operation-controller@16`: https://github.com/MetaMask/core/blob/main/packages/user-operation-controller/CHANGELOG.md#1600 - No breaking changes with an impact. It appeared at first that the `startPollingByNetworkClientId` was breaking, but the user operation controller had an override for that method so it was preserved ([see here](https://github.com/MetaMask/core/pull/4752/files#diff-335af05ece636eb593b348e369dff139dfbfea49ad4e9ba3bb47b4909aab9aefR304)) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28745?quickstart=1) ## **Related issues** Related to https://github.com/MetaMask/MetaMask-planning/issues/3568 ## **Manual testing steps** N/A ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: MetaMask Bot --- app/scripts/metamask-controller.js | 3 +- lavamoat/browserify/beta/policy.json | 69 ++++++------------ lavamoat/browserify/flask/policy.json | 69 ++++++------------ lavamoat/browserify/main/policy.json | 69 ++++++------------ lavamoat/browserify/mmi/policy.json | 69 ++++++------------ package.json | 5 +- ui/pages/bridge/index.test.tsx | 4 +- .../confirm-send-ether.test.js | 4 +- .../confirm-transaction-base.test.js | 4 +- .../confirm-transaction.transaction.test.js | 2 +- ui/pages/swaps/index.test.js | 4 +- ui/store/actions.ts | 7 +- yarn.lock | 71 +++++++++++++------ 13 files changed, 142 insertions(+), 238 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 55888558c6d6..39e34955cac2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -4146,8 +4146,7 @@ export default class MetamaskController extends EventEmitter { ), // GasFeeController - gasFeeStartPollingByNetworkClientId: - gasFeeController.startPollingByNetworkClientId.bind(gasFeeController), + gasFeeStartPolling: gasFeeController.startPolling.bind(gasFeeController), gasFeeStopPollingByPollingToken: gasFeeController.stopPollingByPollingToken.bind(gasFeeController), diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 93308bf0af4a..cf72074493e6 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1439,14 +1439,6 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1454,7 +1446,7 @@ "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, "uuid": true } @@ -2637,13 +2629,13 @@ "@ethersproject/providers": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, "@metamask/network-controller": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, @@ -2684,6 +2676,20 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-query": true, + "@metamask/smart-transactions-controller>@metamask/polling-controller": true, + "bn.js": true, + "uuid": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { "packages": { "@ethersproject/providers": true, @@ -3023,29 +3029,21 @@ "fetch": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "@metamask/user-operation-controller>@metamask/polling-controller": true, - "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, + "@metamask/utils>@metamask/superstruct": true, "bn.js": true, "lodash": true, - "superstruct": true, "uuid": true, "webpack>events": true } }, - "@metamask/user-operation-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/user-operation-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -3053,32 +3051,11 @@ "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "uuid": true } }, - "@metamask/user-operation-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/user-operation-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -5668,12 +5645,6 @@ "string.prototype.matchall>get-intrinsic": true } }, - "superstruct": { - "globals": { - "console.warn": true, - "define": true - } - }, "terser>source-map-support>buffer-from": { "packages": { "browserify>buffer": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 93308bf0af4a..cf72074493e6 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1439,14 +1439,6 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1454,7 +1446,7 @@ "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, "uuid": true } @@ -2637,13 +2629,13 @@ "@ethersproject/providers": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, "@metamask/network-controller": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, @@ -2684,6 +2676,20 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-query": true, + "@metamask/smart-transactions-controller>@metamask/polling-controller": true, + "bn.js": true, + "uuid": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { "packages": { "@ethersproject/providers": true, @@ -3023,29 +3029,21 @@ "fetch": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "@metamask/user-operation-controller>@metamask/polling-controller": true, - "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, + "@metamask/utils>@metamask/superstruct": true, "bn.js": true, "lodash": true, - "superstruct": true, "uuid": true, "webpack>events": true } }, - "@metamask/user-operation-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/user-operation-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -3053,32 +3051,11 @@ "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "uuid": true } }, - "@metamask/user-operation-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/user-operation-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -5668,12 +5645,6 @@ "string.prototype.matchall>get-intrinsic": true } }, - "superstruct": { - "globals": { - "console.warn": true, - "define": true - } - }, "terser>source-map-support>buffer-from": { "packages": { "browserify>buffer": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 93308bf0af4a..cf72074493e6 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1439,14 +1439,6 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1454,7 +1446,7 @@ "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, "uuid": true } @@ -2637,13 +2629,13 @@ "@ethersproject/providers": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, "@metamask/network-controller": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, @@ -2684,6 +2676,20 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-query": true, + "@metamask/smart-transactions-controller>@metamask/polling-controller": true, + "bn.js": true, + "uuid": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { "packages": { "@ethersproject/providers": true, @@ -3023,29 +3029,21 @@ "fetch": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "@metamask/user-operation-controller>@metamask/polling-controller": true, - "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, + "@metamask/utils>@metamask/superstruct": true, "bn.js": true, "lodash": true, - "superstruct": true, "uuid": true, "webpack>events": true } }, - "@metamask/user-operation-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/user-operation-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -3053,32 +3051,11 @@ "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "uuid": true } }, - "@metamask/user-operation-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/user-operation-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -5668,12 +5645,6 @@ "string.prototype.matchall>get-intrinsic": true } }, - "superstruct": { - "globals": { - "console.warn": true, - "define": true - } - }, "terser>source-map-support>buffer-from": { "packages": { "browserify>buffer": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index b135a959d359..e70102a63e51 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1531,14 +1531,6 @@ "uuid": true } }, - "@metamask/gas-fee-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/gas-fee-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -1546,7 +1538,7 @@ "setTimeout": true }, "packages": { - "@metamask/gas-fee-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, "uuid": true } @@ -2729,13 +2721,13 @@ "@ethersproject/providers": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, - "@metamask/gas-fee-controller": true, "@metamask/metamask-eth-abis": true, "@metamask/name-controller>async-mutex": true, "@metamask/network-controller": true, "@metamask/smart-transactions-controller>@metamask/base-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/tx": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@ethereumjs/util": true, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": true, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": true, @@ -2776,6 +2768,20 @@ "webpack>events": true } }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/gas-fee-controller": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-query": true, + "@metamask/smart-transactions-controller>@metamask/polling-controller": true, + "bn.js": true, + "uuid": true + } + }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/nonce-tracker": { "packages": { "@ethersproject/providers": true, @@ -3115,29 +3121,21 @@ "fetch": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-query": true, "@metamask/gas-fee-controller": true, + "@metamask/rpc-errors": true, "@metamask/transaction-controller": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "@metamask/user-operation-controller>@metamask/polling-controller": true, - "@metamask/user-operation-controller>@metamask/rpc-errors": true, "@metamask/user-operation-controller>@metamask/utils": true, + "@metamask/utils>@metamask/superstruct": true, "bn.js": true, "lodash": true, - "superstruct": true, "uuid": true, "webpack>events": true } }, - "@metamask/user-operation-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/user-operation-controller>@metamask/polling-controller": { "globals": { "clearTimeout": true, @@ -3145,32 +3143,11 @@ "setTimeout": true }, "packages": { + "@metamask/base-controller": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, - "@metamask/user-operation-controller>@metamask/base-controller": true, "uuid": true } }, - "@metamask/user-operation-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/user-operation-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/user-operation-controller>@metamask/utils": { "globals": { "TextDecoder": true, @@ -5736,12 +5713,6 @@ "string.prototype.matchall>get-intrinsic": true } }, - "superstruct": { - "globals": { - "console.warn": true, - "define": true - } - }, "terser>source-map-support>buffer-from": { "packages": { "browserify>buffer": true diff --git a/package.json b/package.json index df700ec1b415..5304df2c2ff8 100644 --- a/package.json +++ b/package.json @@ -241,7 +241,6 @@ "@expo/config-plugins/glob": "^10.3.10", "@solana/web3.js/rpc-websockets": "^8.0.1", "@metamask/message-manager": "^10.1.0", - "@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", "@metamask/network-controller@npm:^17.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", @@ -310,7 +309,7 @@ "@metamask/ethjs": "^0.6.0", "@metamask/ethjs-contract": "^0.4.1", "@metamask/ethjs-query": "^0.7.1", - "@metamask/gas-fee-controller": "^18.0.0", + "@metamask/gas-fee-controller": "^21.0.0", "@metamask/jazzicon": "^2.0.0", "@metamask/json-rpc-engine": "^10.0.0", "@metamask/json-rpc-middleware-stream": "^8.0.4", @@ -351,7 +350,7 @@ "@metamask/snaps-utils": "^8.6.0", "@metamask/solana-wallet-snap": "^0.1.9", "@metamask/transaction-controller": "^40.1.0", - "@metamask/user-operation-controller": "^13.0.0", + "@metamask/user-operation-controller": "^16.0.0", "@metamask/utils": "^10.0.1", "@ngraveio/bc-ur": "^1.1.12", "@noble/hashes": "^1.3.3", diff --git a/ui/pages/bridge/index.test.tsx b/ui/pages/bridge/index.test.tsx index 7d5f813513c5..1dc898d71685 100644 --- a/ui/pages/bridge/index.test.tsx +++ b/ui/pages/bridge/index.test.tsx @@ -16,9 +16,7 @@ setBackgroundConnection({ setSwapsLiveness: jest.fn(() => true), setSwapsTokens: jest.fn(), setSwapsTxGasPrice: jest.fn(), - gasFeeStartPollingByNetworkClientId: jest - .fn() - .mockResolvedValue('pollingToken'), + gasFeeStartPolling: jest.fn().mockResolvedValue('pollingToken'), gasFeeStopPollingByPollingToken: jest.fn(), getNetworkConfigurationByNetworkClientId: jest .fn() diff --git a/ui/pages/confirmations/confirm-send-ether/confirm-send-ether.test.js b/ui/pages/confirmations/confirm-send-ether/confirm-send-ether.test.js index 04caf63d12e0..e30dd925c4d3 100644 --- a/ui/pages/confirmations/confirm-send-ether/confirm-send-ether.test.js +++ b/ui/pages/confirmations/confirm-send-ether/confirm-send-ether.test.js @@ -10,9 +10,7 @@ import ConfirmSendEther from './confirm-send-ether'; jest.mock('../components/simulation-details/useSimulationMetrics'); setBackgroundConnection({ - gasFeeStartPollingByNetworkClientId: jest - .fn() - .mockResolvedValue('pollingToken'), + gasFeeStartPolling: jest.fn().mockResolvedValue('pollingToken'), gasFeeStopPollingByPollingToken: jest.fn(), getNetworkConfigurationByNetworkClientId: jest.fn().mockImplementation(() => Promise.resolve({ diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js index 5a846874fed3..56be8fd5aee3 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js @@ -31,9 +31,7 @@ jest.mock('../components/simulation-details/useSimulationMetrics'); const middleware = [thunk]; setBackgroundConnection({ - gasFeeStartPollingByNetworkClientId: jest - .fn() - .mockResolvedValue('pollingToken'), + gasFeeStartPolling: jest.fn().mockResolvedValue('pollingToken'), gasFeeStopPollingByPollingToken: jest.fn(), getNetworkConfigurationByNetworkClientId: jest.fn().mockImplementation(() => Promise.resolve({ diff --git a/ui/pages/confirmations/confirm-transaction/confirm-transaction.transaction.test.js b/ui/pages/confirmations/confirm-transaction/confirm-transaction.transaction.test.js index 09eb13c16919..a7db429a90e8 100644 --- a/ui/pages/confirmations/confirm-transaction/confirm-transaction.transaction.test.js +++ b/ui/pages/confirmations/confirm-transaction/confirm-transaction.transaction.test.js @@ -19,7 +19,7 @@ const middleware = [thunk]; setBackgroundConnection({ getGasFeeTimeEstimate: jest.fn(), - gasFeeStartPollingByNetworkClientId: jest.fn(), + gasFeeStartPolling: jest.fn(), gasFeeStopPollingByPollingToken: jest.fn(), promisifiedBackground: jest.fn(), tryReverseResolveAddress: jest.fn(), diff --git a/ui/pages/swaps/index.test.js b/ui/pages/swaps/index.test.js index 4157b614562f..e9e16d91b208 100644 --- a/ui/pages/swaps/index.test.js +++ b/ui/pages/swaps/index.test.js @@ -33,9 +33,7 @@ setBackgroundConnection({ setSwapsLiveness: jest.fn(() => true), setSwapsTokens: jest.fn(), setSwapsTxGasPrice: jest.fn(), - gasFeeStartPollingByNetworkClientId: jest - .fn() - .mockResolvedValue('pollingToken'), + gasFeeStartPolling: jest.fn().mockResolvedValue('pollingToken'), gasFeeStopPollingByPollingToken: jest.fn(), getNetworkConfigurationByNetworkClientId: jest .fn() diff --git a/ui/store/actions.ts b/ui/store/actions.ts index f3f4e712acf5..90018c4d7389 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4728,10 +4728,9 @@ export async function accountTrackerStopPollingByPollingToken( export async function gasFeeStartPollingByNetworkClientId( networkClientId: string, ) { - const pollingToken = await submitRequestToBackground( - 'gasFeeStartPollingByNetworkClientId', - [networkClientId], - ); + const pollingToken = await submitRequestToBackground('gasFeeStartPolling', [ + { networkClientId }, + ]); await addPollingTokenToAppState(pollingToken); return pollingToken; } diff --git a/yarn.lock b/yarn.lock index 1f5433e3d780..88e84e6d37b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5695,6 +5695,26 @@ __metadata: languageName: node linkType: hard +"@metamask/gas-fee-controller@npm:^21.0.0": + version: 21.0.0 + resolution: "@metamask/gas-fee-controller@npm:21.0.0" + dependencies: + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/controller-utils": "npm:^11.3.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/ethjs-unit": "npm:^0.3.0" + "@metamask/polling-controller": "npm:^11.0.0" + "@metamask/utils": "npm:^9.1.0" + "@types/bn.js": "npm:^5.1.5" + "@types/uuid": "npm:^8.3.0" + bn.js: "npm:^5.2.1" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/network-controller": ^21.0.0 + checksum: 10/8b41c7257f7dc17deb3f550cfdde0288da142d11536bb55c998bec8267fa62243e36fb6468a44224cd90ed2f49ba3ba1dbe93c2b0834a725752c5a66ae283303 + languageName: node + linkType: hard + "@metamask/jazzicon@npm:^2.0.0": version: 2.0.0 resolution: "@metamask/jazzicon@npm:2.0.0" @@ -6183,6 +6203,22 @@ __metadata: languageName: node linkType: hard +"@metamask/polling-controller@npm:^11.0.0": + version: 11.0.0 + resolution: "@metamask/polling-controller@npm:11.0.0" + dependencies: + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/controller-utils": "npm:^11.3.0" + "@metamask/utils": "npm:^9.1.0" + "@types/uuid": "npm:^8.3.0" + fast-json-stable-stringify: "npm:^2.1.0" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/network-controller": ^21.0.0 + checksum: 10/67b563a5d1ce02dc9c2db25ad4ad1fb9f75d5578cf380cce85176ff2cd136addce612c3982653254647b9d8c535374e93d96abb6e500e42076bf3a524a72e75f + languageName: node + linkType: hard + "@metamask/polling-controller@npm:^12.0.1": version: 12.0.1 resolution: "@metamask/polling-controller@npm:12.0.1" @@ -6752,33 +6788,28 @@ __metadata: languageName: node linkType: hard -"@metamask/user-operation-controller@npm:^13.0.0": - version: 13.0.0 - resolution: "@metamask/user-operation-controller@npm:13.0.0" +"@metamask/user-operation-controller@npm:^16.0.0": + version: 16.0.0 + resolution: "@metamask/user-operation-controller@npm:16.0.0" dependencies: - "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/base-controller": "npm:^6.0.0" - "@metamask/controller-utils": "npm:^11.0.0" + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/controller-utils": "npm:^11.3.0" "@metamask/eth-query": "npm:^4.0.0" - "@metamask/gas-fee-controller": "npm:^18.0.0" - "@metamask/keyring-controller": "npm:^17.1.0" - "@metamask/network-controller": "npm:^19.0.0" - "@metamask/polling-controller": "npm:^8.0.0" - "@metamask/rpc-errors": "npm:^6.2.1" - "@metamask/transaction-controller": "npm:^34.0.0" - "@metamask/utils": "npm:^8.3.0" + "@metamask/polling-controller": "npm:^11.0.0" + "@metamask/rpc-errors": "npm:^7.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.1.0" bn.js: "npm:^5.2.1" immer: "npm:^9.0.6" lodash: "npm:^4.17.21" - superstruct: "npm:^1.0.3" uuid: "npm:^8.3.2" peerDependencies: "@metamask/approval-controller": ^7.0.0 - "@metamask/gas-fee-controller": ^18.0.0 + "@metamask/gas-fee-controller": ^21.0.0 "@metamask/keyring-controller": ^17.0.0 - "@metamask/network-controller": ^19.0.0 - "@metamask/transaction-controller": ^34.0.0 - checksum: 10/600dd845dfc30ff852d766bd012ce40b4a6fb2276538d358cbe3ef1ce5815e4dba8f94e4911b7cb0506857133b185923b43af73ec39c7628eb86eedfdaf8dc59 + "@metamask/network-controller": ^21.0.0 + "@metamask/transaction-controller": ^37.0.0 + checksum: 10/36ef43910f9e94ae7823902113acdaf7d4031423930f0a35fe4dd3b948a00e8088ea590354afbdb507b32712761720727f0ee905ad6d3db83ef3f0f145b8452d languageName: node linkType: hard @@ -26828,7 +26859,7 @@ __metadata: "@metamask/ethjs-contract": "npm:^0.4.1" "@metamask/ethjs-query": "npm:^0.7.1" "@metamask/forwarder": "npm:^1.1.0" - "@metamask/gas-fee-controller": "npm:^18.0.0" + "@metamask/gas-fee-controller": "npm:^21.0.0" "@metamask/jazzicon": "npm:^2.0.0" "@metamask/json-rpc-engine": "npm:^10.0.0" "@metamask/json-rpc-middleware-stream": "npm:^8.0.4" @@ -26873,7 +26904,7 @@ __metadata: "@metamask/test-bundler": "npm:^1.0.0" "@metamask/test-dapp": "npm:8.13.0" "@metamask/transaction-controller": "npm:^40.1.0" - "@metamask/user-operation-controller": "npm:^13.0.0" + "@metamask/user-operation-controller": "npm:^16.0.0" "@metamask/utils": "npm:^10.0.1" "@ngraveio/bc-ur": "npm:^1.1.12" "@noble/hashes": "npm:^1.3.3" From f711459a0857c16b32f5382216085c69c6d99dfe Mon Sep 17 00:00:00 2001 From: legobeat <109787230+legobeat@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:32:01 +0900 Subject: [PATCH 06/22] chore: node.js 20.18 (#28058) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Bump Node.js from `20.17.0` to `20.18.0` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28058?quickstart=1) ## **Related issues** ## **Manual testing steps** ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .circleci/config.yml | 6 +++--- .nvmrc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e3ccba9005e..3178a687a617 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,13 +3,13 @@ version: 2.1 executors: node-browsers-small: docker: - - image: cimg/node:20.17-browsers + - image: cimg/node:20.18-browsers resource_class: small environment: NODE_OPTIONS: --max_old_space_size=2048 node-browsers-medium: docker: - - image: cimg/node:20.17-browsers + - image: cimg/node:20.18-browsers resource_class: medium environment: NODE_OPTIONS: --max_old_space_size=3072 @@ -21,7 +21,7 @@ executors: NODE_OPTIONS: --max_old_space_size=6144 node-browsers-medium-plus: docker: - - image: cimg/node:20.17-browsers + - image: cimg/node:20.18-browsers resource_class: medium+ environment: NODE_OPTIONS: --max_old_space_size=4096 diff --git a/.nvmrc b/.nvmrc index 8cfab175cf90..bd67975ba627 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.17 +v20.18 From b6c750d7601fc90727b7c3585626f6afe1fcda44 Mon Sep 17 00:00:00 2001 From: chloeYue <105063779+chloeYue@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:08:52 +0100 Subject: [PATCH 07/22] test: [POM] Migrate add token e2e tests to TS and Page Object Model (#28658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** - Fix flaky test add hide token, the reason of flakiness is a race condition that we assert number of listed tokens before all tokens are displayed - Migrate add token e2e tests to TS and Page Object Model [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27155?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/28681 https://github.com/MetaMask/metamask-extension/issues/28664 ## **Manual testing steps** Check code readability, make sure tests pass. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../redesign/add-token-confirmations.ts | 49 ++++ test/e2e/page-objects/pages/homepage.ts | 125 +++++++- test/e2e/page-objects/pages/test-dapp.ts | 10 - test/e2e/tests/tokens/add-hide-token.spec.js | 270 ------------------ test/e2e/tests/tokens/add-hide-token.spec.ts | 50 ++++ .../tests/tokens/add-token-using-search.ts | 69 +++++ .../tokens/watch-asset-call-add-token.ts | 109 +++++++ 7 files changed, 400 insertions(+), 282 deletions(-) create mode 100644 test/e2e/page-objects/pages/confirmations/redesign/add-token-confirmations.ts delete mode 100644 test/e2e/tests/tokens/add-hide-token.spec.js create mode 100644 test/e2e/tests/tokens/add-hide-token.spec.ts create mode 100644 test/e2e/tests/tokens/add-token-using-search.ts create mode 100644 test/e2e/tests/tokens/watch-asset-call-add-token.ts diff --git a/test/e2e/page-objects/pages/confirmations/redesign/add-token-confirmations.ts b/test/e2e/page-objects/pages/confirmations/redesign/add-token-confirmations.ts new file mode 100644 index 000000000000..fdbfbbd77ec8 --- /dev/null +++ b/test/e2e/page-objects/pages/confirmations/redesign/add-token-confirmations.ts @@ -0,0 +1,49 @@ +import { Driver } from '../../../../webdriver/driver'; + +class AddTokenConfirmation { + driver: Driver; + + private readonly addTokenConfirmationTitle = { + css: '.page-container__title', + text: 'Add suggested tokens', + }; + + private readonly confirmAddTokenButton = + '[data-testid="page-container-footer-next"]'; + + private readonly rejectAddTokenButton = + '[data-testid="page-container-footer-cancel"]'; + + constructor(driver: Driver) { + this.driver = driver; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForSelector(this.addTokenConfirmationTitle); + } catch (e) { + console.log( + 'Timeout while waiting for Add token confirmation page to be loaded', + e, + ); + throw e; + } + console.log('Add token confirmation page is loaded'); + } + + async confirmAddToken(): Promise { + console.log('Confirm add token'); + await this.driver.clickElementAndWaitForWindowToClose( + this.confirmAddTokenButton, + ); + } + + async rejectAddToken(): Promise { + console.log('Reject add token'); + await this.driver.clickElementAndWaitForWindowToClose( + this.rejectAddTokenButton, + ); + } +} + +export default AddTokenConfirmation; diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts index 889ab344d91a..3722f35f98d9 100644 --- a/test/e2e/page-objects/pages/homepage.ts +++ b/test/e2e/page-objects/pages/homepage.ts @@ -45,6 +45,44 @@ class HomePage { private readonly transactionAmountsInActivity = '[data-testid="transaction-list-item-primary-currency"]'; + // Token tab selectors + private readonly assetOptionsButton = '[data-testid="asset-options__button"]'; + + private readonly confirmImportTokenButton = + '[data-testid="import-tokens-modal-import-button"]'; + + private readonly confirmImportTokenMessage = { + text: 'Would you like to import this token?', + tag: 'p', + }; + + private readonly hideTokenButton = '[data-testid="asset-options__hide"]'; + + private readonly hideTokenConfirmationButton = + '[data-testid="hide-token-confirmation__hide"]'; + + private readonly hideTokenConfirmationModalTitle = { + text: 'Hide token', + css: '.hide-token-confirmation__title', + }; + + private readonly importTokenModalTitle = { text: 'Import tokens', tag: 'h4' }; + + private readonly importTokensButton = '[data-testid="importTokens"]'; + + private readonly importTokensNextButton = + '[data-testid="import-tokens-button-next"]'; + + private readonly tokenAmountValue = + '[data-testid="multichain-token-list-item-value"]'; + + private readonly tokenLisiItem = + '[data-testid="multichain-token-list-button"]'; + + private readonly tokenOptionsButton = '[data-testid="import-token-button"]'; + + private readonly tokenSearchInput = 'input[placeholder="Search tokens"]'; + // NFT selectors private readonly confirmImportNftButton = '[data-testid="import-nfts-modal-import-button"]'; @@ -87,6 +125,10 @@ class HomePage { console.log('Home page is loaded'); } + async clickNFTIconOnActivityList() { + await this.driver.clickElement(this.nftIconOnActivityList); + } + async closePopover(): Promise { console.log('Closing popover'); await this.driver.clickElement(this.popoverCloseButton); @@ -114,8 +156,20 @@ class HomePage { await this.driver.clickElement(this.nftTab); } - async clickNFTIconOnActivityList() { - await this.driver.clickElement(this.nftIconOnActivityList); + /** + * Hides a token by clicking on the token name, and confirming the hide modal. + * + * @param tokenName - The name of the token to hide. + */ + async hideToken(tokenName: string): Promise { + console.log(`Hide token ${tokenName} on homepage`); + await this.driver.clickElement({ text: tokenName, tag: 'span' }); + await this.driver.clickElement(this.assetOptionsButton); + await this.driver.clickElement(this.hideTokenButton); + await this.driver.waitForSelector(this.hideTokenConfirmationModalTitle); + await this.driver.clickElementAndWaitToDisappear( + this.hideTokenConfirmationButton, + ); } async startSendFlow(): Promise { @@ -151,6 +205,20 @@ class HomePage { } } + async importTokenBySearch(tokenName: string) { + console.log(`Import token ${tokenName} on homepage by search`); + await this.driver.clickElement(this.tokenOptionsButton); + await this.driver.clickElement(this.importTokensButton); + await this.driver.waitForSelector(this.importTokenModalTitle); + await this.driver.fill(this.tokenSearchInput, tokenName); + await this.driver.clickElement({ text: tokenName, tag: 'p' }); + await this.driver.clickElement(this.importTokensNextButton); + await this.driver.waitForSelector(this.confirmImportTokenMessage); + await this.driver.clickElementAndWaitToDisappear( + this.confirmImportTokenButton, + ); + } + /** * Checks if the toaster message for adding a network is displayed on the homepage. * @@ -329,6 +397,59 @@ class HomePage { await this.driver.waitForSelector(this.successImportNftMessage); } + /** + * Checks if the specified token amount is displayed in the token list. + * + * @param tokenAmount - The token amount to be checked for. + */ + async check_tokenAmountIsDisplayed(tokenAmount: string): Promise { + console.log(`Waiting for token amount ${tokenAmount} to be displayed`); + await this.driver.waitForSelector({ + css: this.tokenAmountValue, + text: tokenAmount, + }); + } + + /** + * Checks if the specified token amount is displayed in the token details modal. + * + * @param tokenName - The name of the token to check for. + * @param tokenAmount - The token amount to be checked for. + */ + async check_tokenAmountInTokenDetailsModal( + tokenName: string, + tokenAmount: string, + ): Promise { + console.log( + `Check that token amount ${tokenAmount} is displayed in token details modal for token ${tokenName}`, + ); + await this.driver.clickElement({ + tag: 'span', + text: tokenName, + }); + await this.driver.waitForSelector({ + css: this.tokenAmountValue, + text: tokenAmount, + }); + } + + /** + * This function checks if the specified number of token items is displayed in the token list. + * + * @param expectedNumber - The number of token items expected to be displayed. Defaults to 1. + * @returns A promise that resolves if the expected number of token items is displayed. + */ + async check_tokenItemNumber(expectedNumber: number = 1): Promise { + console.log(`Waiting for ${expectedNumber} token items to be displayed`); + await this.driver.wait(async () => { + const tokenItems = await this.driver.findElements(this.tokenLisiItem); + return tokenItems.length === expectedNumber; + }, 10000); + console.log( + `Expected number of token items ${expectedNumber} is displayed.`, + ); + } + /** * This function checks if a specified transaction amount at the specified index matches the expected one. * diff --git a/test/e2e/page-objects/pages/test-dapp.ts b/test/e2e/page-objects/pages/test-dapp.ts index afff2f37e57e..31e962933e90 100644 --- a/test/e2e/page-objects/pages/test-dapp.ts +++ b/test/e2e/page-objects/pages/test-dapp.ts @@ -29,11 +29,6 @@ class TestDapp { private readonly depositPiggyBankContractButton = '#depositButton'; - private readonly editConnectButton = { - text: 'Edit', - tag: 'button', - }; - private readonly simpleSendButton = '#sendButton'; private readonly erc721MintButton = '#mintButton'; @@ -60,11 +55,6 @@ class TestDapp { private readonly erc721SetApprovalForAllButton = '#setApprovalForAllButton'; - private readonly localhostCheckbox = { - text: 'Localhost 8545', - tag: 'p', - }; - private readonly localhostNetworkMessage = { css: '#chainId', text: '0x539', diff --git a/test/e2e/tests/tokens/add-hide-token.spec.js b/test/e2e/tests/tokens/add-hide-token.spec.js deleted file mode 100644 index 011d52849feb..000000000000 --- a/test/e2e/tests/tokens/add-hide-token.spec.js +++ /dev/null @@ -1,270 +0,0 @@ -const { strict: assert } = require('assert'); -const { toHex } = require('@metamask/controller-utils'); -const { - defaultGanacheOptions, - withFixtures, - unlockWallet, - WINDOW_TITLES, - clickNestedButton, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); -const { SMART_CONTRACTS } = require('../../seeder/smart-contracts'); -const { CHAIN_IDS } = require('../../../../shared/constants/network'); - -describe('Add hide token', function () { - it('hides the token when clicked', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder() - .withTokensController({ - allTokens: { - [toHex(1337)]: { - '0x5cfe73b6021e818b776b421b1c4db2474086a7e1': [ - { - address: '0x86002be4cdd922de1ccb831582bf99284b99ac12', - decimals: 4, - image: null, - isERC721: false, - symbol: 'TST', - }, - ], - }, - }, - tokens: [ - { - address: '0x86002be4cdd922de1ccb831582bf99284b99ac12', - decimals: 4, - image: null, - isERC721: false, - symbol: 'TST', - }, - ], - }) - .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - await driver.waitForSelector({ - css: '[data-testid="multichain-token-list-item-value"]', - text: '0 TST', - }); - - let assets = await driver.findElements('.multichain-token-list-item'); - assert.equal(assets.length, 2); - - await clickNestedButton(driver, 'Tokens'); - - await driver.clickElement({ text: 'TST', tag: 'span' }); - - await driver.clickElement('[data-testid="asset-options__button"]'); - - await driver.clickElement('[data-testid="asset-options__hide"]'); - // wait for confirm hide modal to be visible - const confirmHideModal = - '[data-testid="hide-token-confirmation-modal"]'; - await driver.findVisibleElement(confirmHideModal); - - await driver.clickElement( - '[data-testid="hide-token-confirmation__hide"]', - ); - - // wait for confirm hide modal to be removed from DOM. - await driver.assertElementNotPresent(confirmHideModal); - - assets = await driver.findElements('.multichain-token-list-item'); - assert.equal(assets.length, 1); - }, - ); - }); -}); - -/* eslint-disable-next-line mocha/max-top-level-suites */ -describe('Add existing token using search', function () { - // Mock call to core to fetch BAT token price - async function mockPriceFetch(mockServer) { - return [ - await mockServer - .forGet('https://price.api.cx.metamask.io/v2/chains/56/spot-prices') - .withQuery({ - tokenAddresses: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', - vsCurrency: 'ETH', - }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - '0x0d8775f648430679a709e98d2b0cb6250d2887ef': { - eth: 0.0001, - }, - }, - }; - }), - ]; - } - it('renders the balance for the chosen token', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder({ inputChainId: CHAIN_IDS.BSC }) - .withPreferencesController({ useTokenDetection: true }) - .withTokenListController({ - tokenList: [ - { - name: 'Basic Attention Token', - symbol: 'BAT', - address: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', - }, - ], - }) - .withAppStateController({ - [CHAIN_IDS.OPTIMISM]: true, - }) - .build(), - ganacheOptions: { - ...defaultGanacheOptions, - chainId: parseInt(CHAIN_IDS.BSC, 16), - }, - title: this.test.fullTitle(), - testSpecificMock: mockPriceFetch, - }, - async ({ driver }) => { - await unlockWallet(driver); - - await driver.clickElement(`[data-testid="import-token-button"]`); - await driver.clickElement(`[data-testid="importTokens"]`); - await driver.fill('input[placeholder="Search tokens"]', 'BAT'); - await driver.clickElement({ - text: 'BAT', - tag: 'p', - }); - await driver.clickElement({ text: 'Next', tag: 'button' }); - await driver.clickElementAndWaitToDisappear( - '[data-testid="import-tokens-modal-import-button"]', - ); - await driver.clickElement( - '[data-testid="account-overview__asset-tab"]', - ); - await driver.clickElement({ - tag: 'span', - text: 'Basic Attention Token', - }); - - await driver.waitForSelector({ - css: '[data-testid="multichain-token-list-item-value"]', - text: '0 BAT', - }); - }, - ); - }); -}); - -describe('Add token using wallet_watchAsset', function () { - const smartContract = SMART_CONTRACTS.HST; - - it('opens a notification that adds a token when wallet_watchAsset is executed, then approves', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - smartContract, - title: this.test.fullTitle(), - }, - async ({ driver, contractRegistry }) => { - const contractAddress = await contractRegistry.getContractAddress( - smartContract, - ); - await unlockWallet(driver); - - await driver.openNewPage('http://127.0.0.1:8080/'); - - await driver.executeScript(` - window.ethereum.request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - address: '${contractAddress}', - symbol: 'TST', - decimals: 4 - }, - } - }) - `); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - await driver.clickElementAndWaitForWindowToClose({ - tag: 'button', - text: 'Add token', - }); - - await driver.switchToWindowWithTitle( - WINDOW_TITLES.ExtensionInFullScreenView, - ); - - await driver.waitForSelector({ - css: '[data-testid="multichain-token-list-item-value"]', - text: '0 TST', - }); - }, - ); - }); - - it('opens a notification that adds a token when wallet_watchAsset is executed, then rejects', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - smartContract, - title: this.test.fullTitle(), - }, - async ({ driver, contractRegistry }) => { - const contractAddress = await contractRegistry.getContractAddress( - smartContract, - ); - await unlockWallet(driver); - - await driver.openNewPage('http://127.0.0.1:8080/'); - - await driver.executeScript(` - window.ethereum.request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - address: '${contractAddress}', - symbol: 'TST', - decimals: 4 - }, - } - }) - `); - - await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); - - await driver.clickElementAndWaitForWindowToClose({ - tag: 'button', - text: 'Cancel', - }); - - await driver.switchToWindowWithTitle( - WINDOW_TITLES.ExtensionInFullScreenView, - ); - - const assetListItems = await driver.findElements( - '.multichain-token-list-item', - ); - - assert.strictEqual(assetListItems.length, 1); - }, - ); - }); -}); diff --git a/test/e2e/tests/tokens/add-hide-token.spec.ts b/test/e2e/tests/tokens/add-hide-token.spec.ts new file mode 100644 index 000000000000..8f85f2d9914e --- /dev/null +++ b/test/e2e/tests/tokens/add-hide-token.spec.ts @@ -0,0 +1,50 @@ +import { toHex } from '@metamask/controller-utils'; +import { withFixtures } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import HomePage from '../../page-objects/pages/homepage'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; + +describe('Add hide token', function () { + it('hides the token when clicked', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withTokensController({ + allTokens: { + [toHex(1337)]: { + '0x5cfe73b6021e818b776b421b1c4db2474086a7e1': [ + { + address: '0x86002be4cdd922de1ccb831582bf99284b99ac12', + decimals: 4, + image: null, + isERC721: false, + symbol: 'TST', + }, + ], + }, + }, + tokens: [ + { + address: '0x86002be4cdd922de1ccb831582bf99284b99ac12', + decimals: 4, + image: null, + isERC721: false, + symbol: 'TST', + }, + ], + }) + .build(), + title: this.test?.fullTitle(), + }, + async ({ driver }) => { + await loginWithBalanceValidation(driver); + const homepage = new HomePage(driver); + await homepage.check_tokenItemNumber(2); + await homepage.check_tokenAmountIsDisplayed('0 TST'); + + await homepage.hideToken('TST'); + await homepage.check_tokenItemNumber(1); + }, + ); + }); +}); diff --git a/test/e2e/tests/tokens/add-token-using-search.ts b/test/e2e/tests/tokens/add-token-using-search.ts new file mode 100644 index 000000000000..79b4715afb91 --- /dev/null +++ b/test/e2e/tests/tokens/add-token-using-search.ts @@ -0,0 +1,69 @@ +import { MockedEndpoint, Mockttp } from 'mockttp'; +import { defaultGanacheOptions, withFixtures } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import HomePage from '../../page-objects/pages/homepage'; +import { loginWithoutBalanceValidation } from '../../page-objects/flows/login.flow'; + +describe('Add existing token using search', function () { + // Mock call to core to fetch BAT token price + async function mockPriceFetch( + mockServer: Mockttp, + ): Promise { + return [ + await mockServer + .forGet('https://price.api.cx.metamask.io/v2/chains/56/spot-prices') + .withQuery({ + tokenAddresses: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + vsCurrency: 'ETH', + }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + '0x0d8775f648430679a709e98d2b0cb6250d2887ef': { + eth: 0.0001, + }, + }, + }; + }), + ]; + } + it('renders the balance for the chosen token', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder({ inputChainId: CHAIN_IDS.BSC }) + .withPreferencesController({ useTokenDetection: true }) + .withTokenListController({ + tokenList: [ + { + name: 'Basic Attention Token', + symbol: 'BAT', + address: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + }, + ], + }) + .withAppStateController({ + [CHAIN_IDS.OPTIMISM]: true, + }) + .build(), + ganacheOptions: { + ...defaultGanacheOptions, + chainId: parseInt(CHAIN_IDS.BSC, 16), + }, + title: this.test?.fullTitle(), + testSpecificMock: mockPriceFetch, + }, + async ({ driver }) => { + await loginWithoutBalanceValidation(driver); + const homepage = new HomePage(driver); + await homepage.check_tokenAmountIsDisplayed('25 BNB'); + await homepage.importTokenBySearch('BAT'); + await homepage.check_tokenAmountInTokenDetailsModal( + 'Basic Attention Token', + '0 BAT', + ); + }, + ); + }); +}); diff --git a/test/e2e/tests/tokens/watch-asset-call-add-token.ts b/test/e2e/tests/tokens/watch-asset-call-add-token.ts new file mode 100644 index 000000000000..35c66dda8ce6 --- /dev/null +++ b/test/e2e/tests/tokens/watch-asset-call-add-token.ts @@ -0,0 +1,109 @@ +import { + defaultGanacheOptions, + withFixtures, + WINDOW_TITLES, +} from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; +import AddTokenConfirmation from '../../page-objects/pages/confirmations/redesign/add-token-confirmations'; +import HomePage from '../../page-objects/pages/homepage'; +import TestDapp from '../../page-objects/pages/test-dapp'; +import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; + +describe('Add token using wallet_watchAsset', function () { + const smartContract = SMART_CONTRACTS.HST; + + it('opens a notification that adds a token when wallet_watchAsset is executed, then approves', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, ganacheServer, contractRegistry }) => { + const contractAddress = await contractRegistry.getContractAddress( + smartContract, + ); + await loginWithBalanceValidation(driver, ganacheServer); + const testDapp = new TestDapp(driver); + await testDapp.openTestDappPage(); + await testDapp.check_pageIsLoaded(); + + await driver.executeScript(` + window.ethereum.request({ + method: 'wallet_watchAsset', + params: { + type: 'ERC20', + options: { + address: '${contractAddress}', + symbol: 'TST', + decimals: 4 + }, + } + }) + `); + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + const addTokenConfirmation = new AddTokenConfirmation(driver); + await addTokenConfirmation.check_pageIsLoaded(); + await addTokenConfirmation.confirmAddToken(); + + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + await new HomePage(driver).check_tokenAmountIsDisplayed('0 TST'); + }, + ); + }); + + it('opens a notification that adds a token when wallet_watchAsset is executed, then rejects', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, ganacheServer, contractRegistry }) => { + const contractAddress = await contractRegistry.getContractAddress( + smartContract, + ); + await loginWithBalanceValidation(driver, ganacheServer); + const testDapp = new TestDapp(driver); + await testDapp.openTestDappPage(); + await testDapp.check_pageIsLoaded(); + + await driver.executeScript(` + window.ethereum.request({ + method: 'wallet_watchAsset', + params: { + type: 'ERC20', + options: { + address: '${contractAddress}', + symbol: 'TST', + decimals: 4 + }, + } + }) + `); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + const addTokenConfirmation = new AddTokenConfirmation(driver); + await addTokenConfirmation.check_pageIsLoaded(); + await addTokenConfirmation.rejectAddToken(); + + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + await new HomePage(driver).check_tokenItemNumber(1); + }, + ); + }); +}); From 96fafbf2a13e8b91eed2eea608089df8f268dded Mon Sep 17 00:00:00 2001 From: Priya Date: Wed, 27 Nov 2024 19:22:18 +0700 Subject: [PATCH 08/22] test: add integration tests for different types of Permit (#27446) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27446?quickstart=1) ## **Related issues** Fixes: [#26134](https://github.com/MetaMask/metamask-extension/issues/26134) ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../signatures/permit-batch.test.tsx | 175 ++++++++++++++++ .../signatures/permit-seaport.test.tsx | 190 ++++++++++++++++++ .../signatures/permit-single.test.tsx | 151 ++++++++++++++ .../signatures/permit-tradeOrder.test.tsx | 129 ++++++++++++ .../confirmations/signatures/permit.test.tsx | 53 +---- .../signatures/signature-helpers.ts | 99 +++++++++ .../info/__snapshots__/info.test.tsx.snap | 16 ++ .../__snapshots__/typed-sign-v1.test.tsx.snap | 2 + .../__snapshots__/typed-sign.test.tsx.snap | 45 +++++ .../confirm/info/typed-sign/typed-sign.tsx | 4 +- .../row/__snapshots__/dataTree.test.tsx.snap | 28 +++ .../components/confirm/row/dataTree.tsx | 1 + .../typedSignDataV1.test.tsx.snap | 2 + .../__snapshots__/typedSignData.test.tsx.snap | 14 ++ .../__snapshots__/confirm.test.tsx.snap | 35 ++++ 15 files changed, 894 insertions(+), 50 deletions(-) create mode 100644 test/integration/confirmations/signatures/permit-batch.test.tsx create mode 100644 test/integration/confirmations/signatures/permit-seaport.test.tsx create mode 100644 test/integration/confirmations/signatures/permit-single.test.tsx create mode 100644 test/integration/confirmations/signatures/permit-tradeOrder.test.tsx create mode 100644 test/integration/confirmations/signatures/signature-helpers.ts diff --git a/test/integration/confirmations/signatures/permit-batch.test.tsx b/test/integration/confirmations/signatures/permit-batch.test.tsx new file mode 100644 index 000000000000..ea311537001c --- /dev/null +++ b/test/integration/confirmations/signatures/permit-batch.test.tsx @@ -0,0 +1,175 @@ +import { act, fireEvent, screen } from '@testing-library/react'; +import nock from 'nock'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { createMockImplementation } from '../../helpers'; +import { tEn } from '../../../lib/i18n-helpers'; +import { + getMetaMaskStateWithUnapprovedPermitSign, + verifyDetails, +} from './signature-helpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +const renderPermitBatchSignature = async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( + account.address, + 'PermitBatch', + ); + + await act(async () => { + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + selectedNetworkClientId: 'testNetworkConfigurationId', + providerConfig: { + type: 'rpc', + nickname: 'test mainnet', + chainId: '0x1', + ticker: 'ETH', + id: 'chain1', + }, + }, + backgroundConnection: backgroundConnectionMocked, + }); + }); +}; + +describe('Permit Batch Signature Tests', () => { + beforeEach(() => { + jest.resetAllMocks(); + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + getTokenStandardAndDetails: { decimals: '2' }, + }), + ); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('renders the permit batch signature with correct titles', async () => { + await renderPermitBatchSignature(); + + expect( + await screen.findByText(tEn('confirmTitlePermitTokens') as string), + ).toBeInTheDocument(); + expect( + await screen.findByText(tEn('confirmTitleDescPermitSignature') as string), + ).toBeInTheDocument(); + }); + + it('displays the correct details in the simulation section', async () => { + await renderPermitBatchSignature(); + + const simulationSection = await screen.findByTestId( + 'confirmation__simulation_section', + ); + expect(simulationSection).toBeInTheDocument(); + + const simulationDetails = [ + 'Estimated changes', + "You're giving the spender permission to spend this many tokens from your account.", + 'Spending cap', + '0xA0b86...6eB48', + '1,461,501,637,3...', + '0xb0B86...6EB48', + '2,461,501,637,3...', + ]; + + verifyDetails(simulationSection, simulationDetails); + }); + + it('displays correct request and message details', async () => { + await renderPermitBatchSignature(); + + const requestDetailsSection = await screen.findByTestId( + 'confirmation_request-section', + ); + const requestDetails = [ + 'Spender', + '0x3fC91...b7FAD', + 'Request from', + 'metamask.github.io', + 'Interacting with', + '0x00000...78BA3', + ]; + expect(requestDetailsSection).toBeInTheDocument(); + verifyDetails(requestDetailsSection, requestDetails); + + await act(async () => { + fireEvent.click(await screen.findByTestId('sectionCollapseButton')); + }); + + const messageDetailsSection = await screen.findByTestId( + 'confirmation_message-section', + ); + expect(messageDetailsSection).toBeInTheDocument(); + expect(messageDetailsSection).toHaveTextContent('Message'); + expect(messageDetailsSection).toHaveTextContent('Primary type:'); + expect(messageDetailsSection).toHaveTextContent('PermitBatch'); + + const messageData0 = await screen.findByTestId( + 'confirmation_data-0-index-0', + ); + const messageData1 = await screen.findByTestId( + 'confirmation_data-1-index-1', + ); + expect(messageDetailsSection).toContainElement(messageData0); + expect(messageDetailsSection).toContainElement(messageData1); + + const messageDetails = [ + { + element: messageData0, + content: [ + 'Token', + 'USDC', + 'Amount', + '1,461,501,637,3...', + 'Expiration', + '05 August 2024, 19:52', + 'Nonce', + '5', + ], + }, + { + element: messageData1, + content: [ + 'Token', + '0xb0B86...6EB48', + 'Amount', + '2,461,501,637,3...', + 'Expiration', + '05 August 2024, 19:54', + 'Nonce', + '6', + ], + }, + ]; + + messageDetails.forEach(({ element, content }) => { + verifyDetails(element, content); + }); + + expect(messageDetailsSection).toHaveTextContent('Spender'); + expect(messageDetailsSection).toHaveTextContent('0x3fC91...b7FAD'); + expect(messageDetailsSection).toHaveTextContent('SigDeadline'); + expect(messageDetailsSection).toHaveTextContent('06 July 2024, 20:22'); + }); +}); diff --git a/test/integration/confirmations/signatures/permit-seaport.test.tsx b/test/integration/confirmations/signatures/permit-seaport.test.tsx new file mode 100644 index 000000000000..be4f6b6064d2 --- /dev/null +++ b/test/integration/confirmations/signatures/permit-seaport.test.tsx @@ -0,0 +1,190 @@ +import { act, screen } from '@testing-library/react'; +import nock from 'nock'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { createMockImplementation } from '../../helpers'; +import { tEn } from '../../../lib/i18n-helpers'; +import { + getMetaMaskStateWithUnapprovedPermitSign, + verifyDetails, +} from './signature-helpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +const renderSeaportSignature = async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( + account.address, + 'PermitSeaport', + ); + + await act(async () => { + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + selectedNetworkClientId: 'testNetworkConfigurationId', + providerConfig: { + type: 'rpc', + nickname: 'test mainnet', + chainId: '0x1', + ticker: 'ETH', + id: 'chain1', + }, + }, + backgroundConnection: backgroundConnectionMocked, + }); + }); +}; + +describe('Permit Seaport Tests', () => { + beforeEach(() => { + jest.resetAllMocks(); + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + getTokenStandardAndDetails: { decimals: '2' }, + }), + ); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('renders seaport signature', async () => { + await renderSeaportSignature(); + + expect( + await screen.findByText(tEn('confirmTitleSignature') as string), + ).toBeInTheDocument(); + expect( + await screen.findByText(tEn('confirmTitleDescSign') as string), + ).toBeInTheDocument(); + }); + + it('renders request details section', async () => { + await renderSeaportSignature(); + + const requestDetailsSection = await screen.findByTestId( + 'confirmation_request-section', + ); + + expect(requestDetailsSection).toBeInTheDocument(); + expect(requestDetailsSection).toHaveTextContent('Request from'); + expect(requestDetailsSection).toHaveTextContent('metamask.github.io'); + expect(requestDetailsSection).toHaveTextContent('Interacting with'); + expect(requestDetailsSection).toHaveTextContent('0x00000...78BA3'); + }); + + it('renders message details section', async () => { + await renderSeaportSignature(); + + const messageDetailsSection = await screen.findByTestId( + 'confirmation_message-section', + ); + expect(messageDetailsSection).toBeInTheDocument(); + const messageDetailsContent = [ + 'Message', + 'Primary type:', + 'OrderComponents', + 'Offerer', + '0x5a6f5...Ac994', + 'Zone', + '0x004C0...60C00', + 'Offer', + ]; + verifyDetails(messageDetailsSection, messageDetailsContent); + }); + + it('renders offer and consideration details', async () => { + await renderSeaportSignature(); + + const offers = await screen.findByTestId('confirmation_data-offer-index-2'); + const offerDetails0 = offers.querySelector( + '[data-testid="confirmation_data-0-index-0"]', + ); + const offerDetails1 = offers.querySelector( + '[data-testid="confirmation_data-1-index-1"]', + ); + const considerations = await screen.findByTestId( + 'confirmation_data-consideration-index-3', + ); + const considerationDetails0 = considerations.querySelector( + '[data-testid="confirmation_data-0-index-0"]', + ); + + expect(offerDetails0).toBeInTheDocument(); + expect(offerDetails1).toBeInTheDocument(); + expect(considerations).toBeInTheDocument(); + expect(considerationDetails0).toBeInTheDocument(); + + const details = [ + { + element: offerDetails0 as HTMLElement, + content: [ + 'ItemType', + '2', + 'Token', + 'MutantApeYachtClub', + 'IdentifierOrCriteria', + '26464', + 'StartAmount', + '1', + 'EndAmount', + '1', + ], + }, + { + element: offerDetails1 as HTMLElement, + content: [ + 'ItemType', + '2', + 'Token', + 'MutantApeYachtClub', + 'IdentifierOrCriteria', + '7779', + 'StartAmount', + '1', + 'EndAmount', + '1', + ], + }, + { + element: considerationDetails0 as HTMLElement, + content: [ + 'ItemType', + '2', + 'Token', + 'MutantApeYachtClub', + 'IdentifierOrCriteria', + '26464', + 'StartAmount', + '1', + 'EndAmount', + '1', + 'Recipient', + '0xDFdc0...25Cc1', + ], + }, + ]; + + details.forEach(({ element, content }) => { + if (element) { + verifyDetails(element, content); + } + }); + }); +}); diff --git a/test/integration/confirmations/signatures/permit-single.test.tsx b/test/integration/confirmations/signatures/permit-single.test.tsx new file mode 100644 index 000000000000..abb5d07c1587 --- /dev/null +++ b/test/integration/confirmations/signatures/permit-single.test.tsx @@ -0,0 +1,151 @@ +import { act, fireEvent, screen } from '@testing-library/react'; +import nock from 'nock'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { createMockImplementation } from '../../helpers'; +import { tEn } from '../../../lib/i18n-helpers'; +import { + getMetaMaskStateWithUnapprovedPermitSign, + verifyDetails, +} from './signature-helpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +const renderSingleBatchSignature = async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( + account.address, + 'PermitSingle', + ); + + await act(async () => { + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + selectedNetworkClientId: 'testNetworkConfigurationId', + providerConfig: { + type: 'rpc', + nickname: 'test mainnet', + chainId: '0x1', + ticker: 'ETH', + id: 'chain1', + }, + }, + backgroundConnection: backgroundConnectionMocked, + }); + }); +}; + +describe('Permit Single Signature Tests', () => { + beforeEach(() => { + jest.resetAllMocks(); + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + getTokenStandardAndDetails: { decimals: '2' }, + }), + ); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('renders permit single signature with correct titles', async () => { + await renderSingleBatchSignature(); + + expect( + await screen.findByText(tEn('confirmTitlePermitTokens') as string), + ).toBeInTheDocument(); + expect( + await screen.findByText(tEn('confirmTitleDescPermitSignature') as string), + ).toBeInTheDocument(); + }); + + it('displays correct details in simulation section', async () => { + await renderSingleBatchSignature(); + + const simulationSection = await screen.findByTestId( + 'confirmation__simulation_section', + ); + const simulationDetails = [ + 'Estimated changes', + "You're giving the spender permission to spend this many tokens from your account.", + 'Spending cap', + '0xA0b86...6eB48', + '1,461,501,637,3...', + ]; + + expect(simulationSection).toBeInTheDocument(); + verifyDetails(simulationSection, simulationDetails); + }); + + it('displays correct details in request section', async () => { + await renderSingleBatchSignature(); + + const requestDetailsSection = await screen.findByTestId( + 'confirmation_request-section', + ); + const requestDetails = [ + 'Spender', + '0x3fC91...b7FAD', + 'Request from', + 'metamask.github.io', + 'Interacting with', + '0x00000...78BA3', + ]; + + expect(requestDetailsSection).toBeInTheDocument(); + verifyDetails(requestDetailsSection, requestDetails); + }); + + it('displays correct details in message section', async () => { + await renderSingleBatchSignature(); + act(async () => { + fireEvent.click(await screen.findByTestId('sectionCollapseButton')); + }); + + const messageDetailsSection = await screen.findByTestId( + 'confirmation_message-section', + ); + const messageDetails = ['Message', 'Primary type:', 'PermitSingle']; + + expect(messageDetailsSection).toBeInTheDocument(); + verifyDetails(messageDetailsSection, messageDetails); + + const messageData0 = await screen.findByTestId( + 'confirmation_data-details-index-0', + ); + const messageData0Details = [ + 'Token', + 'USDC', + 'Amount', + '1,461,501,637,3...', + 'Expiration', + '05 August 2024, 19:52', + 'Nonce', + '5', + ]; + + expect(messageDetailsSection).toContainElement(messageData0); + verifyDetails(messageData0, messageData0Details); + + expect(messageDetailsSection).toHaveTextContent('Spender'); + expect(messageDetailsSection).toHaveTextContent('0x3fC91...b7FAD'); + expect(messageDetailsSection).toHaveTextContent('SigDeadline'); + expect(messageDetailsSection).toHaveTextContent('06 July 2024, 20:22'); + }); +}); diff --git a/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx b/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx new file mode 100644 index 000000000000..429e533ff8f8 --- /dev/null +++ b/test/integration/confirmations/signatures/permit-tradeOrder.test.tsx @@ -0,0 +1,129 @@ +import { act, screen } from '@testing-library/react'; +import nock from 'nock'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { integrationTestRender } from '../../../lib/render-helpers'; +import * as backgroundConnection from '../../../../ui/store/background-connection'; +import { createMockImplementation } from '../../helpers'; +import { tEn } from '../../../lib/i18n-helpers'; +import { + getMetaMaskStateWithUnapprovedPermitSign, + verifyDetails, +} from './signature-helpers'; + +jest.mock('../../../../ui/store/background-connection', () => ({ + ...jest.requireActual('../../../../ui/store/background-connection'), + submitRequestToBackground: jest.fn(), +})); + +const mockedBackgroundConnection = jest.mocked(backgroundConnection); +const backgroundConnectionMocked = { + onNotification: jest.fn(), +}; + +const renderTradeOrderSignature = async () => { + const account = + mockMetaMaskState.internalAccounts.accounts[ + mockMetaMaskState.internalAccounts + .selectedAccount as keyof typeof mockMetaMaskState.internalAccounts.accounts + ]; + + const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( + account.address, + 'TradeOrder', + ); + + await act(async () => { + await integrationTestRender({ + preloadedState: { + ...mockedMetaMaskState, + selectedNetworkClientId: 'testNetworkConfigurationId', + providerConfig: { + type: 'rpc', + nickname: 'test mainnet', + chainId: '0x1', + ticker: 'ETH', + id: 'chain1', + }, + }, + backgroundConnection: backgroundConnectionMocked, + }); + }); +}; + +describe('Permit Trade Order Tests', () => { + beforeEach(() => { + jest.resetAllMocks(); + mockedBackgroundConnection.submitRequestToBackground.mockImplementation( + createMockImplementation({ + getTokenStandardAndDetails: { decimals: '2' }, + }), + ); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('renders trade order signature with correct titles', async () => { + await renderTradeOrderSignature(); + + expect( + await screen.findByText(tEn('confirmTitleSignature') as string), + ).toBeInTheDocument(); + expect( + await screen.findByText(tEn('confirmTitleDescSign') as string), + ).toBeInTheDocument(); + }); + + it('displays correct details in request section', async () => { + await renderTradeOrderSignature(); + + const requestDetailsSection = await screen.findByTestId( + 'confirmation_request-section', + ); + const requestDetails = [ + 'Request from', + 'metamask.github.io', + 'Interacting with', + '0xDef1C...25EfF', + ]; + + expect(requestDetailsSection).toBeInTheDocument(); + verifyDetails(requestDetailsSection, requestDetails); + }); + + it('displays correct details in message section', async () => { + await renderTradeOrderSignature(); + const messageDetailsSection = await screen.findByTestId( + 'confirmation_message-section', + ); + const messageDetails = [ + 'Message', + 'Primary type:', + 'ERC721Order', + 'Direction', + '0', + 'Maker', + '0x8Eeee...73D12', + 'Taker', + '0xCD2a3...DD826', + 'Expiry', + '2524604400', + 'Nonce', + '100131415900000000000000000000000000000083840314483690155566137712510085002484', + 'Erc20Token', + 'Wrapped Ether', + 'Erc20TokenAmount', + '42000000000000', + 'Fees', + 'Erc721Token', + 'Doodles', + 'Erc721TokenId', + '2516', + 'Erc721TokenProperties', + ]; + + expect(messageDetailsSection).toBeInTheDocument(); + verifyDetails(messageDetailsSection, messageDetails); + }); +}); diff --git a/test/integration/confirmations/signatures/permit.test.tsx b/test/integration/confirmations/signatures/permit.test.tsx index 7af3be743f5f..6b736e4add90 100644 --- a/test/integration/confirmations/signatures/permit.test.tsx +++ b/test/integration/confirmations/signatures/permit.test.tsx @@ -1,7 +1,5 @@ -import { ApprovalType } from '@metamask/controller-utils'; import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import nock from 'nock'; -import { CHAIN_IDS } from '@metamask/transaction-controller'; import { MESSAGE_TYPE } from '../../../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -13,6 +11,7 @@ import * as backgroundConnection from '../../../../ui/store/background-connectio import { integrationTestRender } from '../../../lib/render-helpers'; import mockMetaMaskState from '../../data/integration-init-state.json'; import { createMockImplementation } from '../../helpers'; +import { getMetaMaskStateWithUnapprovedPermitSign } from './signature-helpers'; jest.mock('../../../../ui/store/background-connection', () => ({ ...jest.requireActual('../../../../ui/store/background-connection'), @@ -24,52 +23,6 @@ const backgroundConnectionMocked = { onNotification: jest.fn(), }; -const getMetaMaskStateWithUnapprovedPermitSign = (accountAddress: string) => { - const pendingPermitId = 'eae47d40-42a3-11ef-9253-b105fa7dfc9c'; - const pendingPermitTime = new Date().getTime(); - const messageParams = { - from: accountAddress, - version: 'v4', - data: `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Permit":[{"name":"owner","type":"address"},{"name":"spender","type":"address"},{"name":"value","type":"uint256"},{"name":"nonce","type":"uint256"},{"name":"deadline","type":"uint256"}]},"primaryType":"Permit","domain":{"name":"MyToken","version":"1","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","chainId":1},"message":{"owner":"${accountAddress}","spender":"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","value":3000,"nonce":0,"deadline":50000000000}}`, - origin: 'https://metamask.github.io', - signatureMethod: MESSAGE_TYPE.ETH_SIGN_TYPED_DATA_V4, - }; - return { - ...mockMetaMaskState, - preferences: { - ...mockMetaMaskState.preferences, - redesignedConfirmationsEnabled: true, - }, - unapprovedTypedMessages: { - [pendingPermitId]: { - id: pendingPermitId, - chainId: CHAIN_IDS.SEPOLIA, - status: 'unapproved', - time: pendingPermitTime, - type: MESSAGE_TYPE.ETH_SIGN_TYPED_DATA, - securityProviderResponse: null, - msgParams: messageParams, - }, - }, - unapprovedTypedMessagesCount: 1, - pendingApprovals: { - [pendingPermitId]: { - id: pendingPermitId, - origin: 'origin', - time: pendingPermitTime, - type: ApprovalType.EthSignTypedData, - requestData: { - ...messageParams, - metamaskId: pendingPermitId, - }, - requestState: null, - expectsResult: false, - }, - }, - pendingApprovalCount: 1, - }; -}; - describe('Permit Confirmation', () => { beforeEach(() => { jest.resetAllMocks(); @@ -94,6 +47,7 @@ describe('Permit Confirmation', () => { const accountName = account.metadata.name; const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( account.address, + 'Permit', ); await act(async () => { @@ -181,6 +135,7 @@ describe('Permit Confirmation', () => { const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( account.address, + 'Permit', ); await act(async () => { @@ -239,6 +194,7 @@ describe('Permit Confirmation', () => { const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( account.address, + 'Permit', ); await act(async () => { @@ -294,6 +250,7 @@ describe('Permit Confirmation', () => { const mockedMetaMaskState = getMetaMaskStateWithUnapprovedPermitSign( account.address, + 'Permit', ); await act(async () => { diff --git a/test/integration/confirmations/signatures/signature-helpers.ts b/test/integration/confirmations/signatures/signature-helpers.ts new file mode 100644 index 000000000000..5576e6ddf738 --- /dev/null +++ b/test/integration/confirmations/signatures/signature-helpers.ts @@ -0,0 +1,99 @@ +import { ApprovalType } from '@metamask/controller-utils'; +import { MESSAGE_TYPE } from '../../../../shared/constants/app'; +import mockMetaMaskState from '../../data/integration-init-state.json'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; + +const PERMIT_DATA = `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Permit":[{"name":"owner","type":"address"},{"name":"spender","type":"address"},{"name":"value","type":"uint256"},{"name":"nonce","type":"uint256"},{"name":"deadline","type":"uint256"}]},"primaryType":"Permit","domain":{"name":"MyToken","version":"1","verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC","chainId":1},"message":{"owner":"{ownerAddress}","spender":"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","value":3000,"nonce":0,"deadline":50000000000}}`; + +const PERMIT_BATCH_DATA = `{"types":{"PermitBatch":[{"name":"details","type":"PermitDetails[]"},{"name":"spender","type":"address"},{"name":"sigDeadline","type":"uint256"}],"PermitDetails":[{"name":"token","type":"address"},{"name":"amount","type":"uint160"},{"name":"expiration","type":"uint48"},{"name":"nonce","type":"uint48"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"Permit2","chainId":"1","verifyingContract":"0x000000000022d473030f116ddee9f6b43ac78ba3"},"primaryType":"PermitBatch","message":{"details":[{"token":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","amount":"1461501637330902918203684832716283019655932542975","expiration":"1722887542","nonce":"5"},{"token":"0xb0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","amount":"2461501637330902918203684832716283019655932542975","expiration":"1722887642","nonce":"6"}],"spender":"0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad","sigDeadline":"1720297342"}}`; + +const PERMIT_SINGLE_DATA = `{"types":{"PermitSingle":[{"name":"details","type":"PermitDetails"},{"name":"spender","type":"address"},{"name":"sigDeadline","type":"uint256"}],"PermitDetails":[{"name":"token","type":"address"},{"name":"amount","type":"uint160"},{"name":"expiration","type":"uint48"},{"name":"nonce","type":"uint48"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"Permit2","chainId":"1","verifyingContract":"0x000000000022d473030f116ddee9f6b43ac78ba3"},"primaryType":"PermitSingle","message":{"details":{"token":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","amount":"1461501637330902918203684832716283019655932542975","expiration":"1722887542","nonce":"5"},"spender":"0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad","sigDeadline":"1720297342"}}`; + +const SEAPORT_DATA = `{"types":{"OrderComponents":[{"name":"offerer","type":"address"},{"name":"zone","type":"address"},{"name":"offer","type":"OfferItem[]"},{"name":"consideration","type":"ConsiderationItem[]"},{"name":"orderType","type":"uint8"},{"name":"startTime","type":"uint256"},{"name":"endTime","type":"uint256"},{"name":"zoneHash","type":"bytes32"},{"name":"salt","type":"uint256"},{"name":"conduitKey","type":"bytes32"},{"name":"counter","type":"uint256"}],"OfferItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"}],"ConsiderationItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"},{"name":"recipient","type":"address"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"Seaport","version":"1.1","chainId":1,"verifyingContract":"0x000000000022d473030f116ddee9f6b43ac78ba3"},"primaryType":"OrderComponents","message":{"offerer":"0x5a6f5477bdeb7801ba137a9f0dc39c0599bac994","zone":"0x004c00500000ad104d7dbd00e3ae0a5c00560c00","offer":[{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"26464","startAmount":"1","endAmount":"1"},{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"7779","startAmount":"1","endAmount":"1"},{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"4770","startAmount":"1","endAmount":"1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"9594","startAmount":"1","endAmount":"1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"2118","startAmount":"1","endAmount":"1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"1753","startAmount":"1","endAmount":"1"}],"consideration":[{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"26464","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"},{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"7779","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"},{"itemType":"2","token":"0x60e4d786628fea6478f785a6d7e704777c86a7c6","identifierOrCriteria":"4770","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"9594","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"2118","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"},{"itemType":"2","token":"0xba30e5f9bb24caa003e9f2f0497ad287fdf95623","identifierOrCriteria":"1753","startAmount":"1","endAmount":"1","recipient":"0xdfdc0b1cf8e9950d6a860af6501c4fecf7825cc1"}],"orderType":"2","startTime":"1681810415","endTime":"1681983215","zoneHash":"0x0000000000000000000000000000000000000000000000000000000000000000","salt":"1550213294656772168494388599483486699884316127427085531712538817979596","conduitKey":"0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000","counter":"0"}}`; + +const TRADE_ORDER_DATA = `{"types":{"ERC721Order":[{"type":"uint8","name":"direction"},{"type":"address","name":"maker"},{"type":"address","name":"taker"},{"type":"uint256","name":"expiry"},{"type":"uint256","name":"nonce"},{"type":"address","name":"erc20Token"},{"type":"uint256","name":"erc20TokenAmount"},{"type":"Fee[]","name":"fees"},{"type":"address","name":"erc721Token"},{"type":"uint256","name":"erc721TokenId"},{"type":"Property[]","name":"erc721TokenProperties"}],"Fee":[{"type":"address","name":"recipient"},{"type":"uint256","name":"amount"},{"type":"bytes","name":"feeData"}],"Property":[{"type":"address","name":"propertyValidator"},{"type":"bytes","name":"propertyData"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"ZeroEx","version":"1.0.0","chainId":"0x1","verifyingContract":"0xdef1c0ded9bec7f1a1670819833240f027b25eff"},"primaryType":"ERC721Order","message":{"direction":"0","maker":"0x8eeee1781fd885ff5ddef7789486676961873d12","taker":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826","expiry":"2524604400","nonce":"100131415900000000000000000000000000000083840314483690155566137712510085002484","erc20Token":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","erc20TokenAmount":"42000000000000","fees":[],"erc721Token":"0x8a90CAb2b38dba80c64b7734e58Ee1dB38B8992e","erc721TokenId":"2516","erc721TokenProperties":[]}}`; + +const getPermitData = (permitType: string, accountAddress: string) => { + switch (permitType) { + case 'Permit': + return PERMIT_DATA.replace('{ownerAddress}', accountAddress); + case 'PermitBatch': + return PERMIT_BATCH_DATA; + case 'PermitSingle': + return PERMIT_SINGLE_DATA; + case 'PermitSeaport': + return SEAPORT_DATA; + case 'TradeOrder': + return TRADE_ORDER_DATA; + default: + throw new Error(`Unknown permit type: ${permitType}`); + } +}; + +const getMessageParams = (accountAddress: string, data: string) => ({ + from: accountAddress, + version: 'v4', + data, + origin: 'https://metamask.github.io', + signatureMethod: MESSAGE_TYPE.ETH_SIGN_TYPED_DATA_V4, +}); + +export const getMetaMaskStateWithUnapprovedPermitSign = ( + accountAddress: string, + permitType: + | 'Permit' + | 'PermitBatch' + | 'PermitSingle' + | 'PermitSeaport' + | 'TradeOrder', +) => { + const data = getPermitData(permitType, accountAddress); + const pendingPermitId = '48a75190-45ca-11ef-9001-f3886ec2397c'; + const pendingPermitTime = new Date().getTime(); + const messageParams = getMessageParams(accountAddress, data); + + const unapprovedTypedMessage = { + id: pendingPermitId, + status: 'unapproved', + time: pendingPermitTime, + type: MESSAGE_TYPE.ETH_SIGN_TYPED_DATA, + securityProviderResponse: null, + msgParams: messageParams, + chainId: CHAIN_IDS.SEPOLIA, + }; + + const pendingApproval = { + id: pendingPermitId, + origin: 'origin', + time: pendingPermitTime, + type: ApprovalType.EthSignTypedData, + requestData: { + ...messageParams, + metamaskId: pendingPermitId, + }, + requestState: null, + expectsResult: false, + }; + + return { + ...mockMetaMaskState, + preferences: { + ...mockMetaMaskState.preferences, + redesignedConfirmationsEnabled: true, + }, + unapprovedTypedMessages: { + [pendingPermitId]: unapprovedTypedMessage, + }, + unapprovedTypedMessagesCount: 1, + pendingApprovals: { + [pendingPermitId]: pendingApproval, + }, + pendingApprovalCount: 1, + }; +}; + +export const verifyDetails = (element: Element, expectedValues: string[]) => { + expectedValues.forEach((value) => { + expect(element).toHaveTextContent(value); + }); +}; diff --git a/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap index 6d48461f1c89..5bd747959256 100644 --- a/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/__snapshots__/info.test.tsx.snap @@ -1186,6 +1186,7 @@ exports[`Info renders info section for typed sign request 1`] = `
{ return ( <> {isPermit && useTransactionSimulations && } - + {isPermit && ( <> @@ -84,7 +84,7 @@ const TypedSignInfo: React.FC = () => { )} - +
{ // eslint-disable-next-line @typescript-eslint/no-use-before-define diff --git a/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap b/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap index af521e453637..0e8def9c62a0 100644 --- a/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/row/typed-sign-data-v1/__snapshots__/typedSignDataV1.test.tsx.snap @@ -14,6 +14,7 @@ exports[`ConfirmInfoRowTypedSignData should match snapshot 1`] = ` >
Date: Wed, 27 Nov 2024 19:22:28 +0700 Subject: [PATCH 09/22] test: add e2e for transaction decoding (#28204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds test for checking transaction decoding for contract interaction from 4bytes, sourcify and uniswap Also adds tests to verify when all of the above fails then it falls back to the raw hexdata Adds a data-testId to make e2e easier [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28204?quickstart=1) ## **Related issues** Fixes: [#2877](https://github.com/MetaMask/MetaMask-planning/issues/2877) ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .vscode/cspell.json | 1 + .../redesign/transaction-confirmation.ts | 171 ++++++++++++ .../transaction-decoding-redesign.spec.ts | 249 ++++++++++++++++++ .../__snapshots__/approve.test.tsx.snap | 1 + .../transaction-data.test.tsx.snap | 6 + .../transaction-data/transaction-data.tsx | 2 +- 6 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 test/e2e/tests/confirmations/transactions/transaction-decoding-redesign.spec.ts diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 0696498afe86..f962a85ef3ad 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -73,6 +73,7 @@ "shellcheck", "SIWE", "sourcemaps", + "Sourcify", "sprintf", "testcase", "TESTFILES", diff --git a/test/e2e/page-objects/pages/confirmations/redesign/transaction-confirmation.ts b/test/e2e/page-objects/pages/confirmations/redesign/transaction-confirmation.ts index c7f618d3fc61..d2d294c28cd0 100644 --- a/test/e2e/page-objects/pages/confirmations/redesign/transaction-confirmation.ts +++ b/test/e2e/page-objects/pages/confirmations/redesign/transaction-confirmation.ts @@ -1,3 +1,4 @@ +import { strict as assert } from 'assert'; import { tEn } from '../../../../../lib/i18n-helpers'; import { Driver } from '../../../../webdriver/driver'; import { RawLocator } from '../../../common'; @@ -8,6 +9,16 @@ class TransactionConfirmation extends Confirmation { private dappInitiatedHeadingTitle: RawLocator; + private advancedDetailsButton: RawLocator; + + private advancedDetailsSection: RawLocator; + + private advancedDetailsDataFunction: RawLocator; + + private advancedDetailsDataParam: RawLocator; + + private advancedDetailsHexData: RawLocator; + constructor(driver: Driver) { super(driver); @@ -21,6 +32,17 @@ class TransactionConfirmation extends Confirmation { css: 'h3', text: tEn('transferRequest') as string, }; + + this.advancedDetailsButton = `[data-testid="header-advanced-details-button"]`; + + this.advancedDetailsSection = + '[data-testid="advanced-details-data-section"]'; + this.advancedDetailsDataFunction = + '[data-testid="advanced-details-data-function"]'; + this.advancedDetailsDataParam = + '[data-testid="advanced-details-data-param-0"]'; + this.advancedDetailsHexData = + '[data-testid="advanced-details-transaction-hex"]'; } async check_walletInitiatedHeadingTitle() { @@ -30,6 +52,155 @@ class TransactionConfirmation extends Confirmation { async check_dappInitiatedHeadingTitle() { await this.driver.waitForSelector(this.dappInitiatedHeadingTitle); } + + async clickAdvancedDetailsButton() { + await this.driver.clickElement(this.advancedDetailsButton); + } + + async verifyAdvancedDetailsIsDisplayed(type: string) { + const advancedDetailsSection = await this.driver.findElement( + this.advancedDetailsSection, + ); + + await advancedDetailsSection.isDisplayed(); + await advancedDetailsSection + .findElement({ css: this.advancedDetailsDataFunction.toString() }) + .isDisplayed(); + await advancedDetailsSection + .findElement({ css: this.advancedDetailsDataParam.toString() }) + .isDisplayed(); + + const functionInfo = await this.driver.findElement( + this.advancedDetailsDataFunction, + ); + const functionText = await functionInfo.getText(); + + assert.ok( + functionText.includes('Function'), + 'Expected key "Function" to be included in the function text', + ); + assert.ok( + functionText.includes('mintNFTs'), + 'Expected "mintNFTs" to be included in the function text', + ); + + const paramsInfo = await this.driver.findElement( + this.advancedDetailsDataParam, + ); + const paramsText = await paramsInfo.getText(); + + if (type === '4Bytes') { + assert.ok( + paramsText.includes('Param #1'), + 'Expected "Param #1" to be included in the param text', + ); + } else if (type === 'Sourcify') { + assert.ok( + paramsText.includes('Number Of Tokens'), + 'Expected "Number Of Tokens" to be included in the param text', + ); + } + + assert.ok( + paramsText.includes('1'), + 'Expected "1" to be included in the param value', + ); + } + + async verifyAdvancedDetailsHexDataIsDisplayed() { + const advancedDetailsSection = await this.driver.findElement( + this.advancedDetailsSection, + ); + + await advancedDetailsSection.isDisplayed(); + await advancedDetailsSection + .findElement({ css: this.advancedDetailsHexData.toString() }) + .isDisplayed(); + + const hexDataInfo = ( + await this.driver.findElement(this.advancedDetailsHexData) + ).getText(); + + assert.ok( + (await hexDataInfo).includes( + '0x3b4b13810000000000000000000000000000000000000000000000000000000000000001', + ), + 'Expected hex data to be displayed', + ); + } + + async verifyUniswapDecodedTransactionAdvancedDetails() { + const dataSections = await this.driver.findElements( + this.advancedDetailsDataFunction, + ); + + const expectedData = [ + { + functionName: 'WRAP_ETH', + recipient: '0x00000...00002', + amountMin: '100000000000000', + }, + { + functionName: 'V3_SWAP_EXACT_IN', + recipient: '0x00000...00002', + amountIn: '100000000000000', + amountOutMin: '312344', + path0: 'WETH', + path1: '500', + path2: 'USDC', + payerIsUser: 'false', + }, + { + functionName: 'PAY_PORTION', + token: 'USDC', + recipient: '0x27213...71c47', + bips: '25', + }, + { + functionName: 'SWEEP', + token: 'USDC', + recipient: '0x00000...00001', + amountMin: '312344', + }, + ]; + + assert.strictEqual( + dataSections.length, + expectedData.length, + 'Mismatch between data sections and expected data count.', + ); + + await Promise.all( + dataSections.map(async (dataSection, sectionIndex) => { + await dataSection.isDisplayed(); + + const data = expectedData[sectionIndex]; + + const functionText = await dataSection.getText(); + assert.ok( + functionText.includes(data.functionName), + `Expected function name '${data.functionName}' in advanced details.`, + ); + + const params = `[data-testid="advanced-details-${functionText}-params"]`; + + const paramsData = await this.driver.findElement(params); + const paramText = await paramsData.getText(); + + for (const [key, expectedValue] of Object.entries(data)) { + if (key === 'functionName') { + continue; + } + assert.ok( + paramText.includes(expectedValue), + `Expected ${key} '${expectedValue}' in data section ${functionText}.`, + ); + + this.clickScrollToBottomButton(); + } + }), + ); + } } export default TransactionConfirmation; diff --git a/test/e2e/tests/confirmations/transactions/transaction-decoding-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/transaction-decoding-redesign.spec.ts new file mode 100644 index 000000000000..9027355ed192 --- /dev/null +++ b/test/e2e/tests/confirmations/transactions/transaction-decoding-redesign.spec.ts @@ -0,0 +1,249 @@ +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +import { MockttpServer } from 'mockttp'; +import { + createDappTransaction, + DAPP_URL, + unlockWallet, + WINDOW_TITLES, +} from '../../../helpers'; +import TestDapp from '../../../page-objects/pages/test-dapp'; +import { TRANSACTION_DATA_UNISWAP } from '../../../../data/confirmations/transaction-decode'; +import { Ganache } from '../../../seeder/ganache'; +import TransactionConfirmation from '../../../page-objects/pages/confirmations/redesign/transaction-confirmation'; +import ContractAddressRegistry from '../../../seeder/contract-address-registry'; +import { TestSuiteArguments } from './shared'; + +const { defaultGanacheOptions, withFixtures } = require('../../../helpers'); +const FixtureBuilder = require('../../../fixture-builder'); +const { SMART_CONTRACTS } = require('../../../seeder/smart-contracts'); + +describe('Confirmation Redesign Contract Interaction Transaction Decoding', function () { + const smartContract = SMART_CONTRACTS.NFTS; + + describe('Create a mint nft transaction @no-mmi', function () { + it(`decodes 4 bytes transaction data`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + testSpecificMock: mocked4BytesResponse, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await unlockWallet(driver); + const contractAddress = await ( + contractRegistry as ContractAddressRegistry + ).getContractAddress(smartContract); + + const testDapp = new TestDapp(driver); + const confirmation = new TransactionConfirmation(driver); + + await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); + + await testDapp.clickERC721MintButton(); + + await driver.waitAndSwitchToWindowWithTitle(3, WINDOW_TITLES.Dialog); + + await confirmation.clickAdvancedDetailsButton(); + await confirmation.clickScrollToBottomButton(); + await confirmation.verifyAdvancedDetailsIsDisplayed('4Bytes'); + }, + ); + }); + }); + + it(`decodes Sourcify transaction data`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + testSpecificMock: mockedSourcifyResponse, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await unlockWallet(driver); + const contractAddress = await ( + contractRegistry as ContractAddressRegistry + ).getContractAddress(smartContract); + + const testDapp = new TestDapp(driver); + const confirmation = new TransactionConfirmation(driver); + + await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); + + await testDapp.clickERC721MintButton(); + await driver.waitAndSwitchToWindowWithTitle(3, WINDOW_TITLES.Dialog); + + await confirmation.clickAdvancedDetailsButton(); + await confirmation.clickScrollToBottomButton(); + await confirmation.verifyAdvancedDetailsIsDisplayed('Sourcify'); + }, + ); + }); + + it(`falls back to raw hexadecimal when no data is retreived`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await unlockWallet(driver); + const contractAddress = await ( + contractRegistry as ContractAddressRegistry + ).getContractAddress(smartContract); + + const testDapp = new TestDapp(driver); + const confirmation = new TransactionConfirmation(driver); + + await testDapp.openTestDappPage({ contractAddress, url: DAPP_URL }); + + await testDapp.clickERC721MintButton(); + await driver.waitAndSwitchToWindowWithTitle(3, WINDOW_TITLES.Dialog); + + await confirmation.clickAdvancedDetailsButton(); + await confirmation.clickScrollToBottomButton(); + await confirmation.verifyAdvancedDetailsHexDataIsDisplayed(); + }, + ); + }); + + it(`decodes uniswap transaction data`, async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withNetworkControllerOnMainnet() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + testSpecificMock: mockInfura, + title: this.test?.fullTitle(), + }, + async ({ driver, ganacheServer }: TestSuiteArguments) => { + const addresses = await (ganacheServer as Ganache).getAccounts(); + const publicAddress = addresses?.[0] as string; + + await unlockWallet(driver); + const contractAddress = '0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B'; + + const confirmation = new TransactionConfirmation(driver); + + await createDappTransaction(driver, { + data: TRANSACTION_DATA_UNISWAP, + to: contractAddress, + from: publicAddress, + }); + + await driver.waitAndSwitchToWindowWithTitle(3, WINDOW_TITLES.Dialog); + + await confirmation.clickAdvancedDetailsButton(); + await confirmation.clickScrollToBottomButton(); + await confirmation.verifyUniswapDecodedTransactionAdvancedDetails(); + }, + ); + }); +}); + +async function mocked4BytesResponse(mockServer: MockttpServer) { + return await mockServer + .forGet('https://www.4byte.directory/api/v1/signatures/') + .always() + .withQuery({ hex_signature: '0x3b4b1381' }) + .thenCallback(() => ({ + statusCode: 200, + json: { + count: 1, + next: null, + previous: null, + results: [ + { + id: 1, + created_at: '2021-09-14T02:07:09.805000Z', + text_signature: 'mintNFTs(uint256)', + hex_signature: '0x3b4b1381', + bytes_signature: ';K\u0013', + }, + ], + }, + })); +} + +export const SOURCIFY_RESPONSE = { + files: [ + { + name: 'metadata.json', + path: 'contracts/partial_match/11155111/0x076146c765189d51bE3160A2140cF80BFC73ad68/metadata.json', + content: + '{"compiler":{"version":"0.8.18+commit.87f61d96"},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfTokens","type":"uint256"}],"name":"mintNFTs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"events":{"Approval(address,address,uint256)":{"details":"Emitted when `owner` enables `approved` to manage the `tokenId` token."},"ApprovalForAll(address,address,bool)":{"details":"Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets."},"Transfer(address,address,uint256)":{"details":"Emitted when `tokenId` token is transferred from `from` to `to`."}},"kind":"dev","methods":{"approve(address,uint256)":{"details":"See {IERC721-approve}."},"balanceOf(address)":{"details":"See {IERC721-balanceOf}."},"getApproved(uint256)":{"details":"See {IERC721-getApproved}."},"isApprovedForAll(address,address)":{"details":"See {IERC721-isApprovedForAll}."},"name()":{"details":"See {IERC721Metadata-name}."},"ownerOf(uint256)":{"details":"See {IERC721-ownerOf}."},"safeTransferFrom(address,address,uint256)":{"details":"See {IERC721-safeTransferFrom}."},"safeTransferFrom(address,address,uint256,bytes)":{"details":"See {IERC721-safeTransferFrom}."},"setApprovalForAll(address,bool)":{"details":"See {IERC721-setApprovalForAll}."},"supportsInterface(bytes4)":{"details":"See {IERC165-supportsInterface}."},"symbol()":{"details":"See {IERC721Metadata-symbol}."},"tokenURI(uint256)":{"details":"See {IERC721Metadata-tokenURI}."},"transferFrom(address,address,uint256)":{"details":"See {IERC721-transferFrom}."}},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"contracts/TestDappCollectibles.sol":"TestDappNFTs"},"evmVersion":"paris","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"@openzeppelin/contracts/token/ERC721/ERC721.sol":{"keccak256":"0x2c309e7df9e05e6ce15bedfe74f3c61b467fc37e0fae9eab496acf5ea0bbd7ff","license":"MIT","urls":["bzz-raw://7063b5c98711a98018ba4635ac74cee1c1cfa2ea01099498e062699ed9530005","dweb:/ipfs/QmeJ8rGXkcv7RrqLdAW8PCXPAykxVsddfYY6g5NaTwmRFE"]},"@openzeppelin/contracts/token/ERC721/IERC721.sol":{"keccak256":"0x5bce51e11f7d194b79ea59fe00c9e8de9fa2c5530124960f29a24d4c740a3266","license":"MIT","urls":["bzz-raw://7e66dfde185df46104c11bc89d08fa0760737aa59a2b8546a656473d810a8ea4","dweb:/ipfs/QmXvyqtXPaPss2PD7eqPoSao5Szm2n6UMoiG8TZZDjmChR"]},"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol":{"keccak256":"0xa82b58eca1ee256be466e536706850163d2ec7821945abd6b4778cfb3bee37da","license":"MIT","urls":["bzz-raw://6e75cf83beb757b8855791088546b8337e9d4684e169400c20d44a515353b708","dweb:/ipfs/QmYvPafLfoquiDMEj7CKHtvbgHu7TJNPSVPSCjrtjV8HjV"]},"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol":{"keccak256":"0x75b829ff2f26c14355d1cba20e16fe7b29ca58eb5fef665ede48bc0f9c6c74b9","license":"MIT","urls":["bzz-raw://a0a107160525724f9e1bbbab031defc2f298296dd9e331f16a6f7130cec32146","dweb:/ipfs/QmemujxSd7gX8A9M8UwmNbz4Ms3U9FG9QfudUgxwvTmPWf"]},"@openzeppelin/contracts/utils/Address.sol":{"keccak256":"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa","license":"MIT","urls":["bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931","dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm"]},"@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7","license":"MIT","urls":["bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92","dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3"]},"@openzeppelin/contracts/utils/Counters.sol":{"keccak256":"0xf0018c2440fbe238dd3a8732fa8e17a0f9dce84d31451dc8a32f6d62b349c9f1","license":"MIT","urls":["bzz-raw://59e1c62884d55b70f3ae5432b44bb3166ad71ae3acd19c57ab6ddc3c87c325ee","dweb:/ipfs/QmezuXg5GK5oeA4F91EZhozBFekhq5TD966bHPH18cCqhu"]},"@openzeppelin/contracts/utils/Strings.sol":{"keccak256":"0x3088eb2868e8d13d89d16670b5f8612c4ab9ff8956272837d8e90106c59c14a0","license":"MIT","urls":["bzz-raw://b81d9ff6559ea5c47fc573e17ece6d9ba5d6839e213e6ebc3b4c5c8fe4199d7f","dweb:/ipfs/QmPCW1bFisUzJkyjroY3yipwfism9RRCigCcK1hbXtVM8n"]},"@openzeppelin/contracts/utils/introspection/ERC165.sol":{"keccak256":"0xd10975de010d89fd1c78dc5e8a9a7e7f496198085c151648f20cba166b32582b","license":"MIT","urls":["bzz-raw://fb0048dee081f6fffa5f74afc3fb328483c2a30504e94a0ddd2a5114d731ec4d","dweb:/ipfs/QmZptt1nmYoA5SgjwnSgWqgUSDgm4q52Yos3xhnMv3MV43"]},"@openzeppelin/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x447a5f3ddc18419d41ff92b3773fb86471b1db25773e07f877f548918a185bf1","license":"MIT","urls":["bzz-raw://be161e54f24e5c6fae81a12db1a8ae87bc5ae1b0ddc805d82a1440a68455088f","dweb:/ipfs/QmP7C3CHdY9urF4dEMb9wmsp1wMxHF6nhA2yQE5SKiPAdy"]},"@openzeppelin/contracts/utils/math/Math.sol":{"keccak256":"0xe4455ac1eb7fc497bb7402579e7b4d64d928b846fce7d2b6fde06d366f21c2b3","license":"MIT","urls":["bzz-raw://cc8841b3cd48ad125e2f46323c8bad3aa0e88e399ec62acb9e57efa7e7c8058c","dweb:/ipfs/QmSqE4mXHA2BXW58deDbXE8MTcsL5JSKNDbm23sVQxRLPS"]},"@openzeppelin/contracts/utils/math/SignedMath.sol":{"keccak256":"0xf92515413956f529d95977adc9b0567d583c6203fc31ab1c23824c35187e3ddc","license":"MIT","urls":["bzz-raw://c50fcc459e49a9858b6d8ad5f911295cb7c9ab57567845a250bf0153f84a95c7","dweb:/ipfs/QmcEW85JRzvDkQggxiBBLVAasXWdkhEysqypj9EaB6H2g6"]},"base64-sol/base64.sol":{"keccak256":"0xa73959e6ef0b693e4423a562e612370160b934a75e618361ddd8c9c4b8ddbaaf","license":"MIT","urls":["bzz-raw://17c12e16d8d66f3af15d8787920bd41ca6c1e7517a212a6b9cebd4b6d38f36fe","dweb:/ipfs/QmcXMnZUXEz6LRKsm3CSvqdPboAzmezavi8bTg2dRxM2yE"]},"contracts/TestDappCollectibles.sol":{"keccak256":"0x3d2fa0d37970b903e628d8a7b101f8f73513d41d917fbdfac2749d9d1214176c","license":"MIT","urls":["bzz-raw://e01f3a25371accdfbbc2e3c9aeceabf92c5158f318a5e3f5b6c2b6ea3edb0c3b","dweb:/ipfs/QmT89aSnmzTdpRpoCoqCmnrR3Lp7CyXFtpnn2P52YEJULD"]}},"version":1}', + }, + ], +}; + +async function mockedSourcifyResponse(mockServer: MockttpServer) { + return await mockServer + .forGet('https://sourcify.dev/server/files/any/1337/0x') + .always() + .thenCallback(() => ({ + statusCode: 200, + json: SOURCIFY_RESPONSE, + })); +} + +async function mockInfura(mockServer: MockttpServer) { + return await mockServer + .forPost() + .always() + .withJsonBodyIncluding({ + method: 'eth_getCode', + params: ['0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b'], + }) + .thenCallback(() => { + return { + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1', + result: + '0x6080604052600436101561001b575b361561001957600080fd5b005b6000803560e01c90816301ffc9a7146100be57508063150b7a02146100b557806324856bc3146100ac5780633593564c146100a3578063709a1cc21461009a578063bc197c8114610091578063f23a6e61146100885763fa461e330361000e576100836109f2565b61000e565b50610083610960565b50610083610898565b5061008361061d565b50610083610473565b506100836102c5565b50610083610202565b346101ae5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101ae57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101ae57507f4e2312e0000000000000000000000000000000000000000000000000000000008114908115610184575b811561015a575b50151560805260206080f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150148161014e565b7f150b7a020000000000000000000000000000000000000000000000000000000081149150610147565b80fd5b73ffffffffffffffffffffffffffffffffffffffff8116036101cf57565b600080fd5b9181601f840112156101cf5782359167ffffffffffffffff83116101cf57602083818601950101116101cf57565b50346101cf5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5761023d6004356101b1565b6102486024356101b1565b60643567ffffffffffffffff81116101cf576102689036906004016101d4565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b9181601f840112156101cf5782359167ffffffffffffffff83116101cf576020808501948460051b0101116101cf57565b506040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5767ffffffffffffffff600480358281116101cf5761031290369083016101d4565b90926024359081116101cf5761032b9036908401610294565b9490916001958680540361044b57600287558181036104235760005b8281106103575761001960018055565b61038b61036582858a610bde565b357fff000000000000000000000000000000000000000000000000000000000000001690565b6103a96103a361039c84868a610bf6565b3691610daf565b82611590565b91901590816103f8575b506103c057508701610347565b6103f4879186519384937f2c4029e90000000000000000000000000000000000000000000000000000000085528401610e4c565b0390fd5b7f800000000000000000000000000000000000000000000000000000000000000091501615386103b3565b8483517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b8483517f6f5ffb7e000000000000000000000000000000000000000000000000000000008152fd5b5060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf57600467ffffffffffffffff81358181116101cf576104bf90369084016101d4565b9290916024359081116101cf576104d99036908301610294565b9360443542116105f457600194858054036105cb57600286558181036105a25760005b82811061050c5761001960018055565b61051a610365828589610bde565b61052b6103a361039c848689610bf6565b9190159081610577575b50610542575086016104fc565b6103f486916040519384937f2c4029e90000000000000000000000000000000000000000000000000000000085528401610e4c565b7f80000000000000000000000000000000000000000000000000000000000000009150161538610535565b836040517fff633a38000000000000000000000000000000000000000000000000000000008152fd5b836040517f6f5ffb7e000000000000000000000000000000000000000000000000000000008152fd5b826040517f5bf6f916000000000000000000000000000000000000000000000000000000008152fd5b50346101cf576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5760043567ffffffffffffffff81116101cf5761066e9036906004016101d4565b604092919251928380610686600096879586956124c1565b0390827f0000000000000000000000000554f068365ed43dcc98dcd7fd7a8208a5638c725af16106b4610f1d565b501561086e576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f1e8f03f716bc104bf7d728131967a0c771e85ab54d09c1e2d6ed9e0bc4e2a16c916107f1919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f4d2888d29d722226fafa5d9b24f9164c092421e168183602481845afa928315610861575b8693610832575b506040517fa9059cbb0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000ea37093ce161f090e443f304e1bf3a8f14d7bb4073ffffffffffffffffffffffffffffffffffffffff16600482015260248101849052908290829060449082908a905af18015610825575b6107f7575b50506040519081529081906020820190565b0390a180f35b8161081692903d1061081e575b61080e8183610d25565b8101906124cf565b5038806107df565b503d610804565b61082d610f89565b6107da565b610853919350823d841161085a575b61084b8183610d25565b810190610f7a565b913861075b565b503d610841565b610869610f89565b610754565b60046040517f7d529919000000000000000000000000000000000000000000000000000000008152fd5b50346101cf5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf576108d36004356101b1565b6108de6024356101b1565b67ffffffffffffffff6044358181116101cf576108ff903690600401610294565b50506064358181116101cf57610919903690600401610294565b50506084359081116101cf576109339036906004016101d4565b50506040517fbc197c81000000000000000000000000000000000000000000000000000000008152602090f35b50346101cf5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5761099b6004356101b1565b6109a66024356101b1565b60843567ffffffffffffffff81116101cf576109c69036906004016101d4565b505060206040517ff23a6e61000000000000000000000000000000000000000000000000000000008152f35b50346101cf5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cf5760243560043560443567ffffffffffffffff81116101cf57610a489036906004016101d4565b919060009384831393841580610ba4575b610b7a5782610a6d91610a9a940190613b49565b73ffffffffffffffffffffffffffffffffffffffff80911692610a8f83613bcb565b818398929a93614167565b8333911603610b505715610b425750808616908416105b15610ac65750610ac3935033916131e0565b80f35b915091604282511015600014610b0157610afb9350610ae482613c58565b610af6610af133926143d8565b613b90565b614014565b50505080f35b9192905083548211610b1857610ac39233916131e0565b60046040517f739dbe52000000000000000000000000000000000000000000000000000000008152fd5b945080841690861610610ab1565b60046040517f32b13d91000000000000000000000000000000000000000000000000000000008152fd5b60046040517f316cf0eb000000000000000000000000000000000000000000000000000000008152fd5b5085821315610a59565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90821015610bea570190565b610bf2610bae565b0190565b9190811015610c57575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101cf57019081359167ffffffffffffffff83116101cf5760200182360381136101cf579190565b610c5f610bae565b610c00565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117610cb057604052565b610cb8610c64565b604052565b6060810190811067ffffffffffffffff821117610cb057604052565b67ffffffffffffffff8111610cb057604052565b6020810190811067ffffffffffffffff821117610cb057604052565b6040810190811067ffffffffffffffff821117610cb057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610cb057604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111610da2575b01160190565b610daa610c64565b610d9c565b929192610dbb82610d66565b91610dc96040519384610d25565b8294818452818301116101cf578281602093846000960137010152565b60005b838110610df95750506000910152565b8181015183820152602001610de9565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093610e4581518092818752878088019101610de6565b0116010190565b604090610e63939281528160208201520190610e09565b90565b91908260809103126101cf578151610e7d816101b1565b916020810151610e8c816101b1565b916060604083015192015190565b81601f820112156101cf578051610eb081610d66565b92610ebe6040519485610d25565b818452602082840101116101cf57610e639160208085019101610de6565b9190916040818403126101cf57805192602082015167ffffffffffffffff81116101cf57610e639201610e9a565b60405190610f1782610ced565b60008252565b3d15610f48573d90610f2e82610d66565b91610f3c6040519384610d25565b82523d6000602084013e565b606090565b908160609103126101cf578051610f63816101b1565b9160406020830151610f74816101b1565b92015190565b908160209103126101cf575190565b506040513d6000823e3d90fd5b5190610fa1826101b1565b565b908160209103126101cf5751610e63816101b1565b908160609103126101cf5780519160406020830151610f74816101b1565b60405190610fe382610d09565b601782527f43727970746f50756e6b205472616465204661696c65640000000000000000006020830152565b60009103126101cf57565b60209067ffffffffffffffff8111611034575b60051b0190565b61103c610c64565b61102d565b9060209182818303126101cf5780519067ffffffffffffffff82116101cf570181601f820112156101cf578051926110788461101a565b9360409361108885519687610d25565b818652828087019260071b850101938185116101cf578301915b8483106110b25750505050505090565b6080838303126101cf578360809187516110cb81610c94565b85516110d6816101b1565b8152828601516110e5816101b1565b83820152888601516110f6816101b1565b898201526060808701519061110a826101b1565b8201528152019201916110a2565b91908260409103126101cf5760208251610f74816101b1565b519065ffffffffffff821682036101cf57565b91908260809103126101cf5760405161115c81610c94565b606061119b818395805161116f816101b1565b8552602081015161117f816101b1565b602086015261119060408201611131565b604086015201611131565b910152565b91909180830360e081126101cf5760c0136101cf576040516111c181610cbd565b6111cb8483611144565b815260808201516111db816101b1565b602082015260a082015160408201529260c082015167ffffffffffffffff81116101cf57610e639201610e9a565b90610e63939260409173ffffffffffffffffffffffffffffffffffffffff809116845261127b60208501835160609073ffffffffffffffffffffffffffffffffffffffff80825116845260208201511660208401528165ffffffffffff91826040820151166040860152015116910152565b60208201511660a0840152015160c0820152610100908160e08201520190610e09565b519081151582036101cf57565b9160a0838303126101cf5782516112c1816101b1565b926020918282015193604083015193606084015167ffffffffffffffff81116101cf5784019180601f840112156101cf5782516112fd8161101a565b9361130b6040519586610d25565b818552838086019260051b8201019283116101cf578301905b82821061133c57505050506080610e6391930161129e565b838091835161134a816101b1565b815201910190611324565b9190916040818403126101cf5780519267ffffffffffffffff938481116101cf578201936060858303126101cf5760405161138f81610cbd565b85518281116101cf5786019583601f880112156101cf578651966113b28861101a565b906113c06040519283610d25565b888252602098898084019160071b830101918783116101cf578a809101915b83831061141b57505050509060409183526113fb888201610f96565b8884015201516040820152948301519081116101cf57610e639201610e9a565b906080916114298a85611144565b8152019101908a906113df565b939290919373ffffffffffffffffffffffffffffffffffffffff809316815260209460608683015260c082019381519460608085015285518091528760e0850196019060005b8181106114ac5750505090604091610e639697820151166080840152015160a08201526040818403910152610e09565b909196896080826115016001948c5160609073ffffffffffffffffffffffffffffffffffffffff80825116845260208201511660208401528165ffffffffffff91826040820151166040860152015116910152565b01980192910161147c565b908160609103126101cf578051611522816101b1565b9160406020830151611533816101b1565b920151610e63816101b1565b919060a0838203126101cf578251611556816101b1565b9260208101519260408201519260608301519067ffffffffffffffff82116101cf57611589608091610e63938601610e9a565b930161129e565b600192606092909160f81c601f166010811015611b085760088110156118aa578061161957506115cc81602080610fa19451830101910161153f565b909290156115fa576115f573ffffffffffffffffffffffffffffffffffffffff33955b166124e3565b613d69565b6115f573ffffffffffffffffffffffffffffffffffffffff30956115ef565b60018103611684575061163881602080610fa19451830101910161153f565b909290156116655761166073ffffffffffffffffffffffffffffffffffffffff3395166124e3565b613efb565b61166073ffffffffffffffffffffffffffffffffffffffff30956115ef565b600281036116c557506116a381602080610fa19451830101910161150c565b9173ffffffffffffffffffffffffffffffffffffffff80921691339116612ce5565b600381036117925750806020806116e193518301019101611355565b9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31691823b156101cf5761175f92600092836040518096819582947f2a2d80d10000000000000000000000000000000000000000000000000000000084523360048501611436565b03925af18015611785575b611772575b50565b8061177f610fa192610cd9565b8061100f565b61178d610f89565b61176a565b600481036117da57506117b181602080610fa194518301019101610f4d565b91906117d373ffffffffffffffffffffffffffffffffffffffff8092166124e3565b911661276b565b6005810361182257506117f981602080610fa194518301019101610f4d565b919061181b73ffffffffffffffffffffffffffffffffffffffff8092166124e3565b9116612514565b6006810361186a575061184181602080610fa194518301019101610f4d565b919061186373ffffffffffffffffffffffffffffffffffffffff8092166124e3565b9116612663565b9050600781146118775750565b6040517fd76a1e9e0000000000000000000000000000000000000000000000000000000081526004810191909152602490fd5b6008810361191557506118c981602080610fa1945183010191016112ab565b909290156118f6576118f173ffffffffffffffffffffffffffffffffffffffff3395166124e3565b612fbe565b6118f173ffffffffffffffffffffffffffffffffffffffff30956115ef565b60098103611980575061193481602080610fa1945183010191016112ab565b909290156119615761195c73ffffffffffffffffffffffffffffffffffffffff3395166124e3565b61387e565b61195c73ffffffffffffffffffffffffffffffffffffffff30956115ef565b600a8103611a1a57508060208061199c935183010191016111a0565b9073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31691823b156101cf5761175f92600092836040518096819582947f2b67b5700000000000000000000000000000000000000000000000000000000084523360048501611209565b600b8103611a5d575073ffffffffffffffffffffffffffffffffffffffff611a58611a5183602080610fa196518301019101611118565b92166124e3565b612a14565b600c8103611a99575073ffffffffffffffffffffffffffffffffffffffff611a94611a5183602080610fa196518301019101611118565b612baf565b600d8103611abd5750611ab881602080610fa194518301019101611041565b612e78565b92919050600e8303611afb576040517fd76a1e9e00000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b9091600f81146118775750565b919290916018811015611ffd5760108103611b6f5750506000919250611b38816020808594518301019101610edc565b90602082519201907f00000000000000000000000000000000006c3852cbef3e08e8df289169ede5815af1611b6b610f1d565b9091565b60118103611ba6575050611b6b9192507f00000000000000000000000059728544b08ab483533076417fbbb2fd0b17ce3a906121e6565b60128103611bfc5750506000919250611bc9816020808594518301019101610edc565b90602082519201907f0000000000000000000000000fc584529a2aefa997697fafacba5831fac0c22d5af1611b6b610f1d565b60138103611d61575050611c1b91925060208082518301019101610fb8565b9290927f000000000000000000000000b47e3cd837ddf8e4c57f05d70ab865de6e193bbb9260405160208101907f8264fe98000000000000000000000000000000000000000000000000000000008252611cad81611c8185602483019190602083019252565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610d25565b600093849283925191885af194611cc2610f1d565b948615611d5357611cec9073ffffffffffffffffffffffffffffffffffffffff80911692166124e3565b813b15611d4f576040517f8b72a2ec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602481019290925290919082908183816044810161175f565b8380fd5b505050509050610e63610fd6565b60148103611d98575050611b6b9192507f00000000000000000000000059728544b08ab483533076417fbbb2fd0b17ce3a90612386565b9092919060158103611eae5750611dc19350611dfc906020948186808094518301019101610f4d565b604093919351809581927f6352211e000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b038173ffffffffffffffffffffffffffffffffffffffff8096165afa928315611ea1575b600093611e70575b508116911614928315611e385750565b9091507f7dbe7e89000000000000000000000000000000000000000000000000000000006040519182015260048152610e6381610d09565b82919350611e9390873d8911611e9a575b611e8b8183610d25565b810190610fa3565b9290611e28565b503d611e81565b611ea9610f89565b611e20565b60168103611fb45750611ed29350611f306020948286808095518301019101610e66565b6040517efdd58e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff94851660048201526024810192909252949093909284929183919082906044820190565b0392165afa908115611fa7575b600091611f8a575b501092831593611f525750565b9091507f483a6929000000000000000000000000000000000000000000000000000000006040519182015260048152610e6381610d09565b611fa19150853d871161085a5761084b8183610d25565b38611f45565b611faf610f89565b611f3d565b601714611fbe5750565b611fd481602080610fa194518301019101610f4d565b9190611ff673ffffffffffffffffffffffffffffffffffffffff8092166124e3565b9116612888565b60188103612034575050611b6b9192507f00000000000000000000000074312363e45dcaba76c59ec49a7aa8a65a67eed3906121e6565b6019810361208a5750506000919250612057816020808594518301019101610edc565b90602082519201907f0000000000000000000000002b2e8cda09bba9660dca5cb6233787738ad683295af1611b6b610f1d565b601a81036120e057505060009192506120ad816020808594518301019101610edc565b90602082519201907f000000000000000000000000a42f6cada809bcf417deefbdd69c5c5a909249c05af1611b6b610f1d565b601b8103612117575050611b6b9192507f00000000000000000000000074312363e45dcaba76c59ec49a7aa8a65a67eed390612386565b601c810361214e575050611b6b9192507f000000000000000000000000cda72070e455bb31c7690a170224ce43623d0b6f906121e6565b90929190601d810361219b575061217181602080610fa194518301019101610e66565b92909161219473ffffffffffffffffffffffffffffffffffffffff8092166124e3565b9116612928565b92919050601e83036121d9576040517fd76a1e9e00000000000000000000000000000000000000000000000000000000815260048101849052602490fd5b9091601f81146118775750565b9091815182019260a0838503126101cf57602083015193604084015167ffffffffffffffff81116101cf57602080612222930191860101610e9a565b90606084015194612232866101b1565b60a0608086015195612243876101b1565b01519173ffffffffffffffffffffffffffffffffffffffff8096169160009485928392602083519301915af195612278610f1d565b9587612286575b5050505050565b61229091166124e3565b813b15611d4f576040517f42842e0e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116602482015260448101929092529091908290606490829084905af1801561231f575b61230c575b8080808061227f565b8061177f61231992610cd9565b38612303565b612327610f89565b6122fe565b60405161233881610ced565b60008152906000368137565b9192610e6395949160a09473ffffffffffffffffffffffffffffffffffffffff8092168552166020840152604083015260608201528160808201520190610e09565b9091815182019160c0818403126101cf57602081015192604082015167ffffffffffffffff81116101cf576020806123c2930191840101610e9a565b6060820151946123d1866101b1565b6080830151946123e0866101b1565b60c060a08501519401519173ffffffffffffffffffffffffffffffffffffffff8097169160009485928392602083519301915af19661241d610f1d565b968861242c575b505050505050565b61243691166124e3565b9361243f61232c565b94823b156124bd578490612483604051978896879586947ff242432a0000000000000000000000000000000000000000000000000000000086523060048701612344565b03925af180156124b0575b61249d575b8080808080612424565b8061177f6124aa92610cd9565b38612493565b6124b8610f89565b61248e565b8480fd5b908092918237016000815290565b908160209103126101cf57610e639061129e565b73ffffffffffffffffffffffffffffffffffffffff8116600181036125085750503390565b600203610e6357503090565b73ffffffffffffffffffffffffffffffffffffffff1691908261253b57610fa192506142b9565b610fa1927f800000000000000000000000000000000000000000000000000000000000000083036143275791506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152602081602481865afa9081156125d2575b6000916125b4575b5091614327565b6125cc915060203d811161085a5761084b8183610d25565b386125ad565b6125da610f89565b6125a5565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181029291811591840414171561262257565b610fa16125df565b8115612634570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b82158015612760575b6127365773ffffffffffffffffffffffffffffffffffffffff16806126aa57506126a461269c610fa1934761260f565b612710900490565b906142b9565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610fa1939192916127059161269c9190602081602481895afa908115612729575b60009161270b575b5061260f565b91614327565b612723915060203d811161085a5761084b8183610d25565b386126ff565b612731610f89565b6126f7565b60046040517fdeaa01e6000000000000000000000000000000000000000000000000000000008152fd5b50612710831161266c565b90919073ffffffffffffffffffffffffffffffffffffffff16806127ce5750479081106127a4578061279b575050565b610fa1916142b9565b60046040517f6a12f104000000000000000000000000000000000000000000000000000000008152fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290929091602083602481875afa92831561287b575b60009361285b575b508210612831578161282857505050565b610fa192614327565b60046040517f675cae38000000000000000000000000000000000000000000000000000000008152fd5b61287491935060203d811161085a5761084b8183610d25565b9138612817565b612883610f89565b61280f565b73ffffffffffffffffffffffffffffffffffffffff1691823b156101cf576040517f42842e0e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9290921660248301526044820152906000908290606490829084905af1801561291b575b6129125750565b610fa190610cd9565b612923610f89565b61290b565b6040517efdd58e00000000000000000000000000000000000000000000000000000000815230600482015260248101849052929391929173ffffffffffffffffffffffffffffffffffffffff9190911690602083604481855afa928315612a07575b6000936129e7575b508210612831576129a1610f0a565b93813b156101cf576000809461175f604051978896879586947ff242432a0000000000000000000000000000000000000000000000000000000086523060048701612344565b612a0091935060203d811161085a5761084b8183610d25565b9138612992565b612a0f610f89565b61298a565b907f80000000000000000000000000000000000000000000000000000000000000008103612b7c575047905b81612a49575050565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691823b156101cf57612b26926020926040517fd0e30db000000000000000000000000000000000000000000000000000000000815260008160048187875af18015612b6f575b612b5c575b5060006040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015612b4f575b612b385750565b61176f9060203d811161081e5761080e8183610d25565b612b57610f89565b612b31565b8061177f612b6992610cd9565b38612acb565b612b77610f89565b612ac6565b9047821115612a405760046040517f6a12f104000000000000000000000000000000000000000000000000000000008152fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21692909190602083602481875afa928315612cd8575b600093612cb8575b5082106127a45781612c4057505050565b823b156101cf576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052610fa1936000908290602490829084905af18015612cab575b612c98575b506142b9565b8061177f612ca592610cd9565b38612c92565b612cb3610f89565b612c8d565b612cd191935060203d811161085a5761084b8183610d25565b9138612c2f565b612ce0610f89565b612c27565b919273ffffffffffffffffffffffffffffffffffffffff91827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31693843b156101cf5760009484869281608496816040519b8c9a8b997f36c78516000000000000000000000000000000000000000000000000000000008b521660048a01521660248801521660448601521660648401525af1801561291b576129125750565b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612db3570190565b610bf26125df565b602090805115610bea570190565b604090805160011015610bea570190565b6020918151811015612def575b60051b010190565b612df7610bae565b612de7565b60208082019080835283518092528060408094019401926000905b838210612e2657505050505090565b8451805173ffffffffffffffffffffffffffffffffffffffff90811688528185015181168886015281830151811688840152606091820151169087015260809095019493820193600190910190612e17565b805160005b818110612f0257505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba316803b156101cf5761175f6000929183926040519485809481937f0d58b1db00000000000000000000000000000000000000000000000000000000835260048301612dfc565b33612f47612f2e612f138487612dda565b515173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b03612f5a57612f5590612d85565b612e7d565b60046040517fe7002877000000000000000000000000000000000000000000000000000000008152fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161262257565b9190820391821161262257565b61312893919294613042612fee612fd485612dbb565b5173ffffffffffffffffffffffffffffffffffffffff1690565b612ffa612fd486612dc9565b907f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f7f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f61324a565b9384816131c3575b505050613069612f2e612f2e612fd46130638651612f84565b86612dda565b6040517f70a082310000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff8416600483015290946020948587602481875afa9687156131b6575b600097613183575b50916130dc8694928661310d9795613452565b60405180958194829383526004830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa918215613176575b600092613159575b5050612fb1565b1061312f57565b60046040517f849eaf98000000000000000000000000000000000000000000000000000000008152fd5b61316f9250803d1061085a5761084b8183610d25565b3880613121565b61317e610f89565b613119565b859391975086949261310d966131a86130dc93883d8a1161085a5761084b8183610d25565b9993955096509294506130c9565b6131be610f89565b6130c1565b6131d8926131d3612fd487612dbb565b6131e0565b38808461304a565b92919073ffffffffffffffffffffffffffffffffffffffff808216300361320c575050610fa192612514565b808495941161322057610fa1941692612ce5565b60046040517fc4bd89a9000000000000000000000000000000000000000000000000000000008152fd5b9091610e6393613259916133a4565b9290915b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa06133909161336373ffffffffffffffffffffffffffffffffffffffff96946040519260208401967fffffffffffffffffffffffffffffffffffffffff000000000000000000000000809260601b16885260601b16603484015260288352606083019583871067ffffffffffffffff881117613397575b8660405283519020608084019788917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000605594927fff00000000000000000000000000000000000000000000000000000000000000855260601b166001840152601583015260358201520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80810184520182610d25565b5190201690565b61339f610c64565b6132f5565b73ffffffffffffffffffffffffffffffffffffffff8281169082161015611b6b5791565b51906dffffffffffffffffffffffffffff821682036101cf57565b908160609103126101cf576133f7816133c8565b916040613406602084016133c8565b92015163ffffffff811681036101cf5790565b90610e63949360809373ffffffffffffffffffffffffffffffffffffffff92845260208401521660408201528160608201520190610e09565b90600292838351106137945761347f61346d612fd485612dbb565b613479612fd486612dc9565b906133a4565b508351937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86019501906000935b8685106134df575050505050505050565b6134ec612fd48684612dda565b906134fd612fd46001880185612dda565b73ffffffffffffffffffffffffffffffffffffffff928383169660409081519485937f0902f1ac00000000000000000000000000000000000000000000000000000000855260609788868d60049889915afa978815613787575b6000998a99613748575b50508061360695969798996dffffffffffffffffffffffffffff8091169a16921693168314978860001461373e57918291935b87875180927f70a0823100000000000000000000000000000000000000000000000000000000825281806135ea6020978896830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b03915afa918215613731575b600092613714575b5050036137d7565b931561370b578a600094935b878a10156137005761362c612fd4613674938c0189612dda565b907f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f7f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f6137be565b9390935b9761368161232c565b95813b156101cf57600086956136c7600199839751988997889687957f022c0d9f0000000000000000000000000000000000000000000000000000000087528601613419565b03925af180156136f3575b6136e0575b509401936134ce565b8061177f6136ed92610cd9565b386136d7565b6136fb610f89565b6136d2565b505088926000613678565b8a600093613612565b61372a9250803d1061085a5761084b8183610d25565b38806135fe565b613739610f89565b6135f6565b9091829193613594565b829a506136069697989950908161377392903d10613780575b61376b8183610d25565b8101906133e3565b5099909998979695613561565b503d613761565b61378f610f89565b613557565b60046040517fae52ad0c000000000000000000000000000000000000000000000000000000008152fd5b926137cc906137d4936133a4565b91819461325d565b91565b811590818015613876575b61384c57613808610e63946103e59283810293818504149015171561383f575b8261260f565b916103e8808502948504141715613832575b82018092111561262a575b61382d6125df565b61262a565b61383a6125df565b61381a565b6138476125df565b613802565b60046040517f7b9c8916000000000000000000000000000000000000000000000000000000008152fd5b5083156137e2565b91939290927f0000000000000000000000005c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f947f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f9560009560028551106139d257968451917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff928381019081116139c5575b929190835b61395b5750505050851161393157610fa1948461392c926131d3612fd486612dbb565b613452565b60046040517f8ab0bc16000000000000000000000000000000000000000000000000000000008152fd5b929897509091826139996139928b61398a612fd4613983818488018e8682116139b857612dda565b928c612dda565b9086866139fc565b919b613abc565b9980156139ab575b0192919083613909565b6139b36125df565b6139a1565b6139c06125df565b612dda565b6139cd6125df565b613904565b60046040517f20db8267000000000000000000000000000000000000000000000000000000008152fd5b919392906137cc613a0d92866133a4565b92604051907f0902f1ac00000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff606083600481848a165afa928315613aaf575b6000908194613a8d575b5081906dffffffffffffffffffffffffffff80911694169416911614600014611b6b5791565b829450613aa8915060603d81116137805761376b8183610d25565b5093613a67565b613ab7610f89565b613a5d565b909182158015613b41575b61384c57613ad882613b119461260f565b906103e891828102928184041490151715613b34575b82810392818411613b27575b6103e580850294850414911417156138255761262a565b60018101809111613b1f5790565b610e636125df565b613b2f6125df565b613afa565b613b3c6125df565b613aee565b508015613ac7565b91906040838203126101cf57823567ffffffffffffffff81116101cf57830181601f820112156101cf576020918183613b8493359101610daf565b920135610e63816101b1565b7f80000000000000000000000000000000000000000000000000000000000000008114613bbe575b60000390565b613bc66125df565b613bb8565b908151613bd88184613c49565b9260178210613c1f57602b60178201519210613bf557602b015191565b60046040517fa78aa27f000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd9096a3e000000000000000000000000000000000000000000000000000000008152fd5b90601411613bf5576014015190565b8051907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe99182810192818411613d5c575b836008830110613d325760178210613d325781835110613d085760178214613cde57601f8416801560051b0183019182010160178201915b818110613cce5750505052565b8251815260209283019201613cc1565b60046040517fcc94a63a000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3b99b53d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f47aaf07a000000000000000000000000000000000000000000000000000000008152fd5b613d646125df565b613c89565b93909192937f80000000000000000000000000000000000000000000000000000000000000008314613e34575b90613dc5613dd3915b613dae604288511015956143d8565b8515613e2e57305b613dbf89613ecd565b91614126565b90919015613e275750613b90565b9115613df357613dc5613dd3913090613deb87613c58565b929190613d9f565b50109050613dfd57565b60046040517f39d35496000000000000000000000000000000000000000000000000000000008152fd5b9050613b90565b84613db6565b9150613dc5613dd391613e4e612f2e612f2e885189613c49565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290602090829060249082905afa908115613ec0575b600091613ea2575b509391509150613d96565b613eba915060203d811161085a5761084b8183610d25565b38613e97565b613ec8610f89565b613e8f565b90602b825110613d0857602b60405192600b810151600b8501520151602b830152602b825260608201604052565b613f1193919492600055610af6610af1866143d8565b90919015613f785750613f2390613b90565b03613f4e577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600055565b60046040517fd4e0248e000000000000000000000000000000000000000000000000000000008152fd5b613f829150613b90565b613f23565b9073ffffffffffffffffffffffffffffffffffffffff613fb4602092959495604085526040850190610e09565b9416910152565b91908260409103126101cf576020825192015190565b919360a093610e63969573ffffffffffffffffffffffffffffffffffffffff80941685521515602085015260408401521660608201528160808201520190610e09565b612f2e9293612f2e60006040946140b26140596140308a613bcb565b73ffffffffffffffffffffffffffffffffffffffff9b9297919b808916908d16109b8c98614167565b948484146141085761407d6401000276a49a5b611c818a5193849260208401613f87565b8751998a97889687957f128acb0800000000000000000000000000000000000000000000000000000000875260048701613fd1565b03925af180156140fb575b60009283916140cb57509192565b90506140ef91925060403d81116140f4575b6140e78183610d25565b810190613fbb565b919092565b503d6140dd565b614103610f89565b6140bd565b61407d73fffd8963efd1fc6a506488495d951d5263988d259a61406c565b612f2e9293612f2e60006040946140b26140596141428a613bcb565b73ffffffffffffffffffffffffffffffffffffffff9b9297919b808d16908916109b8c985b73ffffffffffffffffffffffffffffffffffffffff92838316848316116142b1575b62ffffff908460405194816020870195168552166040850152166060830152606082526133907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80608084019284841067ffffffffffffffff8511176142a4575b6040849052845190207fff0000000000000000000000000000000000000000000000000000000000000060a086019081527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f0000000000000000000000001f98431c8ad98523631ae4a59f267346ea31f98460601b1660a187015260b58601919091527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460d5909501949094526055835260f50182610d25565b6142ac610c64565b6141e9565b909190614189565b600080809381935af1156142c957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c4544000000000000000000000000006044820152fd5b60009182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d116001600051141617161561437a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b7f80000000000000000000000000000000000000000000000000000000000000008110156101cf579056fea26469706673582212202f2e114dd73237126f72d60b80c2baf2d9fc70c6d00948608ca4fefdc9bf009064736f6c63430008110033', + }, + }; + }); +} diff --git a/ui/pages/confirmations/components/confirm/info/approve/__snapshots__/approve.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/approve/__snapshots__/approve.test.tsx.snap index 19050e5f3f37..8cfe63f27147 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/__snapshots__/approve.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/approve/__snapshots__/approve.test.tsx.snap @@ -780,6 +780,7 @@ exports[` renders component for approve request 1`] = `
+ {method.params.map((param, paramIndex) => ( Date: Wed, 27 Nov 2024 09:06:27 -0330 Subject: [PATCH 10/22] chore: Bump `@metamask/ens-controller` from v13 to v14 (#28746) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Bump `@metamask/ens-controller` to v14. There are two minor breaking changes that impact the constructor and messenger. Changelog: https://github.com/MetaMask/core/blob/main/packages/ens-controller/CHANGELOG.md#1400 This update resolves a peer dependency warning about the ENS controller's dependence upon the network controller (it was expecting v20, but we had v21). [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28746?quickstart=1) ## **Related issues** Relates to https://github.com/MetaMask/MetaMask-planning/issues/3568 ## **Manual testing steps** N/A ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: MetaMask Bot --- app/scripts/metamask-controller.js | 6 ++++-- lavamoat/browserify/beta/policy.json | 10 +--------- lavamoat/browserify/flask/policy.json | 10 +--------- lavamoat/browserify/main/policy.json | 10 +--------- lavamoat/browserify/mmi/policy.json | 10 +--------- package.json | 2 +- yarn.lock | 18 +++++++++--------- 7 files changed, 18 insertions(+), 48 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 39e34955cac2..166322b1ee78 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1052,10 +1052,12 @@ export default class MetamaskController extends EventEmitter { this.ensController = new EnsController({ messenger: this.controllerMessenger.getRestricted({ name: 'EnsController', - allowedActions: ['NetworkController:getNetworkClientById'], + allowedActions: [ + 'NetworkController:getNetworkClientById', + 'NetworkController:getState', + ], allowedEvents: [], }), - provider: this.provider, onNetworkDidChange: networkControllerMessenger.subscribe.bind( networkControllerMessenger, 'NetworkController:networkDidChange', diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index cf72074493e6..88a9a9db85a0 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -794,20 +794,12 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/ens-controller>@metamask/base-controller": true, "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, - "@metamask/ens-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index cf72074493e6..88a9a9db85a0 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -794,20 +794,12 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/ens-controller>@metamask/base-controller": true, "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, - "@metamask/ens-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index cf72074493e6..88a9a9db85a0 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -794,20 +794,12 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/ens-controller>@metamask/base-controller": true, "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, - "@metamask/ens-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index e70102a63e51..3f79af87a50b 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -886,20 +886,12 @@ "@metamask/ens-controller": { "packages": { "@ethersproject/providers": true, + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/ens-controller>@metamask/base-controller": true, "@metamask/ens-controller>@metamask/utils": true, "punycode": true } }, - "@metamask/ens-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/ens-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/package.json b/package.json index 5304df2c2ff8..e7e74534d3e3 100644 --- a/package.json +++ b/package.json @@ -295,7 +295,7 @@ "@metamask/contract-metadata": "^2.5.0", "@metamask/controller-utils": "^11.4.0", "@metamask/design-tokens": "^4.0.0", - "@metamask/ens-controller": "^13.0.0", + "@metamask/ens-controller": "^14.0.0", "@metamask/ens-resolver-snap": "^0.1.2", "@metamask/eth-json-rpc-filters": "^9.0.0", "@metamask/eth-json-rpc-middleware": "patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch", diff --git a/yarn.lock b/yarn.lock index 88e84e6d37b1..8f040e67853b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5089,7 +5089,7 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0, @metamask/controller-utils@npm:^11.4.0, @metamask/controller-utils@npm:^11.4.1, @metamask/controller-utils@npm:^11.4.2, @metamask/controller-utils@npm:^11.4.3": +"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0, @metamask/controller-utils@npm:^11.4.0, @metamask/controller-utils@npm:^11.4.1, @metamask/controller-utils@npm:^11.4.2, @metamask/controller-utils@npm:^11.4.3": version: 11.4.3 resolution: "@metamask/controller-utils@npm:11.4.3" dependencies: @@ -5114,18 +5114,18 @@ __metadata: languageName: node linkType: hard -"@metamask/ens-controller@npm:^13.0.0": - version: 13.0.1 - resolution: "@metamask/ens-controller@npm:13.0.1" +"@metamask/ens-controller@npm:^14.0.0": + version: 14.0.1 + resolution: "@metamask/ens-controller@npm:14.0.1" dependencies: "@ethersproject/providers": "npm:^5.7.0" - "@metamask/base-controller": "npm:^6.0.2" - "@metamask/controller-utils": "npm:^11.0.2" + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/controller-utils": "npm:^11.3.0" "@metamask/utils": "npm:^9.1.0" punycode: "npm:^2.1.1" peerDependencies: - "@metamask/network-controller": ^20.0.0 - checksum: 10/eb3516ac444244f09a8746c0a103699c8482c34c4d0e3f0c82e7a7185f5c554e53c8d3392567c5635d1d7d76b97a15038ab8f7b4d597f033539ba0dbeaacc710 + "@metamask/network-controller": ^21.0.0 + checksum: 10/1b57a781f4c53d7e60afda11b3994e977af1149aa5651c20b4dc56010de597fd9c9ada28847491d3fe862f0a8f08b96b17a759f742e870ca5911609e07f5dc6c languageName: node linkType: hard @@ -26837,7 +26837,7 @@ __metadata: "@metamask/contract-metadata": "npm:^2.5.0" "@metamask/controller-utils": "npm:^11.4.0" "@metamask/design-tokens": "npm:^4.0.0" - "@metamask/ens-controller": "npm:^13.0.0" + "@metamask/ens-controller": "npm:^14.0.0" "@metamask/ens-resolver-snap": "npm:^0.1.2" "@metamask/eslint-config": "npm:^9.0.0" "@metamask/eslint-config-jest": "npm:^9.0.0" From afd7e543935f36c98dc3de705f78a8a1187af754 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 27 Nov 2024 09:06:39 -0330 Subject: [PATCH 11/22] chore: Bump `@metamask/permission-log-controller` to v3.0.1 (#28747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Update `@metamask/permission-log-controller` from v2.0.1 to v3.0.1. No breaking changes that impact the extension. Changelog: https://github.com/MetaMask/core/blob/main/packages/permission-log-controller/CHANGELOG.md#301 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28747?quickstart=1) ## **Related issues** Relates to https://github.com/MetaMask/MetaMask-planning/issues/3568 ## **Manual testing steps** N/A ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: MetaMask Bot --- lavamoat/browserify/beta/policy.json | 10 +-------- lavamoat/browserify/flask/policy.json | 10 +-------- lavamoat/browserify/main/policy.json | 10 +-------- lavamoat/browserify/mmi/policy.json | 10 +-------- package.json | 2 +- yarn.lock | 30 +++++++++------------------ 6 files changed, 15 insertions(+), 57 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 88a9a9db85a0..a1af28ef6669 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2174,18 +2174,10 @@ }, "@metamask/permission-log-controller": { "packages": { - "@metamask/permission-log-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/permission-log-controller>@metamask/utils": true } }, - "@metamask/permission-log-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/permission-log-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 88a9a9db85a0..a1af28ef6669 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2174,18 +2174,10 @@ }, "@metamask/permission-log-controller": { "packages": { - "@metamask/permission-log-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/permission-log-controller>@metamask/utils": true } }, - "@metamask/permission-log-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/permission-log-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 88a9a9db85a0..a1af28ef6669 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2174,18 +2174,10 @@ }, "@metamask/permission-log-controller": { "packages": { - "@metamask/permission-log-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/permission-log-controller>@metamask/utils": true } }, - "@metamask/permission-log-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/permission-log-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 3f79af87a50b..574da95682be 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2266,18 +2266,10 @@ }, "@metamask/permission-log-controller": { "packages": { - "@metamask/permission-log-controller>@metamask/base-controller": true, + "@metamask/base-controller": true, "@metamask/permission-log-controller>@metamask/utils": true } }, - "@metamask/permission-log-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, "@metamask/permission-log-controller>@metamask/utils": { "globals": { "TextDecoder": true, diff --git a/package.json b/package.json index e7e74534d3e3..abd56565d35d 100644 --- a/package.json +++ b/package.json @@ -327,7 +327,7 @@ "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", "@metamask/permission-controller": "^11.0.0", - "@metamask/permission-log-controller": "^2.0.1", + "@metamask/permission-log-controller": "^3.0.1", "@metamask/phishing-controller": "^12.3.0", "@metamask/polling-controller": "^10.0.1", "@metamask/post-message-stream": "^8.0.0", diff --git a/yarn.lock b/yarn.lock index 8f040e67853b..922fc470e8e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5026,16 +5026,6 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@npm:^5.0.1": - version: 5.0.2 - resolution: "@metamask/base-controller@npm:5.0.2" - dependencies: - "@metamask/utils": "npm:^8.3.0" - immer: "npm:^9.0.6" - checksum: 10/f9c142766d8cdb69c0cc93aa5cfdaeae97a8c126a5f30f75d31bfdebbc57e82574dc5a3743eceb9e3106d182d066d1517fb73991bb2d06d861d25fd1dac87dcc - languageName: node - linkType: hard - "@metamask/base-controller@npm:^6.0.0, @metamask/base-controller@npm:^6.0.2": version: 6.0.3 resolution: "@metamask/base-controller@npm:6.0.3" @@ -5747,7 +5737,7 @@ __metadata: languageName: node linkType: hard -"@metamask/json-rpc-engine@npm:^8.0.1, @metamask/json-rpc-engine@npm:^8.0.2": +"@metamask/json-rpc-engine@npm:^8.0.2": version: 8.0.2 resolution: "@metamask/json-rpc-engine@npm:8.0.2" dependencies: @@ -5758,7 +5748,7 @@ __metadata: languageName: node linkType: hard -"@metamask/json-rpc-engine@npm:^9.0.0, @metamask/json-rpc-engine@npm:^9.0.2": +"@metamask/json-rpc-engine@npm:^9.0.0, @metamask/json-rpc-engine@npm:^9.0.2, @metamask/json-rpc-engine@npm:^9.0.3": version: 9.0.3 resolution: "@metamask/json-rpc-engine@npm:9.0.3" dependencies: @@ -6146,14 +6136,14 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-log-controller@npm:^2.0.1": - version: 2.0.1 - resolution: "@metamask/permission-log-controller@npm:2.0.1" +"@metamask/permission-log-controller@npm:^3.0.1": + version: 3.0.1 + resolution: "@metamask/permission-log-controller@npm:3.0.1" dependencies: - "@metamask/base-controller": "npm:^5.0.1" - "@metamask/json-rpc-engine": "npm:^8.0.1" - "@metamask/utils": "npm:^8.3.0" - checksum: 10/e918579a44365d1eb3d100870efc207e3125f7ba3c0fb0a736f1301162b132e77b041142f358438fe0db6644beaac1e5541c9e4ff4ba126e9f4110ab0032e0f6 + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/json-rpc-engine": "npm:^9.0.3" + "@metamask/utils": "npm:^9.1.0" + checksum: 10/90ca40c0c3da705db907ad9c6d1ffaf2ad3ca080313b9d114c1b449635f774d0781a1d9505c607b63c219e5fd3d1fe20c973cf0c7bcc038735a84275d01110a4 languageName: node linkType: hard @@ -26877,7 +26867,7 @@ __metadata: "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/obs-store": "npm:^9.0.0" "@metamask/permission-controller": "npm:^11.0.0" - "@metamask/permission-log-controller": "npm:^2.0.1" + "@metamask/permission-log-controller": "npm:^3.0.1" "@metamask/phishing-controller": "npm:^12.3.0" "@metamask/phishing-warning": "npm:^4.1.0" "@metamask/polling-controller": "npm:^10.0.1" From ddb4c97da0b35f295a5682be3f84a52cf5ccc316 Mon Sep 17 00:00:00 2001 From: Salim TOUBAL Date: Wed, 27 Nov 2024 14:00:08 +0100 Subject: [PATCH 12/22] fix: fix transaction list message on token detail page (#28764) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #28766 ## **Description** If the token chosen in the token details does not correspond to the current network, the message displayed in the activity section should be updated accordingly. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28764?quickstart=1) ## **Related issues** Fixes: #28766 ## **Manual testing steps** 1. run `PORTFOLIO_VIEW=true yarn start` 2. choose any token who is not part of current network and go to token details 3. go to activity section and check the message ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/user-attachments/assets/e0d379b8-dd37-4e87-a845-ff35a08deb76 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/_locales/en/messages.json | 7 ++ .../transaction-list.component.js | 17 ++++- .../transaction-list/transaction-list.test.js | 65 +++++++++++++++++++ ui/pages/asset/components/asset-page.tsx | 4 +- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index e9e9cc807ccd..3b8b2bfa9682 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3403,6 +3403,13 @@ "noTransactions": { "message": "You have no transactions" }, + "noTransactionsChainIdMismatch": { + "message": "Please switch network to view transactions" + }, + "noTransactionsNetworkName": { + "message": "Please switch to $1 network to view transactions", + "description": "$1 represents the network name" + }, "noWebcamFound": { "message": "Your computer's webcam was not found. Please try again." }, diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js index 2fd7d0bc6d1a..47b422457143 100644 --- a/ui/components/app/transaction-list/transaction-list.component.js +++ b/ui/components/app/transaction-list/transaction-list.component.js @@ -55,6 +55,7 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; import { getMultichainNetwork } from '../../../selectors/multichain'; import { endTrace, TraceName } from '../../../../shared/lib/trace'; +import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks'; const PAGE_INCREMENT = 10; @@ -140,6 +141,7 @@ export default function TransactionList({ hideTokenTransactions, tokenAddress, boxProps, + tokenChainId, }) { const [limit, setLimit] = useState(PAGE_INCREMENT); const t = useI18nContext(); @@ -151,7 +153,16 @@ export default function TransactionList({ nonceSortedCompletedTransactionsSelector, ); const chainId = useSelector(getCurrentChainId); + const networkConfigurationsByChainId = useSelector( + getNetworkConfigurationsByChainId, + ); + const networkName = networkConfigurationsByChainId[tokenChainId]?.name; const selectedAccount = useSelector(getSelectedAccount); + const isChainIdMismatch = tokenChainId && tokenChainId !== chainId; + + const noTransactionsMessage = networkName + ? t('noTransactionsNetworkName', [networkName]) + : t('noTransactionsChainIdMismatch'); ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const shouldHideZeroBalanceTokens = useSelector( @@ -383,7 +394,9 @@ export default function TransactionList({ ) : ( - {t('noTransactions')} + {isChainIdMismatch + ? noTransactionsMessage + : t('noTransactions')} )} @@ -407,10 +420,12 @@ TransactionList.propTypes = { hideTokenTransactions: PropTypes.bool, tokenAddress: PropTypes.string, boxProps: PropTypes.object, + tokenChainId: PropTypes.string, }; TransactionList.defaultProps = { hideTokenTransactions: false, tokenAddress: undefined, boxProps: undefined, + tokenChainId: null, }; diff --git a/ui/components/app/transaction-list/transaction-list.test.js b/ui/components/app/transaction-list/transaction-list.test.js index e6e42ab78fc5..ca23f364166b 100644 --- a/ui/components/app/transaction-list/transaction-list.test.js +++ b/ui/components/app/transaction-list/transaction-list.test.js @@ -85,4 +85,69 @@ describe('TransactionList', () => { }, }); }); + + it('renders TransactionList component and shows Chain ID mismatch text if network name is not available', () => { + const store = configureStore(defaultState); + + const { getByText } = renderWithProvider( + + + , + store, + ); + expect( + getByText('Please switch network to view transactions'), + ).toBeInTheDocument(); + }); + + it('renders TransactionList component and shows network name text', () => { + const defaultState2 = { + metamask: { + ...mockState.metamask, + selectedNetworkClientId: 'mainnet', + networkConfigurationsByChainId: { + '0x1': { + blockExplorerUrls: [], + chainId: '0x1', + defaultRpcEndpointIndex: 0, + name: 'Mainnet', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + networkClientId: 'mainnet', + type: 'infura', + url: 'https://mainnet.infura.io/v3/{infuraProjectId}', + }, + ], + }, + '0xe708': { + blockExplorerUrls: [], + chainId: '0xe708', + defaultRpcEndpointIndex: 0, + name: 'Linea Mainnet', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + networkClientId: 'linea-mainnet', + type: 'infura', + url: 'https://linea-mainnet.infura.io/v3/{infuraProjectId}', + }, + ], + }, + }, + transactions: [], + }, + }; + const store = configureStore(defaultState2); + + const { getByText } = renderWithProvider( + + + , + store, + ); + expect( + getByText('Please switch to Linea Mainnet network to view transactions'), + ).toBeInTheDocument(); + }); }); diff --git a/ui/pages/asset/components/asset-page.tsx b/ui/pages/asset/components/asset-page.tsx index 9100126d54fe..570528cd1acb 100644 --- a/ui/pages/asset/components/asset-page.tsx +++ b/ui/pages/asset/components/asset-page.tsx @@ -428,9 +428,9 @@ const AssetPage = ({ {t('yourActivity')} {type === AssetType.native ? ( - + ) : ( - + )} From afe6dc0007c91ce79b495c916de50d35411a37b4 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 27 Nov 2024 19:38:48 +0530 Subject: [PATCH 13/22] feat: adding metrics for signature decoding (#28719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adding metrics for signature decoding. ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/3644 ## **Manual testing steps** 1. Enable signature deccoding locally 2. Open permit page 3. Check that metrics are recorded for signature decoding information ## **Screenshots/Recordings** NA ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../signatures/signature-helpers.ts | 20 +++ .../permit-simulation.test.tsx.snap | 6 +- .../permit-simulation.test.tsx | 21 +++ .../permit-simulation/permit-simulation.tsx | 3 + .../info/typed-sign/typed-sign.test.tsx | 1 + .../hooks/useDecodedSignatureMetrics.test.ts | 135 ++++++++++++++++++ .../hooks/useDecodedSignatureMetrics.ts | 45 ++++++ 7 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 ui/pages/confirmations/hooks/useDecodedSignatureMetrics.test.ts create mode 100644 ui/pages/confirmations/hooks/useDecodedSignatureMetrics.ts diff --git a/test/e2e/tests/confirmations/signatures/signature-helpers.ts b/test/e2e/tests/confirmations/signatures/signature-helpers.ts index a4c5e9309249..d360c46cf921 100644 --- a/test/e2e/tests/confirmations/signatures/signature-helpers.ts +++ b/test/e2e/tests/confirmations/signatures/signature-helpers.ts @@ -37,6 +37,8 @@ type AssertSignatureMetricsOptions = { withAnonEvents?: boolean; securityAlertReason?: string; securityAlertResponse?: string; + decodingChangeTypes?: string[]; + decodingResponse?: string; }; type SignatureEventProperty = { @@ -49,6 +51,8 @@ type SignatureEventProperty = { security_alert_response: string; signature_type: string; eip712_primary_type?: string; + decoding_change_types?: string[]; + decoding_response?: string; ui_customizations?: string[]; location?: string; }; @@ -67,6 +71,8 @@ const signatureAnonProperties = { * @param uiCustomizations * @param securityAlertReason * @param securityAlertResponse + * @param decodingChangeTypes + * @param decodingResponse */ function getSignatureEventProperty( signatureType: string, @@ -74,6 +80,8 @@ function getSignatureEventProperty( uiCustomizations: string[], securityAlertReason: string = BlockaidReason.checkingChain, securityAlertResponse: string = BlockaidResultType.Loading, + decodingChangeTypes?: string[], + decodingResponse?: string, ): SignatureEventProperty { const signatureEventProperty: SignatureEventProperty = { account_type: 'MetaMask', @@ -91,6 +99,10 @@ function getSignatureEventProperty( signatureEventProperty.eip712_primary_type = primaryType; } + if (decodingResponse) { + signatureEventProperty.decoding_change_types = decodingChangeTypes; + signatureEventProperty.decoding_response = decodingResponse; + } return signatureEventProperty; } @@ -123,6 +135,8 @@ export async function assertSignatureConfirmedMetrics({ withAnonEvents = false, securityAlertReason, securityAlertResponse, + decodingChangeTypes, + decodingResponse, }: AssertSignatureMetricsOptions) { const events = await getEventPayloads(driver, mockedEndpoints); const signatureEventProperty = getSignatureEventProperty( @@ -131,6 +145,8 @@ export async function assertSignatureConfirmedMetrics({ uiCustomizations, securityAlertReason, securityAlertResponse, + decodingChangeTypes, + decodingResponse, ); assertSignatureRequestedMetrics( @@ -164,6 +180,8 @@ export async function assertSignatureRejectedMetrics({ withAnonEvents = false, securityAlertReason, securityAlertResponse, + decodingChangeTypes, + decodingResponse, }: AssertSignatureMetricsOptions) { const events = await getEventPayloads(driver, mockedEndpoints); const signatureEventProperty = getSignatureEventProperty( @@ -172,6 +190,8 @@ export async function assertSignatureRejectedMetrics({ uiCustomizations, securityAlertReason, securityAlertResponse, + decodingChangeTypes, + decodingResponse, ); assertSignatureRequestedMetrics( diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap index 1551e2a8bf8a..fef756f178c2 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap @@ -23,7 +23,7 @@ exports[`PermitSimulation should not render default simulation if decodingLoadin

{ @@ -14,6 +15,7 @@ jest.mock('../../../../../../../store/actions', () => { getTokenStandardAndDetails: jest .fn() .mockResolvedValue({ decimals: 2, standard: 'ERC20' }), + updateEventFragment: jest.fn(), }; }); @@ -44,6 +46,25 @@ describe('PermitSimulation', () => { }); }); + it('should call hook to register signature metrics properties', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData: undefined, + }); + const mockStore = configureMockStore([])(state); + + const mockedUseDecodedSignatureMetrics = jest + .spyOn(SignatureMetrics, 'useDecodedSignatureMetrics') + .mockImplementation(() => ''); + + await act(async () => { + renderWithConfirmContextProvider(, mockStore); + + expect(mockedUseDecodedSignatureMetrics).toHaveBeenCalledTimes(1); + }); + }); + it('should render default simulation if decoding api returns error', async () => { const state = getMockTypedSignConfirmStateForRequest({ ...permitSignatureMsg, diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index 86055425fa46..d78a9afbe1b8 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { SignatureRequestType } from '../../../../../types/confirm'; import { useConfirmContext } from '../../../../../context/confirm'; +import { useDecodedSignatureMetrics } from '../../../../../hooks/useDecodedSignatureMetrics'; import { DefaultSimulation } from './default-simulation'; import { DecodedSimulation } from './decoded-simulation'; @@ -9,6 +10,8 @@ const PermitSimulation: React.FC = () => { const { currentConfirmation } = useConfirmContext(); const { decodingLoading, decodingData } = currentConfirmation; + useDecodedSignatureMetrics(); + if ( decodingData?.error || (decodingData?.stateChanges === undefined && decodingLoading !== true) diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx index e6c93e964687..68f3c011f338 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx @@ -31,6 +31,7 @@ jest.mock( jest.mock('../../../../../../store/actions', () => { return { getTokenStandardAndDetails: jest.fn().mockResolvedValue({ decimals: 2 }), + updateEventFragment: jest.fn(), }; }); diff --git a/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.test.ts b/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.test.ts new file mode 100644 index 000000000000..c0023104ba8b --- /dev/null +++ b/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.test.ts @@ -0,0 +1,135 @@ +import { + DecodingData, + DecodingDataChangeType, +} from '@metamask/signature-controller'; + +import { getMockTypedSignConfirmStateForRequest } from '../../../../test/data/confirmations/helper'; +import { renderHookWithConfirmContextProvider } from '../../../../test/lib/confirmations/render-helpers'; +import { permitSignatureMsg } from '../../../../test/data/confirmations/typed_sign'; +import * as SignatureEventFragment from './useSignatureEventFragment'; +import { useDecodedSignatureMetrics } from './useDecodedSignatureMetrics'; + +const decodingData: DecodingData = { + stateChanges: [ + { + assetType: 'ERC20', + changeType: DecodingDataChangeType.Approve, + address: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad', + amount: '1461501637330902918203684832716283019655932542975', + contractAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', + }, + ], +}; + +describe('useDecodedSignatureMetrics', () => { + process.env.ENABLE_SIGNATURE_DECODING = 'true'; + it('should not call updateSignatureEventFragment if decodingLoading is true', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: true, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(0); + }); + + it('should call updateSignatureEventFragment with correct parameters if there are no state changes', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(1); + expect(mockUpdateSignatureEventFragment).toHaveBeenLastCalledWith({ + properties: { + decoding_change_types: [], + decoding_response: 'NO_CHANGE', + }, + }); + }); + + it('should call updateSignatureEventFragment with correct parameters if there are state changes', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(1); + expect(mockUpdateSignatureEventFragment).toHaveBeenLastCalledWith({ + properties: { + decoding_change_types: ['APPROVE'], + decoding_response: 'CHANGE', + }, + }); + }); + + it('should call updateSignatureEventFragment with correct parameters if response has error', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData: { + stateChanges: [], + error: { + type: 'SOME_ERROR', + message: 'some message', + }, + }, + }); + + const mockUpdateSignatureEventFragment = jest.fn(); + jest + .spyOn(SignatureEventFragment, 'useSignatureEventFragment') + .mockImplementation(() => ({ + updateSignatureEventFragment: mockUpdateSignatureEventFragment, + })); + + renderHookWithConfirmContextProvider( + () => useDecodedSignatureMetrics(), + state, + ); + + expect(mockUpdateSignatureEventFragment).toHaveBeenCalledTimes(1); + expect(mockUpdateSignatureEventFragment).toHaveBeenLastCalledWith({ + properties: { + decoding_change_types: [], + decoding_response: 'SOME_ERROR', + }, + }); + }); +}); diff --git a/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.ts b/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.ts new file mode 100644 index 000000000000..1bf508bce655 --- /dev/null +++ b/ui/pages/confirmations/hooks/useDecodedSignatureMetrics.ts @@ -0,0 +1,45 @@ +import { DecodingDataStateChange } from '@metamask/signature-controller'; +import { useEffect } from 'react'; + +import { SignatureRequestType } from '../types/confirm'; +import { useConfirmContext } from '../context/confirm'; +import { useSignatureEventFragment } from './useSignatureEventFragment'; + +enum DecodingResponseType { + Change = 'CHANGE', + NoChange = 'NO_CHANGE', +} + +export function useDecodedSignatureMetrics() { + const { updateSignatureEventFragment } = useSignatureEventFragment(); + const { currentConfirmation } = useConfirmContext(); + const { decodingLoading, decodingData } = currentConfirmation; + + const decodingChangeTypes = (decodingData?.stateChanges ?? []).map( + (change: DecodingDataStateChange) => change.changeType, + ); + + const decodingResponse = + decodingData?.error?.type ?? + (decodingChangeTypes.length + ? DecodingResponseType.Change + : DecodingResponseType.NoChange); + + useEffect(() => { + if (decodingLoading || !process.env.ENABLE_SIGNATURE_DECODING) { + return; + } + + updateSignatureEventFragment({ + properties: { + decoding_response: decodingResponse, + decoding_change_types: decodingChangeTypes, + }, + }); + }, [ + decodingResponse, + decodingLoading, + decodingChangeTypes, + updateSignatureEventFragment, + ]); +} From 85baff382ffa17b9ccbcfa07eaf24a6f4bf412c7 Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:43:50 -0500 Subject: [PATCH 14/22] refactor: move `getCurrentChainId` from `selectors/selectors.js` to `shared/modules/selectors/networks.ts` (#27647) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts the selector `getCurrentChainId` from functions from JS to TS and moves it to the `shared` folder. Also updates some functions to match the actual expect return values and fixes some types. Why only this function? I'm trying to solve circular dependency issues. `getCurrentChainId` is so widely used in the codebase, it makes it very complicated to untangle. I've moved it to `shared/modules/selectors/networks.ts` --------- Co-authored-by: Howard Braham --- app/scripts/background.js | 4 +- .../tx-verification-middleware.ts | 4 +- app/scripts/metamask-controller.js | 8 ++-- shared/modules/selectors/feature-flags.ts | 8 ++-- shared/modules/selectors/networks.ts | 37 +++++++++++-------- .../modules/selectors/smart-transactions.ts | 5 +-- .../network-filter/network-filter.tsx | 6 ++- .../assets/nfts/nft-details/nft-details.tsx | 2 +- .../app/assets/nfts/nfts-items/nfts-items.js | 2 +- .../app/currency-input/currency-input.js | 7 ++-- .../hooks/useTokenExchangeRate.tsx | 6 +-- .../detected-token-selection-popover.js | 6 ++- .../detected-token-values.js | 2 +- .../app/detected-token/detected-token.js | 10 ++--- .../hide-token-confirmation-modal.js | 6 ++- .../keyring-snap-removal-warning.tsx | 2 +- .../transaction-list.component.js | 4 +- .../aggregated-percentage-overview.test.tsx | 5 ++- .../aggregated-percentage-overview.tsx | 2 +- .../app/wallet-overview/coin-buttons.tsx | 2 +- .../app/wallet-overview/eth-overview.js | 2 +- .../account-details-display.js | 2 +- .../account-list-item-menu.js | 2 +- .../asset-picker-modal/Asset.tsx | 3 +- .../asset-picker-modal/AssetList.tsx | 2 +- .../asset-picker-modal.test.tsx | 2 +- .../asset-picker-modal/asset-picker-modal.tsx | 3 +- .../detected-token-banner.js | 4 +- .../import-nfts-modal/import-nfts-modal.js | 2 +- .../import-tokens-modal.js | 4 +- .../network-list-menu/network-list-menu.tsx | 4 +- .../quote-card/hooks/useEthFeeData.test.tsx | 2 +- .../quote-card/hooks/useEthFeeData.tsx | 2 +- .../hooks/useTranslatedNetworkName.test.tsx | 2 +- .../hooks/useTranslatedNetworkName.tsx | 2 +- .../percentage-and-amount-change.test.tsx | 5 ++- .../percentage-and-amount-change.tsx | 2 +- ui/ducks/domains.js | 2 +- ui/ducks/ramps/ramps.test.ts | 10 ++++- ui/ducks/ramps/ramps.ts | 3 +- ui/ducks/send/helpers.js | 2 +- ui/ducks/send/helpers.test.js | 9 ++++- ui/ducks/send/send.js | 10 ++--- ui/ducks/swaps/swaps.js | 2 +- ui/hooks/bridge/useLatestBalance.ts | 7 +--- ui/hooks/ramps/useRamps/useRamps.ts | 6 +-- ...eAccountTotalCrossChainFiatBalance.test.ts | 1 + ui/hooks/useAccountTotalFiatBalance.js | 2 +- ui/hooks/useCurrentAsset.js | 2 +- .../useGetFormattedTokensPerChain.test.ts | 8 +++- ui/hooks/useGetFormattedTokensPerChain.ts | 3 +- ui/hooks/useNftsCollections.js | 3 +- ui/hooks/useSwappedTokenValue.js | 2 +- ui/hooks/useTokensToSearch.js | 2 +- ui/index.js | 2 +- ui/pages/asset/components/token-buttons.tsx | 2 +- .../confirm-add-suggested-nft.js | 2 +- .../create-account/connect-hardware/index.js | 2 +- ui/pages/institutional/custody/custody.tsx | 6 +-- ui/pages/routes/routes.container.js | 10 ++--- .../smart-transaction-status-page.tsx | 3 +- ui/pages/swaps/awaiting-swap/awaiting-swap.js | 3 +- .../swaps/hooks/useUpdateSwapsState.test.ts | 2 +- ui/pages/swaps/hooks/useUpdateSwapsState.ts | 2 +- ui/pages/swaps/index.js | 2 +- .../list-with-search/list-with-search.js | 2 +- .../prepare-swap-page/prepare-swap-page.js | 2 +- .../swaps/prepare-swap-page/review-quote.js | 2 +- .../item-list/item-list.component.js | 2 +- .../list-item-search.component.js | 2 +- .../smart-transaction-status.js | 2 +- ui/selectors/confirm-transaction.js | 6 ++- ui/selectors/institutional/selectors.ts | 5 ++- ui/selectors/multichain.ts | 2 +- ui/selectors/nft.ts | 5 ++- ui/selectors/selectors.js | 16 +++----- ui/selectors/selectors.test.js | 23 +++++++++--- ui/selectors/transactions.js | 6 ++- 78 files changed, 203 insertions(+), 155 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 90a52b6c0d19..6a047c79ac16 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -53,9 +53,7 @@ import { FakeLedgerBridge, FakeTrezorBridge, } from '../../test/stub/keyring-bridge'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { getCurrentChainId } from '../../ui/selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { addNonceToCsp } from '../../shared/modules/add-nonce-to-csp'; import { checkURLForProviderInjection } from '../../shared/modules/provider-injection'; import migrations from './migrations'; diff --git a/app/scripts/lib/tx-verification/tx-verification-middleware.ts b/app/scripts/lib/tx-verification/tx-verification-middleware.ts index 7abdf73e3637..e21a38f1e726 100644 --- a/app/scripts/lib/tx-verification/tx-verification-middleware.ts +++ b/app/scripts/lib/tx-verification/tx-verification-middleware.ts @@ -20,9 +20,7 @@ import { TRUSTED_SIGNERS, } from '../../../../shared/constants/verification'; import { MESSAGE_TYPE } from '../../../../shared/constants/app'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { getCurrentChainId } from '../../../../ui/selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; export type TxParams = { chainId?: `0x${string}`; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 166322b1ee78..1f907d94a976 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -236,10 +236,10 @@ import { TOKEN_TRANSFER_LOG_TOPIC_HASH, TRANSFER_SINFLE_LOG_TOPIC_HASH, } from '../../shared/lib/transactions-controller-utils'; -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { getCurrentChainId } from '../../ui/selectors/selectors'; -import { getProviderConfig } from '../../shared/modules/selectors/networks'; +import { + getCurrentChainId, + getProviderConfig, +} from '../../shared/modules/selectors/networks'; import { endTrace, trace } from '../../shared/lib/trace'; // eslint-disable-next-line import/no-restricted-paths import { isSnapId } from '../../ui/helpers/utils/snaps'; diff --git a/shared/modules/selectors/feature-flags.ts b/shared/modules/selectors/feature-flags.ts index 429df2c06da6..4d27f139f68f 100644 --- a/shared/modules/selectors/feature-flags.ts +++ b/shared/modules/selectors/feature-flags.ts @@ -1,7 +1,5 @@ -// TODO: Remove restricted import -// eslint-disable-next-line import/no-restricted-paths -import { getCurrentChainId } from '../../../ui/selectors/selectors'; // TODO: Migrate shared selectors to this file. import { getNetworkNameByChainId } from '../feature-flags'; +import { ProviderConfigState, getCurrentChainId } from './networks'; type FeatureFlagsMetaMaskState = { metamask: { @@ -21,7 +19,9 @@ type FeatureFlagsMetaMaskState = { }; }; -export function getFeatureFlagsByChainId(state: FeatureFlagsMetaMaskState) { +export function getFeatureFlagsByChainId( + state: ProviderConfigState & FeatureFlagsMetaMaskState, +) { const chainId = getCurrentChainId(state); const networkName = getNetworkNameByChainId(chainId); const featureFlags = state.metamask.swapsState?.swapsFeatureFlags; diff --git a/shared/modules/selectors/networks.ts b/shared/modules/selectors/networks.ts index 41aad6da6948..bc8d05b5164d 100644 --- a/shared/modules/selectors/networks.ts +++ b/shared/modules/selectors/networks.ts @@ -1,33 +1,32 @@ import { RpcEndpointType, type NetworkConfiguration, - type NetworkState as _NetworkState, + type NetworkState as InternalNetworkState, } from '@metamask/network-controller'; import { createSelector } from 'reselect'; import { NetworkStatus } from '../../constants/network'; import { createDeepEqualSelector } from './util'; -export type NetworkState = { metamask: _NetworkState }; +export type NetworkState = { + metamask: InternalNetworkState; +}; export type NetworkConfigurationsState = { metamask: { - networkConfigurations: Record< - string, - MetaMaskExtensionNetworkConfiguration - >; + networkConfigurations: Record; }; }; export type SelectedNetworkClientIdState = { - metamask: { - selectedNetworkClientId: string; - }; + metamask: Pick; }; -export type MetaMaskExtensionNetworkConfiguration = NetworkConfiguration; - export type NetworkConfigurationsByChainIdState = { - metamask: Pick<_NetworkState, 'networkConfigurationsByChainId'>; + metamask: Pick; +}; + +export type NetworksMetadataState = { + metamask: Pick; }; export type ProviderConfigState = NetworkConfigurationsByChainIdState & @@ -49,6 +48,7 @@ export function getSelectedNetworkClientId( * Get the provider configuration for the current selected network. * * @param state - Redux state object. + * @throws `new Error('Provider configuration not found')` If the provider configuration is not found. */ export const getProviderConfig = createSelector( (state: ProviderConfigState) => getNetworkConfigurationsByChainId(state), @@ -81,13 +81,13 @@ export const getProviderConfig = createSelector( } } } - return undefined; // should not be reachable + throw new Error('Provider configuration not found'); }, ); export function getNetworkConfigurations( state: NetworkConfigurationsState, -): Record { +): Record { return state.metamask.networkConfigurations; } @@ -106,9 +106,16 @@ export function isNetworkLoading(state: NetworkState) { ); } -export function getInfuraBlocked(state: NetworkState) { +export function getInfuraBlocked( + state: SelectedNetworkClientIdState & NetworksMetadataState, +) { return ( state.metamask.networksMetadata[getSelectedNetworkClientId(state)] .status === NetworkStatus.Blocked ); } + +export function getCurrentChainId(state: ProviderConfigState) { + const { chainId } = getProviderConfig(state); + return chainId; +} diff --git a/shared/modules/selectors/smart-transactions.ts b/shared/modules/selectors/smart-transactions.ts index b88d5f7c029b..e7c12f1e7f70 100644 --- a/shared/modules/selectors/smart-transactions.ts +++ b/shared/modules/selectors/smart-transactions.ts @@ -4,7 +4,6 @@ import { SKIP_STX_RPC_URL_CHECK_CHAIN_IDS, } from '../../constants/smartTransactions'; import { - getCurrentChainId, getCurrentNetwork, accountSupportsSmartTx, getPreferences, @@ -12,7 +11,7 @@ import { // eslint-disable-next-line import/no-restricted-paths } from '../../../ui/selectors/selectors'; // TODO: Migrate shared selectors to this file. import { isProduction } from '../environment'; -import { NetworkState } from './networks'; +import { getCurrentChainId, NetworkState } from './networks'; type SmartTransactionsMetaMaskState = { metamask: { @@ -108,7 +107,7 @@ export const getSmartTransactionsPreferenceEnabled = createSelector( ); export const getCurrentChainSupportsSmartTransactions = ( - state: SmartTransactionsMetaMaskState, + state: NetworkState, ): boolean => { const chainId = getCurrentChainId(state); return getAllowedSmartTransactionsChainIds().includes(chainId); diff --git a/ui/components/app/assets/asset-list/network-filter/network-filter.tsx b/ui/components/app/assets/asset-list/network-filter/network-filter.tsx index de68d8d6e13e..e1fdd8db9535 100644 --- a/ui/components/app/assets/asset-list/network-filter/network-filter.tsx +++ b/ui/components/app/assets/asset-list/network-filter/network-filter.tsx @@ -2,14 +2,16 @@ import React, { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { setTokenNetworkFilter } from '../../../../../store/actions'; import { - getCurrentChainId, getCurrentNetwork, getPreferences, getShouldHideZeroBalanceTokens, getSelectedAccount, getAllChainsToPoll, } from '../../../../../selectors'; -import { getNetworkConfigurationsByChainId } from '../../../../../../shared/modules/selectors/networks'; +import { + getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../../../shared/modules/selectors/networks'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { SelectableListItem } from '../sort-control/sort-control'; import { Text } from '../../../../component-library/text/text'; diff --git a/ui/components/app/assets/nfts/nft-details/nft-details.tsx b/ui/components/app/assets/nfts/nft-details/nft-details.tsx index 0dc9ec05250c..49408dfd5fa4 100644 --- a/ui/components/app/assets/nfts/nft-details/nft-details.tsx +++ b/ui/components/app/assets/nfts/nft-details/nft-details.tsx @@ -20,8 +20,8 @@ import { import { useI18nContext } from '../../../../../hooks/useI18nContext'; import { shortenAddress } from '../../../../../helpers/utils/util'; import { getNftImageAlt } from '../../../../../helpers/utils/nfts'; +import { getCurrentChainId } from '../../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getCurrentCurrency, getCurrentNetwork, getIpfsGateway, diff --git a/ui/components/app/assets/nfts/nfts-items/nfts-items.js b/ui/components/app/assets/nfts/nfts-items/nfts-items.js index 9dd53cd541fc..990477e6fc89 100644 --- a/ui/components/app/assets/nfts/nfts-items/nfts-items.js +++ b/ui/components/app/assets/nfts/nfts-items/nfts-items.js @@ -19,8 +19,8 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../../../shared/constants/app'; // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import { getEnvironmentType } from '../../../../../../app/scripts/lib/util'; +import { getCurrentChainId } from '../../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getIpfsGateway, getSelectedInternalAccount, getCurrentNetwork, diff --git a/ui/components/app/currency-input/currency-input.js b/ui/components/app/currency-input/currency-input.js index 3efcecb35150..83bd3c2edce8 100644 --- a/ui/components/app/currency-input/currency-input.js +++ b/ui/components/app/currency-input/currency-input.js @@ -6,12 +6,11 @@ import { BlockSize } from '../../../helpers/constants/design-system'; import UnitInput from '../../ui/unit-input'; import CurrencyDisplay from '../../ui/currency-display'; import { getNativeCurrency } from '../../../ducks/metamask/metamask'; -import { getProviderConfig } from '../../../../shared/modules/selectors/networks'; import { + getProviderConfig, getCurrentChainId, - getCurrentCurrency, - getShouldShowFiat, -} from '../../../selectors'; +} from '../../../../shared/modules/selectors/networks'; +import { getCurrentCurrency, getShouldShowFiat } from '../../../selectors'; import { EtherDenomination } from '../../../../shared/constants/common'; import { Numeric } from '../../../../shared/modules/Numeric'; import { useIsOriginalNativeTokenSymbol } from '../../../hooks/useIsOriginalNativeTokenSymbol'; diff --git a/ui/components/app/currency-input/hooks/useTokenExchangeRate.tsx b/ui/components/app/currency-input/hooks/useTokenExchangeRate.tsx index 2460098b5f8f..d9db9b284f72 100644 --- a/ui/components/app/currency-input/hooks/useTokenExchangeRate.tsx +++ b/ui/components/app/currency-input/hooks/useTokenExchangeRate.tsx @@ -1,10 +1,8 @@ import { useMemo, useState } from 'react'; import { toChecksumAddress } from 'ethereumjs-util'; import { shallowEqual, useSelector } from 'react-redux'; -import { - getCurrentChainId, - getTokenExchangeRates, -} from '../../../../selectors'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; +import { getTokenExchangeRates } from '../../../../selectors'; import { Numeric } from '../../../../../shared/modules/Numeric'; import { getConversionRate, diff --git a/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js b/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js index ff205f7844f0..4c674a5c437f 100644 --- a/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js +++ b/ui/components/app/detected-token/detected-token-selection-popover/detected-token-selection-popover.js @@ -10,13 +10,15 @@ import { MetaMetricsTokenEventSource, } from '../../../../../shared/constants/metametrics'; import { - getAllDetectedTokensForSelectedAddress, getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../../shared/modules/selectors/networks'; +import { + getAllDetectedTokensForSelectedAddress, getCurrentNetwork, getDetectedTokensInCurrentNetwork, getPreferences, } from '../../../../selectors'; -import { getNetworkConfigurationsByChainId } from '../../../../../shared/modules/selectors/networks'; import Popover from '../../../ui/popover'; import Box from '../../../ui/box'; diff --git a/ui/components/app/detected-token/detected-token-values/detected-token-values.js b/ui/components/app/detected-token/detected-token-values/detected-token-values.js index 07c70edcf196..e3b377f51e88 100644 --- a/ui/components/app/detected-token/detected-token-values/detected-token-values.js +++ b/ui/components/app/detected-token/detected-token-values/detected-token-values.js @@ -8,8 +8,8 @@ import { TextVariant, } from '../../../../helpers/constants/design-system'; import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getSelectedAddress, getUseCurrencyRateCheck, } from '../../../../selectors'; diff --git a/ui/components/app/detected-token/detected-token.js b/ui/components/app/detected-token/detected-token.js index bb215b4f7301..12957e1aca31 100644 --- a/ui/components/app/detected-token/detected-token.js +++ b/ui/components/app/detected-token/detected-token.js @@ -9,15 +9,15 @@ import { setNewTokensImported, } from '../../../store/actions'; import { - getAllDetectedTokensForSelectedAddress, getCurrentChainId, - getDetectedTokensInCurrentNetwork, - getPreferences, -} from '../../../selectors'; -import { getSelectedNetworkClientId, getNetworkConfigurationsByChainId, } from '../../../../shared/modules/selectors/networks'; +import { + getAllDetectedTokensForSelectedAddress, + getDetectedTokensInCurrentNetwork, + getPreferences, +} from '../../../selectors'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { diff --git a/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js b/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js index c34e4897d50a..bbe72241add8 100644 --- a/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js +++ b/ui/components/app/modals/hide-token-confirmation-modal/hide-token-confirmation-modal.js @@ -9,8 +9,10 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../../shared/constants/metametrics'; -import { getCurrentChainId } from '../../../../selectors'; -import { getNetworkConfigurationsByChainId } from '../../../../../shared/modules/selectors/networks'; +import { + getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../../shared/modules/selectors/networks'; function mapStateToProps(state) { return { diff --git a/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx b/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx index 2377076690b0..831c69a587dd 100644 --- a/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx +++ b/ui/components/app/snaps/keyring-snap-removal-warning/keyring-snap-removal-warning.tsx @@ -25,7 +25,7 @@ import { } from '../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import InfoTooltip from '../../../ui/info-tooltip'; -import { getCurrentChainId } from '../../../../selectors'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { KeyringAccountListItem } from './keyring-account-list-item'; export default function KeyringRemovalSnapWarning({ diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js index 47b422457143..0a0d081016fb 100644 --- a/ui/components/app/transaction-list/transaction-list.component.js +++ b/ui/components/app/transaction-list/transaction-list.component.js @@ -15,6 +15,9 @@ import { } from '../../../selectors/transactions'; import { getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../shared/modules/selectors/networks'; +import { getSelectedAccount, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) getShouldHideZeroBalanceTokens, @@ -55,7 +58,6 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; import { getMultichainNetwork } from '../../../selectors/multichain'; import { endTrace, TraceName } from '../../../../shared/lib/trace'; -import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks'; const PAGE_INCREMENT = 10; diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx index 7610890d48da..10fd24ec550c 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.test.tsx @@ -8,8 +8,8 @@ import { getShouldHideZeroBalanceTokens, getTokensMarketData, getPreferences, - getCurrentChainId, } from '../../../selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; import { AggregatedPercentageOverview } from './aggregated-percentage-overview'; @@ -27,6 +27,9 @@ jest.mock('../../../selectors', () => ({ getPreferences: jest.fn(), getShouldHideZeroBalanceTokens: jest.fn(), getTokensMarketData: jest.fn(), +})); + +jest.mock('../../../../shared/modules/selectors/networks', () => ({ getCurrentChainId: jest.fn(), })); diff --git a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx index 89bc94dab774..d50feebbbd74 100644 --- a/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx +++ b/ui/components/app/wallet-overview/aggregated-percentage-overview.tsx @@ -9,8 +9,8 @@ import { getShouldHideZeroBalanceTokens, getTokensMarketData, getPreferences, - getCurrentChainId, } from '../../../selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { useAccountTotalFiatBalance } from '../../../hooks/useAccountTotalFiatBalance'; // TODO: Remove restricted import diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index c3708eaef344..0fcb9f2e2389 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -55,7 +55,6 @@ import { getMemoizedUnapprovedTemplatedConfirmations, ///: END:ONLY_INCLUDE_IF getNetworkConfigurationIdByChainId, - getCurrentChainId, } from '../../../selectors'; import Tooltip from '../../ui/tooltip'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -100,6 +99,7 @@ import { getMultichainNativeCurrency, } from '../../../selectors/multichain'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; type CoinButtonsProps = { account: InternalAccount; diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index c42986ab30ab..1228a995ae54 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { EthMethod } from '@metamask/keyring-api'; import { isEqual } from 'lodash'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { isBalanceCached, getIsSwapsChain, - getCurrentChainId, getSelectedInternalAccount, getSelectedAccountCachedBalance, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) diff --git a/ui/components/multichain/account-details/account-details-display.js b/ui/components/multichain/account-details/account-details-display.js index ce6390b07296..d5af76004887 100644 --- a/ui/components/multichain/account-details/account-details-display.js +++ b/ui/components/multichain/account-details/account-details-display.js @@ -7,7 +7,6 @@ import EditableLabel from '../../ui/editable-label/editable-label'; import { setAccountLabel } from '../../../store/actions'; import { - getCurrentChainId, getHardwareWalletType, getInternalAccountByAddress, } from '../../../selectors'; @@ -30,6 +29,7 @@ import { MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; export const AccountDetailsDisplay = ({ accounts, diff --git a/ui/components/multichain/account-list-item-menu/account-list-item-menu.js b/ui/components/multichain/account-list-item-menu/account-list-item-menu.js index 3bb1295eabce..f81752d928fe 100644 --- a/ui/components/multichain/account-list-item-menu/account-list-item-menu.js +++ b/ui/components/multichain/account-list-item-menu/account-list-item-menu.js @@ -6,8 +6,8 @@ import { mmiActionsFactory } from '../../../store/institutional/institution-back ///: END:ONLY_INCLUDE_IF import { MetaMetricsContext } from '../../../contexts/metametrics'; import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getHardwareWalletType, getAccountTypeForKeyring, getPinnedAccountsList, diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx index 47d487f5ffd8..f6e7e7f3ccc9 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/Asset.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { BigNumber } from 'bignumber.js'; -import { getTokenList, getCurrentChainId } from '../../../../selectors'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; +import { getTokenList } from '../../../../selectors'; import { useTokenFiatAmount } from '../../../../hooks/useTokenFiatAmount'; import { TokenListItem } from '../../token-list-item'; import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx index 1017ef1aad6e..6f867d25ca85 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/AssetList.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useSelector } from 'react-redux'; import classnames from 'classnames'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getCurrentCurrency, getSelectedAccountCachedBalance, } from '../../../../selectors'; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx index 566783abed11..fc4796073c74 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.test.tsx @@ -12,7 +12,6 @@ import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import mockState from '../../../../../test/data/mock-send-state.json'; import { AssetType } from '../../../../../shared/constants/transaction'; import { - getCurrentChainId, getCurrentCurrency, getNativeCurrencyImage, getSelectedAccountCachedBalance, @@ -30,6 +29,7 @@ import { getTopAssets } from '../../../../ducks/swaps/swaps'; import { getRenderableTokenData } from '../../../../hooks/useTokensToSearch'; import * as actions from '../../../../store/actions'; import { getSwapsBlockedTokens } from '../../../../ducks/send'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { AssetPickerModal } from './asset-picker-modal'; import AssetList from './AssetList'; import { ERC20Asset } from './types'; diff --git a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx index 729a580f269e..48c56c5106ec 100644 --- a/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-picker-modal/asset-picker-modal.tsx @@ -29,10 +29,9 @@ import { import { useI18nContext } from '../../../../hooks/useI18nContext'; import { AssetType } from '../../../../../shared/constants/transaction'; - +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { getAllTokens, - getCurrentChainId, getCurrentCurrency, getNativeCurrencyImage, getSelectedAccountCachedBalance, diff --git a/ui/components/multichain/detected-token-banner/detected-token-banner.js b/ui/components/multichain/detected-token-banner/detected-token-banner.js index 4f76fa22fb35..7cd315d57cab 100644 --- a/ui/components/multichain/detected-token-banner/detected-token-banner.js +++ b/ui/components/multichain/detected-token-banner/detected-token-banner.js @@ -6,11 +6,13 @@ import classNames from 'classnames'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getCurrentChainId, + getNetworkConfigurationsByChainId, +} from '../../../../shared/modules/selectors/networks'; +import { getDetectedTokensInCurrentNetwork, getAllDetectedTokensForSelectedAddress, getPreferences, } from '../../../selectors'; -import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, diff --git a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js index 549f0ab5c70d..f0ff312f1f57 100644 --- a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js +++ b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js @@ -22,8 +22,8 @@ import { } from '../../../helpers/constants/design-system'; import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getIsMainnet, getSelectedInternalAccount, getOpenSeaEnabled, diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js index a1194f2285f2..b6ffda5b8d9e 100644 --- a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js @@ -13,6 +13,9 @@ import { Tab, Tabs } from '../../ui/tabs'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { getCurrentChainId, + getSelectedNetworkClientId, +} from '../../../../shared/modules/selectors/networks'; +import { getInternalAccounts, getIsDynamicTokenListAvailable, getIsMainnet, @@ -27,7 +30,6 @@ import { getTestNetworkBackgroundColor, getTokenExchangeRates, } from '../../../selectors'; -import { getSelectedNetworkClientId } from '../../../../shared/modules/selectors/networks'; import { addImportedTokens, clearPendingTokens, diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx index 8b18170adc6c..07d4147ad763 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.tsx +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -36,9 +36,11 @@ import { FEATURED_RPCS, TEST_CHAINS, } from '../../../../shared/constants/network'; -import { getNetworkConfigurationsByChainId } from '../../../../shared/modules/selectors/networks'; import { + getNetworkConfigurationsByChainId, getCurrentChainId, +} from '../../../../shared/modules/selectors/networks'; +import { getShowTestNetworks, getOnboardedInThisUISession, getShowNetworkBanner, diff --git a/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.test.tsx b/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.test.tsx index ff3ff4265703..1ef3347e3133 100644 --- a/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.test.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.test.tsx @@ -9,9 +9,9 @@ import { getUsedSwapsGasPrice } from '../../../../../../../ducks/swaps/swaps'; import { getCurrentCurrency, checkNetworkAndAccountSupports1559, - getCurrentChainId, getIsSwapsChain, } from '../../../../../../../selectors'; +import { getCurrentChainId } from '../../../../../../../../shared/modules/selectors/networks'; import useEthFeeData from './useEthFeeData'; jest.mock('react-redux', () => ({ diff --git a/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.tsx b/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.tsx index 25a77edfced4..04fa59ce072a 100644 --- a/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/hooks/useEthFeeData.tsx @@ -11,9 +11,9 @@ import { EtherDenomination } from '../../../../../../../../shared/constants/comm import { getCurrentCurrency, checkNetworkAndAccountSupports1559, - getCurrentChainId, getIsSwapsChain, } from '../../../../../../../selectors/selectors'; +import { getCurrentChainId } from '../../../../../../../../shared/modules/selectors/networks'; import { fetchAndSetSwapsGasPriceInfo, getUsedSwapsGasPrice, diff --git a/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.test.tsx b/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.test.tsx index 8bed4bf9eb2e..f47c988f2a74 100644 --- a/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.test.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.test.tsx @@ -2,7 +2,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useSelector } from 'react-redux'; import { CHAIN_IDS } from '../../../../../../../../shared/constants/network'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; -import { getCurrentChainId } from '../../../../../../../selectors'; +import { getCurrentChainId } from '../../../../../../../../shared/modules/selectors/networks'; import useTranslatedNetworkName from './useTranslatedNetworkName'; jest.mock('react-redux', () => ({ diff --git a/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.tsx b/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.tsx index 366a87d6947c..276d84d4bc15 100644 --- a/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.tsx +++ b/ui/components/multichain/pages/send/components/quote-card/hooks/useTranslatedNetworkName.tsx @@ -2,7 +2,7 @@ import { useSelector } from 'react-redux'; import { toHex } from '@metamask/controller-utils'; import { CHAIN_IDS } from '../../../../../../../../shared/constants/network'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; -import { getCurrentChainId } from '../../../../../../../selectors'; +import { getCurrentChainId } from '../../../../../../../../shared/modules/selectors/networks'; export default function useTranslatedNetworkName() { const chainId = useSelector(getCurrentChainId); diff --git a/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.test.tsx b/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.test.tsx index 439c030a59cd..f157afa64d8b 100644 --- a/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.test.tsx +++ b/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.test.tsx @@ -8,8 +8,8 @@ import { getCurrentCurrency, getSelectedAccountCachedBalance, getTokensMarketData, - getCurrentChainId, } from '../../../../../selectors'; +import { getCurrentChainId } from '../../../../../../shared/modules/selectors/networks'; import { getConversionRate, getNativeCurrency, @@ -28,6 +28,9 @@ jest.mock('../../../../../selectors', () => ({ getCurrentCurrency: jest.fn(), getSelectedAccountCachedBalance: jest.fn(), getTokensMarketData: jest.fn(), +})); + +jest.mock('../../../../../../shared/modules/selectors/networks', () => ({ getCurrentChainId: jest.fn(), })); diff --git a/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.tsx b/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.tsx index f1ba436ef47f..dc0aeaa5a25c 100644 --- a/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.tsx +++ b/ui/components/multichain/token-list-item/price/percentage-and-amount-change/percentage-and-amount-change.tsx @@ -9,8 +9,8 @@ import { TextColor, TextVariant, } from '../../../../../helpers/constants/design-system'; +import { getCurrentChainId } from '../../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getCurrentCurrency, getSelectedAccountCachedBalance, getTokensMarketData, diff --git a/ui/ducks/domains.js b/ui/ducks/domains.js index 2b0146cde1ba..b1053fe9903e 100644 --- a/ui/ducks/domains.js +++ b/ui/ducks/domains.js @@ -7,11 +7,11 @@ import { } from '@metamask/snaps-rpc-methods'; import { getAddressBookEntry, - getCurrentChainId, getNameLookupSnapsIds, getPermissionSubjects, getSnapMetadata, } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { handleSnapRequest } from '../store/actions'; import { NO_RESOLUTION_FOR_DOMAIN } from '../pages/confirmations/send/send.constants'; import { CHAIN_CHANGED } from '../store/actionConstants'; diff --git a/ui/ducks/ramps/ramps.test.ts b/ui/ducks/ramps/ramps.test.ts index 8bd6865295d8..a2d58e258fc9 100644 --- a/ui/ducks/ramps/ramps.test.ts +++ b/ui/ducks/ramps/ramps.test.ts @@ -1,6 +1,7 @@ import { configureStore, Store } from '@reduxjs/toolkit'; import RampAPI from '../../helpers/ramps/rampApi/rampAPI'; -import { getCurrentChainId, getUseExternalServices } from '../../selectors'; +import { getUseExternalServices } from '../../selectors'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { CHAIN_IDS } from '../../../shared/constants/network'; import { getMultichainIsBitcoin } from '../../selectors/multichain'; import { MultichainNetworks } from '../../../shared/constants/multichain/networks'; @@ -15,9 +16,14 @@ import { defaultBuyableChains } from './constants'; jest.mock('../../helpers/ramps/rampApi/rampAPI'); const mockedRampAPI = RampAPI as jest.Mocked; +jest.mock('../../../shared/modules/selectors/networks', () => ({ + getCurrentChainId: jest.fn(), + getNetworkConfigurationsByChainId: jest.fn(), + getSelectedNetworkClientId: jest.fn(), +})); + jest.mock('../../selectors', () => ({ ...jest.requireActual('../../selectors'), - getCurrentChainId: jest.fn(), getUseExternalServices: jest.fn(), getNames: jest.fn(), })); diff --git a/ui/ducks/ramps/ramps.ts b/ui/ducks/ramps/ramps.ts index a6732996d74b..b4e25e19a7ae 100644 --- a/ui/ducks/ramps/ramps.ts +++ b/ui/ducks/ramps/ramps.ts @@ -1,6 +1,7 @@ import { createSelector } from 'reselect'; import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; -import { getCurrentChainId, getUseExternalServices } from '../../selectors'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; +import { getUseExternalServices } from '../../selectors'; import RampAPI from '../../helpers/ramps/rampApi/rampAPI'; import { hexToDecimal } from '../../../shared/modules/conversion.utils'; import { getMultichainIsBitcoin } from '../../selectors/multichain'; diff --git a/ui/ducks/send/helpers.js b/ui/ducks/send/helpers.js index b48e05034f59..5fcee6f7c88b 100644 --- a/ui/ducks/send/helpers.js +++ b/ui/ducks/send/helpers.js @@ -18,10 +18,10 @@ import { generateERC1155TransferData, getAssetTransferData, } from '../../pages/confirmations/send/send.utils'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { checkNetworkAndAccountSupports1559, getConfirmationExchangeRates, - getCurrentChainId, getGasPriceInHexWei, getTokenExchangeRates, } from '../../selectors'; diff --git a/ui/ducks/send/helpers.test.js b/ui/ducks/send/helpers.test.js index 7129ffca8194..87bf1bcbcdc6 100644 --- a/ui/ducks/send/helpers.test.js +++ b/ui/ducks/send/helpers.test.js @@ -12,10 +12,10 @@ import { generateERC20TransferData, generateERC721TransferData, } from '../../pages/confirmations/send/send.utils'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { checkNetworkAndAccountSupports1559, getConfirmationExchangeRates, - getCurrentChainId, getTokenExchangeRates, } from '../../selectors'; import { getGasFeeEstimates, getNativeCurrency } from '../metamask/metamask'; @@ -48,10 +48,15 @@ jest.mock('../../pages/confirmations/send/send.utils', () => ({ getAssetTransferData: jest.fn(), })); +jest.mock('../../../shared/modules/selectors/networks', () => ({ + getCurrentChainId: jest.fn(), + getNetworkConfigurationsByChainId: jest.fn(), + getSelectedNetworkClientId: jest.fn(), +})); + jest.mock('../../selectors', () => ({ checkNetworkAndAccountSupports1559: jest.fn(), getConfirmationExchangeRates: jest.fn(), - getCurrentChainId: jest.fn(), getGasPriceInHexWei: jest.fn(), getTokenExchangeRates: jest.fn(), })); diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 15acc3355d8f..36ccf9d260da 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -38,8 +38,12 @@ import { isTokenBalanceSufficient, } from '../../pages/confirmations/send/send.utils'; import { - getAdvancedInlineGasShown, getCurrentChainId, + getSelectedNetworkClientId, + getProviderConfig, +} from '../../../shared/modules/selectors/networks'; +import { + getAdvancedInlineGasShown, getGasPriceInHexWei, getIsMainnet, getTargetAccount, @@ -56,10 +60,6 @@ import { getIsSwapsChain, getUseExternalServices, } from '../../selectors'; -import { - getSelectedNetworkClientId, - getProviderConfig, -} from '../../../shared/modules/selectors/networks'; import { displayWarning, hideLoadingIndication, diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 4886d1dbdad7..bc1c675e4d21 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -56,12 +56,12 @@ import { getValueFromWeiHex, hexWEIToDecGWEI, } from '../../../shared/modules/conversion.utils'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { getSelectedAccount, getTokenExchangeRates, getUSDConversionRate, getSwapsDefaultToken, - getCurrentChainId, isHardwareWallet, getHardwareWalletType, checkNetworkAndAccountSupports1559, diff --git a/ui/hooks/bridge/useLatestBalance.ts b/ui/hooks/bridge/useLatestBalance.ts index 524d65503249..b3398dc18333 100644 --- a/ui/hooks/bridge/useLatestBalance.ts +++ b/ui/hooks/bridge/useLatestBalance.ts @@ -2,11 +2,8 @@ import { useSelector } from 'react-redux'; import { Hex } from '@metamask/utils'; import { Numeric } from '../../../shared/modules/Numeric'; import { DEFAULT_PRECISION } from '../useCurrencyDisplay'; -import { - getCurrentChainId, - getSelectedInternalAccount, - SwapsEthToken, -} from '../../selectors'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; +import { getSelectedInternalAccount, SwapsEthToken } from '../../selectors'; import { SwapsTokenObject } from '../../../shared/constants/swaps'; import { calcLatestSrcBalance } from '../../../shared/modules/bridge-utils/balance'; import { useAsyncResult } from '../useAsyncResult'; diff --git a/ui/hooks/ramps/useRamps/useRamps.ts b/ui/hooks/ramps/useRamps/useRamps.ts index 429abbf1a96b..3dd230eaf6ea 100644 --- a/ui/hooks/ramps/useRamps/useRamps.ts +++ b/ui/hooks/ramps/useRamps/useRamps.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react'; import { useSelector } from 'react-redux'; -import { CaipChainId } from '@metamask/utils'; +import { CaipChainId, Hex } from '@metamask/utils'; import { ChainId } from '../../../../shared/constants/network'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getDataCollectionForMarketing, getMetaMetricsId, getParticipateInMetaMetrics, @@ -32,7 +32,7 @@ const useRamps = ( const isMarketingEnabled = useSelector(getDataCollectionForMarketing); const getBuyURI = useCallback( - (_chainId: ChainId | CaipChainId) => { + (_chainId: Hex | CaipChainId) => { const params = new URLSearchParams(); params.set('metamaskEntry', metamaskEntry); params.set('chainId', _chainId); diff --git a/ui/hooks/useAccountTotalCrossChainFiatBalance.test.ts b/ui/hooks/useAccountTotalCrossChainFiatBalance.test.ts index dd5b8aaab579..b41fc38a9930 100644 --- a/ui/hooks/useAccountTotalCrossChainFiatBalance.test.ts +++ b/ui/hooks/useAccountTotalCrossChainFiatBalance.test.ts @@ -28,6 +28,7 @@ jest.mock('../ducks/metamask/metamask', () => ({ jest.mock('../../shared/modules/selectors/networks', () => ({ getSelectedNetworkClientId: jest.fn(), getNetworkConfigurationsByChainId: jest.fn(), + getCurrentChainId: jest.fn(), })); const mockGetCurrencyRates = getCurrencyRates as jest.Mock; diff --git a/ui/hooks/useAccountTotalFiatBalance.js b/ui/hooks/useAccountTotalFiatBalance.js index aa1f906473ef..1c86b29ea8ea 100644 --- a/ui/hooks/useAccountTotalFiatBalance.js +++ b/ui/hooks/useAccountTotalFiatBalance.js @@ -1,8 +1,8 @@ import { shallowEqual, useSelector } from 'react-redux'; import { toChecksumAddress } from 'ethereumjs-util'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { getAllTokens, - getCurrentChainId, getCurrentCurrency, getMetaMaskCachedBalances, getTokenExchangeRates, diff --git a/ui/hooks/useCurrentAsset.js b/ui/hooks/useCurrentAsset.js index 97fa27fc196c..d6c9f367c22a 100644 --- a/ui/hooks/useCurrentAsset.js +++ b/ui/hooks/useCurrentAsset.js @@ -1,7 +1,7 @@ import { useSelector } from 'react-redux'; import { useRouteMatch } from 'react-router-dom'; import { getTokens } from '../ducks/metamask/metamask'; -import { getCurrentChainId } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { ASSET_ROUTE } from '../helpers/constants/routes'; import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP, diff --git a/ui/hooks/useGetFormattedTokensPerChain.test.ts b/ui/hooks/useGetFormattedTokensPerChain.test.ts index 973a16b0e648..231faae66bf9 100644 --- a/ui/hooks/useGetFormattedTokensPerChain.test.ts +++ b/ui/hooks/useGetFormattedTokensPerChain.test.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { renderHook } from '@testing-library/react-hooks'; import { act } from 'react-dom/test-utils'; -import { getAllTokens, getCurrentChainId } from '../selectors'; +import { getAllTokens } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { useGetFormattedTokensPerChain } from './useGetFormattedTokensPerChain'; import { stringifyBalance } from './useTokenBalances'; @@ -9,8 +10,11 @@ jest.mock('react-redux', () => ({ useSelector: jest.fn((selector) => selector()), })); -jest.mock('../selectors', () => ({ +jest.mock('../../shared/modules/selectors/networks', () => ({ getCurrentChainId: jest.fn(), +})); + +jest.mock('../selectors', () => ({ getAllTokens: jest.fn(), })); diff --git a/ui/hooks/useGetFormattedTokensPerChain.ts b/ui/hooks/useGetFormattedTokensPerChain.ts index a69f5be9e1e0..21a5c1a7a78a 100644 --- a/ui/hooks/useGetFormattedTokensPerChain.ts +++ b/ui/hooks/useGetFormattedTokensPerChain.ts @@ -1,7 +1,8 @@ import { useSelector } from 'react-redux'; import { BN } from 'bn.js'; import { Token } from '@metamask/assets-controllers'; -import { getAllTokens, getCurrentChainId } from '../selectors'; +import { getAllTokens } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { hexToDecimal } from '../../shared/modules/conversion.utils'; import { TokenWithBalance } from '../components/multichain/asset-picker-amount/asset-picker-modal/types'; diff --git a/ui/hooks/useNftsCollections.js b/ui/hooks/useNftsCollections.js index cfb3958da6b3..3c6a861d3d6a 100644 --- a/ui/hooks/useNftsCollections.js +++ b/ui/hooks/useNftsCollections.js @@ -2,7 +2,8 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { isEqual } from 'lodash'; import { getNfts, getNftContracts } from '../ducks/metamask/metamask'; -import { getCurrentChainId, getSelectedInternalAccount } from '../selectors'; +import { getSelectedInternalAccount } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { usePrevious } from './usePrevious'; import { useI18nContext } from './useI18nContext'; diff --git a/ui/hooks/useSwappedTokenValue.js b/ui/hooks/useSwappedTokenValue.js index c7007346d7c0..1c67577cbef5 100644 --- a/ui/hooks/useSwappedTokenValue.js +++ b/ui/hooks/useSwappedTokenValue.js @@ -5,7 +5,7 @@ import { isSwapsDefaultTokenAddress, isSwapsDefaultTokenSymbol, } from '../../shared/modules/swaps.utils'; -import { getCurrentChainId } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { useTokenFiatAmount } from './useTokenFiatAmount'; /** diff --git a/ui/hooks/useTokensToSearch.js b/ui/hooks/useTokensToSearch.js index 7604ca0a22bc..bf37f6cb08fe 100644 --- a/ui/hooks/useTokensToSearch.js +++ b/ui/hooks/useTokensToSearch.js @@ -8,9 +8,9 @@ import { getTokenExchangeRates, getCurrentCurrency, getSwapsDefaultToken, - getCurrentChainId, getTokenList, } from '../selectors'; +import { getCurrentChainId } from '../../shared/modules/selectors/networks'; import { getConversionRate } from '../ducks/metamask/metamask'; import { getSwapsTokens } from '../ducks/swaps/swaps'; diff --git a/ui/index.js b/ui/index.js index 8cf2048cba41..a63b2acc86fa 100644 --- a/ui/index.js +++ b/ui/index.js @@ -19,6 +19,7 @@ import { COPY_OPTIONS } from '../shared/constants/copy'; import switchDirection from '../shared/lib/switch-direction'; import { setupLocale } from '../shared/lib/error-utils'; import { trace, TraceName } from '../shared/lib/trace'; +import { getCurrentChainId } from '../shared/modules/selectors/networks'; import * as actions from './store/actions'; import configureStore from './store/store'; import { @@ -29,7 +30,6 @@ import { getNetworkToAutomaticallySwitchTo, getSwitchedNetworkDetails, getUseRequestQueue, - getCurrentChainId, } from './selectors'; import { ALERT_STATE } from './ducks/alerts'; import { diff --git a/ui/pages/asset/components/token-buttons.tsx b/ui/pages/asset/components/token-buttons.tsx index 6f14e6ae8799..94e6f2674929 100644 --- a/ui/pages/asset/components/token-buttons.tsx +++ b/ui/pages/asset/components/token-buttons.tsx @@ -27,9 +27,9 @@ import { getIsBridgeChain, getCurrentKeyring, ///: END:ONLY_INCLUDE_IF - getCurrentChainId, getNetworkConfigurationIdByChainId, } from '../../../selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) import useBridging from '../../../hooks/bridge/useBridging'; ///: END:ONLY_INCLUDE_IF diff --git a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.js b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.js index dda856d64abd..a94216acf64e 100644 --- a/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.js +++ b/ui/pages/confirm-add-suggested-nft/confirm-add-suggested-nft.js @@ -27,8 +27,8 @@ import { Box, Text, } from '../../components/component-library'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getRpcPrefsForCurrentProvider, getSuggestedNfts, getIpfsGateway, diff --git a/ui/pages/create-account/connect-hardware/index.js b/ui/pages/create-account/connect-hardware/index.js index 85464baccb69..22b335c75b17 100644 --- a/ui/pages/create-account/connect-hardware/index.js +++ b/ui/pages/create-account/connect-hardware/index.js @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import * as actions from '../../../store/actions'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getMetaMaskAccounts, getRpcPrefsForCurrentProvider, getMetaMaskAccountsConnected, diff --git a/ui/pages/institutional/custody/custody.tsx b/ui/pages/institutional/custody/custody.tsx index c4bdc4b7a252..bb4f7d570555 100644 --- a/ui/pages/institutional/custody/custody.tsx +++ b/ui/pages/institutional/custody/custody.tsx @@ -39,10 +39,8 @@ import { CUSTODY_ACCOUNT_ROUTE, DEFAULT_ROUTE, } from '../../../helpers/constants/routes'; -import { - getCurrentChainId, - getSelectedInternalAccount, -} from '../../../selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; +import { getSelectedInternalAccount } from '../../../selectors'; import { getMMIConfiguration } from '../../../selectors/institutional/selectors'; import { getInstitutionalConnectRequests } from '../../../ducks/institutional/institutional'; import CustodyAccountList from '../account-list'; diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index c155be4ba488..28f4291ee37c 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -1,6 +1,11 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { compose } from 'redux'; +import { + getCurrentChainId, + isNetworkLoading, + getProviderConfig, +} from '../../../shared/modules/selectors/networks'; import { getAllAccountsOnNetworkAreEmpty, getIsNetworkUsed, @@ -8,7 +13,6 @@ import { getPreferences, getTheme, getIsTestnet, - getCurrentChainId, getShouldShowSeedPhraseReminder, isCurrentProviderCustom, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -25,10 +29,6 @@ import { getUnapprovedTransactions, getPendingApprovals, } from '../../selectors'; -import { - isNetworkLoading, - getProviderConfig, -} from '../../../shared/modules/selectors/networks'; import { lockMetamask, hideImportNftsModal, diff --git a/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx b/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx index 5e4ef2511a51..38769760b058 100644 --- a/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx +++ b/ui/pages/smart-transactions/smart-transaction-status-page/smart-transaction-status-page.tsx @@ -26,7 +26,8 @@ import { IconColor, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { getCurrentChainId, getFullTxData } from '../../../selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; +import { getFullTxData } from '../../../selectors'; import { BaseUrl } from '../../../../shared/constants/urls'; import { hideLoadingIndication } from '../../../store/actions'; import { hexToDecimal } from '../../../../shared/modules/conversion.utils'; diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.js index 111af726acfa..d39608d49d2b 100644 --- a/ui/pages/swaps/awaiting-swap/awaiting-swap.js +++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.js @@ -12,9 +12,8 @@ import { MetaMetricsEventCategory, MetaMetricsEventName, } from '../../../../shared/constants/metametrics'; - +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getCurrentCurrency, getRpcPrefsForCurrentProvider, getUSDConversionRate, diff --git a/ui/pages/swaps/hooks/useUpdateSwapsState.test.ts b/ui/pages/swaps/hooks/useUpdateSwapsState.test.ts index f01df2ab50bc..35034d5e888f 100644 --- a/ui/pages/swaps/hooks/useUpdateSwapsState.test.ts +++ b/ui/pages/swaps/hooks/useUpdateSwapsState.test.ts @@ -13,9 +13,9 @@ import { setTopAssets, } from '../../../ducks/swaps/swaps'; import { setSwapsTokens } from '../../../store/actions'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { checkNetworkAndAccountSupports1559, - getCurrentChainId, getIsSwapsChain, getUseExternalServices, } from '../../../selectors'; diff --git a/ui/pages/swaps/hooks/useUpdateSwapsState.ts b/ui/pages/swaps/hooks/useUpdateSwapsState.ts index dc44c51a8489..6ecb9b815073 100644 --- a/ui/pages/swaps/hooks/useUpdateSwapsState.ts +++ b/ui/pages/swaps/hooks/useUpdateSwapsState.ts @@ -12,9 +12,9 @@ import { setTopAssets, } from '../../../ducks/swaps/swaps'; import { setSwapsTokens } from '../../../store/actions'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { checkNetworkAndAccountSupports1559, - getCurrentChainId, getIsSwapsChain, getUseExternalServices, } from '../../../selectors'; diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index e16166297545..8685bf12ca4c 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -19,12 +19,12 @@ import { I18nContext } from '../../contexts/i18n'; import { getSelectedAccount, - getCurrentChainId, getIsSwapsChain, isHardwareWallet, getHardwareWalletType, getTokenList, } from '../../selectors/selectors'; +import { getCurrentChainId } from '../../../shared/modules/selectors/networks'; import { getQuotes, clearSwapsState, diff --git a/ui/pages/swaps/list-with-search/list-with-search.js b/ui/pages/swaps/list-with-search/list-with-search.js index 6208e6d0f8bb..16951409d678 100644 --- a/ui/pages/swaps/list-with-search/list-with-search.js +++ b/ui/pages/swaps/list-with-search/list-with-search.js @@ -19,7 +19,7 @@ import ItemList from '../searchable-item-list/item-list'; import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils'; import { I18nContext } from '../../../contexts/i18n'; import { fetchToken } from '../swaps.util'; -import { getCurrentChainId } from '../../../selectors/selectors'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; let timeoutIdForSearch; diff --git a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js index 1e5eb5179e2c..63af7ff75ad1 100644 --- a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js +++ b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js @@ -54,11 +54,11 @@ import { getLatestAddedTokenTo, getUsedQuote, } from '../../../ducks/swaps/swaps'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { getSwapsDefaultToken, getTokenExchangeRates, getCurrentCurrency, - getCurrentChainId, getRpcPrefsForCurrentProvider, getTokenList, isHardwareWallet, diff --git a/ui/pages/swaps/prepare-swap-page/review-quote.js b/ui/pages/swaps/prepare-swap-page/review-quote.js index 680ece113f5d..96c7cf9ac4c8 100644 --- a/ui/pages/swaps/prepare-swap-page/review-quote.js +++ b/ui/pages/swaps/prepare-swap-page/review-quote.js @@ -45,13 +45,13 @@ import { getSmartTransactionFees, getCurrentSmartTransactionsEnabled, } from '../../../ducks/swaps/swaps'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { conversionRateSelector, getSelectedAccount, getCurrentCurrency, getTokenExchangeRates, getSwapsDefaultToken, - getCurrentChainId, isHardwareWallet, getHardwareWalletType, checkNetworkAndAccountSupports1559, diff --git a/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js b/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js index bd1bb5aa5aaf..9a35d48db403 100644 --- a/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js +++ b/ui/pages/swaps/searchable-item-list/item-list/item-list.component.js @@ -7,8 +7,8 @@ import UrlIcon from '../../../../components/ui/url-icon'; import Button from '../../../../components/ui/button'; import ActionableMessage from '../../../../components/ui/actionable-message/actionable-message'; import { I18nContext } from '../../../../contexts/i18n'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import { - getCurrentChainId, getRpcPrefsForCurrentProvider, getUseCurrencyRateCheck, } from '../../../../selectors'; diff --git a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js index 079089ef36bd..5ebd28762085 100644 --- a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js +++ b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js @@ -8,7 +8,7 @@ import TextField from '../../../../components/ui/text-field'; import { usePrevious } from '../../../../hooks/usePrevious'; import { isValidHexAddress } from '../../../../../shared/modules/hexstring-utils'; import { fetchToken } from '../../swaps.util'; -import { getCurrentChainId } from '../../../../selectors/selectors'; +import { getCurrentChainId } from '../../../../../shared/modules/selectors/networks'; import SearchIcon from '../../../../components/ui/icon/search-icon'; const renderAdornment = () => ( diff --git a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js index d3127c9a94f3..ae051eeeefc1 100644 --- a/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js +++ b/ui/pages/swaps/smart-transaction-status/smart-transaction-status.js @@ -13,10 +13,10 @@ import { cancelSwapsSmartTransaction, getUsedQuote, } from '../../../ducks/swaps/swaps'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { isHardwareWallet, getHardwareWalletType, - getCurrentChainId, getRpcPrefsForCurrentProvider, } from '../../../selectors'; import { diff --git a/ui/selectors/confirm-transaction.js b/ui/selectors/confirm-transaction.js index b68d598a0839..10183c916652 100644 --- a/ui/selectors/confirm-transaction.js +++ b/ui/selectors/confirm-transaction.js @@ -28,11 +28,13 @@ import { subtractHexes, sumHexes, } from '../../shared/modules/conversion.utils'; -import { getProviderConfig } from '../../shared/modules/selectors/networks'; +import { + getProviderConfig, + getCurrentChainId, +} from '../../shared/modules/selectors/networks'; import { getAveragePriceEstimateInHexWEI } from './custom-gas'; import { checkNetworkAndAccountSupports1559, - getCurrentChainId, getMetaMaskAccounts, getTokenExchangeRates, } from './selectors'; diff --git a/ui/selectors/institutional/selectors.ts b/ui/selectors/institutional/selectors.ts index eb70e0fcd72c..6ef1c0bf14ca 100644 --- a/ui/selectors/institutional/selectors.ts +++ b/ui/selectors/institutional/selectors.ts @@ -175,12 +175,13 @@ export function getIsCustodianSupportedChain( // @ts-expect-error state types don't match const selectedAccount = getSelectedInternalAccount(state); const accountType = getAccountType(state); - const providerConfig = getProviderConfig(state); - if (!selectedAccount || !accountType || !providerConfig) { + if (!selectedAccount || !accountType) { throw new Error('Invalid state'); } + const providerConfig = getProviderConfig(state); + if (typeof providerConfig.chainId !== 'string') { throw new Error('Chain ID must be a string'); } diff --git a/ui/selectors/multichain.ts b/ui/selectors/multichain.ts index 903b5d0a4a71..375949db877f 100644 --- a/ui/selectors/multichain.ts +++ b/ui/selectors/multichain.ts @@ -29,10 +29,10 @@ import { getProviderConfig, NetworkState, getNetworkConfigurationsByChainId, + getCurrentChainId, } from '../../shared/modules/selectors/networks'; import { AccountsState, getSelectedInternalAccount } from './accounts'; import { - getCurrentChainId, getCurrentCurrency, getIsMainnet, getMaybeSelectedInternalAccount, diff --git a/ui/selectors/nft.ts b/ui/selectors/nft.ts index ab3836714923..15b564f3d422 100644 --- a/ui/selectors/nft.ts +++ b/ui/selectors/nft.ts @@ -1,5 +1,6 @@ import { Nft, NftContract } from '@metamask/assets-controllers'; import { createSelector } from 'reselect'; +import { NetworkState } from '../../shared/modules/selectors/networks'; import { getMemoizedCurrentChainId } from './selectors'; export type NftState = { @@ -62,9 +63,9 @@ export const getNftContractsByAddressByChain = createSelector( ); export const getNftContractsByAddressOnCurrentChain = createSelector( + (state: NftState & NetworkState) => getMemoizedCurrentChainId(state), getNftContractsByAddressByChain, - getMemoizedCurrentChainId, - (nftContractsByAddressByChain, currentChainId) => { + (currentChainId, nftContractsByAddressByChain) => { return nftContractsByAddressByChain[currentChainId] ?? {}; }, ); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 154864dc9af4..e853e28d300b 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -12,6 +12,12 @@ import { NameType } from '@metamask/name-controller'; import { TransactionStatus } from '@metamask/transaction-controller'; import { isEvmAccountType } from '@metamask/keyring-api'; import { RpcEndpointType } from '@metamask/network-controller'; +import { + getCurrentChainId, + getProviderConfig, + getSelectedNetworkClientId, + getNetworkConfigurationsByChainId, +} from '../../shared/modules/selectors/networks'; // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import { addHexPrefix, getEnvironmentType } from '../../app/scripts/lib/util'; @@ -104,11 +110,6 @@ import { MultichainNativeAssets } from '../../shared/constants/multichain/assets import { BridgeFeatureFlagsKey } from '../../app/scripts/controllers/bridge/types'; import { hasTransactionData } from '../../shared/modules/transaction.utils'; import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; -import { - getProviderConfig, - getSelectedNetworkClientId, - getNetworkConfigurationsByChainId, -} from '../../shared/modules/selectors/networks'; import { createDeepEqualSelector } from '../../shared/modules/selectors/util'; import { getAllUnapprovedTransactions, @@ -132,11 +133,6 @@ export function getNetworkIdentifier(state) { return nickname || rpcUrl || type; } -export function getCurrentChainId(state) { - const { chainId } = getProviderConfig(state); - return chainId; -} - export function getMetaMetricsId(state) { const { metaMetricsId } = state.metamask; return metaMetricsId; diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index c749d8ff3fe7..a7c5613eb169 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -13,9 +13,13 @@ import { createMockInternalAccount } from '../../test/jest/mocks'; import { mockNetworkState } from '../../test/stub/networks'; import { DeleteRegulationStatus } from '../../shared/constants/metametrics'; import { selectSwitchedNetworkNeverShowMessage } from '../components/app/toast-master/selectors'; -import { getProviderConfig } from '../../shared/modules/selectors/networks'; +import * as networkSelectors from '../../shared/modules/selectors/networks'; import * as selectors from './selectors'; +jest.mock('../../shared/modules/selectors/networks', () => ({ + ...jest.requireActual('../../shared/modules/selectors/networks'), +})); + jest.mock('../../app/scripts/lib/util', () => ({ ...jest.requireActual('../../app/scripts/lib/util'), getEnvironmentType: jest.fn().mockReturnValue('popup'), @@ -189,7 +193,7 @@ describe('Selectors', () => { expect(selectors.getSwitchedNetworkDetails(state)).toStrictEqual({ imageUrl: './images/eth_logo.svg', - nickname: getProviderConfig(state).nickname, + nickname: networkSelectors.getProviderConfig(state).nickname, origin, }); }); @@ -1997,7 +2001,10 @@ describe('#getConnectedSitesList', () => { }); it('returns the token object for the overridden chainId when overrideChainId is provided', () => { - const getCurrentChainIdSpy = jest.spyOn(selectors, 'getCurrentChainId'); + const getCurrentChainIdSpy = jest.spyOn( + networkSelectors, + 'getCurrentChainId', + ); const expectedToken = { symbol: 'POL', name: 'Polygon', @@ -2116,7 +2123,10 @@ describe('#getConnectedSitesList', () => { it('respects the overrideChainId parameter', () => { process.env.METAMASK_ENVIRONMENT = 'production'; - const getCurrentChainIdSpy = jest.spyOn(selectors, 'getCurrentChainId'); + const getCurrentChainIdSpy = jest.spyOn( + networkSelectors, + 'getCurrentChainId', + ); const result = selectors.getIsSwapsChain(mockState, '0x89'); expect(result).toBe(true); @@ -2169,7 +2179,10 @@ describe('#getConnectedSitesList', () => { }); it('respects the overrideChainId parameter', () => { - const getCurrentChainIdSpy = jest.spyOn(selectors, 'getCurrentChainId'); + const getCurrentChainIdSpy = jest.spyOn( + networkSelectors, + 'getCurrentChainId', + ); const result = selectors.getIsBridgeChain(mockState, '0x89'); diff --git a/ui/selectors/transactions.js b/ui/selectors/transactions.js index 9428a6fbd8c3..4e5b038b3cc8 100644 --- a/ui/selectors/transactions.js +++ b/ui/selectors/transactions.js @@ -12,12 +12,14 @@ import { import txHelper from '../helpers/utils/tx-helper'; import { SmartTransactionStatus } from '../../shared/constants/transaction'; import { hexToDecimal } from '../../shared/modules/conversion.utils'; -import { getProviderConfig } from '../../shared/modules/selectors/networks'; +import { + getProviderConfig, + getCurrentChainId, +} from '../../shared/modules/selectors/networks'; import { createDeepEqualSelector, filterAndShapeUnapprovedTransactions, } from '../../shared/modules/selectors/util'; -import { getCurrentChainId } from './selectors'; import { getSelectedInternalAccount } from './accounts'; import { hasPendingApprovals, getApprovalRequestsByType } from './approvals'; From 70b017ab3ee83658fdaa748a8c768202113838d9 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Wed, 27 Nov 2024 16:34:40 +0100 Subject: [PATCH 15/22] fix(wallet-overview): prevent send button clicked event to be sent twice (#28772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28772?quickstart=1) ## **Related issues** Fixes: - https://github.com/MetaMask/metamask-extension/pull/28593/files#r1860667737 ## **Manual testing steps** N/A (being covered by unit tests) ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ui/components/app/wallet-overview/coin-buttons.tsx | 13 ------------- .../app/wallet-overview/eth-overview.test.js | 2 ++ .../app/wallet-overview/non-evm-overview.test.tsx | 2 ++ 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/ui/components/app/wallet-overview/coin-buttons.tsx b/ui/components/app/wallet-overview/coin-buttons.tsx index 0fcb9f2e2389..01182ea1f70a 100644 --- a/ui/components/app/wallet-overview/coin-buttons.tsx +++ b/ui/components/app/wallet-overview/coin-buttons.tsx @@ -375,19 +375,6 @@ const CoinButtons = ({ } ///: END:ONLY_INCLUDE_IF default: { - trackEvent( - { - event: MetaMetricsEventName.NavSendButtonClicked, - category: MetaMetricsEventCategory.Navigation, - properties: { - token_symbol: 'ETH', - location: 'Home', - text: 'Send', - chain_id: chainId, - }, - }, - { excludeMetaMetricsId: false }, - ); await setCorrectChain(); await dispatch(startNewDraftTransaction({ type: AssetType.native })); history.push(SEND_ROUTE); diff --git a/ui/components/app/wallet-overview/eth-overview.test.js b/ui/components/app/wallet-overview/eth-overview.test.js index 5af6990d4499..104764251cf8 100644 --- a/ui/components/app/wallet-overview/eth-overview.test.js +++ b/ui/components/app/wallet-overview/eth-overview.test.js @@ -445,6 +445,7 @@ describe('EthOverview', () => { expect(buyButton).not.toBeDisabled(); fireEvent.click(buyButton); + expect(mockTrackEvent).toHaveBeenCalledTimes(1); expect(mockTrackEvent).toHaveBeenCalledWith({ event: MetaMetricsEventName.NavBuyButtonClicked, category: MetaMetricsEventCategory.Navigation, @@ -539,6 +540,7 @@ describe('EthOverview', () => { expect(sendButton).not.toBeDisabled(); fireEvent.click(sendButton); + expect(mockTrackEvent).toHaveBeenCalledTimes(1); expect(mockTrackEvent).toHaveBeenCalledWith( { event: MetaMetricsEventName.NavSendButtonClicked, diff --git a/ui/components/app/wallet-overview/non-evm-overview.test.tsx b/ui/components/app/wallet-overview/non-evm-overview.test.tsx index aa49eb77e79d..cfffb9bdc7ce 100644 --- a/ui/components/app/wallet-overview/non-evm-overview.test.tsx +++ b/ui/components/app/wallet-overview/non-evm-overview.test.tsx @@ -305,6 +305,7 @@ describe('NonEvmOverview', () => { expect(buyButton).not.toBeDisabled(); fireEvent.click(buyButton as HTMLElement); + expect(mockTrackEvent).toHaveBeenCalledTimes(1); expect(mockTrackEvent).toHaveBeenCalledWith({ event: MetaMetricsEventName.NavBuyButtonClicked, category: MetaMetricsEventCategory.Navigation, @@ -381,6 +382,7 @@ describe('NonEvmOverview', () => { expect(sendButton).not.toBeDisabled(); fireEvent.click(sendButton as HTMLElement); + expect(mockTrackEvent).toHaveBeenCalledTimes(1); expect(mockTrackEvent).toHaveBeenCalledWith( { event: MetaMetricsEventName.NavSendButtonClicked, From 78a218f7e02e6662911e7d814f93bede21fb6fb4 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 27 Nov 2024 12:07:49 -0330 Subject: [PATCH 16/22] chore: Bump `@metamask/eth-json-rpc-middleware` to v14.0.2 (#28755) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Bump `@metamask/eth-json-rpc-middleware` from v14.0.1 (+ patch) to v14.0.2. The v14.0.2 release contains the same change as was added in the patch being removed. This bump has no functional changes. Changelog: https://github.com/MetaMask/eth-json-rpc-middleware/blob/main/CHANGELOG.md#1402 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28755?quickstart=1) ## **Related issues** Context: * https://github.com/MetaMask/metamask-extension/pull/27021 * https://github.com/MetaMask/eth-json-rpc-middleware/pull/334 ## **Manual testing steps** N/A ## **Screenshots/Recordings** N/A ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- ...rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch | 13 ----- package.json | 2 +- yarn.lock | 49 ++++++------------- 3 files changed, 16 insertions(+), 48 deletions(-) delete mode 100644 .yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch diff --git a/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch b/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch deleted file mode 100644 index e82feb182c3a..000000000000 --- a/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/dist/wallet.js b/dist/wallet.js -index fce8272ab926443df4c5971c811664f849791425..9237ffcaaea2260e01182feecec667b10edd35a0 100644 ---- a/dist/wallet.js -+++ b/dist/wallet.js -@@ -293,7 +293,7 @@ exports.createWalletMiddleware = createWalletMiddleware; - */ - function validateVerifyingContract(data) { - const { domain: { verifyingContract } = {} } = (0, normalize_1.parseTypedMessage)(data); -- if (verifyingContract && !(0, utils_1.isValidHexAddress)(verifyingContract)) { -+ if (verifyingContract && verifyingContract !== 'cosmos' && !(0, utils_1.isValidHexAddress)(verifyingContract)) { - throw rpc_errors_1.rpcErrors.invalidInput(); - } - } diff --git a/package.json b/package.json index abd56565d35d..3edf8174a2fb 100644 --- a/package.json +++ b/package.json @@ -298,7 +298,7 @@ "@metamask/ens-controller": "^14.0.0", "@metamask/ens-resolver-snap": "^0.1.2", "@metamask/eth-json-rpc-filters": "^9.0.0", - "@metamask/eth-json-rpc-middleware": "patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch", + "@metamask/eth-json-rpc-middleware": "^14.0.2", "@metamask/eth-ledger-bridge-keyring": "^5.0.1", "@metamask/eth-query": "^4.0.0", "@metamask/eth-sig-util": "^7.0.1", diff --git a/yarn.lock b/yarn.lock index 922fc470e8e6..5744409b28be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5286,25 +5286,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-json-rpc-middleware@npm:14.0.1": - version: 14.0.1 - resolution: "@metamask/eth-json-rpc-middleware@npm:14.0.1" - dependencies: - "@metamask/eth-block-tracker": "npm:^11.0.1" - "@metamask/eth-json-rpc-provider": "npm:^4.1.1" - "@metamask/eth-sig-util": "npm:^7.0.3" - "@metamask/json-rpc-engine": "npm:^9.0.2" - "@metamask/rpc-errors": "npm:^6.3.1" - "@metamask/utils": "npm:^9.1.0" - "@types/bn.js": "npm:^5.1.5" - bn.js: "npm:^5.2.1" - klona: "npm:^2.0.6" - pify: "npm:^5.0.0" - safe-stable-stringify: "npm:^2.4.3" - checksum: 10/39beecb0d2be19854b132fd615aee1f29195602d3db902f52755260b26a2c37c0a91cd635a09d4dc16f922d32bb229003b338228ae29577c5151d880fad04637 - languageName: node - linkType: hard - "@metamask/eth-json-rpc-middleware@npm:^13.0.0": version: 13.0.0 resolution: "@metamask/eth-json-rpc-middleware@npm:13.0.0" @@ -5324,41 +5305,41 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-json-rpc-middleware@npm:^15.0.0": - version: 15.0.0 - resolution: "@metamask/eth-json-rpc-middleware@npm:15.0.0" +"@metamask/eth-json-rpc-middleware@npm:^14.0.2": + version: 14.0.2 + resolution: "@metamask/eth-json-rpc-middleware@npm:14.0.2" dependencies: "@metamask/eth-block-tracker": "npm:^11.0.1" - "@metamask/eth-json-rpc-provider": "npm:^4.1.5" + "@metamask/eth-json-rpc-provider": "npm:^4.1.1" "@metamask/eth-sig-util": "npm:^7.0.3" - "@metamask/json-rpc-engine": "npm:^10.0.0" - "@metamask/rpc-errors": "npm:^7.0.0" + "@metamask/json-rpc-engine": "npm:^9.0.2" + "@metamask/rpc-errors": "npm:^6.3.1" "@metamask/utils": "npm:^9.1.0" "@types/bn.js": "npm:^5.1.5" bn.js: "npm:^5.2.1" klona: "npm:^2.0.6" pify: "npm:^5.0.0" safe-stable-stringify: "npm:^2.4.3" - checksum: 10/3c48d34264c695535f2b4e819fb602d835b6ed37309116a06d04d1b706a7335e0205cd4ccdbf1d3e9dc15ebf40d88954a9a2dc18a91f223dcd6d6392e026a5e9 + checksum: 10/8094efcd23bb5a1335f7d41cfb6193e035cc30329c2989f09b65f8870178ce960a643a4818a00fc791076a51d629511190d54f3b327165ac6f966c307dc9b90d languageName: node linkType: hard -"@metamask/eth-json-rpc-middleware@patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch": - version: 14.0.1 - resolution: "@metamask/eth-json-rpc-middleware@patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch::version=14.0.1&hash=96e7e0" +"@metamask/eth-json-rpc-middleware@npm:^15.0.0": + version: 15.0.0 + resolution: "@metamask/eth-json-rpc-middleware@npm:15.0.0" dependencies: "@metamask/eth-block-tracker": "npm:^11.0.1" - "@metamask/eth-json-rpc-provider": "npm:^4.1.1" + "@metamask/eth-json-rpc-provider": "npm:^4.1.5" "@metamask/eth-sig-util": "npm:^7.0.3" - "@metamask/json-rpc-engine": "npm:^9.0.2" - "@metamask/rpc-errors": "npm:^6.3.1" + "@metamask/json-rpc-engine": "npm:^10.0.0" + "@metamask/rpc-errors": "npm:^7.0.0" "@metamask/utils": "npm:^9.1.0" "@types/bn.js": "npm:^5.1.5" bn.js: "npm:^5.2.1" klona: "npm:^2.0.6" pify: "npm:^5.0.0" safe-stable-stringify: "npm:^2.4.3" - checksum: 10/d1d97a845a8a9a5931c3853c6e2768a97ba289d676a2a8b6111077531943f9647430ef8e3f2a05f4643760ffdab1af0dc72574ca3010feadbdfab3dec345b7c8 + checksum: 10/3c48d34264c695535f2b4e819fb602d835b6ed37309116a06d04d1b706a7335e0205cd4ccdbf1d3e9dc15ebf40d88954a9a2dc18a91f223dcd6d6392e026a5e9 languageName: node linkType: hard @@ -26836,7 +26817,7 @@ __metadata: "@metamask/eslint-config-typescript": "npm:^9.0.1" "@metamask/eslint-plugin-design-tokens": "npm:^1.1.0" "@metamask/eth-json-rpc-filters": "npm:^9.0.0" - "@metamask/eth-json-rpc-middleware": "patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch" + "@metamask/eth-json-rpc-middleware": "npm:^14.0.2" "@metamask/eth-json-rpc-provider": "npm:^4.1.6" "@metamask/eth-ledger-bridge-keyring": "npm:^5.0.1" "@metamask/eth-query": "npm:^4.0.0" From 478537374b88070b68d08a6c8d9a124a2f4c4003 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 27 Nov 2024 21:11:05 +0530 Subject: [PATCH 17/22] feat: on UI side filtering put typed sign V4 requests for which decoding data is displayed (#28762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Filter our typed sign requests for which decoding api results are displayed. ## **Related issues** Fixes: https://github.com/MetaMask/MetaMask-planning/issues/3682 ## **Manual testing steps** 1. Enable signature decoding locally 2. Submit request for permit signatures on test dapp of supported seaport signatures 3. It should work as expected ## **Screenshots/Recordings** Screenshot 2024-11-27 at 3 14 40 PM Screenshot 2024-11-27 at 3 14 31 PM ## **Pre-merge author checklist** - [X] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- shared/constants/signatures.ts | 1 + test/data/confirmations/typed_sign.ts | 53 +++ .../app/confirm/info/row/section.tsx | 5 +- .../static-simulation/static-simulation.tsx | 22 +- .../permit-simulation.test.tsx.snap | 345 ------------------ .../decoded-simulation.test.tsx.snap | 115 ------ .../default-simulation/index.ts | 1 - .../permit-simulation.test.tsx | 112 ------ .../permit-simulation/permit-simulation.tsx | 25 -- .../decoded-simulation.test.tsx | 19 +- .../decoded-simulation/decoded-simulation.tsx | 10 +- .../decoded-simulation/index.ts | 0 .../typed-sign-v4-simulation/index.ts | 1 + .../native-value-display.test.tsx | 0 .../native-value-display.tsx | 4 +- .../permit-simulation.test.tsx.snap} | 4 +- .../permit-simulation/index.ts | 0 .../permit-simulation.test.tsx} | 8 +- .../permit-simulation/permit-simulation.tsx} | 4 +- .../typed-sign-v4-simulation.test.tsx | 160 ++++++++ .../typed-sign-v4-simulation.tsx | 63 ++++ .../__snapshots__/value-display.test.tsx.snap | 0 .../value-display/value-display.test.tsx | 0 .../value-display/value-display.tsx | 4 +- .../confirm/info/typed-sign/typed-sign.tsx | 8 +- 25 files changed, 348 insertions(+), 616 deletions(-) delete mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap delete mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/__snapshots__/decoded-simulation.test.tsx.snap delete mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/index.ts delete mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx delete mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/decoded-simulation/decoded-simulation.test.tsx (79%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/decoded-simulation/decoded-simulation.tsx (92%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/decoded-simulation/index.ts (100%) create mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/index.ts rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/native-value-display/native-value-display.test.tsx (100%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/native-value-display/native-value-display.tsx (96%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation/default-simulation/__snapshots__/default-simulation.test.tsx.snap => typed-sign-v4-simulation/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap} (98%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{ => typed-sign-v4-simulation}/permit-simulation/index.ts (100%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation/default-simulation/default-simulation.test.tsx => typed-sign-v4-simulation/permit-simulation/permit-simulation.test.tsx} (92%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation/default-simulation/default-simulation.tsx => typed-sign-v4-simulation/permit-simulation/permit-simulation.tsx} (97%) create mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.test.tsx create mode 100644 ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.tsx rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/value-display/__snapshots__/value-display.test.tsx.snap (100%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/value-display/value-display.test.tsx (100%) rename ui/pages/confirmations/components/confirm/info/typed-sign/{permit-simulation => typed-sign-v4-simulation}/value-display/value-display.tsx (97%) diff --git a/shared/constants/signatures.ts b/shared/constants/signatures.ts index 3ecc56be8630..48b17afe51d9 100644 --- a/shared/constants/signatures.ts +++ b/shared/constants/signatures.ts @@ -9,6 +9,7 @@ export enum PrimaryTypePermit { PermitBatchTransferFrom = 'PermitBatchTransferFrom', PermitSingle = 'PermitSingle', PermitTransferFrom = 'PermitTransferFrom', + PermitWitnessTransferFrom = 'PermitWitnessTransferFrom', } /** diff --git a/test/data/confirmations/typed_sign.ts b/test/data/confirmations/typed_sign.ts index 831d561f0cb2..b0984684f12d 100644 --- a/test/data/confirmations/typed_sign.ts +++ b/test/data/confirmations/typed_sign.ts @@ -188,6 +188,59 @@ export const permitSignatureMsg = { }, } as SignatureRequestType; +export const seaportSignatureMsg = { + chainId: '0x1', + id: 'e9297d91-aca0-11ef-9ac4-417a173450d3', + messageParams: { + data: '{"types":{"OrderComponents":[{"name":"offerer","type":"address"},{"name":"zone","type":"address"},{"name":"offer","type":"OfferItem[]"},{"name":"consideration","type":"ConsiderationItem[]"},{"name":"orderType","type":"uint8"},{"name":"startTime","type":"uint256"},{"name":"endTime","type":"uint256"},{"name":"zoneHash","type":"bytes32"},{"name":"salt","type":"uint256"},{"name":"conduitKey","type":"bytes32"},{"name":"counter","type":"uint256"}],"OfferItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"}],"ConsiderationItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"},{"name":"recipient","type":"address"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"Seaport","version":"1.1","chainId":"0x1","verifyingContract":"0x00000000006c3852cbef3e08e8df289169ede581"},"primaryType":"OrderComponents","message":{"offerer":"0x935E73EDb9fF52E23BaC7F7e043A1ecD06d05477","zone":"0x004c00500000ad104d7dbd00e3ae0a5c00560c00","offer":[{"itemType":"2","token":"0x922dc160f2ab743312a6bb19dd5152c1d3ecca33","identifierOrCriteria":"176","startAmount":"1","endAmount":"1"}],"consideration":[{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"0","endAmount":"0","recipient":"0x935E73EDb9fF52E23BaC7F7e043A1ecD06d05477"},{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"25000000000000000","endAmount":"25000000000000000","recipient":"0x8de9c5a032463c561423387a9648c5c7bcc5bc90"},{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"50000000000000000","endAmount":"50000000000000000","recipient":"0x5c6139cd9ff1170197f13935c58f825b422c744c"}],"orderType":"3","startTime":"1660565524","endTime":"1661170320","zoneHash":"0x3000000000000000000000000000000000000000000000000000000000000000","salt":"5965482869793190759363249887602871532","conduitKey":"0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000","counter":"0"}}', + from: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477', + version: 'V4', + signatureMethod: 'eth_signTypedData_v4', + metamaskId: 'e9297d90-aca0-11ef-9ac4-417a173450d3', + origin: 'https://develop.d3bkcslj57l47p.amplifyapp.com', + requestId: 1376479613, + }, + networkClientId: 'mainnet', + securityAlertResponse: { + result_type: 'loading', + reason: 'CheckingChain', + securityAlertId: 'def3b0ef-c96b-4c87-b1b1-c69cc02a0f78', + }, + status: 'unapproved', + time: 1732699257833, + type: 'eth_signTypedData', + version: 'V4', + decodingLoading: false, + decodingData: { + stateChanges: [ + { + assetType: 'NATIVE', + changeType: 'RECEIVE', + address: '', + amount: '0', + contractAddress: '', + }, + { + assetType: 'ERC721', + changeType: 'LISTING', + address: '', + amount: '', + contractAddress: '0x922dc160f2ab743312a6bb19dd5152c1d3ecca33', + tokenID: '176', + }, + ], + }, + msgParams: { + data: '{"types":{"OrderComponents":[{"name":"offerer","type":"address"},{"name":"zone","type":"address"},{"name":"offer","type":"OfferItem[]"},{"name":"consideration","type":"ConsiderationItem[]"},{"name":"orderType","type":"uint8"},{"name":"startTime","type":"uint256"},{"name":"endTime","type":"uint256"},{"name":"zoneHash","type":"bytes32"},{"name":"salt","type":"uint256"},{"name":"conduitKey","type":"bytes32"},{"name":"counter","type":"uint256"}],"OfferItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"}],"ConsiderationItem":[{"name":"itemType","type":"uint8"},{"name":"token","type":"address"},{"name":"identifierOrCriteria","type":"uint256"},{"name":"startAmount","type":"uint256"},{"name":"endAmount","type":"uint256"},{"name":"recipient","type":"address"}],"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}]},"domain":{"name":"Seaport","version":"1.4","chainId":"0x1","verifyingContract":"0x00000000006c3852cbef3e08e8df289169ede581"},"primaryType":"OrderComponents","message":{"offerer":"0x935E73EDb9fF52E23BaC7F7e043A1ecD06d05477","zone":"0x004c00500000ad104d7dbd00e3ae0a5c00560c00","offer":[{"itemType":"2","token":"0x922dc160f2ab743312a6bb19dd5152c1d3ecca33","identifierOrCriteria":"176","startAmount":"1","endAmount":"1"}],"consideration":[{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"0","endAmount":"0","recipient":"0x935E73EDb9fF52E23BaC7F7e043A1ecD06d05477"},{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"25000000000000000","endAmount":"25000000000000000","recipient":"0x8de9c5a032463c561423387a9648c5c7bcc5bc90"},{"itemType":"0","token":"0x0000000000000000000000000000000000000000","identifierOrCriteria":"0","startAmount":"50000000000000000","endAmount":"50000000000000000","recipient":"0x5c6139cd9ff1170197f13935c58f825b422c744c"}],"orderType":"3","startTime":"1660565524","endTime":"1661170320","zoneHash":"0x3000000000000000000000000000000000000000000000000000000000000000","salt":"5965482869793190759363249887602871532","conduitKey":"0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000","counter":"0"}}', + from: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477', + version: 'V4', + signatureMethod: 'eth_signTypedData_v4', + metamaskId: 'e9297d90-aca0-11ef-9ac4-417a173450d3', + origin: 'https://develop.d3bkcslj57l47p.amplifyapp.com', + requestId: 1376479613, + }, +} as SignatureRequestType; + export const permitNFTSignatureMsg = { id: 'c5067710-87cf-11ef-916c-71f266571322', chainId: CHAIN_IDS.GOERLI, diff --git a/ui/components/app/confirm/info/row/section.tsx b/ui/components/app/confirm/info/row/section.tsx index 7087fd3cd138..0725c08aae35 100644 --- a/ui/components/app/confirm/info/row/section.tsx +++ b/ui/components/app/confirm/info/row/section.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { CSSProperties } from 'react'; import { Box } from '../../../../component-library'; import { BackgroundColor, @@ -8,12 +8,14 @@ import { export type ConfirmInfoSectionProps = { children: React.ReactNode | string; noPadding?: boolean; + style?: CSSProperties; 'data-testid'?: string; }; export const ConfirmInfoSection = ({ children, noPadding, + style = {}, 'data-testid': dataTestId, }: ConfirmInfoSectionProps) => { return ( @@ -23,6 +25,7 @@ export const ConfirmInfoSection = ({ borderRadius={BorderRadius.MD} padding={noPadding ? 0 : 2} marginBottom={4} + style={style} > {children} diff --git a/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx index 9a34abd8009d..14a6d82d5a26 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx @@ -7,20 +7,38 @@ import { } from '../../../../../../../components/app/confirm/info/row'; import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; import { + AlignItems, Display, JustifyContent, } from '../../../../../../../helpers/constants/design-system'; import Preloader from '../../../../../../../components/ui/icon/preloader'; +const CollapsedSectionStyles = { + display: Display.Flex, + alignItems: AlignItems.center, + justifyContent: JustifyContent.spaceBetween, +}; + const StaticSimulation: React.FC<{ title: string; titleTooltip: string; description?: string; simulationElements: React.ReactNode; isLoading?: boolean; -}> = ({ title, titleTooltip, description, simulationElements, isLoading }) => { + isCollapsed?: boolean; +}> = ({ + title, + titleTooltip, + description, + simulationElements, + isLoading, + isCollapsed = false, +}) => { return ( - + {description && } diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap deleted file mode 100644 index fef756f178c2..000000000000 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap +++ /dev/null @@ -1,345 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PermitSimulation should not render default simulation if decodingLoading is true 1`] = ` -
-
-
-
-
-

- Estimated changes -

-
-
- -
-
-
-
-
-
- - - - - - - - - -
-
-
-`; - -exports[`PermitSimulation should render default simulation if decoding api does not return result 1`] = ` -
-
-
-
-
-

- Estimated changes -

-
-
- -
-
-
-
-
-

- You're giving the spender permission to spend this many tokens from your account. -

-
-
-
-
-
-

- Spending cap -

-
-
-
-
-
-
-
-
-

- 30 -

-
-
-
-
-
- -

- 0xCcCCc...ccccC -

-
-
-
-
-
-
-
-
-
-`; - -exports[`PermitSimulation should render default simulation if decoding api returns error 1`] = ` -
-
-
-
-
-

- Estimated changes -

-
-
- -
-
-
-
-
-

- You're giving the spender permission to spend this many tokens from your account. -

-
-
-
-
-
-

- Spending cap -

-
-
-
-
-
-
-
-
-

- 30 -

-
-
-
-
-
- -

- 0xCcCCc...ccccC -

-
-
-
-
-
-
-
-
-
-`; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/__snapshots__/decoded-simulation.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/__snapshots__/decoded-simulation.test.tsx.snap deleted file mode 100644 index ed4b45276a4b..000000000000 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/__snapshots__/decoded-simulation.test.tsx.snap +++ /dev/null @@ -1,115 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DecodedSimulation renders component correctly 1`] = ` -
-
-
-
-
-

- Estimated changes -

-
-
- -
-
-
-
-
-
-
-
-

- Spending cap -

-
-
-
-
-
-
-
-

- 1,461,501,637,3... -

-
-
-
-
-
- -

- 0x6B175...71d0F -

-
-
-
-
-
-
-
-
-`; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/index.ts b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/index.ts deleted file mode 100644 index b9f6c48b65db..000000000000 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DefaultSimulation } from './default-simulation'; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx deleted file mode 100644 index a06c53f16ba3..000000000000 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React from 'react'; -import configureMockStore from 'redux-mock-store'; -import { act } from 'react-dom/test-utils'; -import { waitFor } from '@testing-library/dom'; - -import { getMockTypedSignConfirmStateForRequest } from '../../../../../../../../test/data/confirmations/helper'; -import { renderWithConfirmContextProvider } from '../../../../../../../../test/lib/confirmations/render-helpers'; -import { permitSignatureMsg } from '../../../../../../../../test/data/confirmations/typed_sign'; -import { memoizedGetTokenStandardAndDetails } from '../../../../../utils/token'; -import * as SignatureMetrics from '../../../../../hooks/useDecodedSignatureMetrics'; -import PermitSimulation from './permit-simulation'; - -jest.mock('../../../../../../../store/actions', () => { - return { - getTokenStandardAndDetails: jest - .fn() - .mockResolvedValue({ decimals: 2, standard: 'ERC20' }), - updateEventFragment: jest.fn(), - }; -}); - -describe('PermitSimulation', () => { - afterEach(() => { - jest.clearAllMocks(); - - /** Reset memoized function using getTokenStandardAndDetails for each test */ - memoizedGetTokenStandardAndDetails?.cache?.clear?.(); - }); - - it('should render default simulation if decoding api does not return result', async () => { - const state = getMockTypedSignConfirmStateForRequest({ - ...permitSignatureMsg, - decodingLoading: false, - decodingData: undefined, - }); - const mockStore = configureMockStore([])(state); - - await act(async () => { - const { container, findByText } = renderWithConfirmContextProvider( - , - mockStore, - ); - - expect(await findByText('30')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - }); - - it('should call hook to register signature metrics properties', async () => { - const state = getMockTypedSignConfirmStateForRequest({ - ...permitSignatureMsg, - decodingLoading: false, - decodingData: undefined, - }); - const mockStore = configureMockStore([])(state); - - const mockedUseDecodedSignatureMetrics = jest - .spyOn(SignatureMetrics, 'useDecodedSignatureMetrics') - .mockImplementation(() => ''); - - await act(async () => { - renderWithConfirmContextProvider(, mockStore); - - expect(mockedUseDecodedSignatureMetrics).toHaveBeenCalledTimes(1); - }); - }); - - it('should render default simulation if decoding api returns error', async () => { - const state = getMockTypedSignConfirmStateForRequest({ - ...permitSignatureMsg, - decodingLoading: false, - decodingData: { - stateChanges: null, - error: { - message: 'some error', - type: 'SOME_ERROR', - }, - }, - }); - const mockStore = configureMockStore([])(state); - - await act(async () => { - const { container, findByText } = renderWithConfirmContextProvider( - , - mockStore, - ); - - expect(await findByText('30')).toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - }); - - it('should not render default simulation if decodingLoading is true', async () => { - const state = getMockTypedSignConfirmStateForRequest({ - ...permitSignatureMsg, - decodingLoading: true, - }); - const mockStore = configureMockStore([])(state); - - await act(async () => { - const { container, queryByTestId } = renderWithConfirmContextProvider( - , - mockStore, - ); - - await waitFor(() => { - expect(queryByTestId('30')).not.toBeInTheDocument(); - expect(container).toMatchSnapshot(); - }); - }); - }); -}); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx deleted file mode 100644 index d78a9afbe1b8..000000000000 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -import { SignatureRequestType } from '../../../../../types/confirm'; -import { useConfirmContext } from '../../../../../context/confirm'; -import { useDecodedSignatureMetrics } from '../../../../../hooks/useDecodedSignatureMetrics'; -import { DefaultSimulation } from './default-simulation'; -import { DecodedSimulation } from './decoded-simulation'; - -const PermitSimulation: React.FC = () => { - const { currentConfirmation } = useConfirmContext(); - const { decodingLoading, decodingData } = currentConfirmation; - - useDecodedSignatureMetrics(); - - if ( - decodingData?.error || - (decodingData?.stateChanges === undefined && decodingLoading !== true) - ) { - return ; - } - - return ; -}; - -export default PermitSimulation; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.test.tsx similarity index 79% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.test.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.test.tsx index 86f30472b0e5..93cc6b9e4474 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.test.tsx @@ -68,12 +68,27 @@ describe('DecodedSimulation', () => { }); const mockStore = configureMockStore([])(state); - const { container } = renderWithConfirmContextProvider( + const { findByText } = renderWithConfirmContextProvider( , mockStore, ); - expect(container).toMatchSnapshot(); + expect(await findByText('Estimated changes')).toBeInTheDocument(); + expect(await findByText('Spending cap')).toBeInTheDocument(); + expect(await findByText('1,461,501,637,3...')).toBeInTheDocument(); + }); + + it('renders unavailable message if no state change is returned', async () => { + const state = getMockTypedSignConfirmStateForRequest(permitSignatureMsg); + const mockStore = configureMockStore([])(state); + + const { findByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + + expect(await findByText('Estimated changes')).toBeInTheDocument(); + expect(await findByText('Unavailable')).toBeInTheDocument(); }); describe('getStateChangeToolip', () => { diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.tsx similarity index 92% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.tsx index cf774483ee6c..53295852c566 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/decoded-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/decoded-simulation.tsx @@ -8,6 +8,7 @@ import { Hex } from '@metamask/utils'; import { TokenStandard } from '../../../../../../../../../shared/constants/transaction'; import { ConfirmInfoRow } from '../../../../../../../../components/app/confirm/info/row'; +import { Text } from '../../../../../../../../components/component-library'; import { useI18nContext } from '../../../../../../../../hooks/useI18nContext'; import { SignatureRequestType } from '../../../../../../types/confirm'; import { useConfirmContext } from '../../../../../../context/confirm'; @@ -116,8 +117,15 @@ const DecodedSimulation: React.FC = () => { {t('simulationDetailsUnavailable')} + ) + } isLoading={decodingLoading} + isCollapsed={decodingLoading || !stateChangeFragment.length} /> ); }; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/index.ts b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/index.ts similarity index 100% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/decoded-simulation/index.ts rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/decoded-simulation/index.ts diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/index.ts b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/index.ts new file mode 100644 index 000000000000..a5a36ea02dff --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/index.ts @@ -0,0 +1 @@ +export { default as TypedSignV4Simulation } from './typed-sign-v4-simulation'; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/native-value-display/native-value-display.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/native-value-display/native-value-display.test.tsx similarity index 100% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/native-value-display/native-value-display.test.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/native-value-display/native-value-display.test.tsx diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/native-value-display/native-value-display.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/native-value-display/native-value-display.tsx similarity index 96% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/native-value-display/native-value-display.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/native-value-display/native-value-display.tsx index b198680f4e96..8451d3b6e899 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/native-value-display/native-value-display.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/native-value-display/native-value-display.tsx @@ -109,7 +109,9 @@ const NativeValueDisplay: React.FC = ({ - {fiatValue && } + {fiatValue !== undefined && ( + + )} ); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/__snapshots__/default-simulation.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap similarity index 98% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/__snapshots__/default-simulation.test.tsx.snap rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap index 2b6dee2b8c0c..1c301e59b14f 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/__snapshots__/default-simulation.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DefaultSimulation renders component correctly 1`] = ` +exports[`PermitSimulation renders component correctly 1`] = `
`; -exports[`DefaultSimulation renders correctly for NFT permit 1`] = ` +exports[`PermitSimulation renders correctly for NFT permit 1`] = `
{ return { @@ -19,7 +19,7 @@ jest.mock('../../../../../../../../store/actions', () => { }; }); -describe('DefaultSimulation', () => { +describe('PermitSimulation', () => { afterEach(() => { jest.clearAllMocks(); @@ -33,7 +33,7 @@ describe('DefaultSimulation', () => { await act(async () => { const { container, findByText } = renderWithConfirmContextProvider( - , + , mockStore, ); @@ -48,7 +48,7 @@ describe('DefaultSimulation', () => { await act(async () => { const { container, findByText } = renderWithConfirmContextProvider( - , + , mockStore, ); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/default-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/permit-simulation.tsx similarity index 97% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/default-simulation.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/permit-simulation.tsx index c39a88585e35..0e5c117beba9 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/default-simulation/default-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/permit-simulation/permit-simulation.tsx @@ -38,7 +38,7 @@ function extractTokenDetailsByPrimaryType( return isNonArrayObject ? [tokenDetails] : tokenDetails; } -const DefaultPermitSimulation: React.FC = () => { +const PermitSimulation: React.FC = () => { const t = useI18nContext(); const { currentConfirmation } = useConfirmContext(); const msgData = currentConfirmation.msgParams?.data; @@ -115,4 +115,4 @@ const DefaultPermitSimulation: React.FC = () => { ); }; -export default DefaultPermitSimulation; +export default PermitSimulation; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.test.tsx new file mode 100644 index 000000000000..36d20e30fc66 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.test.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import { act } from 'react-dom/test-utils'; +import { + DecodingData, + DecodingDataChangeType, +} from '@metamask/signature-controller'; +import { waitFor } from '@testing-library/dom'; + +import { getMockTypedSignConfirmStateForRequest } from '../../../../../../../../test/data/confirmations/helper'; +import { renderWithConfirmContextProvider } from '../../../../../../../../test/lib/confirmations/render-helpers'; +import { + permitSignatureMsg, + seaportSignatureMsg, +} from '../../../../../../../../test/data/confirmations/typed_sign'; +import { memoizedGetTokenStandardAndDetails } from '../../../../../utils/token'; +import TypedSignV4Simulation from './typed-sign-v4-simulation'; + +jest.mock('../../../../../../../store/actions', () => { + return { + getTokenStandardAndDetails: jest + .fn() + .mockResolvedValue({ decimals: 2, standard: 'ERC20' }), + }; +}); + +const decodingData: DecodingData = { + stateChanges: [ + { + assetType: 'ERC20', + changeType: DecodingDataChangeType.Approve, + address: '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad', + amount: '1461501637330902918203684832716283019655932542975', + contractAddress: '0x6b175474e89094c44da98b954eedeac495271d0f', + }, + ], +}; + +describe('PermitSimulation', () => { + afterEach(() => { + jest.clearAllMocks(); + + /** Reset memoized function using getTokenStandardAndDetails for each test */ + memoizedGetTokenStandardAndDetails?.cache?.clear?.(); + }); + + it('should render default simulation if decoding api does not return result', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData: undefined, + }); + const mockStore = configureMockStore([])(state); + + await act(async () => { + const { findByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + + expect(await findByText('30')).toBeInTheDocument(); + expect(await findByText('Estimated changes')).toBeInTheDocument(); + expect( + await findByText( + "You're giving the spender permission to spend this many tokens from your account.", + ), + ).toBeInTheDocument(); + }); + }); + + it('should render default simulation if decoding api returns error', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData: { + stateChanges: null, + error: { + message: 'some error', + type: 'SOME_ERROR', + }, + }, + }); + const mockStore = configureMockStore([])(state); + + await act(async () => { + const { findByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + + expect(await findByText('30')).toBeInTheDocument(); + expect(await findByText('Estimated changes')).toBeInTheDocument(); + expect( + await findByText( + "You're giving the spender permission to spend this many tokens from your account.", + ), + ).toBeInTheDocument(); + }); + }); + + it('should not render default simulation if decodingLoading is true', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: true, + }); + const mockStore = configureMockStore([])(state); + + await act(async () => { + const { queryByTestId } = renderWithConfirmContextProvider( + , + mockStore, + ); + + await waitFor(() => { + expect(queryByTestId('30')).not.toBeInTheDocument(); + expect(queryByTestId('Estimated changes')).toBeInTheDocument(); + expect( + queryByTestId( + "You're giving the spender permission to spend this many tokens from your account.", + ), + ).not.toBeInTheDocument(); + }); + }); + }); + + it('should render decoding simulation for permits', async () => { + const state = getMockTypedSignConfirmStateForRequest({ + ...permitSignatureMsg, + decodingLoading: false, + decodingData, + }); + const mockStore = configureMockStore([])(state); + + await act(async () => { + const { findByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + + expect(await findByText('Estimated changes')).toBeInTheDocument(); + expect(await findByText('Spending cap')).toBeInTheDocument(); + expect(await findByText('1,461,501,637,3...')).toBeInTheDocument(); + }); + }); + + it.only('should render decoding simulation for seaport request', async () => { + const state = getMockTypedSignConfirmStateForRequest(seaportSignatureMsg); + const mockStore = configureMockStore([])(state); + + await act(async () => { + const { findByText } = renderWithConfirmContextProvider( + , + mockStore, + ); + + expect(await findByText('You receive')).toBeInTheDocument(); + expect(await findByText('You list')).toBeInTheDocument(); + }); + }); +}); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.tsx new file mode 100644 index 000000000000..ebea09e18f15 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/typed-sign-v4-simulation.tsx @@ -0,0 +1,63 @@ +import React from 'react'; + +import { PRIMARY_TYPES_PERMIT } from '../../../../../../../../shared/constants/signatures'; +import { parseTypedDataMessage } from '../../../../../../../../shared/modules/transaction.utils'; +import { SignatureRequestType } from '../../../../../types/confirm'; +import { isPermitSignatureRequest } from '../../../../../utils'; +import { useConfirmContext } from '../../../../../context/confirm'; +import { useDecodedSignatureMetrics } from '../../../../../hooks/useDecodedSignatureMetrics'; +import { DecodedSimulation } from './decoded-simulation'; +import { PermitSimulation } from './permit-simulation'; + +const NON_PERMIT_SUPPORTED_TYPES_SIGNS = [ + { + domainName: 'Seaport', + primaryTypeList: ['BulkOrder'], + versionList: ['1.4', '1.5', '1.6'], + }, + { + domainName: 'Seaport', + primaryTypeList: ['OrderComponents'], + }, +]; + +const isSupportedByDecodingAPI = (signatureRequest: SignatureRequestType) => { + const { + domain: { name, version }, + primaryType, + } = parseTypedDataMessage( + (signatureRequest as SignatureRequestType).msgParams?.data as string, + ); + const isPermit = PRIMARY_TYPES_PERMIT.includes(primaryType); + const nonPermitSupportedTypes = NON_PERMIT_SUPPORTED_TYPES_SIGNS.some( + ({ domainName, primaryTypeList, versionList }) => + name === domainName && + primaryTypeList.includes(primaryType) && + (!versionList || versionList.includes(version)), + ); + return isPermit || nonPermitSupportedTypes; +}; + +const TypedSignV4Simulation: React.FC = () => { + const { currentConfirmation } = useConfirmContext(); + const isPermit = isPermitSignatureRequest(currentConfirmation); + const supportedByDecodingAPI = isSupportedByDecodingAPI(currentConfirmation); + useDecodedSignatureMetrics(); + + if (!supportedByDecodingAPI) { + return null; + } + + const { decodingData, decodingLoading } = currentConfirmation; + + if ( + ((!decodingLoading && decodingData === undefined) || decodingData?.error) && + isPermit + ) { + return ; + } + + return ; +}; + +export default TypedSignV4Simulation; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/__snapshots__/value-display.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/__snapshots__/value-display.test.tsx.snap similarity index 100% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/__snapshots__/value-display.test.tsx.snap rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/__snapshots__/value-display.test.tsx.snap diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/value-display.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/value-display.test.tsx similarity index 100% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/value-display.test.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/value-display.test.tsx diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/value-display.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/value-display.tsx similarity index 97% rename from ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/value-display.tsx rename to ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/value-display.tsx index 2867522b42ab..c41f99f2f320 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/value-display/value-display.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign-v4-simulation/value-display/value-display.tsx @@ -157,7 +157,9 @@ const PermitSimulationValueDisplay: React.FC< /> - {fiatValue && } + {fiatValue !== undefined && ( + + )} ); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx index 94677d56d979..0937e0dc1117 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx @@ -3,6 +3,7 @@ import { useSelector } from 'react-redux'; import { isValidAddress } from 'ethereumjs-util'; import { ConfirmInfoAlertRow } from '../../../../../../components/app/confirm/info/row/alert-row/alert-row'; +import { MESSAGE_TYPE } from '../../../../../../../shared/constants/app'; import { parseTypedDataMessage } from '../../../../../../../shared/modules/transaction.utils'; import { RowAlertKey } from '../../../../../../components/app/confirm/info/row/constants'; import { @@ -24,7 +25,7 @@ import { selectUseTransactionSimulations } from '../../../../selectors/preferenc import { ConfirmInfoRowTypedSignData } from '../../row/typed-sign-data/typedSignData'; import { isSnapId } from '../../../../../../helpers/utils/snaps'; import { SigningInWithRow } from '../shared/sign-in-with-row/sign-in-with-row'; -import { PermitSimulation } from './permit-simulation'; +import { TypedSignV4Simulation } from './typed-sign-v4-simulation'; const TypedSignInfo: React.FC = () => { const t = useI18nContext(); @@ -43,6 +44,9 @@ const TypedSignInfo: React.FC = () => { } = parseTypedDataMessage(currentConfirmation.msgParams.data as string); const isPermit = isPermitSignatureRequest(currentConfirmation); + const isTypedSignV4 = + currentConfirmation.msgParams.signatureMethod === + MESSAGE_TYPE.ETH_SIGN_TYPED_DATA_V4; const isOrder = isOrderSignatureRequest(currentConfirmation); const tokenContract = isPermit || isOrder ? verifyingContract : undefined; const { decimalsNumber } = useGetTokenStandardAndDetails(tokenContract); @@ -56,7 +60,7 @@ const TypedSignInfo: React.FC = () => { return ( <> - {isPermit && useTransactionSimulations && } + {isTypedSignV4 && useTransactionSimulations && } {isPermit && ( <> From a2651c025a78c5c84eb395728d371ac9127301e3 Mon Sep 17 00:00:00 2001 From: sahar-fehri Date: Wed, 27 Nov 2024 16:59:45 +0100 Subject: [PATCH 18/22] fix: add dispatch detect Nfts on network switch (#28769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** PR to fix detecting NFTs when a user switches networks while on the NFT tab [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28769?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** https://github.com/user-attachments/assets/bea181c6-b252-4c0c-bc16-dd48abaeae13 ### **After** https://github.com/user-attachments/assets/4c674e18-09c7-4b9f-81d3-fc42e59f2bc2 ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: salimtb --- .../multichain/network-list-menu/network-list-menu.test.js | 3 +++ .../multichain/network-list-menu/network-list-menu.tsx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/ui/components/multichain/network-list-menu/network-list-menu.test.js b/ui/components/multichain/network-list-menu/network-list-menu.test.js index 93a55538a4ab..ec4539aa55da 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.test.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.test.js @@ -22,6 +22,7 @@ const mockSetActiveNetwork = jest.fn(); const mockUpdateCustomNonce = jest.fn(); const mockSetNextNonce = jest.fn(); const mockSetTokenNetworkFilter = jest.fn(); +const mockDetectNfts = jest.fn(); jest.mock('../../../store/actions.ts', () => ({ setShowTestNetworks: () => mockSetShowTestNetworks, @@ -32,6 +33,7 @@ jest.mock('../../../store/actions.ts', () => ({ setNetworkClientIdForDomain: (network, id) => mockSetNetworkClientIdForDomain(network, id), setTokenNetworkFilter: () => mockSetTokenNetworkFilter, + detectNfts: () => mockDetectNfts, })); const MOCK_ORIGIN = 'https://portfolio.metamask.io'; @@ -218,6 +220,7 @@ describe('NetworkListMenu', () => { expect(mockSetActiveNetwork).toHaveBeenCalled(); expect(mockUpdateCustomNonce).toHaveBeenCalled(); expect(mockSetNextNonce).toHaveBeenCalled(); + expect(mockDetectNfts).toHaveBeenCalled(); }); it('shows the correct selected network when networks share the same chain ID', () => { diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx index 07d4147ad763..28680b24ba45 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.tsx +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -29,6 +29,7 @@ import { updateCustomNonce, setNextNonce, setTokenNetworkFilter, + detectNfts, } from '../../../store/actions'; import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, @@ -289,6 +290,7 @@ export const NetworkListMenu = ({ onClose }: { onClose: () => void }) => { dispatch(toggleNetworkMenu()); dispatch(updateCustomNonce('')); dispatch(setNextNonce('')); + dispatch(detectNfts()); // as a user, I don't want my network selection to force update my filter when I have "All Networks" toggled on // however, if I am already filtered on "Current Network", we'll want to filter by the selected network when the network changes From 068c456890caa990e5af47cd836838afbee33f74 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Wed, 27 Nov 2024 10:17:08 -0600 Subject: [PATCH 19/22] feat: PortfolioView: Add feature flag check for polling intervals (#28501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds a remote endpoint check to see if the remote API wants us to update the number of seconds between polling intervals for balances. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28501?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Open the extension 2. See the network request being made in the Networks devtools panel 3. Change conditional in response to `if (true) {` and hardcode a `pollInterval` value 4. See `this.tokenBalancesController.setIntervalLength` successfully called. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/scripts/metamask-controller.js | 27 ++++++++++++- app/scripts/metamask-controller.test.js | 51 +++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1f907d94a976..dd1d956eed1a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -195,7 +195,7 @@ import { ExcludedSnapEndowments, } from '../../shared/constants/permissions'; import { UI_NOTIFICATIONS } from '../../shared/notifications'; -import { MILLISECOND, SECOND } from '../../shared/constants/time'; +import { MILLISECOND, MINUTE, SECOND } from '../../shared/constants/time'; import { ORIGIN_METAMASK, POLLING_TOKEN_ENVIRONMENT_TYPES, @@ -244,6 +244,7 @@ import { endTrace, trace } from '../../shared/lib/trace'; // eslint-disable-next-line import/no-restricted-paths import { isSnapId } from '../../ui/helpers/utils/snaps'; import { BridgeStatusAction } from '../../shared/types/bridge-status'; +import fetchWithCache from '../../shared/lib/fetch-with-cache'; import { BalancesController as MultichainBalancesController } from './lib/accounts/BalancesController'; import { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -2637,6 +2638,29 @@ export default class MetamaskController extends EventEmitter { } } + // Provides a method for getting feature flags for the multichain + // initial rollout, such that we can remotely modify polling interval + getInfuraFeatureFlags() { + fetchWithCache({ + url: 'https://swap.api.cx.metamask.io/featureFlags', + cacheRefreshTime: MINUTE * 20, + }) + .then(this.onFeatureFlagResponseReceived) + .catch((e) => { + // API unreachable (?) + log.warn('Feature flag endpoint is unreachable', e); + }); + } + + onFeatureFlagResponseReceived(response) { + const { multiChainAssets = {} } = response; + const { pollInterval } = multiChainAssets; + // Polling interval is provided in seconds + if (pollInterval > 0) { + this.tokenBalancesController.setIntervalLength(pollInterval * SECOND); + } + } + postOnboardingInitialization() { const { usePhishDetect } = this.preferencesController.state; @@ -2669,6 +2693,7 @@ export default class MetamaskController extends EventEmitter { triggerNetworkrequests() { this.txController.startIncomingTransactionPolling(); this.tokenDetectionController.enable(); + this.getInfuraFeatureFlags(); } stopNetworkRequests() { diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 0e08a0ac27c0..447ea3da9503 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -39,6 +39,7 @@ import { flushPromises } from '../../test/lib/timer-helpers'; import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { createMockInternalAccount } from '../../test/jest/mocks'; import { mockNetworkState } from '../../test/stub/networks'; +import { SECOND } from '../../shared/constants/time'; import { BalancesController as MultichainBalancesController, BTC_BALANCES_UPDATE_TIME as MULTICHAIN_BALANCES_UPDATE_TIME, @@ -2632,6 +2633,56 @@ describe('MetaMaskController', () => { }); }); + describe('onFeatureFlagResponseReceived', () => { + const metamaskController = new MetaMaskController({ + showUserConfirmation: noop, + encryptor: mockEncryptor, + initState: cloneDeep(firstTimeState), + initLangCode: 'en_US', + platform: { + showTransactionNotification: () => undefined, + getVersion: () => 'foo', + }, + browser: browserPolyfillMock, + infuraProjectId: 'foo', + isFirstMetaMaskControllerSetup: true, + }); + + beforeEach(() => { + jest.spyOn( + metamaskController.tokenBalancesController, + 'setIntervalLength', + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not set the interval length if the pollInterval is 0', () => { + metamaskController.onFeatureFlagResponseReceived({ + multiChainAssets: { + pollInterval: 0, + }, + }); + expect( + metamaskController.tokenBalancesController.setIntervalLength, + ).not.toHaveBeenCalled(); + }); + + it('should set the interval length if the pollInterval is greater than 0', () => { + const pollInterval = 10; + metamaskController.onFeatureFlagResponseReceived({ + multiChainAssets: { + pollInterval, + }, + }); + expect( + metamaskController.tokenBalancesController.setIntervalLength, + ).toHaveBeenCalledWith(pollInterval * SECOND); + }); + }); + describe('MV3 Specific behaviour', () => { beforeAll(async () => { mockIsManifestV3.mockReturnValue(true); From f249b8ce8ea0ffa8fd71b343b1c881ba3e98cff6 Mon Sep 17 00:00:00 2001 From: Hassan Malik <41640681+hmalik88@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:00:08 -0500 Subject: [PATCH 20/22] feat: Integrate Snap notification services (#27975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? Snap notifications were moved into the `NotificationServicesController` to reduce code duplication. 2. What is the improvement/solution? The `NotificationController` and associated code was deleted, as well as code that was written to process snap notifications. Parts of the codebase that expect snap notifications from the `NotificationController` source were also updated. See https://github.com/MetaMask/core/pull/4809 for changes made to the `NotificationServicesController` ## **Screenshots/Recordings** ### **After** https://github.com/user-attachments/assets/e84f5209-20fa-4b1d-ace7-2eec08bb2329 ## **Manual testing steps** 1. Pull down https://github.com/hmalik88/swiss-knife-snap and run `yarn start` to serve the snap and site. 2. Build this branch using `yarn start:flask` 3. Install the snap from the local host site. 4. Trigger a notification with the "trigger a within limit notification" button and observe the results. ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/scripts/background.js | 19 +++-- app/scripts/constants/sentry-state.ts | 3 - app/scripts/metamask-controller.js | 82 ++++++++----------- app/scripts/metamask-controller.test.js | 30 ------- app/scripts/migrations/133.test.ts | 46 +++++++++++ app/scripts/migrations/133.ts | 43 ++++++++++ app/scripts/migrations/index.js | 1 + lavamoat/browserify/beta/policy.json | 30 ------- lavamoat/browserify/flask/policy.json | 30 ------- lavamoat/browserify/main/policy.json | 30 ------- lavamoat/browserify/mmi/policy.json | 30 ------- package.json | 1 - test/data/mock-state.json | 16 ---- ...rs-after-init-opt-in-background-state.json | 1 - .../errors-after-init-opt-in-ui-state.json | 1 - .../__snapshots__/app-header.test.js.snap | 14 +--- .../useCounter.test.tsx | 11 +-- .../metamask-notifications/useCounter.tsx | 18 ++-- ui/hooks/useNotificationTimeouts.ts | 25 ++++++ .../notification-components/snap/snap.tsx | 37 +++++++-- .../notifications-list-item.stories.tsx | 50 +---------- ...otifications-list-read-all-button.test.tsx | 2 +- .../notifications-list-read-all-button.tsx | 25 +++--- .../notifications/notifications-list.test.tsx | 3 +- ui/pages/notifications/notifications-list.tsx | 11 ++- ui/pages/notifications/notifications.test.tsx | 2 - ui/pages/notifications/notifications.tsx | 68 +++++++-------- ui/pages/notifications/snap/types/types.ts | 17 ---- ui/pages/notifications/snap/utils/utils.ts | 17 ---- .../metamask-notifications.ts | 42 +++++++++- ui/selectors/selectors.js | 51 ------------ ui/selectors/selectors.test.js | 27 ------ ui/store/actions.ts | 66 ++++++++------- yarn.lock | 12 --- 34 files changed, 334 insertions(+), 527 deletions(-) create mode 100644 app/scripts/migrations/133.test.ts create mode 100644 app/scripts/migrations/133.ts create mode 100644 ui/hooks/useNotificationTimeouts.ts delete mode 100644 ui/pages/notifications/snap/types/types.ts delete mode 100644 ui/pages/notifications/snap/utils/utils.ts diff --git a/app/scripts/background.js b/app/scripts/background.js index 6a047c79ac16..e39d96943d77 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -1046,11 +1046,6 @@ export function setupController( updateBadge, ); - controller.controllerMessenger.subscribe( - METAMASK_CONTROLLER_EVENTS.NOTIFICATIONS_STATE_CHANGE, - updateBadge, - ); - /** * Formats a count for display as a badge label. * @@ -1119,8 +1114,14 @@ export function setupController( controller.notificationServicesController.state; const snapNotificationCount = Object.values( - controller.notificationController.state.notifications, - ).filter((notification) => notification.readDate === null).length; + controller.notificationServicesController.state + .metamaskNotificationsList, + ).filter( + (notification) => + notification.type === + NotificationServicesController.Constants.TRIGGER_TYPES.SNAP && + notification.readDate === null, + ).length; const featureAnnouncementCount = isFeatureAnnouncementsEnabled ? controller.notificationServicesController.state.metamaskNotificationsList.filter( @@ -1138,7 +1139,9 @@ export function setupController( !notification.isRead && notification.type !== NotificationServicesController.Constants.TRIGGER_TYPES - .FEATURES_ANNOUNCEMENT, + .FEATURES_ANNOUNCEMENT && + notification.type !== + NotificationServicesController.Constants.TRIGGER_TYPES.SNAP, ).length : 0; diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index 289bc0a0d29c..f18fb96d85fd 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -203,9 +203,6 @@ export const SENTRY_BACKGROUND_STATE = { allNfts: false, ignoredNfts: false, }, - NotificationController: { - notifications: false, - }, OnboardingController: { completedOnboarding: true, firstTimeFlowType: true, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index dd1d956eed1a..052e428dd55a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -80,7 +80,6 @@ import { LoggingController, LogType } from '@metamask/logging-controller'; import { PermissionLogController } from '@metamask/permission-log-controller'; import { RateLimitController } from '@metamask/rate-limit-controller'; -import { NotificationController } from '@metamask/notification-controller'; import { CronjobController, JsonSnapsRegistry, @@ -376,6 +375,7 @@ import { sanitizeUIState } from './lib/state-utils'; import BridgeStatusController from './controllers/bridge-status/bridge-status-controller'; import { BRIDGE_STATUS_CONTROLLER_NAME } from './controllers/bridge-status/constants'; +const { TRIGGER_TYPES } = NotificationServicesController.Constants; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) // The process of updating the badge happens in app/scripts/background.js. @@ -387,7 +387,6 @@ export const METAMASK_CONTROLLER_EVENTS = { 'NotificationServicesController:notificationsListUpdated', METAMASK_NOTIFICATIONS_MARK_AS_READ: 'NotificationServicesController:markNotificationsAsRead', - NOTIFICATIONS_STATE_CHANGE: 'NotificationController:stateChange', }; // stream channels @@ -1432,13 +1431,6 @@ export default class MetamaskController extends EventEmitter { }, }); - this.notificationController = new NotificationController({ - messenger: this.controllerMessenger.getRestricted({ - name: 'NotificationController', - }), - state: initState.NotificationController, - }); - this.rateLimitController = new RateLimitController({ state: initState.RateLimitController, messenger: this.controllerMessenger.getRestricted({ @@ -1466,11 +1458,21 @@ export default class MetamaskController extends EventEmitter { rateLimitTimeout: 300000, }, showInAppNotification: { - method: (origin, message) => { + method: (origin, args) => { + const { message } = args; + + const notification = { + data: { + message, + origin, + }, + type: TRIGGER_TYPES.SNAP, + readDate: null, + }; + this.controllerMessenger.call( - 'NotificationController:show', - origin, - message, + 'NotificationServicesController:updateMetamaskNotificationsList', + notification, ); return null; @@ -2486,7 +2488,6 @@ export default class MetamaskController extends EventEmitter { SnapController: this.snapController, CronjobController: this.cronjobController, SnapsRegistry: this.snapsRegistry, - NotificationController: this.notificationController, SnapInterfaceController: this.snapInterfaceController, SnapInsightsController: this.snapInsightsController, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -2542,7 +2543,6 @@ export default class MetamaskController extends EventEmitter { SnapController: this.snapController, CronjobController: this.cronjobController, SnapsRegistry: this.snapsRegistry, - NotificationController: this.notificationController, SnapInterfaceController: this.snapInterfaceController, SnapInsightsController: this.snapInsightsController, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -2866,7 +2866,7 @@ export default class MetamaskController extends EventEmitter { origin, 'showInAppNotification', origin, - args.message, + args, ), updateSnapState: this.controllerMessenger.call.bind( this.controllerMessenger, @@ -2912,24 +2912,6 @@ export default class MetamaskController extends EventEmitter { }; } - /** - * Deletes the specified notifications from state. - * - * @param {string[]} ids - The notifications ids to delete. - */ - dismissNotifications(ids) { - this.notificationController.dismiss(ids); - } - - /** - * Updates the readDate attribute of the specified notifications. - * - * @param {string[]} ids - The notifications ids to mark as read. - */ - markNotificationsAsRead(ids) { - this.notificationController.markRead(ids); - } - /** * Sets up BaseController V2 event subscriptions. Currently, this includes * the subscriptions necessary to notify permission subjects of account @@ -3137,16 +3119,16 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger.subscribe( `${this.snapController.name}:snapUninstalled`, (truncatedSnap) => { - const notificationIds = Object.values( - this.notificationController.state.notifications, - ).reduce((idList, notification) => { - if (notification.origin === truncatedSnap.id) { - idList.push(notification.id); - } - return idList; - }, []); - - this.dismissNotifications(notificationIds); + const notificationIds = this.notificationServicesController + .getNotificationsByType(TRIGGER_TYPES.SNAP) + .filter( + (notification) => notification.data.origin === truncatedSnap.id, + ) + .map((notification) => notification.id); + + this.notificationServicesController.deleteNotificationsById( + notificationIds, + ); const snapId = truncatedSnap.id; const snapCategory = this._getSnapMetadata(snapId)?.category; @@ -3919,8 +3901,6 @@ export default class MetamaskController extends EventEmitter { this.controllerMessenger, 'SnapController:revokeDynamicPermissions', ), - dismissNotifications: this.dismissNotifications.bind(this), - markNotificationsAsRead: this.markNotificationsAsRead.bind(this), disconnectOriginFromSnap: this.controllerMessenger.call.bind( this.controllerMessenger, 'SnapController:disconnectOrigin', @@ -4259,6 +4239,14 @@ export default class MetamaskController extends EventEmitter { notificationServicesController.fetchAndUpdateMetamaskNotifications.bind( notificationServicesController, ), + deleteNotificationsById: + notificationServicesController.deleteNotificationsById.bind( + notificationServicesController, + ), + getNotificationsByType: + notificationServicesController.getNotificationsByType.bind( + notificationServicesController, + ), markMetamaskNotificationsAsRead: notificationServicesController.markMetamaskNotificationsAsRead.bind( notificationServicesController, @@ -4487,8 +4475,6 @@ export default class MetamaskController extends EventEmitter { // Clear snap state this.snapController.clearState(); - // Clear notification state - this.notificationController.clear(); // clear accounts in AccountTrackerController this.accountTrackerController.clearAccounts(); diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 447ea3da9503..a596277154eb 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -207,8 +207,6 @@ const TEST_INTERNAL_ACCOUNT = { type: EthAccountType.Eoa, }; -const NOTIFICATION_ID = 'NHL8f2eSSTn9TKBamRLiU'; - const ALT_MAINNET_RPC_URL = 'http://localhost:8545'; const POLYGON_RPC_URL = 'https://polygon.llamarpc.com'; @@ -249,17 +247,6 @@ const firstTimeState = { }, ), }, - NotificationController: { - notifications: { - [NOTIFICATION_ID]: { - id: NOTIFICATION_ID, - origin: 'local:http://localhost:8086/', - createdDate: 1652967897732, - readDate: null, - message: 'Hello, http://localhost:8086!', - }, - }, - }, PhishingController: { phishingLists: [ { @@ -2006,23 +1993,6 @@ describe('MetaMaskController', () => { }); }); - describe('markNotificationsAsRead', () => { - it('marks the notification as read', () => { - metamaskController.markNotificationsAsRead([NOTIFICATION_ID]); - const readNotification = - metamaskController.getState().notifications[NOTIFICATION_ID]; - expect(readNotification.readDate).not.toBeNull(); - }); - }); - - describe('dismissNotifications', () => { - it('deletes the notification from state', () => { - metamaskController.dismissNotifications([NOTIFICATION_ID]); - const state = metamaskController.getState().notifications; - expect(Object.values(state)).not.toContain(NOTIFICATION_ID); - }); - }); - describe('getTokenStandardAndDetails', () => { it('gets token data from the token list if available, and with a balance retrieved by fetchTokenBalance', async () => { const providerResultStub = { diff --git a/app/scripts/migrations/133.test.ts b/app/scripts/migrations/133.test.ts new file mode 100644 index 000000000000..76dc66a0d611 --- /dev/null +++ b/app/scripts/migrations/133.test.ts @@ -0,0 +1,46 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './133'; + +const oldVersion = 132; + +describe('migration #133', () => { + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + + expect(newStorage.meta).toStrictEqual({ version }); + }); + + describe('NotificationController', () => { + it('does nothing if NotificationController is not in state', async () => { + const oldState = { + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual(oldState); + }); + + it('deletes the NotificationController from state', async () => { + const oldState = { + NotificationController: {}, + OtherController: {}, + }; + + const transformedState = await migrate({ + meta: { version: oldVersion }, + data: cloneDeep(oldState), + }); + + expect(transformedState.data).toEqual({ OtherController: {} }); + }); + }); +}); diff --git a/app/scripts/migrations/133.ts b/app/scripts/migrations/133.ts new file mode 100644 index 000000000000..864f9d1b9637 --- /dev/null +++ b/app/scripts/migrations/133.ts @@ -0,0 +1,43 @@ +import { hasProperty } from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 133; + +/** + * This migration removes the notification controller from state. Previously used for + * snap notifications, it is no longer needed now that snap notifications will live in the + * notification services controller. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly + * what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by + * controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +/** + * Remove the notification controller from state (and any persisted notifications). + * + * @param state - The persisted MetaMask state, keyed by controller. + */ +function transformState(state: Record): void { + // we're removing the NotificationController in favor of the NotificationServicesController + if (hasProperty(state, 'NotificationController')) { + delete state.NotificationController; + } +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 6cde292ba55d..e95a9bf7a9da 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -153,6 +153,7 @@ const migrations = [ require('./130'), require('./131'), require('./132'), + require('./133'), ]; export default migrations; diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index a1af28ef6669..077ee743996a 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1951,36 +1951,6 @@ "unstable_autotrackMemoize": true } }, - "@metamask/notification-controller": { - "packages": { - "@metamask/notification-controller>@metamask/base-controller": true, - "@metamask/notification-controller>@metamask/utils": true, - "@metamask/notification-controller>nanoid": true - } - }, - "@metamask/notification-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/notification-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/notification-controller>nanoid": { "globals": { "crypto.getRandomValues": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index a1af28ef6669..077ee743996a 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1951,36 +1951,6 @@ "unstable_autotrackMemoize": true } }, - "@metamask/notification-controller": { - "packages": { - "@metamask/notification-controller>@metamask/base-controller": true, - "@metamask/notification-controller>@metamask/utils": true, - "@metamask/notification-controller>nanoid": true - } - }, - "@metamask/notification-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/notification-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/notification-controller>nanoid": { "globals": { "crypto.getRandomValues": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index a1af28ef6669..077ee743996a 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1951,36 +1951,6 @@ "unstable_autotrackMemoize": true } }, - "@metamask/notification-controller": { - "packages": { - "@metamask/notification-controller>@metamask/base-controller": true, - "@metamask/notification-controller>@metamask/utils": true, - "@metamask/notification-controller>nanoid": true - } - }, - "@metamask/notification-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/notification-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/notification-controller>nanoid": { "globals": { "crypto.getRandomValues": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 574da95682be..80bed5591fea 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2043,36 +2043,6 @@ "unstable_autotrackMemoize": true } }, - "@metamask/notification-controller": { - "packages": { - "@metamask/notification-controller>@metamask/base-controller": true, - "@metamask/notification-controller>@metamask/utils": true, - "@metamask/notification-controller>nanoid": true - } - }, - "@metamask/notification-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/notification-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/notification-controller>nanoid": { "globals": { "crypto.getRandomValues": true diff --git a/package.json b/package.json index 3edf8174a2fb..cf851ae1c09f 100644 --- a/package.json +++ b/package.json @@ -322,7 +322,6 @@ "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", - "@metamask/notification-controller": "^6.0.0", "@metamask/notification-services-controller": "^0.14.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", diff --git a/test/data/mock-state.json b/test/data/mock-state.json index b315dfa203eb..3131b8335287 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -646,22 +646,6 @@ "lastUpdated": "September 26, 2024" } }, - "notifications": { - "test": { - "id": "test", - "origin": "local:http://localhost:8086/", - "createdDate": 1652967897732, - "readDate": null, - "message": "Hello, http://localhost:8086!" - }, - "test2": { - "id": "test2", - "origin": "local:http://localhost:8086/", - "createdDate": 1652967897732, - "readDate": 1652967897732, - "message": "Hello, http://localhost:8086!" - } - }, "accountsByChainId": { "0x5": { "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index 3c2430fbe063..450ef4ee7ebb 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -171,7 +171,6 @@ "allNfts": "object", "ignoredNfts": "object" }, - "NotificationController": { "notifications": "object" }, "NotificationServicesController": { "subscriptionAccountsSeen": "object", "isMetamaskNotificationsFeatureSeen": "boolean", diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 01f94b55d5c7..c890588da16a 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -209,7 +209,6 @@ "database": null, "lastUpdated": null, "databaseUnavailable": "boolean", - "notifications": "object", "interfaces": "object", "insights": "object", "names": "object", diff --git a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap index 247f7aeb5c78..fc0ab6f50467 100644 --- a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap +++ b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap @@ -609,19 +609,7 @@ exports[`App Header unlocked state matches snapshot: unlocked 1`] = `
-
-

- 1 -

-
-
+ />