From 08d1854986101fce1b8442a4fe00481ef443b74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Regadas?= Date: Mon, 4 Nov 2024 17:14:53 +0000 Subject: [PATCH 1/2] feat: adds the experimental toggle for Solana (#28190) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR sdds a new experimental toggle for Solana accounts. Everything is code fenced for Flask build. ![Screenshot 2024-10-31 at 11 50 38](https://github.com/user-attachments/assets/8e6a290c-b22d-42a7-8602-ac61410ebdb4) ## **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. --- app/_locales/en/messages.json | 9 ++++ .../preferences-controller.test.ts | 15 ++++++ .../controllers/preferences-controller.ts | 20 ++++++++ app/scripts/metamask-controller.js | 4 ++ shared/constants/metametrics.ts | 1 + .../experimental-tab.component.tsx | 47 +++++++++++++++++++ .../experimental-tab.container.ts | 12 +++++ .../experimental-tab/experimental-tab.test.js | 19 +++++++- ui/selectors/selectors.js | 12 +++++ ui/store/actions.ts | 10 ++++ 10 files changed, 148 insertions(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 61171df6e922..08675837b34d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -5224,6 +5224,15 @@ "message": "Contact the creators of $1 for further support.", "description": "This is shown when the insight snap throws an error. $1 is the snap name" }, + "solanaSupportSectionTitle": { + "message": "Solana" + }, + "solanaSupportToggleDescription": { + "message": "Turning on this feature will give you the option to add a Solana Account to your MetaMask Extension derived from your existing Secret Recovery Phrase. This is an experimental Beta feature, so you should use it at your own risk." + }, + "solanaSupportToggleTitle": { + "message": "Enable \"Add a new Solana account (Beta)\"" + }, "somethingDoesntLookRight": { "message": "Something doesn't look right? $1", "description": "A false positive message for users to contact support. $1 is a link to the support page." diff --git a/app/scripts/controllers/preferences-controller.test.ts b/app/scripts/controllers/preferences-controller.test.ts index 74daf39e17ad..a4b91a8d3b1a 100644 --- a/app/scripts/controllers/preferences-controller.test.ts +++ b/app/scripts/controllers/preferences-controller.test.ts @@ -853,4 +853,19 @@ describe('preferences controller', () => { ); }); }); + + describe('setSolanaSupportEnabled', () => { + const { controller } = setupController({}); + it('has the default value as false', () => { + expect(controller.state.solanaSupportEnabled).toStrictEqual(false); + }); + + it('sets the solanaSupportEnabled property in state to true and then false', () => { + controller.setSolanaSupportEnabled(true); + expect(controller.state.solanaSupportEnabled).toStrictEqual(true); + + controller.setSolanaSupportEnabled(false); + expect(controller.state.solanaSupportEnabled).toStrictEqual(false); + }); + }); }); diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index f6537952d651..e1cdb2e8a4f6 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -140,6 +140,7 @@ export type PreferencesControllerState = Omit< useRequestQueue: boolean; ///: BEGIN:ONLY_INCLUDE_IF(build-flask) watchEthereumAccountEnabled: boolean; + solanaSupportEnabled: boolean; ///: END:ONLY_INCLUDE_IF bitcoinSupportEnabled: boolean; bitcoinTestnetSupportEnabled: boolean; @@ -184,6 +185,9 @@ export const getDefaultPreferencesControllerState = openSeaEnabled: true, securityAlertsEnabled: true, watchEthereumAccountEnabled: false, + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + solanaSupportEnabled: false, + ///: END:ONLY_INCLUDE_IF bitcoinSupportEnabled: false, bitcoinTestnetSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -340,6 +344,10 @@ const controllerMetadata = { persist: true, anonymous: false, }, + solanaSupportEnabled: { + persist: true, + anonymous: false, + }, bitcoinSupportEnabled: { persist: true, anonymous: false, @@ -669,6 +677,18 @@ export class PreferencesController extends BaseController< state.watchEthereumAccountEnabled = watchEthereumAccountEnabled; }); } + + /** + * Setter for the `solanaSupportEnabled` property. + * + * @param solanaSupportEnabled - Whether or not the user wants to + * enable the "Add a new Solana account" button. + */ + setSolanaSupportEnabled(solanaSupportEnabled: boolean): void { + this.update((state) => { + state.solanaSupportEnabled = solanaSupportEnabled; + }); + } ///: END:ONLY_INCLUDE_IF /** diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b43ef72cae5c..a85233ca5119 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3315,6 +3315,10 @@ export default class MetamaskController extends EventEmitter { preferencesController.setWatchEthereumAccountEnabled.bind( preferencesController, ), + setSolanaSupportEnabled: + preferencesController.setSolanaSupportEnabled.bind( + preferencesController, + ), ///: END:ONLY_INCLUDE_IF setBitcoinSupportEnabled: preferencesController.setBitcoinSupportEnabled.bind( diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 2d6a87b7d1c6..4a5c21ab5752 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -607,6 +607,7 @@ export enum MetaMetricsEventName { BridgeLinkClicked = 'Bridge Link Clicked', BitcoinSupportToggled = 'Bitcoin Support Toggled', BitcoinTestnetSupportToggled = 'Bitcoin Testnet Support Toggled', + SolanaSupportToggled = 'Solana Support Toggled', CurrentCurrency = 'Current Currency', DappViewed = 'Dapp Viewed', DecryptionApproved = 'Decryption Approved', diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx index f1d1f610a7a4..0771428474d5 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx @@ -36,6 +36,10 @@ import { SurveyUrl } from '../../../../shared/constants/urls'; type ExperimentalTabProps = { watchAccountEnabled: boolean; setWatchAccountEnabled: (value: boolean) => void; + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + solanaSupportEnabled: boolean; + setSolanaSupportEnabled: (value: boolean) => void; + ///: END:ONLY_INCLUDE_IF bitcoinSupportEnabled: boolean; setBitcoinSupportEnabled: (value: boolean) => void; bitcoinTestnetSupportEnabled: boolean; @@ -369,6 +373,44 @@ export default class ExperimentalTab extends PureComponent ); } + + renderSolanaSupport() { + const { t, trackEvent } = this.context; + const { solanaSupportEnabled, setSolanaSupportEnabled } = this.props; + + return ( + <> + + {t('solanaSupportSectionTitle')} + + {this.renderToggleSection({ + title: t('solanaSupportToggleTitle'), + description: t('solanaSupportToggleDescription'), + toggleValue: solanaSupportEnabled, + toggleCallback: (value) => { + trackEvent({ + event: MetaMetricsEventName.SolanaSupportToggled, + category: MetaMetricsEventCategory.Settings, + properties: { + enabled: !value, + }, + }); + setSolanaSupportEnabled(!value); + }, + toggleContainerDataTestId: 'solana-support-toggle-div', + toggleDataTestId: 'solana-support-toggle', + toggleOffLabel: t('off'), + toggleOnLabel: t('on'), + })} + + ); + } ///: END:ONLY_INCLUDE_IF render() { @@ -398,6 +440,11 @@ export default class ExperimentalTab extends PureComponent this.renderBitcoinSupport() ///: END:ONLY_INCLUDE_IF } + { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + this.renderSolanaSupport() + ///: END:ONLY_INCLUDE_IF + } ); } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.ts b/ui/pages/settings/experimental-tab/experimental-tab.container.ts index f4d408565bfd..e3be762afd1c 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.ts +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.ts @@ -13,8 +13,14 @@ import { setRedesignedConfirmationsEnabled, setRedesignedTransactionsEnabled, setWatchEthereumAccountEnabled, + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + setSolanaSupportEnabled, + ///: END:ONLY_INCLUDE_IF } from '../../../store/actions'; import { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + getIsSolanaSupportEnabled, + ///: END:ONLY_INCLUDE_IF getIsBitcoinSupportEnabled, getIsBitcoinTestnetSupportEnabled, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) @@ -37,6 +43,9 @@ const mapStateToProps = (state: MetaMaskReduxState) => { const petnamesEnabled = getPetnamesEnabled(state); const featureNotificationsEnabled = getFeatureNotificationsEnabled(state); return { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + solanaSupportEnabled: getIsSolanaSupportEnabled(state), + ///: END:ONLY_INCLUDE_IF watchAccountEnabled: getIsWatchEthereumAccountEnabled(state), bitcoinSupportEnabled: getIsBitcoinSupportEnabled(state), bitcoinTestnetSupportEnabled: getIsBitcoinTestnetSupportEnabled(state), @@ -55,6 +64,9 @@ const mapDispatchToProps = (dispatch: MetaMaskReduxDispatch) => { return { setWatchAccountEnabled: (value: boolean) => setWatchEthereumAccountEnabled(value), + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + setSolanaSupportEnabled: (value: boolean) => setSolanaSupportEnabled(value), + ///: END:ONLY_INCLUDE_IF setBitcoinSupportEnabled: (value: boolean) => setBitcoinSupportEnabled(value), setBitcoinTestnetSupportEnabled: (value: boolean) => diff --git a/ui/pages/settings/experimental-tab/experimental-tab.test.js b/ui/pages/settings/experimental-tab/experimental-tab.test.js index 784b2dc3b5d3..08d02bf94215 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.test.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.test.js @@ -30,7 +30,7 @@ describe('ExperimentalTab', () => { const { getAllByRole } = render(); const toggle = getAllByRole('checkbox'); - expect(toggle).toHaveLength(8); + expect(toggle).toHaveLength(9); }); it('enables add account snap', async () => { @@ -108,4 +108,21 @@ describe('ExperimentalTab', () => { expect(setBitcoinSupportEnabled).toHaveBeenNthCalledWith(1, true); }); }); + + it('enables the experimental solana account feature', async () => { + const setSolanaSupportEnabled = jest.fn(); + const { getByTestId } = render( + {}, + { + setSolanaSupportEnabled, + solanaSupportEnabled: false, + }, + ); + const toggle = getByTestId('solana-support-toggle'); + + fireEvent.click(toggle); + await waitFor(() => { + expect(setSolanaSupportEnabled).toHaveBeenNthCalledWith(1, true); + }); + }); }); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 70e970c4c9b3..8b44dd715aba 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -2364,6 +2364,18 @@ export function getIsBitcoinSupportEnabled(state) { return state.metamask.bitcoinSupportEnabled; } +///: BEGIN:ONLY_INCLUDE_IF(build-flask) +/** + * Get the state of the `solanaSupportEnabled` flag. + * + * @param {*} state + * @returns The state of the `solanaSupportEnabled` flag. + */ +export function getIsSolanaSupportEnabled(state) { + return state.metamask.solanaSupportEnabled; +} +///: END:ONLY_INCLUDE_IF + /** * Get the state of the `bitcoinTestnetSupportEnabled` flag. * diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 886739d2d54f..11df6a3f3e20 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -5002,6 +5002,16 @@ export async function setBitcoinTestnetSupportEnabled(value: boolean) { } } +///: BEGIN:ONLY_INCLUDE_IF(build-flask) +export async function setSolanaSupportEnabled(value: boolean) { + try { + await submitRequestToBackground('setSolanaSupportEnabled', [value]); + } catch (error) { + logErrorWithMessage(error); + } +} +///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) export async function setAddSnapAccountEnabled(value: boolean): Promise { try { From 63ea629b7892a32f8d3d8b943927610e2270a7cb Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 4 Nov 2024 11:24:11 -0600 Subject: [PATCH 2/2] fix: Fix alignment of long RPC labels in Networks menu (#28244) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Fixes alignment for long RPC labels in the networks list [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28244?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Add BNB Chain to MetaMask 2. Create a second RPC for it with the label `BNB Smart Chain (previously Binance Smart Chain Mainnet)` 3. See the label left-aligned ## **Screenshots/Recordings** ### **Before** ![image (2)](https://github.com/user-attachments/assets/eeb1a3e4-257b-42db-9130-abc79928553c) ### **After** SCR-20241104-iwaw ## **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. --- ui/components/multichain/network-list-item/index.scss | 4 ++++ .../multichain/network-list-item/network-list-item.tsx | 1 + 2 files changed, 5 insertions(+) diff --git a/ui/components/multichain/network-list-item/index.scss b/ui/components/multichain/network-list-item/index.scss index 6597487c9cf6..cfbe80f2ce48 100644 --- a/ui/components/multichain/network-list-item/index.scss +++ b/ui/components/multichain/network-list-item/index.scss @@ -37,4 +37,8 @@ &__delete { visibility: hidden; } + + &__rpc-endpoint { + max-width: 100%; + } } diff --git a/ui/components/multichain/network-list-item/network-list-item.tsx b/ui/components/multichain/network-list-item/network-list-item.tsx index f80572eb1e4a..204ad7a5861f 100644 --- a/ui/components/multichain/network-list-item/network-list-item.tsx +++ b/ui/components/multichain/network-list-item/network-list-item.tsx @@ -190,6 +190,7 @@ export const NetworkListItem = ({ as="button" variant={TextVariant.bodySmMedium} color={TextColor.textAlternative} + ellipsis > {rpcEndpoint.name ?? new URL(rpcEndpoint.url).host}