diff --git a/CHANGELOG.md b/CHANGELOG.md index 802af6f48c6c..18db909ce3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [11.10.0] +### Added +- Added preset network image avatars in the 'Select a network' pop-up ([#22643](https://github.com/MetaMask/metamask-extension/pull/22643)) +- Expanded Blockaid banner support to include send requests from wallets ([#22321](https://github.com/MetaMask/metamask-extension/pull/22321)) +- Expanded Blockaid banner support to include BNB chain, Polygon, Arbitrum, Optimism, Avalanche, and Linea networks ([#22633](https://github.com/MetaMask/metamask-extension/pull/22633)) +- Added support for imToken Wallet using EIP-4527 ([#21804](https://github.com/MetaMask/metamask-extension/pull/21804)) +- [FLASK] Introduced user operation support, enhancing transaction handling and alert display for user operations ([#22469](https://github.com/MetaMask/metamask-extension/pull/22469)) +- [FLASK] Added support for the Lattice hardware wallet in MV3 ([#22528](https://github.com/MetaMask/metamask-extension/pull/22528)) +- [FLASK] Added a 'You're sending to a contract' warning to the new send page ([#22551](https://github.com/MetaMask/metamask-extension/pull/22551)) + +### Changed +- Improved error messaging for Ledger connection issues ([#21038](https://github.com/MetaMask/metamask-extension/pull/21038)) +- Updated 'What's New' and 'Settings' to communicate the extension's additional network coverage ([#22618](https://github.com/MetaMask/metamask-extension/pull/22618)) +- Enhanced Token List layout for clearer token name and value display ([#22601](https://github.com/MetaMask/metamask-extension/pull/22601)) +- Updated the date for the upcoming user survey displayed on the home screen ([#22581](https://github.com/MetaMask/metamask-extension/pull/22581)) +- Improved QR code density for compatibility with the ZERO hardware wallet ([#22135](https://github.com/MetaMask/metamask-extension/pull/22135)) +- Updated snaps packages to the latest versions ([#22595](https://github.com/MetaMask/metamask-extension/pull/22595)) +- Updated the Gas API URL to ensure accurate gas fee calculations for Send and Swap transactions ([#22544](https://github.com/MetaMask/metamask-extension/pull/22544)) + +### Fixed +- Fixed an issue where clicking on the redirection link did not close the notification window ([#22583](https://github.com/MetaMask/metamask-extension/pull/22583)) +- Improved Blockaid 'What's New' image display for users in OS dark mode ([#22649](https://github.com/MetaMask/metamask-extension/pull/22649)) +- Added Blockaid dark mode support in the 'What's New' feature and introduced a new theme management utility ([#22613](https://github.com/MetaMask/metamask-extension/pull/22613)) +- Ensured all security alert banners correctly trigger events ([#22553](https://github.com/MetaMask/metamask-extension/pull/22553)) +- Updated 'What's New' Blockaid image to have a transparent background ([#22539](https://github.com/MetaMask/metamask-extension/pull/22539)) +- Fixed unresponsiveness on the phishing warning page ([#22645](https://github.com/MetaMask/metamask-extension/pull/22645)) +- Improved Trezor integration by updating to the correct version of the SDK ([#22591](https://github.com/MetaMask/metamask-extension/pull/22591)) +- Improved account list links to display the correct explorer domain based on the selected network ([#22483](https://github.com/MetaMask/metamask-extension/pull/22483)) +- Improved the reliability of snap installations by resolving an underlying technical issue ([#22602](https://github.com/MetaMask/metamask-extension/pull/22602)) + ## [11.9.5] ### Fixed - Fixed sometimes failing confirmation screen security validation checks ([$22978](https://github.com/MetaMask/metamask-extension/pull/22978)) @@ -4359,7 +4389,8 @@ Update styles and spacing on the critical error page ([#20350](https://github.c ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v11.9.5...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v11.10.0...HEAD +[11.10.0]: https://github.com/MetaMask/metamask-extension/compare/v11.9.5...v11.10.0 [11.9.5]: https://github.com/MetaMask/metamask-extension/compare/v11.9.4...v11.9.5 [11.9.4]: https://github.com/MetaMask/metamask-extension/compare/v11.9.3...v11.9.4 [11.9.3]: https://github.com/MetaMask/metamask-extension/compare/v11.9.2...v11.9.3 diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 7b6977e25821..257611b700c5 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Wenn Sie diese Anfrage genehmigen, kann jemand Ihre bei Blur aufgelisteten Assets stehlen." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Aufgrund eines Fehlers wurde diese Anfrage vom Sicherheitsanbieter nicht überprüft. Gehen Sie mit Bedacht vor." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 00bff68f0a7d..aa32480036bd 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Εάν εγκρίνετε αυτό το αίτημα, κάποιος μπορεί να κλέψει τα περιουσιακά σας στοιχεία που είναι καταχωρημένα στο Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Λόγω κάποιου σφάλματος, αυτό το αίτημα δεν επαληθεύτηκε από τον πάροχο ασφαλείας. Προχωρήστε με προσοχή." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index b6df11414bc6..9591a91da069 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -601,7 +601,7 @@ "blockaidDescriptionBlurFarming": { "message": "If you approve this request, someone can steal your assets listed on Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Because of an error, this request was not verified by the security provider. Proceed with caution." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index f7b538aca64a..b53d55eff620 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Si aprueba esta solicitud, alguien puede robar sus activos enlistados en Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Debido a un error, el proveedor de seguridad no verificó esta solicitud. Proceda con precaución." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index c1105e208141..d477599060dd 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Si vous approuvez cette demande, quelqu’un pourrait s'emparer de vos actifs répertoriés sur Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "À la suite d’une erreur, cette demande n’a pas été vérifiée par le fournisseur de services de sécurité. Veuillez agir avec prudence." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 85d174911664..50027209b4f5 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "यदि आप इस रिक्वेस्ट को स्वीकार करते हैं, तो कोई Blur पर लिस्टेड आपके सारे एसेट चुरा सकता है।" }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "कोई समस्या होने के कारण, इस रिक्वेस्ट को सिक्यूरिटी प्रोवाइडर द्वारा वेरीफ़ाई नहीं किया गया। सावधानी से आगे बढ़ें।" }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 60a00061ec26..fdbd48de0e55 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Jika Anda menyetujui permintaan ini, seseorang dapat mencuri aset Anda yang terdaftar di Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Karena terjadi kesalahan, permintaan ini tidak diverifikasi oleh penyedia keamanan. Lanjutkan dengan hati-hati." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index ca9452e7092a..076254176f8b 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "このリクエストを承認すると、Blurに登録されている資産を誰かに盗まれる可能性があります。" }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "エラーが発生したため、このリクエストはセキュリティプロバイダーにより確認されませんでした。慎重に進めてください。" }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index 03b0bf2861fc..668779dfc90d 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "이 요청을 승인하면, Blur에 있는 자산을 타인이 갈취할 수 있습니다." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "오류로 인해 보안업체에서 이 요청을 확인하지 못했습니다. 주의하여 진행하세요." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 67d40144bf57..eac5878b5df5 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Se você aprovar essa solicitação, alguém poderá roubar seus ativos listados na Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Em razão de um erro, essa solicitação não foi confirmada pelo provedor de segurança. Prossiga com cautela." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 993611e4dc9d..857be230dc68 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Если вы одобрите этот запрос, кто-то может украсть ваши активы, указанные в Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Из-за ошибки этот запрос не был подтвержден поставщиком услуг безопасности. Действуйте осторожно." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 701370abde42..1127db132f1c 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Kung aaprubahan mo ang kahilingang ito, posibleng nakawin ng ibang tao ang mga asset mo na nakalista sa Blur." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Dahil sa error, hindi na-verify ang kahilingang ito ng tagapagbigay ng seguridad. Magpatuloy nang may pag-iingat." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 8f549b74f9a6..429d838df65d 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Bu talebi onaylarsanız birisi Blur üzerinde yer alan varlıklarınızı çalabilir." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Bu talep bir hatadan dolayı güvenlik sağlayıcısı tarafından doğrulanmadı. Dikkatli bir şekilde ilerleyin." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 1c0386cbb69c..e0705352ea6b 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "Nếu bạn chấp thuận yêu cầu này, người khác có thể đánh cắp tài sản được niêm yết trên Blur của bạn." }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "Do có lỗi, yêu cầu này đã không được nhà cung cấp dịch vụ bảo mật xác minh. Hãy thực hiện cẩn thận." }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 99e51271cfe0..7b336018586b 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -556,7 +556,7 @@ "blockaidDescriptionBlurFarming": { "message": "如果您批准此请求,则有人可以窃取您列于Blur上的资产。" }, - "blockaidDescriptionFailed": { + "blockaidDescriptionErrored": { "message": "由于出现错误,安全提供程序无法验证此请求。请谨慎操作。" }, "blockaidDescriptionMaliciousDomain": { diff --git a/app/scripts/lib/createRPCMethodTrackingMiddleware.js b/app/scripts/lib/createRPCMethodTrackingMiddleware.js index d16bb9f74b4f..02bbb7c55df8 100644 --- a/app/scripts/lib/createRPCMethodTrackingMiddleware.js +++ b/app/scripts/lib/createRPCMethodTrackingMiddleware.js @@ -216,6 +216,15 @@ export default function createRPCMethodTrackingMiddleware({ BlockaidResultType.NotApplicable; eventProperties.security_alert_reason = req.securityAlertResponse?.reason ?? BlockaidReason.notApplicable; + + if ( + req.securityAlertResponse?.result_type === + BlockaidResultType.Errored && + req.securityAlertResponse?.description + ) { + eventProperties.security_alert_description = + req.securityAlertResponse.description; + } ///: END:ONLY_INCLUDE_IF const snapAndHardwareInfo = await getSnapAndHardwareInfoForMetrics( diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index 281506421605..ff21e4502806 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -35,10 +35,16 @@ const createMiddleWare = ( const networkController = { state: { providerConfig: { chainId: chainId || CHAIN_IDS.MAINNET } }, }; + const appStateController = { + addSignatureSecurityAlertResponse: () => undefined, + }; + return createPPOMMiddleware( ppomController as any, preferenceController as any, networkController as any, + appStateController as any, + () => undefined, ); }; @@ -87,7 +93,7 @@ describe('PPOMMiddleware', () => { expect(req.securityAlertResponse).toBeUndefined(); }); - it('should set Failed type in response if usePPOM throw error', async () => { + it('should set error type in response if usePPOM throw error', async () => { const usePPOM = async () => { throw new Error('some error'); }; @@ -98,10 +104,10 @@ describe('PPOMMiddleware', () => { }; await middlewareFunction(req, undefined, () => undefined); expect((req.securityAlertResponse as any)?.result_type).toBe( - BlockaidResultType.Failed, + BlockaidResultType.Errored, ); expect((req.securityAlertResponse as any)?.reason).toBe( - BlockaidReason.failed, + BlockaidReason.errored, ); }); diff --git a/app/scripts/lib/ppom/ppom-middleware.ts b/app/scripts/lib/ppom/ppom-middleware.ts index 4f742e4af078..87c25d6f1fae 100644 --- a/app/scripts/lib/ppom/ppom-middleware.ts +++ b/app/scripts/lib/ppom/ppom-middleware.ts @@ -73,15 +73,16 @@ export function createPPOMMiddleware( .usePPOM(async (ppom: PPOM) => { try { const securityAlertResponse = await ppom.validateJsonRpc(req); + securityAlertResponse.securityAlertId = securityAlertId; return securityAlertResponse; } catch (error: any) { sentry?.captureException(error); + const errorObject = error as unknown as Error; console.error('Error validating JSON RPC using PPOM: ', error); const securityAlertResponse = { - result_type: BlockaidResultType.Failed, - reason: BlockaidReason.failed, - description: - 'Validating the confirmation failed by throwing error.', + result_type: BlockaidResultType.Errored, + reason: BlockaidReason.errored, + description: `${errorObject.name}: ${errorObject.message}`, }; return securityAlertResponse; @@ -114,12 +115,13 @@ export function createPPOMMiddleware( } } } catch (error: any) { + const errorObject = error as unknown as Error; sentry?.captureException(error); console.error('Error validating JSON RPC using PPOM: ', error); req.securityAlertResponse = { - result_type: BlockaidResultType.Failed, - reason: BlockaidReason.failed, - description: 'Validating the confirmation failed by throwing error.', + result_type: BlockaidResultType.Errored, + reason: BlockaidReason.errored, + description: `${errorObject.name}: ${errorObject.message}`, }; } finally { next(); diff --git a/app/scripts/lib/transaction/util.ts b/app/scripts/lib/transaction/util.ts index cb621794a08f..5c0516eb7c2e 100644 --- a/app/scripts/lib/transaction/util.ts +++ b/app/scripts/lib/transaction/util.ts @@ -119,7 +119,7 @@ const PPOM_EXCLUDED_TRANSACTION_TYPES = [ export async function addTransaction( request: AddTransactionRequest, ///: BEGIN:ONLY_INCLUDE_IF(blockaid) - updateSecurityAlertResponseByTxId: ( + updateSecurityAlertResponseByTxId?: ( req: AddTransactionOptions | undefined, securityAlertResponse: SecurityAlertResponse, ) => void, @@ -169,19 +169,19 @@ export async function addTransaction( return securityAlertResponse; } catch (e) { captureException(e); + const errorObject = e as unknown as Error; console.error('Error validating JSON RPC using PPOM: ', e); const securityAlertResponse = { securityAlertId, - result_type: BlockaidResultType.Failed, - reason: BlockaidReason.failed, - description: - 'Validating the confirmation failed by throwing error.', + result_type: BlockaidResultType.Errored, + reason: BlockaidReason.errored, + description: `${errorObject.name}: ${errorObject.message}`, }; return securityAlertResponse; } }) .then((securityAlertResponse) => { - updateSecurityAlertResponseByTxId(request.transactionOptions, { + updateSecurityAlertResponseByTxId?.(request.transactionOptions, { ...securityAlertResponse, securityAlertId, }); @@ -193,6 +193,7 @@ export async function addTransaction( securityAlertId, }; } catch (e) { + console.error('Error validating JSON RPC using PPOM: ', e); captureException(e); } } diff --git a/package.json b/package.json index ab99b795cb53..5c7da0edb156 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "11.9.5", + "version": "11.10.0", "private": true, "repository": { "type": "git", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 563665135b41..397fa0472039 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -773,7 +773,7 @@ export enum MetaMetricsEventUiCustomization { FlaggedAsSafetyUnknown = 'flagged_as_safety_unknown', FlaggedAsWarning = 'flagged_as_warning', GasEstimationFailed = 'gas_estimation_failed', - SecurityAlertFailed = 'security_alert_failed', + SecurityAlertError = 'security_alert_error', Siwe = 'sign_in_with_ethereum', } diff --git a/shared/constants/security-provider.ts b/shared/constants/security-provider.ts index e18888d25178..537d0bd41e5c 100644 --- a/shared/constants/security-provider.ts +++ b/shared/constants/security-provider.ts @@ -47,7 +47,7 @@ export enum BlockaidReason { other = 'other', // MetaMask defined reasons - failed = 'Failed', + errored = 'Error', notApplicable = 'NotApplicable', inProgress = 'validation_in_progress', } @@ -59,7 +59,6 @@ export enum BlockaidResultType { Errored = 'Error', // MetaMask defined result types - Failed = 'Failed', NotApplicable = 'NotApplicable', Loading = 'loading', } diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index e986c302df1d..210790844a50 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -719,6 +719,24 @@ const sendScreenToConfirmScreen = async ( await driver.fill('[data-testid="ens-input"]', recipientAddress); await driver.fill('.unit-input__input', quantity); if (process.env.MULTICHAIN) { + // check if element exists and click it + await driver + .findElement({ + text: 'I understand', + tag: 'button', + }) + .then( + (_found) => { + driver.clickElement({ + text: 'I understand', + tag: 'button', + }); + }, + (error) => { + console.error('Element not found.', error); + }, + ); + await driver.clickElement({ text: 'Continue', tag: 'button', diff --git a/test/e2e/tests/account-token-list.spec.js b/test/e2e/tests/account-token-list.spec.js index 582a5cd59e90..1285027ed3e3 100644 --- a/test/e2e/tests/account-token-list.spec.js +++ b/test/e2e/tests/account-token-list.spec.js @@ -2,27 +2,24 @@ const { strict: assert } = require('assert'); const { withFixtures, defaultGanacheOptions, - unlockWallet, + logInWithBalanceValidation, } = require('../helpers'); + const FixtureBuilder = require('../fixture-builder'); -const { SMART_CONTRACTS } = require('../seeder/smart-contracts'); describe('Settings', function () { - const smartContract = SMART_CONTRACTS.ERC1155; it('Should match the value of token list item and account list item for eth conversion', async function () { await withFixtures( { - dapp: true, fixtures: new FixtureBuilder().build(), - defaultGanacheOptions, - smartContract, + ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), }, - async ({ driver }) => { - await unlockWallet(driver); + async ({ driver, ganacheServer }) => { + await logInWithBalanceValidation(driver, ganacheServer); await driver.clickElement('[data-testid="home__asset-tab"]'); - const tokenValue = '0 ETH'; + const tokenValue = '25 ETH'; const tokenListAmount = await driver.findElement( '[data-testid="multichain-token-list-item-value"]', ); @@ -33,7 +30,7 @@ describe('Settings', function () { '.multichain-account-list-item .multichain-account-list-item__avatar-currency .currency-display-component__text', ); - assert.equal(await accountTokenValue.getText(), '0', 'ETH'); + assert.equal(await accountTokenValue.getText(), '25', 'ETH'); }, ); }); @@ -41,14 +38,12 @@ describe('Settings', function () { it('Should match the value of token list item and account list item for fiat conversion', async function () { await withFixtures( { - dapp: true, fixtures: new FixtureBuilder().build(), - defaultGanacheOptions, - smartContract, + ganacheOptions: defaultGanacheOptions, title: this.test.fullTitle(), }, - async ({ driver }) => { - await unlockWallet(driver); + async ({ driver, ganacheServer }) => { + await logInWithBalanceValidation(driver, ganacheServer); await driver.clickElement( '[data-testid="account-options-menu-button"]', @@ -65,18 +60,16 @@ describe('Settings', function () { ); await driver.clickElement('[data-testid="home__asset-tab"]'); - const tokenValue = '0 ETH'; const tokenListAmount = await driver.findElement( - '[data-testid="multichain-token-list-item-value"]', + '.eth-overview__primary-container', ); - assert.equal(await tokenListAmount.getText(), tokenValue); - + assert.equal(await tokenListAmount.getText(), '$42,500.00\nUSD'); await driver.clickElement('[data-testid="account-menu-icon"]'); const accountTokenValue = await driver.waitForSelector( - '.multichain-account-list-item .multichain-account-list-item__avatar-currency .currency-display-component__text', + '.multichain-account-list-item .multichain-account-list-item__asset', ); - assert.equal(await accountTokenValue.getText(), '0', 'ETH'); + assert.equal(await accountTokenValue.getText(), '$42,500.00USD'); }, ); }); diff --git a/test/e2e/tests/network/update-network.spec.ts b/test/e2e/tests/network/update-network.spec.ts new file mode 100644 index 000000000000..db5d2a43402b --- /dev/null +++ b/test/e2e/tests/network/update-network.spec.ts @@ -0,0 +1,113 @@ +import { strict as assert } from 'assert'; +import { Suite } from 'mocha'; +import FixtureBuilder from '../../fixture-builder'; +import { + defaultGanacheOptions, + unlockWallet, + withFixtures, +} from '../../helpers'; +import { Driver } from '../../webdriver/driver'; + +const selectors = { + accountOptionsMenuButton: '[data-testid="account-options-menu-button"]', + informationSymbol: '[data-testid="info-tooltip"]', + settingsOption: { text: 'Settings', tag: 'div' }, + networkOption: { text: 'Networks', tag: 'div' }, + generalOption: { text: 'General', tag: 'div' }, + ethereumNetwork: { text: 'Ethereum Mainnet', tag: 'div' }, + deleteButton: { text: 'Delete', tag: 'button' }, + cancelButton: { text: 'Cancel', tag: 'button' }, + saveButton: { text: 'Save', tag: 'button' }, + updatedNetworkDropDown: { tag: 'span', text: 'Update Network' }, + errorMessageInvalidUrl: { + tag: 'h6', + text: 'URLs require the appropriate HTTP/HTTPS prefix.', + }, + networkNameInputField: '[data-testid="network-form-network-name"]', + rpcUrlInputField: '[data-testid="network-form-rpc-url"]', + chainIdInputField: '[data-testid="network-form-chain-id"]', + errorContainer: '.settings-tab__error', +}; + +const inputData = { + networkName: 'Update Network', + rpcUrl: 'test', + chainId: '0x539', +}; + +async function navigateToEditNetwork(driver: Driver) { + await driver.clickElement(selectors.accountOptionsMenuButton); + await driver.clickElement(selectors.settingsOption); + await driver.clickElement(selectors.networkOption); +} +describe('Update Network:', function (this: Suite) { + it('update network details and validate the ui elements', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + + async ({ driver }: { driver: Driver }) => { + await unlockWallet(driver); + await navigateToEditNetwork(driver); + await driver.fill( + selectors.networkNameInputField, + inputData.networkName, + ); + await driver.fill(selectors.chainIdInputField, inputData.chainId); + await driver.clickElement(selectors.saveButton); + + // Validate the network name is updated + const updatedNetworkNamePresent = await driver.isElementPresent( + selectors.updatedNetworkDropDown, + ); + assert.equal( + updatedNetworkNamePresent, + true, + 'Network name is not updated', + ); + + await navigateToEditNetwork(driver); + await driver.fill(selectors.rpcUrlInputField, inputData.rpcUrl); + + // Validate the error message that appears for the invalid url format + const errorMessage = await driver.isElementPresent( + selectors.errorMessageInvalidUrl, + ); + assert.equal( + errorMessage, + true, + 'Error message for the invalid url did not appear', + ); + + // Validate the Save button is disabled for the invalid url format + const saveButton = await driver.findElement(selectors.saveButton); + const saveButtonEnabled = await saveButton.isEnabled(); + assert.equal(saveButtonEnabled, false, 'Save button is enabled'); + + // Validate the information symbol appears for chain id + const informationSymbolAppears = await driver.isElementPresent( + selectors.informationSymbol, + ); + assert.equal( + informationSymbolAppears, + true, + 'Information symbol did not appear for chain id', + ); + + await driver.clickElement(selectors.ethereumNetwork); + + // Validate the Save,Cancel Delete button is not present for the default network + await driver.assertElementNotPresent(selectors.deleteButton); + await driver.assertElementNotPresent(selectors.cancelButton); + await driver.assertElementNotPresent(selectors.saveButton); + + // Validate the error does not appear for updating the network name and chain id + await driver.clickElement(selectors.generalOption); + await driver.assertElementNotPresent(selectors.errorContainer); + }, + ); + }); +}); \ No newline at end of file diff --git a/test/e2e/tests/ppom-blockaid-alert-simple-send.spec.js b/test/e2e/tests/ppom-blockaid-alert-simple-send.spec.js index bf99c19c12c8..cb4dce5ba29c 100644 --- a/test/e2e/tests/ppom-blockaid-alert-simple-send.spec.js +++ b/test/e2e/tests/ppom-blockaid-alert-simple-send.spec.js @@ -222,8 +222,8 @@ describe('Simple Send Security Alert - Blockaid @no-mmi', function () { await sendScreenToConfirmScreen( driver, - '0x985c30949c92df7a0bd42e0f3e3d539ece98db24', - '1', + '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', + '1.1', ); // await driver.delay(100000) const expectedTitle = 'Request may not be safe'; diff --git a/test/scenarios/13. sign/sign typed data v3 with hardware wallet.csv b/test/scenarios/13. sign/sign typed data v3 with hardware wallet.csv new file mode 100644 index 000000000000..cebeaffd5c02 --- /dev/null +++ b/test/scenarios/13. sign/sign typed data v3 with hardware wallet.csv @@ -0,0 +1,25 @@ +Steps,Test Steps,Preconditions,Test Data,Expected Result,Notes +1,Open the extension.,,,The Welcome Back screen is shown., +2,Proceed to Unlock the wallet.,,password (8 characters min).,"The Ether balance is shown on the overview. +The wallet address is shown on the overview.", +3,"Click on account menu icon. Click ""Add account or hardware wallet"".",,,"The ""Add account"" modal is shown.", +4,"On ""Add account"" modal, click ""Add hardware wallet"" button.",,,"""Connect a hardware wallet"" screen is shown. User can choose between different options to connect a hardware: Ledger, Trezor, Lattice, or QR-based. ""Continue"" button is disabled.", +5,Choose an option to connect hardware wallet.,We need to have a QR-based hardware wallet set up to test this functionality.,"e.g. choose ""QR-based""","""Continue"" button is enabled.", +6,"Unlock the QR-based wallet and sync it with MetaMask.",,Password for hardware wallet,"Hardware wallet is detected by MetaMask. ""Select an account"" screen is shown on MetaMask, accounts on hardware wallet are shown on this screen.","Note: using Ledger or Trezor won't work for this flow, since they don't support Sign Typed Data V3." +7,"Choose one or multiples accounts that user wants to connect. Then click ""Unlock"".",,,, +8,Click account menu icon to open accounts list.,,,"In accounts list, all selected hardware wallet accounts are shown, and they are all flagged with harware wallet name to be distinguished from other accounts.", +9,Select one hardware wallet account.,,,"The Ether balance for the selected hardware wallet account is shown on the overview. +The selected account address is shown on the overview.", +10,Open the test dapp in another tab.,,https://metamask.github.io/test-dapp/,, +11,Click Connect.,,,"The MetaMask popup is opened with the Connect with MetaMask screen displayed. +Your imported hardware wallet account is selected.", +12,Click Next and Connect with the hardware wallet account.,,,"The MetaMask popup is closed. +You are connected to the test dapp.", +13,"Click ""Sign Typed Data V3"".",,,"The MetaMask popup is open. +The signature message is displayed in JSON formatting.", +14,"Click ""Sign"".",,,The QR code modal with the signature request is displayed., +15,"Scan the QR code with the QR-based wallet.",,,The signature QR code is displayed in the QR-based wallet., +15,"Click ""Get Signature"".",,,The computer camera is opened., +16,Scan the Signature from the QR-based wallet.,,,"The signature hash is displayed in the test-dapp result. +The MetaMask popup is closed.", +17,Verify signed hash.,,,The signed address is correctly verified.,The address shown in the test dapp is the same as the hardware wallet account. diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index 585386ece792..dece08d61f14 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -34,6 +34,7 @@ import { getShouldHideZeroBalanceTokens, getCurrentNetwork, getSelectedAccountCachedBalance, + getShowFiatInTestnets, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) getSwapsDefaultToken, getCurrentKeyring, @@ -106,10 +107,15 @@ const EthOverview = ({ className, showAddress }) => { selectedAddress, shouldHideZeroBalanceTokens, ); + const showFiatInTestnets = useSelector(getShowFiatInTestnets); + const showFiat = + TEST_NETWORKS.includes(currentNetwork?.nickname) && !showFiatInTestnets; - const balanceToUse = TEST_NETWORKS.includes(currentNetwork?.nickname) - ? balance - : totalWeiBalance; + let balanceToUse = totalWeiBalance; + + if (showFiat) { + balanceToUse = balance; + } const isSwapsChain = useSelector(getIsSwapsChain); @@ -196,7 +202,10 @@ const EthOverview = ({ className, showAddress }) => { ? PRIMARY : SECONDARY } - showFiat={!TEST_NETWORKS.includes(currentNetwork?.nickname)} + showFiat={ + !showFiat || + !TEST_NETWORKS.includes(currentNetwork?.nickname) + } ethNumberOfDecimals={4} hideTitle /> diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap index cc754b6c907f..0acee96e7f7d 100644 --- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap +++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap @@ -71,12 +71,12 @@ exports[`AccountListItem renders AccountListItem component and shows account nam >
- $3.31 + $880.18 { setAccountListItemMenuElement(ref); }; - + const showFiatInTestnets = useSelector(getShowFiatInTestnets); + const showFiat = + TEST_NETWORKS.includes(currentNetwork?.nickname) && !showFiatInTestnets; const { totalWeiBalance, orderedTokenList } = useAccountTotalFiatBalance( identity.address, ); - const balanceToTranslate = TEST_NETWORKS.includes(currentNetwork?.nickname) - ? totalWeiBalance - : identity.balance; + let balanceToTranslate = totalWeiBalance; + if (showFiat) { + balanceToTranslate = identity.balance; + } // If this is the selected item in the Account menu, // scroll the item into view @@ -209,7 +213,9 @@ export const AccountListItem = ({ ethNumberOfDecimals={MAXIMUM_CURRENCY_DECIMALS} value={balanceToTranslate} type={PRIMARY} - showFiat={!TEST_NETWORKS.includes(currentNetwork?.nickname)} + showFiat={ + !showFiat || !TEST_NETWORKS.includes(currentNetwork?.nickname) + } /> diff --git a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap index 04ea196e505b..d213c98faec4 100644 --- a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap +++ b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap @@ -74,12 +74,12 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >
- $537,761.36 + $880.18 { let uiCustomization; - if (resultType === BlockaidResultType.Failed) { - uiCustomization = [MetaMetricsEventUiCustomization.SecurityAlertFailed]; - } else if (resultType === BlockaidResultType.Malicious) { + if (resultType === BlockaidResultType.Malicious) { uiCustomization = [MetaMetricsEventUiCustomization.FlaggedAsMalicious]; } else if (resultType === BlockaidResultType.Warning) { uiCustomization = [MetaMetricsEventUiCustomization.FlaggedAsWarning]; + } else if (resultType === BlockaidResultType.Errored) { + uiCustomization = [MetaMetricsEventUiCustomization.SecurityAlertError]; } return uiCustomization; @@ -60,6 +60,7 @@ export const getBlockaidMetricsProps = ({ securityAlertResponse }) => { providerRequestsCount, reason, result_type: resultType, + description, } = securityAlertResponse; const uiCustomization = getBlockaidMetricUiCustomization(resultType); @@ -70,6 +71,11 @@ export const getBlockaidMetricsProps = ({ securityAlertResponse }) => { if (resultType !== BlockaidResultType.Benign) { params.security_alert_reason = reason ?? BlockaidReason.notApplicable; } + + if (resultType === BlockaidResultType.Errored && description) { + params.security_alert_description = description; + } + params.security_alert_response = resultType ?? BlockaidResultType.NotApplicable; diff --git a/ui/helpers/utils/metrics.test.js b/ui/helpers/utils/metrics.test.js index b75f5b7b2693..d1a2f1a5d72e 100644 --- a/ui/helpers/utils/metrics.test.js +++ b/ui/helpers/utils/metrics.test.js @@ -54,18 +54,18 @@ describe('getBlockaidMetricsProps', () => { ); }); - it('includes "security_alert_failed" ui_customization when type is failed', () => { + it('includes "security_alert_error" ui_customization when type is error', () => { const result = getBlockaidMetricsProps({ securityAlertResponse: { ...securityAlertResponse, - result_type: BlockaidResultType.Failed, + result_type: BlockaidResultType.Errored, }, }); expect(result).toStrictEqual({ security_alert_reason: BlockaidReason.setApprovalForAll, - security_alert_response: BlockaidResultType.Failed, - ui_customizations: ['security_alert_failed'], + security_alert_response: BlockaidResultType.Errored, + ui_customizations: ['security_alert_error'], }); }); @@ -132,6 +132,21 @@ describe('getBlockaidMetricsProps', () => { }); }); + it('includes "security_alert_error" ui_customization when type is an error', () => { + const result = getBlockaidMetricsProps({ + securityAlertResponse: { + ...securityAlertResponse, + result_type: BlockaidResultType.Errored, + reason: 'error: error message', + }, + }); + expect(result).toStrictEqual({ + ui_customizations: ['security_alert_error'], + security_alert_response: BlockaidResultType.Errored, + security_alert_reason: 'error: error message', + }); + }); + it('excludes eth call counts if providerRequestsCount is empty', () => { const result = getBlockaidMetricsProps({ securityAlertResponse: { diff --git a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap index 1979110264ea..04b2a4843aab 100644 --- a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap +++ b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/__snapshots__/blockaid-banner-alert.test.js.snap @@ -93,7 +93,7 @@ exports[`Blockaid Banner Alert should render 'danger' UI when securityAlertRespo
`; -exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Failed 1`] = ` +exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Error 1`] = `
) : null; - const isFailedResultType = resultType === BlockaidResultType.Failed; + const isFailedResultType = resultType === BlockaidResultType.Errored; const severity = resultType === BlockaidResultType.Malicious diff --git a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js index b34d9d9b0e0a..4abd67e10685 100644 --- a/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js +++ b/ui/pages/confirmations/components/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js @@ -70,13 +70,13 @@ describe('Blockaid Banner Alert', () => { expect(container.querySelector('.mm-banner-alert')).toBeNull(); }); - it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Failed}`, () => { + it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Errored}`, () => { const { container } = renderWithProvider( , @@ -140,13 +140,13 @@ describe('Blockaid Banner Alert', () => { expect(getByText('This is a deceptive request')).toBeInTheDocument(); }); - it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.failed}"`, () => { + it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.errored}"`, () => { const { getByText } = renderWithProvider( , @@ -248,14 +248,14 @@ describe('Blockaid Banner Alert', () => { }); describe('when constructing the Blockaid Report URL', () => { - describe(`when result_type='${BlockaidResultType.Failed}'`, () => { + describe(`when result_type='${BlockaidResultType.Errored}'`, () => { it('should pass the classification as "error" and the resultType as "Error"', () => { const { getByRole } = renderWithProvider( , @@ -276,7 +276,7 @@ describe('Blockaid Banner Alert', () => { 'If you approve this request, a third party known for scams might take all your assets.', [BlockaidReason.blurFarming]: 'If you approve this request, someone can steal your assets listed on Blur.', - [BlockaidReason.failed]: + [BlockaidReason.errored]: 'Because of an error, this request was not verified by the security provider. Proceed with caution.', [BlockaidReason.maliciousDomain]: "You're interacting with a malicious domain. If you approve this request, you might lose your assets.", diff --git a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap b/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap index cf1180fdc481..9b0bd89d8cfb 100644 --- a/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap +++ b/ui/pages/onboarding-flow/add-network-modal/__snapshots__/add-network-modal.test.js.snap @@ -60,6 +60,7 @@ exports[`Add Network Modal should render 1`] = `
@@ -90,6 +91,7 @@ exports[`Add Network Modal should render 1`] = `
@@ -144,6 +146,7 @@ exports[`Add Network Modal should render 1`] = ` @@ -201,6 +204,7 @@ exports[`Add Network Modal should render 1`] = ` diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js index f7b892b894a0..e71f6bf3a0a7 100644 --- a/ui/pages/settings/networks-tab/networks-form/networks-form.js +++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js @@ -709,6 +709,7 @@ const NetworksForm = ({ titleText={t('networkName')} value={networkName} disabled={viewOnly} + dataTestId="network-form-network-name" />
{ return state.metamask.useSafeChainsListValidation; }; +export function getShowFiatInTestnets(state) { + const { showFiatInTestnets } = getPreferences(state); + return showFiatInTestnets; +} + /** * To get the useCurrencyRateCheck flag which to check if the user prefers currency conversion *