diff --git a/CHANGELOG.md b/CHANGELOG.md index 6618f4aaa525..fdaf4f59cc7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [11.11.1] +### Added +- Adds a staking button to the mainnet Ethereum token list item ([#22347](https://github.com/MetaMask/metamask-extension/pull/22347)) + ## [11.11.0] ### Added - Added 'Pet Names' feature, allowing users to see preferred or suggested nicknames in the places of addesses @@ -4424,7 +4428,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.11.0...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v11.11.1...HEAD +[11.11.1]: https://github.com/MetaMask/metamask-extension/compare/v11.11.0...v11.11.1 [11.11.0]: https://github.com/MetaMask/metamask-extension/compare/v11.10.1...v11.11.0 [11.10.1]: https://github.com/MetaMask/metamask-extension/compare/v11.10.0...v11.10.1 [11.10.0]: https://github.com/MetaMask/metamask-extension/compare/v11.9.5...v11.10.0 diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 3af1c1b4e964..a0a20eed26b0 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3177,6 +3177,18 @@ "notificationsPetnamesTitle": { "message": "Suggested nicknames are here!" }, + "notificationsStakingPortfolioActionText": { + "message": "Got it", + "description": "Action text for a notification in the 'See What's New' popup. Tells users that staking is now available in the portfolio app." + }, + "notificationsStakingPortfolioDescription": { + "message": "Now you can stake ETH and manage your rewards all in one place.", + "description": "Description for a notification in the 'See What's New' popup. Tells users that staking is now available in the portfolio app." + }, + "notificationsStakingPortfolioTitle": { + "message": "Stake smarter in Portfolio", + "description": "Title for a notification in the 'See What's New' popup. Tells users that staking is now available in the portfolio app." + }, "notificationsU2FLedgerLiveDescription": { "message": "U2F and Ledger Live are no longer available on Chrome. You can still connect Ledger devices on Chrome using Webhid.", "description": "Description of a notification in the 'See What's New' popup. Describes the U2F and Ledger Live connection modes are now deprecated" diff --git a/app/images/icons/stake.svg b/app/images/icons/stake.svg new file mode 100644 index 000000000000..54c6762e3cab --- /dev/null +++ b/app/images/icons/stake.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/images/portfolio-stake-notification-light-mode.png b/app/images/portfolio-stake-notification-light-mode.png new file mode 100644 index 000000000000..33cbe2933920 Binary files /dev/null and b/app/images/portfolio-stake-notification-light-mode.png differ diff --git a/package.json b/package.json index bcbbd956dba0..dfcae21af58d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "11.11.0", + "version": "11.11.1", "private": true, "repository": { "type": "git", diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 3db2c67f6785..f5d556be3f03 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -595,6 +595,7 @@ export enum MetaMetricsEventName { SrpViewSrpText = 'Views SRP', SrpCopiedToClipboard = 'Copies SRP to clipboard', SrpToConfirmBackup = 'SRP Backup Confirm Displayed', + StakingEntryPointClicked = 'Stake Button Clicked', SupportLinkClicked = 'Support Link Clicked', TermsOfUseShown = 'Terms of Use Shown', TermsOfUseAccepted = 'Terms of Use Accepted', diff --git a/shared/notifications/index.js b/shared/notifications/index.js index 57b2e0892a54..7e57c92ba9c0 100644 --- a/shared/notifications/index.js +++ b/shared/notifications/index.js @@ -9,7 +9,8 @@ export const NOTIFICATION_OPEN_BETA_SNAPS = 26; export const NOTIFICATION_BUY_SELL_BUTTON = 27; export const NOTIFICATION_U2F_LEDGER_LIVE = 28; export const NOTIFICATION_BLOCKAID_DEFAULT = 29; -export const NOTIFICATION_PETNAMES = 30; +export const NOTIFICATION_STAKING_PORTFOLIO = 30; +export const NOTIFICATION_PETNAMES = 31; export const UI_NOTIFICATIONS = { 1: { @@ -178,6 +179,14 @@ export const UI_NOTIFICATIONS = { date: null, }, ///: END:ONLY_INCLUDE_IF + [NOTIFICATION_STAKING_PORTFOLIO]: { + id: Number(NOTIFICATION_STAKING_PORTFOLIO), + date: null, + image: { + src: 'images/portfolio-stake-notification-light-mode.png', + width: '100%', + }, + }, [NOTIFICATION_PETNAMES]: { id: Number(NOTIFICATION_PETNAMES), date: null, @@ -477,6 +486,17 @@ export const getTranslatedUINotifications = ( ) : '', }, + [NOTIFICATION_STAKING_PORTFOLIO]: { + ...UI_NOTIFICATIONS[NOTIFICATION_STAKING_PORTFOLIO], + title: t('notificationsStakingPortfolioTitle'), + description: [t('notificationsStakingPortfolioDescription')], + actionText: t('notificationsStakingPortfolioActionText'), + date: UI_NOTIFICATIONS[NOTIFICATION_STAKING_PORTFOLIO].date + ? new Intl.DateTimeFormat(formattedLocale).format( + new Date(UI_NOTIFICATIONS[NOTIFICATION_STAKING_PORTFOLIO].date), + ) + : '', + }, ///: BEGIN:ONLY_INCLUDE_IF(blockaid) [NOTIFICATION_BLOCKAID_DEFAULT]: { ...UI_NOTIFICATIONS[NOTIFICATION_BLOCKAID_DEFAULT], diff --git a/test/e2e/tests/add-hide-token.spec.js b/test/e2e/tests/add-hide-token.spec.js index 9b2e184ff00b..e86b5d1c870e 100644 --- a/test/e2e/tests/add-hide-token.spec.js +++ b/test/e2e/tests/add-hide-token.spec.js @@ -54,7 +54,7 @@ describe('Add hide token', function () { await driver.clickElement({ text: 'Tokens', tag: 'button' }); - await driver.clickElement({ text: 'TST', tag: 'p' }); + await driver.clickElement({ text: 'TST', tag: 'span' }); await driver.clickElement('[data-testid="asset-options__button"]'); diff --git a/test/e2e/tests/add-multiple-tokens.spec.js b/test/e2e/tests/add-multiple-tokens.spec.js index a1da4eac1708..d6d9200fe464 100644 --- a/test/e2e/tests/add-multiple-tokens.spec.js +++ b/test/e2e/tests/add-multiple-tokens.spec.js @@ -99,7 +99,7 @@ describe('Multiple ERC20 Watch Asset', function () { // Check all three tokens have been added to the token list. const addedTokens = await driver.findElements({ - tag: 'p', + tag: 'span', text: 'TST', }); assert.equal(addedTokens.length, 3); diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js index 9696f7eb51e1..0c3a1a141ff0 100644 --- a/ui/components/app/asset-list/asset-list.js +++ b/ui/components/app/asset-list/asset-list.js @@ -18,6 +18,7 @@ import { ///: END:ONLY_INCLUDE_IF getSelectedAccount, getPreferences, + getIsMainnet, } from '../../../selectors'; import { getNativeCurrency, @@ -62,6 +63,7 @@ const AssetList = ({ onClickAsset }) => { const nativeCurrency = useSelector(getNativeCurrency); const showFiat = useSelector(getShouldShowFiat); const chainId = useSelector(getCurrentChainId); + const isMainnet = useSelector(getIsMainnet); const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences); const { ticker, type } = useSelector(getProviderConfig); const isOriginalNativeSymbol = useIsOriginalNativeTokenSymbol( @@ -123,6 +125,12 @@ const AssetList = ({ onClickAsset }) => { const defaultSwapsToken = useSelector(getSwapsDefaultToken); ///: END:ONLY_INCLUDE_IF + let isStakeable = isMainnet; + + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) + isStakeable = false; + ///: END:ONLY_INCLUDE_IF + return ( <> {detectedTokens.length > 0 && @@ -211,6 +219,7 @@ const AssetList = ({ onClickAsset }) => { tokenImage={balanceIsLoading ? null : primaryTokenImage} isOriginalTokenSymbol={isOriginalNativeSymbol} isNativeCurrency + isStakeable={isStakeable} />
{ updateViewedNotifications({ [NOTIFICATION_U2F_LEDGER_LIVE]: true }); }, + [NOTIFICATION_STAKING_PORTFOLIO]: () => { + updateViewedNotifications({ [NOTIFICATION_STAKING_PORTFOLIO]: true }); + }, ///: BEGIN:ONLY_INCLUDE_IF(blockaid) [NOTIFICATION_BLOCKAID_DEFAULT]: () => { updateViewedNotifications({ [NOTIFICATION_BLOCKAID_DEFAULT]: true }); @@ -371,6 +375,7 @@ export default function WhatsNewPopup({ onClose }) { [NOTIFICATION_OPEN_BETA_SNAPS]: renderFirstNotification, [NOTIFICATION_BUY_SELL_BUTTON]: renderFirstNotification, [NOTIFICATION_U2F_LEDGER_LIVE]: renderFirstNotification, + [NOTIFICATION_STAKING_PORTFOLIO]: renderFirstNotification, ///: BEGIN:ONLY_INCLUDE_IF(blockaid) [NOTIFICATION_BLOCKAID_DEFAULT]: renderFirstNotification, ///: END:ONLY_INCLUDE_IF diff --git a/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap b/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap index 2d5fa9de4841..c64a5372e1a9 100644 --- a/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap +++ b/ui/components/multichain/token-list-item/__snapshots__/token-list-item.test.js.snap @@ -41,7 +41,7 @@ exports[`TokenListItem should render correctly 1`] = ` class="mm-box mm-box--display-flex mm-box--gap-1 mm-box--flex-direction-row mm-box--justify-content-space-between" >
{ const t = useI18nContext(); const primaryTokenImage = useSelector(getNativeCurrencyImage); const trackEvent = useContext(MetaMetricsContext); + const metaMetricsId = useSelector(getMetaMetricsId); const chainId = useSelector(getCurrentChainId); // Scam warning @@ -82,7 +88,45 @@ export const TokenListItem = ({ title === CURRENCY_SYMBOLS.ETH && isOriginalTokenSymbol ? t('networkNameEthereum') : title; - + const stakeableTitle = ( + { + e.preventDefault(); + e.stopPropagation(); + const url = getPortfolioUrl('stake', 'ext_stake_button', metaMetricsId); + global.platform.openTab({ url }); + trackEvent({ + event: MetaMetricsEventName.StakingEntryPointClicked, + category: MetaMetricsEventCategory.Tokens, + properties: { + location: 'Token List Item', + text: 'Stake', + chain_id: chainId, + token_symbol: tokenSymbol, + }, + }); + }} + > + + + {t('stake')} + + + + ); // Used for badge icon const currentNetwork = useSelector(getCurrentNetwork); const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); @@ -160,7 +204,10 @@ export const TokenListItem = ({ justifyContent={JustifyContent.spaceBetween} gap={1} > - + {title?.length > 12 ? ( - {tokenSymbol} + {isStakeable ? ( + <> + {tokenSymbol} {stakeableTitle} + + ) : ( + tokenSymbol + )} ) : ( @@ -184,7 +237,13 @@ export const TokenListItem = ({ variant={TextVariant.bodyMd} ellipsis > - {tokenSymbol} + {isStakeable ? ( + + {tokenSymbol} {stakeableTitle} + + ) : ( + tokenSymbol + )} )} @@ -205,9 +264,10 @@ export const TokenListItem = ({ {secondary} @@ -317,4 +377,8 @@ TokenListItem.propTypes = { * isNativeCurrency represents if this item is the native currency */ isNativeCurrency: PropTypes.bool, + /** + * isStakeable represents if this item is stakeable + */ + isStakeable: PropTypes.bool, }; diff --git a/ui/components/multichain/token-list-item/token-list-item.stories.js b/ui/components/multichain/token-list-item/token-list-item.stories.js index 0cf57ae7defc..473fd6e2affb 100644 --- a/ui/components/multichain/token-list-item/token-list-item.stories.js +++ b/ui/components/multichain/token-list-item/token-list-item.stories.js @@ -64,6 +64,9 @@ DefaultStory.decorators = [ ), ]; +DefaultStory.args = { + isStakeable: true, +}; export const ChaosStory = (args) => (
{ + beforeAll(() => { + global.platform = { openTab: jest.fn() }; + openTabSpy = jest.spyOn(global.platform, 'openTab'); + }); const props = { onClick: jest.fn(), }; @@ -59,4 +65,28 @@ describe('TokenListItem', () => { expect(props.onClick).toHaveBeenCalled(); }); + + it('handles clicking staking opens tab', async () => { + const store = configureMockStore()(state); + const { queryByTestId } = renderWithProvider( + , + store, + ); + + const stakeButton = queryByTestId( + `staking-entrypoint-${CHAIN_IDS.MAINNET}`, + ); + + expect(stakeButton).toBeInTheDocument(); + expect(stakeButton).not.toBeDisabled(); + + fireEvent.click(stakeButton); + expect(openTabSpy).toHaveBeenCalledTimes(1); + + await waitFor(() => + expect(openTabSpy).toHaveBeenCalledWith({ + url: expect.stringContaining('/stake?metamaskEntry=ext_stake_button'), + }), + ); + }); }); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index d7c244f3544d..1cea69491d61 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -95,6 +95,7 @@ import { NOTIFICATION_OPEN_BETA_SNAPS, NOTIFICATION_PETNAMES, NOTIFICATION_U2F_LEDGER_LIVE, + NOTIFICATION_STAKING_PORTFOLIO, } from '../../shared/notifications'; import { SURVEY_DATE, @@ -1277,6 +1278,7 @@ function getAllowedAnnouncementIds(state) { [NOTIFICATION_OPEN_BETA_SNAPS]: true, [NOTIFICATION_BUY_SELL_BUTTON]: true, [NOTIFICATION_U2F_LEDGER_LIVE]: currentKeyringIsLedger && !isFirefox, + [NOTIFICATION_STAKING_PORTFOLIO]: true, ///: BEGIN:ONLY_INCLUDE_IF(blockaid) [NOTIFICATION_BLOCKAID_DEFAULT]: true, ///: END:ONLY_INCLUDE_IF