From f526c170e28407356b1dbb3a7b9570805c4378e6 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Thu, 3 Aug 2023 13:41:16 -0230 Subject: [PATCH 01/23] v10.34.2 --- CHANGELOG.md | 9 ++++++++- package.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3b398cc8..989ee2e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.34.3] +### Fixed +- Ensure users phishing warning list is properly updated ([#20381](https://github.com/MetaMask/metamask-extension/pull/20381)) +- Fix inaccurate info in swaps flow for zero-balance tokens ([#20388](https://github.com/MetaMask/metamask-extension/pull/20388)) +- Fix 'Global Menu Explorer / Account Details' What's New notification display ([#20371](https://github.com/MetaMask/metamask-extension/pull/20371)) + ## [10.34.2] ### Added - Add Address Details and View on Explorer to Global Menu ([#20013](https://github.com/MetaMask/metamask-extension/pull/20013)) @@ -3875,7 +3881,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.3...HEAD +[10.34.3]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...v10.34.3 [10.34.2]: https://github.com/MetaMask/metamask-extension/compare/v10.34.1...v10.34.2 [10.34.1]: https://github.com/MetaMask/metamask-extension/compare/v10.34.0...v10.34.1 [10.34.0]: https://github.com/MetaMask/metamask-extension/compare/v10.33.1...v10.34.0 diff --git a/package.json b/package.json index 93c082075..280eb8e0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "10.34.2", + "version": "10.34.3", "private": true, "repository": { "type": "git", From 75dcb03069d607e6f99cf7d72ed587e6baf12cb4 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 3 Aug 2023 13:12:21 -0230 Subject: [PATCH 02/23] Force an update of the phishing warning configuration (#20381) The "last fetched" state for the `PhishingController` has been deleted to force an immediate full update of the phishing configuration state. We're doing this because the state was cleared in v10.34.2 because the format of that state had changed. This has been implemented in migration 92. The previous migration 92 has been renamed to 93 because it won't be included until a future release. We need the migrations to remain sequential, and this will save us from having to resolve a complex conflict when releasing this. --- app/scripts/migrations/092.test.ts | 78 ++++++++++++++++++++++++++++++ app/scripts/migrations/092.ts | 35 ++++++++++++++ app/scripts/migrations/index.js | 2 + 3 files changed, 115 insertions(+) create mode 100644 app/scripts/migrations/092.test.ts create mode 100644 app/scripts/migrations/092.ts diff --git a/app/scripts/migrations/092.test.ts b/app/scripts/migrations/092.test.ts new file mode 100644 index 000000000..b44c04602 --- /dev/null +++ b/app/scripts/migrations/092.test.ts @@ -0,0 +1,78 @@ +import { cloneDeep } from 'lodash'; +import { migrate, version } from './092'; + +const PREVIOUS_VERSION = version - 1; + +describe('migration #92', () => { + it('should update the version metadata', async () => { + const oldStorage = { + meta: { + version: PREVIOUS_VERSION, + }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ + version, + }); + }); + + it('should return state unaltered if there is no phishing controller state', async () => { + const oldData = { + other: 'data', + }; + const oldStorage = { + meta: { + version: PREVIOUS_VERSION, + }, + data: oldData, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + expect(newStorage.data).toStrictEqual(oldData); + }); + + it('should return state unaltered if there is no phishing controller last fetched state', async () => { + const oldData = { + other: 'data', + PhishingController: { + whitelist: [], + }, + }; + const oldStorage = { + meta: { + version: PREVIOUS_VERSION, + }, + data: oldData, + }; + + const newStorage = await migrate(cloneDeep(oldStorage)); + expect(newStorage.data).toStrictEqual(oldData); + }); + + it('should remove both last fetched properties from phishing controller state', async () => { + const oldData = { + other: 'data', + PhishingController: { + whitelist: [], + hotlistLastFetched: 0, + stalelistLastFetched: 0, + }, + }; + const oldStorage = { + meta: { + version: PREVIOUS_VERSION, + }, + data: oldData, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + other: 'data', + PhishingController: { + whitelist: [], + }, + }); + }); +}); diff --git a/app/scripts/migrations/092.ts b/app/scripts/migrations/092.ts new file mode 100644 index 000000000..bf5469614 --- /dev/null +++ b/app/scripts/migrations/092.ts @@ -0,0 +1,35 @@ +import { cloneDeep } from 'lodash'; +import { hasProperty, isObject } from '@metamask/utils'; + +export const version = 92; + +/** + * Delete `stalelistLastFetched` and `hotlistLastFetched` to force a phishing configuration refresh + * because the format has changed. + * + * @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: { + meta: { version: number }; + data: Record; +}) { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + versionedData.data = transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + if ( + hasProperty(state, 'PhishingController') && + isObject(state.PhishingController) + ) { + delete state.PhishingController.stalelistLastFetched; + delete state.PhishingController.hotlistLastFetched; + } + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index a37648e78..dc4fcd4e0 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -95,6 +95,7 @@ import * as m088 from './088'; import * as m089 from './089'; import * as m090 from './090'; import * as m091 from './091'; +import * as m092 from './092'; const migrations = [ m002, @@ -187,6 +188,7 @@ const migrations = [ m089, m090, m091, + m092, ]; export default migrations; From eda24aab4fad9ae20bbddb02f00d68b5a0c26fb2 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Thu, 3 Aug 2023 09:16:01 -0500 Subject: [PATCH 03/23] Fix 'Global Menu Explorer / Account Details' What's New (#20371) --- shared/notifications/index.js | 1 - ui/components/app/whats-new-popup/whats-new-popup.js | 4 ++++ ui/selectors/selectors.js | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/shared/notifications/index.js b/shared/notifications/index.js index bc74e3222..f5d496b8b 100644 --- a/shared/notifications/index.js +++ b/shared/notifications/index.js @@ -119,7 +119,6 @@ export const UI_NOTIFICATIONS = { date: null, image: { src: 'images/global-menu-block-explorer.svg', - width: '100%', }, }, }; diff --git a/ui/components/app/whats-new-popup/whats-new-popup.js b/ui/components/app/whats-new-popup/whats-new-popup.js index 5743cc841..ba9675113 100644 --- a/ui/components/app/whats-new-popup/whats-new-popup.js +++ b/ui/components/app/whats-new-popup/whats-new-popup.js @@ -100,6 +100,9 @@ function getActionFunctionById(id, history) { updateViewedNotifications({ 21: true }); history.push(PREPARE_SWAP_ROUTE); }, + 22: () => { + updateViewedNotifications({ 22: true }); + }, }; return actionFunctions[id]; @@ -360,6 +363,7 @@ export default function WhatsNewPopup({ 18: renderFirstNotification, 19: renderFirstNotification, 21: renderFirstNotification, + 22: renderFirstNotification, }; return ( diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index a07c73788..2559c5340 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -995,6 +995,7 @@ function getAllowedAnnouncementIds(state) { 19: false, 20: currentKeyringIsLedger && isFirefox, 21: isSwapsChain, + 22: true, }; } From 87b3ac62f19e9a33e77feecdbe563a2140ee7f00 Mon Sep 17 00:00:00 2001 From: Daniel <80175477+dan437@users.noreply.github.com> Date: Thu, 3 Aug 2023 22:49:50 +0200 Subject: [PATCH 04/23] Improvements to Swaps quote auto-selection logic, fix and edge case with zero-balance tokens (#20388) * Add Token To into assets again (reverting commit 51f46eb65f48bdf4980f400a589bf1ac63a65222 ) * Update cleanup for an unswapped Token To from the Tokens list * Call "setLatestAddedTokenTo" conditionally * Update an E2E test for insufficient balance notification --- test/e2e/swaps/swaps-notifications.spec.js | 2 +- ui/ducks/swaps/swaps.js | 71 ++++++++++--------- ui/pages/swaps/build-quote/build-quote.js | 14 +++- .../swaps/build-quote/build-quote.test.js | 1 + ui/pages/swaps/index.js | 32 +++++++++ .../prepare-swap-page/prepare-swap-page.js | 14 +++- .../prepare-swap-page.test.js | 1 + 7 files changed, 99 insertions(+), 36 deletions(-) diff --git a/test/e2e/swaps/swaps-notifications.spec.js b/test/e2e/swaps/swaps-notifications.spec.js index 4d24bf5fe..42f17d5ec 100644 --- a/test/e2e/swaps/swaps-notifications.spec.js +++ b/test/e2e/swaps/swaps-notifications.spec.js @@ -111,7 +111,7 @@ describe('Swaps - notifications', function () { }); await checkNotification(driver, { title: 'Insufficient balance', - text: 'You need 50 more TESTETH to complete this swap', + text: 'You need 43.4467 more TESTETH to complete this swap', }); await reviewQuote(driver, { swapFrom: 'TESTETH', diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 1c3501cb2..e44fe44fb 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -121,6 +121,7 @@ const initialState = { currentSmartTransactionsError: '', swapsSTXLoading: false, transactionSettingsOpened: false, + latestAddedTokenTo: '', }; const slice = createSlice({ @@ -150,6 +151,9 @@ const slice = createSlice({ setFetchingQuotes: (state, action) => { state.fetchingQuotes = action.payload; }, + setLatestAddedTokenTo: (state, action) => { + state.latestAddedTokenTo = action.payload; + }, setFromToken: (state, action) => { state.fromToken = action.payload; }, @@ -245,6 +249,8 @@ export const getToToken = (state) => state.swaps.toToken; export const getFetchingQuotes = (state) => state.swaps.fetchingQuotes; +export const getLatestAddedTokenTo = (state) => state.swaps.latestAddedTokenTo; + export const getQuotesFetchStartTime = (state) => state.swaps.quotesFetchStartTime; @@ -479,6 +485,7 @@ const { setAggregatorMetadata, setBalanceError, setFetchingQuotes, + setLatestAddedTokenTo, setFromToken, setFromTokenError, setFromTokenInputValue, @@ -502,6 +509,7 @@ export { setAggregatorMetadata, setBalanceError, setFetchingQuotes, + setLatestAddedTokenTo, setFromToken as setSwapsFromToken, setFromTokenError, setFromTokenInputValue, @@ -665,7 +673,12 @@ export const fetchQuotesAndSetQuoteState = ( iconUrl: fromTokenIconUrl, balance: fromTokenBalance, } = selectedFromToken; - const { address: toTokenAddress, symbol: toTokenSymbol } = selectedToToken; + const { + address: toTokenAddress, + symbol: toTokenSymbol, + decimals: toTokenDecimals, + iconUrl: toTokenIconUrl, + } = selectedToToken; // pageRedirectionDisabled is true if quotes prefetching is active (a user is on the Build Quote page). // In that case we just want to silently prefetch quotes without redirecting to the quotes loading page. if (!pageRedirectionDisabled) { @@ -676,6 +689,30 @@ export const fetchQuotesAndSetQuoteState = ( const contractExchangeRates = getTokenExchangeRates(state); + if ( + toTokenAddress && + toTokenSymbol !== swapsDefaultToken.symbol && + contractExchangeRates[toTokenAddress] === undefined && + !isTokenAlreadyAdded(toTokenAddress, getTokens(state)) + ) { + await dispatch( + addToken( + toTokenAddress, + toTokenSymbol, + toTokenDecimals, + toTokenIconUrl, + true, + ), + ); + await dispatch(setLatestAddedTokenTo(toTokenAddress)); + } else { + const latestAddedTokenTo = getLatestAddedTokenTo(state); + // Only reset the latest added Token To if it's a different token. + if (latestAddedTokenTo !== toTokenAddress) { + await dispatch(setLatestAddedTokenTo('')); + } + } + if ( fromTokenAddress && fromTokenSymbol !== swapsDefaultToken.symbol && @@ -831,36 +868,6 @@ export const fetchQuotesAndSetQuoteState = ( }; }; -const addTokenTo = (dispatch, state) => { - const fetchParams = getFetchParams(state); - const swapsDefaultToken = getSwapsDefaultToken(state); - const contractExchangeRates = getTokenExchangeRates(state); - const selectedToToken = - getToToken(state) || fetchParams?.metaData?.destinationTokenInfo || {}; - const { - address: toTokenAddress, - symbol: toTokenSymbol, - decimals: toTokenDecimals, - iconUrl: toTokenIconUrl, - } = selectedToToken; - if ( - toTokenAddress && - toTokenSymbol !== swapsDefaultToken.symbol && - contractExchangeRates[toTokenAddress] === undefined && - !isTokenAlreadyAdded(toTokenAddress, getTokens(state)) - ) { - dispatch( - addToken( - toTokenAddress, - toTokenSymbol, - toTokenDecimals, - toTokenIconUrl, - true, - ), - ); - } -}; - export const signAndSendSwapsSmartTransaction = ({ unsignedTransaction, trackEvent, @@ -960,7 +967,6 @@ export const signAndSendSwapsSmartTransaction = ({ dispatch(setCurrentSmartTransactionsError(StxErrorTypes.unavailable)); return; } - addTokenTo(dispatch, state); if (approveTxParams) { updatedApproveTxParams.gas = `0x${decimalToHex( fees.approvalTxFees?.gasLimit || 0, @@ -1204,7 +1210,6 @@ export const signAndSendTransactions = ( history.push(AWAITING_SIGNATURES_ROUTE); } - addTokenTo(dispatch, state); if (approveTxParams) { if (networkAndAccountSupports1559) { approveTxParams.maxFeePerGas = maxFeePerGas; diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js index 7c2836703..9ed74343c 100644 --- a/ui/pages/swaps/build-quote/build-quote.js +++ b/ui/pages/swaps/build-quote/build-quote.js @@ -48,6 +48,7 @@ import { getIsFeatureFlagLoaded, getCurrentSmartTransactionsError, getSmartTransactionFees, + getLatestAddedTokenTo, } from '../../../ducks/swaps/swaps'; import { getSwapsDefaultToken, @@ -84,6 +85,7 @@ import { import { resetSwapsPostFetchState, + ignoreTokens, setBackgroundSwapRouteState, clearSwapsQuotes, stopPollingForQuotes, @@ -144,6 +146,7 @@ export default function BuildQuote({ const tokenList = useSelector(getTokenList, isEqual); const quotes = useSelector(getQuotes, isEqual); const areQuotesPresent = Object.keys(quotes).length > 0; + const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual); const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual); const conversionRate = useSelector(getConversionRate); @@ -347,12 +350,21 @@ export default function BuildQuote({ ? getURLHostName(blockExplorerTokenLink) : t('etherscan'); + const { address: toAddress } = toToken || {}; const onToSelect = useCallback( (token) => { + if (latestAddedTokenTo && token.address !== toAddress) { + dispatch( + ignoreTokens({ + tokensToIgnore: toAddress, + dontShowLoadingIndicator: true, + }), + ); + } dispatch(setSwapToToken(token)); setVerificationClicked(false); }, - [dispatch], + [dispatch, latestAddedTokenTo, toAddress], ); const hideDropdownItemIf = useCallback( diff --git a/ui/pages/swaps/build-quote/build-quote.test.js b/ui/pages/swaps/build-quote/build-quote.test.js index e09ec1149..ea8c2cc70 100644 --- a/ui/pages/swaps/build-quote/build-quote.test.js +++ b/ui/pages/swaps/build-quote/build-quote.test.js @@ -29,6 +29,7 @@ const createProps = (customProps = {}) => { setBackgroundConnection({ resetPostFetchState: jest.fn(), + ignoreTokens: jest.fn(), setBackgroundSwapRouteState: jest.fn(), clearSwapsQuotes: jest.fn(), stopPollingForQuotes: jest.fn(), diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index 351561f0b..1a37271b4 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -50,6 +50,7 @@ import { navigateBackToBuildQuote, getSwapRedesignEnabled, setTransactionSettingsOpened, + getLatestAddedTokenTo, } from '../../ducks/swaps/swaps'; import { checkNetworkAndAccountSupports1559, @@ -79,6 +80,7 @@ import { import { resetBackgroundSwapsState, setSwapsTokens, + ignoreTokens, setBackgroundSwapRouteState, setSwapsErrorKey, } from '../../store/actions'; @@ -134,6 +136,7 @@ export default function Swap() { const routeState = useSelector(getBackgroundSwapRouteState); const selectedAccount = useSelector(getSelectedAccount, shallowEqual); const quotes = useSelector(getQuotes, isEqual); + const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual); const txList = useSelector(currentNetworkTxListSelector, shallowEqual); const tradeTxId = useSelector(getTradeTxId); const approveTxId = useSelector(getApproveTxId); @@ -209,6 +212,32 @@ export default function Swap() { swapsErrorKey = SWAP_FAILED_ERROR; } + const clearTemporaryTokenRef = useRef(); + useEffect(() => { + clearTemporaryTokenRef.current = () => { + if (latestAddedTokenTo && (!isAwaitingSwapRoute || conversionError)) { + dispatch( + ignoreTokens({ + tokensToIgnore: latestAddedTokenTo, + dontShowLoadingIndicator: true, + }), + ); + } + }; + }, [ + conversionError, + dispatch, + latestAddedTokenTo, + destinationTokenInfo, + fetchParams, + isAwaitingSwapRoute, + ]); + useEffect(() => { + return () => { + clearTemporaryTokenRef.current(); + }; + }, []); + // eslint-disable-next-line useEffect(() => { if (!isSwapsChain) { @@ -283,6 +312,7 @@ export default function Swap() { const beforeUnloadEventAddedRef = useRef(); useEffect(() => { const fn = () => { + clearTemporaryTokenRef.current(); if (isLoadingQuotesRoute) { dispatch(prepareToLeaveSwaps()); } @@ -349,6 +379,7 @@ export default function Swap() { } const redirectToDefaultRoute = async () => { + clearTemporaryTokenRef.current(); dispatch(clearSwapsState()); await dispatch(resetBackgroundSwapsState()); history.push(DEFAULT_ROUTE); @@ -400,6 +431,7 @@ export default function Swap() {
{ + clearTemporaryTokenRef.current(); dispatch(clearSwapsState()); await dispatch(resetBackgroundSwapsState()); history.push(DEFAULT_ROUTE); 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 e1f0c5f0e..de7027c4c 100644 --- a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js +++ b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.js @@ -54,6 +54,7 @@ import { getAggregatorMetadata, getTransactionSettingsOpened, setTransactionSettingsOpened, + getLatestAddedTokenTo, } from '../../../ducks/swaps/swaps'; import { getSwapsDefaultToken, @@ -92,6 +93,7 @@ import { } from '../../../../shared/constants/swaps'; import { resetSwapsPostFetchState, + ignoreTokens, clearSwapsQuotes, stopPollingForQuotes, setSmartTransactionsOptInStatus, @@ -182,6 +184,7 @@ export default function PrepareSwapPage({ const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider, shallowEqual); const tokenList = useSelector(getTokenList, isEqual); const quotes = useSelector(getQuotes, isEqual); + const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual); const numberOfQuotes = Object.keys(quotes).length; const areQuotesPresent = numberOfQuotes > 0; const swapsErrorKey = useSelector(getSwapsErrorKey); @@ -449,12 +452,21 @@ export default function PrepareSwapPage({ ? getURLHostName(blockExplorerTokenLink) : t('etherscan'); + const { address: toAddress } = toToken || {}; const onToSelect = useCallback( (token) => { + if (latestAddedTokenTo && token.address !== toAddress) { + dispatch( + ignoreTokens({ + tokensToIgnore: toAddress, + dontShowLoadingIndicator: true, + }), + ); + } dispatch(setSwapToToken(token)); setVerificationClicked(false); }, - [dispatch], + [dispatch, latestAddedTokenTo, toAddress], ); const tokensWithBalancesFromToken = tokensWithBalances.find((token) => diff --git a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.test.js b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.test.js index 61255621f..9f0a499f7 100644 --- a/ui/pages/swaps/prepare-swap-page/prepare-swap-page.test.js +++ b/ui/pages/swaps/prepare-swap-page/prepare-swap-page.test.js @@ -27,6 +27,7 @@ const createProps = (customProps = {}) => { setBackgroundConnection({ resetPostFetchState: jest.fn(), + ignoreTokens: jest.fn(), setBackgroundSwapRouteState: jest.fn(), clearSwapsQuotes: jest.fn(), stopPollingForQuotes: jest.fn(), From 15bf697538d59714933eaa85865b311c5b839e4e Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Sun, 6 Aug 2023 20:42:30 -0230 Subject: [PATCH 05/23] Version v10.34.4 --- CHANGELOG.md | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 989ee2e8f..b2b42df2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.34.4] + ## [10.34.3] ### Fixed - Ensure users phishing warning list is properly updated ([#20381](https://github.com/MetaMask/metamask-extension/pull/20381)) @@ -3881,7 +3883,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Uncategorized - Added the ability to restore accounts from seed words. -[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.3...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.34.4...HEAD +[10.34.4]: https://github.com/MetaMask/metamask-extension/compare/v10.34.3...v10.34.4 [10.34.3]: https://github.com/MetaMask/metamask-extension/compare/v10.34.2...v10.34.3 [10.34.2]: https://github.com/MetaMask/metamask-extension/compare/v10.34.1...v10.34.2 [10.34.1]: https://github.com/MetaMask/metamask-extension/compare/v10.34.0...v10.34.1 diff --git a/package.json b/package.json index 280eb8e0d..297c63612 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "10.34.3", + "version": "10.34.4", "private": true, "repository": { "type": "git", From 9fdf2f0076a7fb22ea8153b77d81eba0d8f9da82 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Mon, 7 Aug 2023 13:46:05 +0200 Subject: [PATCH 06/23] [FLASK] `snaps@0.38.1-flask.1` (#20420) --- builds.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builds.yml b/builds.yml index 019da9378..26101ddac 100644 --- a/builds.yml +++ b/builds.yml @@ -52,7 +52,7 @@ buildTypes: - SEGMENT_FLASK_WRITE_KEY - ALLOW_LOCAL_SNAPS: true - REQUIRE_SNAPS_ALLOWLIST: false - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.35.2-flask.1/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.38.1-flask.1/index.html - SUPPORT_LINK: https://metamask-flask.zendesk.com/hc - SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new - INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID @@ -71,7 +71,7 @@ buildTypes: - SEGMENT_FLASK_WRITE_KEY - ALLOW_LOCAL_SNAPS: true - REQUIRE_SNAPS_ALLOWLIST: false - - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.35.2-flask.1/index.html + - IFRAME_EXECUTION_ENVIRONMENT_URL: https://execution.metamask.io/0.38.1-flask.1/index.html - SUPPORT_LINK: https://metamask-flask.zendesk.com/hc - SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new - INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID From ce0fd8069e60b86ace8565b8302e605e945fc4b0 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Mon, 7 Aug 2023 09:34:56 -0230 Subject: [PATCH 07/23] Update changelog for v10.34.4 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b42df2f..d50937b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## [10.34.4] +## Changed +- Updated snaps execution environment ([#20420](https://github.com/MetaMask/metamask-extension/pull/20420)) ## [10.34.3] ### Fixed From 0c8bd0ce4a0f4b23a0b212e8130c51b5c93a2999 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Mon, 7 Aug 2023 09:38:47 -0230 Subject: [PATCH 08/23] Update CHANGELOG.md Co-authored-by: Frederik Bolding --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d50937b4c..583701a69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## [10.34.4] -## Changed +### Changed - Updated snaps execution environment ([#20420](https://github.com/MetaMask/metamask-extension/pull/20420)) ## [10.34.3] From ae76364c174fe2edcd0608a56ec9d1764f1f1f45 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 14 Aug 2023 08:27:12 -0500 Subject: [PATCH 09/23] Create What's New storybook item (#20392) * Create What's New storybook item * Remove unnecessary button type attribute * Remove unneccessary provider from story --- .storybook/test-data.js | 9 +++++++++ .../app/whats-new-popup/whats-new-popup.js | 13 +++++++------ .../whats-new-popup/whats-new-popup.stories.js | 16 ++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 ui/components/app/whats-new-popup/whats-new-popup.stories.js diff --git a/.storybook/test-data.js b/.storybook/test-data.js index a8bf65c9b..4b60d17c9 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -19,6 +19,15 @@ const state = { url: 'https://metamask.github.io/test-dapp/', }, metamask: { + announcements: { + 22: { + id: 22, + date: null, + image: { + src: 'images/global-menu-block-explorer.svg', + }, + } + }, tokenList: { '0x514910771af9ca656af840dff83e8264ecf986ca': { address: '0x514910771af9ca656af840dff83e8264ecf986ca', diff --git a/ui/components/app/whats-new-popup/whats-new-popup.js b/ui/components/app/whats-new-popup/whats-new-popup.js index 7b1b0cb4f..ad9786a9c 100644 --- a/ui/components/app/whats-new-popup/whats-new-popup.js +++ b/ui/components/app/whats-new-popup/whats-new-popup.js @@ -10,7 +10,7 @@ import { useEqualityCheck } from '../../../hooks/useEqualityCheck'; import Popover from '../../ui/popover'; import { Text, - Button, + ButtonPrimary, ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) IconName, ///: END:ONLY_INCLUDE_IN @@ -202,8 +202,7 @@ const renderFirstNotification = ({
{placeImageBelowDescription && imageComponent} {actionText && ( - + )} { ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) customButton && customButton.name === 'mmi-portfolio' && ( - + ) ///: END:ONLY_INCLUDE_IN } diff --git a/ui/components/app/whats-new-popup/whats-new-popup.stories.js b/ui/components/app/whats-new-popup/whats-new-popup.stories.js new file mode 100644 index 000000000..01760b891 --- /dev/null +++ b/ui/components/app/whats-new-popup/whats-new-popup.stories.js @@ -0,0 +1,16 @@ +import React from 'react'; +import WhatsNewPopup from '.'; + +export default { + title: 'Components/Multichain/WhatsNewPopup', + component: WhatsNewPopup, + argTypes: { + onClose: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; From f69180c174aba91746c95a18f0a0b9914fdd42c2 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 14 Aug 2023 09:52:52 -0500 Subject: [PATCH 10/23] UX: Remove unwanted spacing from last NFT collection (#20442) --- ui/components/app/nfts-items/index.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/components/app/nfts-items/index.scss b/ui/components/app/nfts-items/index.scss index 225cd2149..f8d7839fb 100644 --- a/ui/components/app/nfts-items/index.scss +++ b/ui/components/app/nfts-items/index.scss @@ -2,6 +2,10 @@ &__collection { margin-bottom: 24px; + &:last-child { + margin-bottom: 0; + } + &-accordion-title { cursor: pointer; } From d28f699c573ad1148a9e90ec7333702ea495e86e Mon Sep 17 00:00:00 2001 From: Danyal Prout Date: Mon, 14 Aug 2023 08:22:02 -0700 Subject: [PATCH 11/23] Specify that Base is a multilayer network (#20097) --- shared/constants/network.ts | 4 ++++ ui/selectors/selectors.js | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/shared/constants/network.ts b/shared/constants/network.ts index d9f0fb99e..18910c207 100644 --- a/shared/constants/network.ts +++ b/shared/constants/network.ts @@ -138,6 +138,8 @@ export const CHAIN_IDS = { BSC_TESTNET: '0x61', OPTIMISM: '0xa', OPTIMISM_TESTNET: '0x1a4', + BASE: '0x2105', + BASE_TESTNET: '0x14a33', POLYGON: '0x89', POLYGON_TESTNET: '0x13881', AVALANCHE: '0xa86a', @@ -553,6 +555,8 @@ export const BUYABLE_CHAINS_MAP: { ChainId, | typeof CHAIN_IDS.LOCALHOST | typeof CHAIN_IDS.OPTIMISM_TESTNET + | typeof CHAIN_IDS.BASE_TESTNET + | typeof CHAIN_IDS.BASE | typeof CHAIN_IDS.BSC_TESTNET | typeof CHAIN_IDS.POLYGON_TESTNET | typeof CHAIN_IDS.AVALANCHE_TESTNET diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 0f05d28b6..9b0632343 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -1307,11 +1307,22 @@ export function getIsOptimism(state) { ); } +export function getIsBase(state) { + return ( + getCurrentChainId(state) === CHAIN_IDS.BASE || + getCurrentChainId(state) === CHAIN_IDS.BASE_TESTNET + ); +} + +export function getIsOpStack(state) { + return getIsOptimism(state) || getIsBase(state); +} + export function getIsMultiLayerFeeNetwork(state) { - return getIsOptimism(state); + return getIsOpStack(state); } /** - * To retrieve the maxBaseFee and priotitFee teh user has set as default + * To retrieve the maxBaseFee and priorityFee the user has set as default * * @param {*} state * @returns Boolean From d6eecf8584c44aca0ff16acf750a512c0fa5a961 Mon Sep 17 00:00:00 2001 From: Michele Esposito <34438276+mikesposito@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:23:28 +0200 Subject: [PATCH 12/23] Use `createNewVaultAndRestore` from core `KeyringController` (#19816) * refactor: use createNewVaultAndRestore from core kc * test: use createNewVaultAndRestore from core * refactor: apply @mcmire suggestion * fix: mnemonic conversion --- app/scripts/metamask-controller.js | 38 ++++++++++++++----------- app/scripts/metamask-controller.test.js | 4 +-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 93777335a..fa120200f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -89,6 +89,7 @@ import { ERC20, ERC721, } from '@metamask/controller-utils'; +import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; @@ -2905,8 +2906,6 @@ export default class MetamaskController extends EventEmitter { const seedPhraseAsBuffer = Buffer.from(encodedSeedPhrase); - const { keyringController } = this; - // clear known identities this.preferencesController.setAddresses([]); @@ -2930,29 +2929,22 @@ export default class MetamaskController extends EventEmitter { this.txController.txStateManager.clearUnapprovedTxs(); // create new vault - const vault = await keyringController.createNewVaultAndRestore( + const vault = await this.coreKeyringController.createNewVaultAndRestore( password, - seedPhraseAsBuffer, + this._convertMnemonicToWordlistIndices(seedPhraseAsBuffer), ); const ethQuery = new EthQuery(this.provider); - accounts = await keyringController.getAccounts(); + accounts = await this.coreKeyringController.getAccounts(); lastBalance = await this.getBalance( accounts[accounts.length - 1], ethQuery, ); - const [primaryKeyring] = this.coreKeyringController.getKeyringsByType( - KeyringType.hdKeyTree, - ); - if (!primaryKeyring) { - throw new Error('MetamaskController - No HD Key Tree found'); - } - // seek out the first zero balance while (lastBalance !== '0x0') { - await keyringController.addNewAccount(primaryKeyring); - accounts = await keyringController.getAccounts(); + await this.coreKeyringController.addNewAccount(accounts.length); + accounts = await this.coreKeyringController.getAccounts(); lastBalance = await this.getBalance( accounts[accounts.length - 1], ethQuery, @@ -2962,7 +2954,7 @@ export default class MetamaskController extends EventEmitter { // remove extra zero balance account potentially created from seeking ahead if (accounts.length > 1 && lastBalance === '0x0') { await this.removeAccount(accounts[accounts.length - 1]); - accounts = await keyringController.getAccounts(); + accounts = await this.coreKeyringController.getAccounts(); } // This must be set as soon as possible to communicate to the @@ -2973,8 +2965,6 @@ export default class MetamaskController extends EventEmitter { this.preferencesController.getLedgerTransportPreference(); this.setLedgerTransportPreference(transportPreference); - // set new identities - this.preferencesController.setAddresses(accounts); this.selectFirstIdentity(); return vault; @@ -2983,6 +2973,20 @@ export default class MetamaskController extends EventEmitter { } } + /** + * Encodes a BIP-39 mnemonic as the indices of words in the English BIP-39 wordlist. + * + * @param {Buffer} mnemonic - The BIP-39 mnemonic. + * @returns {Buffer} The Unicode code points for the seed phrase formed from the words in the wordlist. + */ + _convertMnemonicToWordlistIndices(mnemonic) { + const indices = mnemonic + .toString() + .split(' ') + .map((word) => wordlist.indexOf(word)); + return new Uint8Array(new Uint16Array(indices).buffer); + } + /** * Get an account balance from the AccountTracker or request it directly from the network. * diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index b4fadf9d4..b11e34372 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -307,7 +307,7 @@ describe('MetaMaskController', function () { 'createNewVaultAndKeychain', ); sandbox.spy( - metamaskController.keyringController, + metamaskController.coreKeyringController, 'createNewVaultAndRestore', ); }); @@ -423,7 +423,7 @@ describe('MetaMaskController', function () { await metamaskController.createNewVaultAndRestore(password, TEST_SEED); assert( - metamaskController.keyringController.createNewVaultAndRestore + metamaskController.coreKeyringController.createNewVaultAndRestore .calledTwice, ); }); From ee4bf2d2641b7d2d0732f0e16b891f4c3ae52d44 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 14 Aug 2023 11:08:59 -0500 Subject: [PATCH 13/23] Fix #19829: UX: Multichain: Move "Import Tokens" to Modal (#19553) * Move Import Tokens to Modal * Better dimensions for long token name * Add padding above tabs --- app/_locales/am/messages.json | 3 - app/_locales/ar/messages.json | 3 - app/_locales/bg/messages.json | 3 - app/_locales/bn/messages.json | 3 - app/_locales/ca/messages.json | 3 - app/_locales/cs/messages.json | 3 - app/_locales/da/messages.json | 3 - app/_locales/de/messages.json | 6 - app/_locales/el/messages.json | 6 - app/_locales/en/messages.json | 6 - app/_locales/es/messages.json | 6 - app/_locales/es_419/messages.json | 6 - app/_locales/et/messages.json | 3 - app/_locales/fa/messages.json | 3 - app/_locales/fi/messages.json | 3 - app/_locales/fil/messages.json | 3 - app/_locales/fr/messages.json | 6 - app/_locales/he/messages.json | 3 - app/_locales/hi/messages.json | 6 - app/_locales/hr/messages.json | 3 - app/_locales/ht/messages.json | 3 - app/_locales/hu/messages.json | 3 - app/_locales/id/messages.json | 6 - app/_locales/it/messages.json | 6 - app/_locales/ja/messages.json | 6 - app/_locales/kn/messages.json | 3 - app/_locales/ko/messages.json | 6 - app/_locales/lt/messages.json | 3 - app/_locales/lv/messages.json | 3 - app/_locales/ms/messages.json | 3 - app/_locales/no/messages.json | 3 - app/_locales/ph/messages.json | 3 - app/_locales/pl/messages.json | 3 - app/_locales/pt/messages.json | 6 - app/_locales/pt_BR/messages.json | 6 - app/_locales/ro/messages.json | 3 - app/_locales/ru/messages.json | 6 - app/_locales/sk/messages.json | 3 - app/_locales/sl/messages.json | 3 - app/_locales/sr/messages.json | 3 - app/_locales/sv/messages.json | 3 - app/_locales/sw/messages.json | 3 - app/_locales/ta/messages.json | 3 - app/_locales/tl/messages.json | 6 - app/_locales/tr/messages.json | 6 - app/_locales/uk/messages.json | 3 - app/_locales/vi/messages.json | 6 - app/_locales/zh_CN/messages.json | 6 - app/_locales/zh_TW/messages.json | 6 - test/e2e/metamask-ui.spec.js | 11 +- test/e2e/tests/add-hide-token.spec.js | 6 +- .../tests/custom-token-add-approve.spec.js | 18 +- test/e2e/tests/import-tokens.spec.js | 12 +- test/e2e/tests/token-details.spec.js | 18 +- ui/components/app/app-components.scss | 1 + ui/components/app/import-token/index.scss | 1 + .../app}/import-token/token-list/index.js | 0 .../app}/import-token/token-list/index.scss | 2 - .../token-list-placeholder/index.js | 0 .../token-list-placeholder.component.js | 35 + .../token-list-placeholder.stories.js | 2 +- .../token-list/token-list.component.js | 2 +- .../token-list/token-list.container.js | 0 .../app}/import-token/token-search/index.js | 0 .../token-search/token-search.component.js | 26 +- .../token-search/token-search.stories.js | 4 +- .../import-token-link/import-token-link.js | 7 +- .../import-token-link.test.js | 9 +- .../import-tokens-modal-confirm.js | 108 +++ .../import-tokens-modal-confirm.stories.js | 100 +++ .../import-tokens-modal.js | 649 +++++++++++++++++ .../import-tokens-modal.scss | 67 ++ .../import-tokens-modal.stories.js | 62 ++ .../import-tokens-modal.test.js | 200 ++++++ .../multichain/import-tokens-modal/index.js | 1 + ui/components/multichain/index.js | 1 + .../multichain/multichain-components.scss | 1 + .../page-container.component.js | 16 +- ui/components/ui/popover/index.scss | 4 + ui/ducks/app/app.ts | 14 + .../confirm-import-token.js | 150 ---- .../confirm-import-token.stories.js | 46 -- .../confirm-import-token.test.js | 128 ---- ui/pages/confirm-import-token/index.js | 3 - ui/pages/confirm-import-token/index.scss | 50 -- ui/pages/import-token/README.mdx | 31 - .../import-token/import-token.component.js | 659 ------------------ .../import-token/import-token.container.js | 68 -- ui/pages/import-token/import-token.stories.js | 120 ---- ui/pages/import-token/import-token.test.js | 178 ----- ui/pages/import-token/index.js | 3 - ui/pages/import-token/index.scss | 79 --- .../token-list-placeholder/index.scss | 27 - .../token-list-placeholder.component.js | 31 - ui/pages/pages.scss | 2 - ui/pages/routes/routes.component.js | 24 +- ui/pages/routes/routes.container.js | 3 + .../__snapshots__/security-tab.test.js.snap | 1 + .../security-tab/security-tab.component.js | 1 + ui/store/actionConstants.ts | 2 + ui/store/actions.ts | 12 + 101 files changed, 1339 insertions(+), 1860 deletions(-) create mode 100644 ui/components/app/import-token/index.scss rename ui/{pages => components/app}/import-token/token-list/index.js (100%) rename ui/{pages => components/app}/import-token/token-list/index.scss (96%) rename ui/{pages => components/app}/import-token/token-list/token-list-placeholder/index.js (100%) create mode 100644 ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js rename ui/{pages => components/app}/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js (79%) rename ui/{pages => components/app}/import-token/token-list/token-list.component.js (96%) rename ui/{pages => components/app}/import-token/token-list/token-list.container.js (100%) rename ui/{pages => components/app}/import-token/token-search/index.js (100%) rename ui/{pages => components/app}/import-token/token-search/token-search.component.js (69%) rename ui/{pages => components/app}/import-token/token-search/token-search.stories.js (76%) create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal.js create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal.scss create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js create mode 100644 ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js create mode 100644 ui/components/multichain/import-tokens-modal/index.js delete mode 100644 ui/pages/confirm-import-token/confirm-import-token.js delete mode 100644 ui/pages/confirm-import-token/confirm-import-token.stories.js delete mode 100644 ui/pages/confirm-import-token/confirm-import-token.test.js delete mode 100644 ui/pages/confirm-import-token/index.js delete mode 100644 ui/pages/confirm-import-token/index.scss delete mode 100644 ui/pages/import-token/README.mdx delete mode 100644 ui/pages/import-token/import-token.component.js delete mode 100644 ui/pages/import-token/import-token.container.js delete mode 100644 ui/pages/import-token/import-token.stories.js delete mode 100644 ui/pages/import-token/import-token.test.js delete mode 100644 ui/pages/import-token/index.js delete mode 100644 ui/pages/import-token/index.scss delete mode 100644 ui/pages/import-token/token-list/token-list-placeholder/index.scss delete mode 100644 ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index cea1b3cc3..a815821b9 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -609,9 +609,6 @@ "searchResults": { "message": "ውጤቶችን ፈልግ" }, - "searchTokens": { - "message": "ተለዋጭ ስሞችን ፈልግ" - }, "securityAndPrivacy": { "message": "ደህንነት እና ግላዊነት" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index 1be520429..db4a5a6fe 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "نتائج البحث" }, - "searchTokens": { - "message": "البحث عن العملات الرمزية" - }, "securityAndPrivacy": { "message": "الأمن والخصوصية" }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index 3743fcbf0..5e41be222 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -620,9 +620,6 @@ "searchResults": { "message": "Резултати от търсенето" }, - "searchTokens": { - "message": "Търсене на маркери" - }, "securityAndPrivacy": { "message": "Сигурност и поверителност" }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index 941fb82da..dfa33cf7c 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -618,9 +618,6 @@ "searchResults": { "message": "অনুসন্ধানের ফলাফলগুলি" }, - "searchTokens": { - "message": "টোকেনগুলি অনুসন্ধান করুন" - }, "securityAndPrivacy": { "message": "নিরাপত্তা এবং গোপনীয়তা" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index d5abf0507..fd33ec6f3 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -605,9 +605,6 @@ "searchResults": { "message": "Resultats de Cerca" }, - "searchTokens": { - "message": "Tokens per cercar" - }, "securityAndPrivacy": { "message": "Seguretat i privacitat" }, diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json index 1a617a7b7..b0702badb 100644 --- a/app/_locales/cs/messages.json +++ b/app/_locales/cs/messages.json @@ -289,9 +289,6 @@ "search": { "message": "Hledat" }, - "searchTokens": { - "message": "Hledat tokeny" - }, "seedPhraseReq": { "message": "klíčové fráze mají 12 slov" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index 85f449875..fb5b26f48 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -605,9 +605,6 @@ "searchResults": { "message": "Søg Resultater" }, - "searchTokens": { - "message": "Søg efter tokens" - }, "securityAndPrivacy": { "message": "Sikkerhed & Privatliv" }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 9379779b6..01948c981 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Benutzerdefiniertes Netzwerk hinzufügen" }, - "addCustomToken": { - "message": "Kunden-Token hinzufügen" - }, "addEthereumChainConfirmationDescription": { "message": "Dadurch kann dieses Netzwerk innerhalb MetaMask verwendet werden." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Suchergebnisse" }, - "searchTokens": { - "message": "Token suchen" - }, "secretRecoveryPhrase": { "message": "Geheime Wiederherstellungsphrase" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 5875c30e2..691927f66 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Προσθήκη προσαρμοσμένου δικτύου" }, - "addCustomToken": { - "message": "Προσθήκη Προσαρμοσμένου Token" - }, "addEthereumChainConfirmationDescription": { "message": "Αυτό θα επιτρέψει σε αυτό το δίκτυο να χρησιμοποιηθεί στο MetaMask." }, @@ -2977,9 +2974,6 @@ "searchAccounts": { "message": "Αναζήτηση Λογαριασμών" }, - "searchTokens": { - "message": "Αναζήτηση Tokens" - }, "secretRecoveryPhrase": { "message": "Μυστική Φράση Ανάκτησης" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 18e28b7d9..a5073b434 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -192,9 +192,6 @@ "addCustomNetwork": { "message": "Add custom network" }, - "addCustomToken": { - "message": "Add custom token" - }, "addEthereumChainConfirmationDescription": { "message": "This will allow this network to be used within MetaMask." }, @@ -3653,9 +3650,6 @@ "searchResults": { "message": "Search results" }, - "searchTokens": { - "message": "Search tokens" - }, "secretRecoveryPhrase": { "message": "Secret Recovery Phrase" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index be5afad23..2f094077c 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Agregar red personalizada" }, - "addCustomToken": { - "message": "Añadir token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Esto permitirá que la red se utilice en MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Resultados de la búsqueda" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase secreta de recuperación" }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 6e209ec6b..1c3adff3a 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -100,9 +100,6 @@ "addContact": { "message": "Agregar contacto" }, - "addCustomToken": { - "message": "Añadir token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Esto permitirá que la red se utilice en MetaMask." }, @@ -1882,9 +1879,6 @@ "searchResults": { "message": "Resultados de la búsqueda" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase secreta de recuperación" }, diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index 073f73d75..e0b6889e9 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -614,9 +614,6 @@ "searchResults": { "message": "Otsingutulemused" }, - "searchTokens": { - "message": "Lubade otsimine" - }, "securityAndPrivacy": { "message": "Turvalisus ja privaatsus" }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index cf24c0c61..54db00df5 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "نتایج جستجو" }, - "searchTokens": { - "message": "رمزیاب های جستجو" - }, "securityAndPrivacy": { "message": "امنیت و حریم خصوصی" }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index 478620170..9bf17fcef 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "Hakutulokset" }, - "searchTokens": { - "message": "Hae tietueita" - }, "securityAndPrivacy": { "message": "Turva & yksityisyys" }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index b99f5a5ea..bb8270894 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -548,9 +548,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "securityAndPrivacy": { "message": "Seguridad at Privacy" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index 340f707c6..1e990f8d7 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Ajouter un réseau personnalisé" }, - "addCustomToken": { - "message": "Ajouter un jeton personnalisé" - }, "addEthereumChainConfirmationDescription": { "message": "Cela permettra d’utiliser ce réseau dans MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Résultats de la recherche" }, - "searchTokens": { - "message": "Rechercher des jetons" - }, "secretRecoveryPhrase": { "message": "Phrase secrète de récupération" }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index 15dd5adac..ab70fe6ee 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -621,9 +621,6 @@ "searchResults": { "message": "תוצאות חיפוש" }, - "searchTokens": { - "message": "חיפוש טוקנים" - }, "securityAndPrivacy": { "message": "אבטחה ופרטיות" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 1a69af91b..d80b47528 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "कस्टम नेटवर्क जोड़ें" }, - "addCustomToken": { - "message": "कस्टम टोकन जोड़ें" - }, "addEthereumChainConfirmationDescription": { "message": "इससे इस नेटवर्क को MetaMask के अंदर उपयोग करने की अनुमति मिलेगी।" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "खोज परिणाम" }, - "searchTokens": { - "message": "टोकन खोजें" - }, "secretRecoveryPhrase": { "message": "सीक्रेट रिकवरी फ्रेज" }, diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index de36eb424..aa20baa31 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -617,9 +617,6 @@ "searchResults": { "message": "Rezultati pretraživanja" }, - "searchTokens": { - "message": "Pretraži tokene" - }, "securityAndPrivacy": { "message": "Sigurnost i privatnost" }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index 601e049be..d14e5f7a7 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -452,9 +452,6 @@ "searchResults": { "message": "Rezilta rechèch" }, - "searchTokens": { - "message": "Rechèch Tokens" - }, "seedPhraseReq": { "message": "Seed fraz yo se 12 long mo" }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index a1373e469..ca77ac427 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -617,9 +617,6 @@ "searchResults": { "message": "Keresési eredmények" }, - "searchTokens": { - "message": "Keresés a tokenek között" - }, "securityAndPrivacy": { "message": "Biztonság és adatvédelem" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 7a7fc5eb2..62e53b537 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Tambahkan jaringan khusus" }, - "addCustomToken": { - "message": "Tambahkan token kustom" - }, "addEthereumChainConfirmationDescription": { "message": "Tindakan ini akan membantu jaringan ini agar dapat digunakan dengan MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Cari hasil" }, - "searchTokens": { - "message": "Cari token" - }, "secretRecoveryPhrase": { "message": "Frasa Pemulihan Rahasia" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index a301f751f..4545daa82 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -155,9 +155,6 @@ "addContact": { "message": "Aggiungi contatto" }, - "addCustomToken": { - "message": "Aggiungi token personalizzato" - }, "addEthereumChainConfirmationDescription": { "message": "Ciò consentirà a questa rete di essere utilizzata all'interno di MetaMask." }, @@ -1380,9 +1377,6 @@ "searchResults": { "message": "Risultati Ricerca" }, - "searchTokens": { - "message": "Cerca Tokens" - }, "securityAndPrivacy": { "message": "Sicurezza & Privacy" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 58225cb1e..1c4c4827e 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "カスタムネットワークを追加" }, - "addCustomToken": { - "message": "カスタムトークンを追加" - }, "addEthereumChainConfirmationDescription": { "message": "これにより、このネットワークはMetaMask内で使用できるようになります。" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "検索結果" }, - "searchTokens": { - "message": "トークンを検索" - }, "secretRecoveryPhrase": { "message": "シークレットリカバリーフレーズ" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index c4138b3a8..62a51a898 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳು" }, - "searchTokens": { - "message": "ಟೋಕನ್‌ಗಳನ್ನು ಹುಡುಕಿ" - }, "securityAndPrivacy": { "message": "ಭದ್ರತೆ ಮತ್ತು ಗೌಪ್ಯತೆ" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index fccf6041d..741f9de5a 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "커스텀 네트워크 추가" }, - "addCustomToken": { - "message": "커스텀 토큰 추가" - }, "addEthereumChainConfirmationDescription": { "message": "이렇게 하면 MetaMask 내에서 이 네트워크를 사용할 수 있습니다." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "검색 결과" }, - "searchTokens": { - "message": "토큰 검색" - }, "secretRecoveryPhrase": { "message": "비밀 복구 구문" }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index 36d58197b..f1294dbde 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "Paieškos rezultatai" }, - "searchTokens": { - "message": "Ieškoti žetonų" - }, "securityAndPrivacy": { "message": "Sauga ir privatumas" }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index 70e2a3333..a2c9cc425 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -620,9 +620,6 @@ "searchResults": { "message": "Meklēšanas rezultāti" }, - "searchTokens": { - "message": "Meklēt marķierus" - }, "securityAndPrivacy": { "message": "Drošība un konfidencialitāte" }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index e1ff1b6e3..e94e1ccae 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -604,9 +604,6 @@ "searchResults": { "message": "Hasil Carian" }, - "searchTokens": { - "message": "Cari Token" - }, "securityAndPrivacy": { "message": "Keselamatan & Privasi" }, diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index e53ecfc02..820dbcf35 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -608,9 +608,6 @@ "searchResults": { "message": "Søkeresultater" }, - "searchTokens": { - "message": "Søk i sjetonger" - }, "securityAndPrivacy": { "message": "Sikkerhet og personvern" }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index 33e995e54..755965d2a 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -1211,9 +1211,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "securityAndPrivacy": { "message": "Seguridad at Privacy" }, diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index 32664ebda..9ec3a0677 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -618,9 +618,6 @@ "searchResults": { "message": "Wyniki wyszukiwania" }, - "searchTokens": { - "message": "Szukaj tokenów" - }, "securityAndPrivacy": { "message": "Bezpieczeństwo i prywatność" }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index c3d06b188..540d8d39a 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Adicionar rede personalizada" }, - "addCustomToken": { - "message": "Adicionar token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Resultados da busca" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase Secreta de Recuperação" }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index b023dbb67..bde907241 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -100,9 +100,6 @@ "addContact": { "message": "Adicionar contato" }, - "addCustomToken": { - "message": "Adicionar token personalizado" - }, "addEthereumChainConfirmationDescription": { "message": "Isso permitirá que essa rede seja usada dentro da MetaMask." }, @@ -1882,9 +1879,6 @@ "searchResults": { "message": "Resultados da busca" }, - "searchTokens": { - "message": "Buscar tokens" - }, "secretRecoveryPhrase": { "message": "Frase de Recuperação Secreta" }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index d39e5b4e6..7b0ae2062 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -611,9 +611,6 @@ "searchResults": { "message": "Rezultate căutare" }, - "searchTokens": { - "message": "Căutați token-uri" - }, "securityAndPrivacy": { "message": "Securitate și confidențialitate" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 2e0f11573..22c7cff59 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Добавить пользовательскую сеть" }, - "addCustomToken": { - "message": "Добавить пользовательский токен" - }, "addEthereumChainConfirmationDescription": { "message": "Это позволит использовать эту сеть в MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Результаты поиска" }, - "searchTokens": { - "message": "Поиск токенов" - }, "secretRecoveryPhrase": { "message": "Секретная фраза для восстановления" }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 3dcf16933..277713b1c 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -596,9 +596,6 @@ "searchResults": { "message": "Výsledky vyhľadávania" }, - "searchTokens": { - "message": "Hledat tokeny" - }, "securityAndPrivacy": { "message": "Bezpečnosť a súkromie" }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index 92c40f8d4..b81cc3630 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -612,9 +612,6 @@ "searchResults": { "message": "Rezultati iskanja" }, - "searchTokens": { - "message": "Iskanje žetonov" - }, "securityAndPrivacy": { "message": "Varnost in zasebnost" }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index e061fa55a..54a833755 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -615,9 +615,6 @@ "searchResults": { "message": "Rezultati pretrage" }, - "searchTokens": { - "message": "Pretražite tokene" - }, "securityAndPrivacy": { "message": "Bezbednost i privatnost" }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 9f02c1ce2..637c22227 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -608,9 +608,6 @@ "searchResults": { "message": "Sökresultat" }, - "searchTokens": { - "message": "Sök tokens" - }, "securityAndPrivacy": { "message": "Säkerhet och integritet" }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index 0ddfe8ac6..1b8fc27d0 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -602,9 +602,6 @@ "searchResults": { "message": "Matokeo ya Utafutaji" }, - "searchTokens": { - "message": "Tafuta Vianzio" - }, "securityAndPrivacy": { "message": "Ulinzi na Faragha" }, diff --git a/app/_locales/ta/messages.json b/app/_locales/ta/messages.json index eb6f2c499..909ff25ec 100644 --- a/app/_locales/ta/messages.json +++ b/app/_locales/ta/messages.json @@ -359,9 +359,6 @@ "search": { "message": "தேடல்" }, - "searchTokens": { - "message": "தேடல் டோக்கன்ஸ்" - }, "seedPhraseReq": { "message": "விதை வாக்கியங்கள் 12 வார்த்தைகள் நீண்டவை" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index cff8ca4b2..82311d906 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Magdagdag ng custom na network" }, - "addCustomToken": { - "message": "Magdagdag ng Custom na Token" - }, "addEthereumChainConfirmationDescription": { "message": "Magpapahintulot ito sa network na ito na gamitin sa loob ng MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Mga Resulta ng Paghahanap" }, - "searchTokens": { - "message": "Maghanap ng Mga Token" - }, "secretRecoveryPhrase": { "message": "Lihim na recovery phrase" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index b181d5d95..b6ad12c28 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Özel ağ ekle" }, - "addCustomToken": { - "message": "Özel token ekle" - }, "addEthereumChainConfirmationDescription": { "message": "Bu, bu ağın MetaMas dahilinde kullanılmasına olanak tanıyacaktır." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Arama sonuçları" }, - "searchTokens": { - "message": "Token ara" - }, "secretRecoveryPhrase": { "message": "Gizli Kurtarma İfadesi" }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index e9505d2b2..bba5b27f1 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -624,9 +624,6 @@ "searchResults": { "message": "Результати пошуку" }, - "searchTokens": { - "message": "Шукати токени" - }, "securityAndPrivacy": { "message": "Безпека й конфіденційність" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 982edf876..ea52fc82b 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "Thêm mạng tùy chỉnh" }, - "addCustomToken": { - "message": "Thêm token tùy chỉnh" - }, "addEthereumChainConfirmationDescription": { "message": "Thao tác này sẽ cho phép sử dụng mạng này trong MetaMask." }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "Kết quả tìm kiếm" }, - "searchTokens": { - "message": "Tìm kiếm token" - }, "secretRecoveryPhrase": { "message": "Cụm Mật Khẩu Khôi Phục Bí Mật" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 303e62866..2aade1fdf 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -186,9 +186,6 @@ "addCustomNetwork": { "message": "添加自定义网络" }, - "addCustomToken": { - "message": "添加自定义代币" - }, "addEthereumChainConfirmationDescription": { "message": "这将允许在 MetaMask 中使用此网络。" }, @@ -2980,9 +2977,6 @@ "searchResults": { "message": "搜索结果" }, - "searchTokens": { - "message": "搜索代币" - }, "secretRecoveryPhrase": { "message": "助记词" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 276d101e5..8a95e870f 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -42,9 +42,6 @@ "addContact": { "message": "新增合約" }, - "addCustomToken": { - "message": "Add Custom Token" - }, "addEthereumChainConfirmationDescription": { "message": "這會允許在 MetaMask 內使用這個網路。" }, @@ -1133,9 +1130,6 @@ "searchResults": { "message": "搜尋結果" }, - "searchTokens": { - "message": "搜尋代幣" - }, "secureWallet": { "message": "Secure Wallet" }, diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index ac5ac1861..86964698f 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -257,13 +257,18 @@ describe('MetaMask', function () { }); await driver.delay(regularDelayMs); - await driver.fill('#custom-address', tokenAddress); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + tokenAddress, + ); await driver.delay(regularDelayMs); - await driver.clickElement({ text: 'Add custom token', tag: 'button' }); + await driver.clickElement({ text: 'Next', tag: 'button' }); await driver.delay(regularDelayMs); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.delay(regularDelayMs); }); diff --git a/test/e2e/tests/add-hide-token.spec.js b/test/e2e/tests/add-hide-token.spec.js index b1ead17a8..533611ff2 100644 --- a/test/e2e/tests/add-hide-token.spec.js +++ b/test/e2e/tests/add-hide-token.spec.js @@ -108,13 +108,15 @@ describe('Add existing token using search', function () { await driver.press('#password', driver.Key.ENTER); await driver.clickElement({ text: 'Import tokens', tag: 'button' }); - await driver.fill('#search-tokens', 'BAT'); + await driver.fill('input[placeholder="Search"]', 'BAT'); await driver.clickElement({ text: 'BAT', tag: 'span', }); await driver.clickElement({ text: 'Next', tag: 'button' }); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.waitForSelector({ css: '.token-overview__primary-balance', diff --git a/test/e2e/tests/custom-token-add-approve.spec.js b/test/e2e/tests/custom-token-add-approve.spec.js index 2cae7ff50..f5f58e5d8 100644 --- a/test/e2e/tests/custom-token-add-approve.spec.js +++ b/test/e2e/tests/custom-token-add-approve.spec.js @@ -51,20 +51,24 @@ describe('Create token, approve token and approve token without gas', function ( text: 'Custom token', tag: 'button', }); - await driver.fill('#custom-address', contractAddress); - await driver.waitForSelector('#custom-decimals'); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + contractAddress, + ); + await driver.waitForSelector( + '[data-testid="import-tokens-modal-custom-decimals"]', + ); await driver.delay(2000); await driver.clickElement({ - text: 'Add custom token', + text: 'Next', tag: 'button', }); await driver.delay(2000); - await driver.clickElement({ - text: 'Import tokens', - tag: 'button', - }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); // renders balance for newly created token await driver.clickElement('.app-header__logo-container'); diff --git a/test/e2e/tests/import-tokens.spec.js b/test/e2e/tests/import-tokens.spec.js index d86d5f097..01adbb2ac 100644 --- a/test/e2e/tests/import-tokens.spec.js +++ b/test/e2e/tests/import-tokens.spec.js @@ -37,14 +37,20 @@ describe('Import flow', function () { await driver.delay(regularDelayMs); await driver.clickElement('[data-testid="import-token-button"]'); - await driver.fill('input[placeholder="Search tokens"]', 'cha'); + await driver.fill('input[placeholder="Search"]', 'cha'); await driver.clickElement('.token-list__token'); await driver.clickElement('.token-list__token:nth-of-type(2)'); await driver.clickElement('.token-list__token:nth-of-type(3)'); - await driver.clickElement({ css: 'button', text: 'Next' }); - await driver.clickElement({ css: 'button', text: 'Import' }); + await driver.clickElement({ + css: '.import-tokens-modal button', + text: 'Next', + }); + await driver.clickElement({ + css: '.import-tokens-modal button', + text: 'Import', + }); await driver.clickElement('.asset-breadcrumb'); diff --git a/test/e2e/tests/token-details.spec.js b/test/e2e/tests/token-details.spec.js index 07c4a8570..e365b760d 100644 --- a/test/e2e/tests/token-details.spec.js +++ b/test/e2e/tests/token-details.spec.js @@ -30,11 +30,19 @@ describe('Token Details', function () { const tokenAddress = '0x2EFA2Cb29C2341d8E5Ba7D3262C9e9d6f1Bf3711'; const tokenSymbol = 'AAVE'; - await driver.fill('#custom-address', tokenAddress); - await driver.waitForSelector('#custom-symbol-helper-text'); - await driver.fill('#custom-symbol', tokenSymbol); - await driver.clickElement({ text: 'Add custom token', tag: 'button' }); - await driver.clickElement({ text: 'Import tokens', tag: 'button' }); + await driver.fill( + '[data-testid="import-tokens-modal-custom-address"]', + tokenAddress, + ); + await driver.waitForSelector('p.mm-box--color-error-default'); + await driver.fill( + '[data-testid="import-tokens-modal-custom-symbol"]', + tokenSymbol, + ); + await driver.clickElement({ text: 'Next', tag: 'button' }); + await driver.clickElement( + '[data-testid="import-tokens-modal-import-button"]', + ); await driver.clickElement('[aria-label="Asset options"]'); await driver.clickElement({ text: 'Token details', tag: 'div' }); diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss index b3a9ed194..017e7deb6 100644 --- a/ui/components/app/app-components.scss +++ b/ui/components/app/app-components.scss @@ -11,6 +11,7 @@ @import 'confirm-data/index'; @import 'confirmation-warning-modal/index'; @import 'custom-nonce/index'; +@import 'import-token/index'; @import 'nfts-items/index'; @import 'nfts-tab/index'; @import 'nft-details/index'; diff --git a/ui/components/app/import-token/index.scss b/ui/components/app/import-token/index.scss new file mode 100644 index 000000000..24b210c93 --- /dev/null +++ b/ui/components/app/import-token/index.scss @@ -0,0 +1 @@ +@import 'token-list/index'; diff --git a/ui/pages/import-token/token-list/index.js b/ui/components/app/import-token/token-list/index.js similarity index 100% rename from ui/pages/import-token/token-list/index.js rename to ui/components/app/import-token/token-list/index.js diff --git a/ui/pages/import-token/token-list/index.scss b/ui/components/app/import-token/token-list/index.scss similarity index 96% rename from ui/pages/import-token/token-list/index.scss rename to ui/components/app/import-token/token-list/index.scss index b776d093c..e00298ada 100644 --- a/ui/pages/import-token/token-list/index.scss +++ b/ui/components/app/import-token/token-list/index.scss @@ -1,5 +1,3 @@ -@import 'token-list-placeholder/index'; - .token-list { &__title { @include H7; diff --git a/ui/pages/import-token/token-list/token-list-placeholder/index.js b/ui/components/app/import-token/token-list/token-list-placeholder/index.js similarity index 100% rename from ui/pages/import-token/token-list/token-list-placeholder/index.js rename to ui/components/app/import-token/token-list/token-list-placeholder/index.js diff --git a/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js new file mode 100644 index 000000000..7cbf662b1 --- /dev/null +++ b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ZENDESK_URLS from '../../../../../helpers/constants/zendesk-url'; +import { ButtonLink, Text, Box } from '../../../../component-library'; +import { + Display, + FlexDirection, + TextAlign, + TextColor, + AlignItems, +} from '../../../../../helpers/constants/design-system'; + +export default class TokenListPlaceholder extends Component { + static contextTypes = { + t: PropTypes.func, + }; + + render() { + return ( + + + {this.context.t('addAcquiredTokens')} + + + {this.context.t('learnMoreUpperCase')} + + + ); + } +} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js similarity index 79% rename from ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js rename to ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js index 72386103d..2cbe8f608 100644 --- a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js +++ b/ui/components/app/import-token/token-list/token-list-placeholder/token-list-placeholder.stories.js @@ -2,7 +2,7 @@ import React from 'react'; import TokenListPlaceholder from './token-list-placeholder.component'; export default { - title: 'Pages/ImportToken/TokenList/TokenListPlaceholder', + title: 'Components/App/TokenList/TokenListPlaceholder', }; export const DefaultStory = () => { diff --git a/ui/pages/import-token/token-list/token-list.component.js b/ui/components/app/import-token/token-list/token-list.component.js similarity index 96% rename from ui/pages/import-token/token-list/token-list.component.js rename to ui/components/app/import-token/token-list/token-list.component.js index fed54e5ad..e57d06be2 100644 --- a/ui/pages/import-token/token-list/token-list.component.js +++ b/ui/components/app/import-token/token-list/token-list.component.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { checkExistingAddresses } from '../../../helpers/utils/util'; +import { checkExistingAddresses } from '../../../../helpers/utils/util'; import TokenListPlaceholder from './token-list-placeholder'; export default class TokenList extends Component { diff --git a/ui/pages/import-token/token-list/token-list.container.js b/ui/components/app/import-token/token-list/token-list.container.js similarity index 100% rename from ui/pages/import-token/token-list/token-list.container.js rename to ui/components/app/import-token/token-list/token-list.container.js diff --git a/ui/pages/import-token/token-search/index.js b/ui/components/app/import-token/token-search/index.js similarity index 100% rename from ui/pages/import-token/token-search/index.js rename to ui/components/app/import-token/token-search/index.js diff --git a/ui/pages/import-token/token-search/token-search.component.js b/ui/components/app/import-token/token-search/token-search.component.js similarity index 69% rename from ui/pages/import-token/token-search/token-search.component.js rename to ui/components/app/import-token/token-search/token-search.component.js index b6546cd78..853d226ae 100644 --- a/ui/pages/import-token/token-search/token-search.component.js +++ b/ui/components/app/import-token/token-search/token-search.component.js @@ -1,10 +1,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Fuse from 'fuse.js'; -import InputAdornment from '@material-ui/core/InputAdornment'; -import TextField from '../../../components/ui/text-field'; -import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; -import SearchIcon from '../../../components/ui/icon/search-icon'; +import { isEqualCaseInsensitive } from '../../../../../shared/modules/string-utils'; +import { TextFieldSearch } from '../../../component-library'; +import { BlockSize } from '../../../../helpers/constants/design-system'; export default class TokenSearch extends Component { static contextTypes = { @@ -57,30 +56,19 @@ export default class TokenSearch extends Component { this.props.onSearch({ searchQuery, results }); } - renderAdornment() { - return ( - - - - ); - } - render() { const { error } = this.props; const { searchQuery } = this.state; return ( - this.handleSearch(e.target.value)} error={error} - fullWidth autoFocus - autoComplete="off" - startAdornment={this.renderAdornment()} + autoComplete={false} + width={BlockSize.Full} /> ); } diff --git a/ui/pages/import-token/token-search/token-search.stories.js b/ui/components/app/import-token/token-search/token-search.stories.js similarity index 76% rename from ui/pages/import-token/token-search/token-search.stories.js rename to ui/components/app/import-token/token-search/token-search.stories.js index 8b0156474..57ec57cea 100644 --- a/ui/pages/import-token/token-search/token-search.stories.js +++ b/ui/components/app/import-token/token-search/token-search.stories.js @@ -1,9 +1,9 @@ import React from 'react'; -import testData from '../../../../.storybook/test-data'; +import testData from '../../../../../.storybook/test-data'; import TokenSearch from './token-search.component'; export default { - title: 'Pages/ImportToken/TokenSearch', + title: 'Components/App/ImportToken/TokenSearch', argTypes: { error: { diff --git a/ui/components/multichain/import-token-link/import-token-link.js b/ui/components/multichain/import-token-link/import-token-link.js index 699db9136..914e8110f 100644 --- a/ui/components/multichain/import-token-link/import-token-link.js +++ b/ui/components/multichain/import-token-link/import-token-link.js @@ -1,6 +1,5 @@ import React, { useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { ButtonLink, IconName, Box } from '../../component-library'; @@ -10,8 +9,7 @@ import { Size, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes'; -import { detectNewTokens } from '../../../store/actions'; +import { detectNewTokens, showImportTokensModal } from '../../../store/actions'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { MetaMetricsEventCategory, @@ -25,7 +23,6 @@ import { export const ImportTokenLink = ({ className, ...props }) => { const trackEvent = useContext(MetaMetricsContext); const t = useI18nContext(); - const history = useHistory(); const dispatch = useDispatch(); const isTokenDetectionSupported = useSelector(getIsTokenDetectionSupported); @@ -48,7 +45,7 @@ export const ImportTokenLink = ({ className, ...props }) => { data-testid="import-token-button" startIconName={IconName.Add} onClick={() => { - history.push(IMPORT_TOKEN_ROUTE); + dispatch(showImportTokensModal()); trackEvent({ event: MetaMetricsEventName.TokenImportButtonClicked, category: MetaMetricsEventCategory.Navigation, diff --git a/ui/components/multichain/import-token-link/import-token-link.test.js b/ui/components/multichain/import-token-link/import-token-link.test.js index a4763f6de..f4008cfc3 100644 --- a/ui/components/multichain/import-token-link/import-token-link.test.js +++ b/ui/components/multichain/import-token-link/import-token-link.test.js @@ -19,7 +19,12 @@ jest.mock('react-router-dom', () => { }); jest.mock('../../../store/actions.ts', () => ({ - detectNewTokens: jest.fn().mockReturnValue({ type: '' }), + detectNewTokens: jest + .fn() + .mockImplementation(() => ({ type: 'DETECT_TOKENS' })), + showImportTokensModal: jest + .fn() + .mockImplementation(() => ({ type: 'UI_IMPORT_TOKENS_POPOVER_OPEN' })), })); describe('Import Token Link', () => { @@ -90,6 +95,6 @@ describe('Import Token Link', () => { const importToken = screen.getByTestId('import-token-button'); fireEvent.click(importToken); - expect(mockPushHistory).toHaveBeenCalledWith('/import-token'); + expect(screen.getByText('Import tokens')).toBeInTheDocument(); }); }); diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js new file mode 100644 index 000000000..43d4808f8 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.js @@ -0,0 +1,108 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import PropTypes from 'prop-types'; +import { + Box, + ButtonPrimary, + ButtonSecondary, + Text, +} from '../../component-library'; +import { + AlignItems, + Display, + Size, + TextAlign, + TextColor, + TextVariant, +} from '../../../helpers/constants/design-system'; +import TokenBalance from '../../ui/token-balance/token-balance'; +import Identicon from '../../ui/identicon'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { getPendingTokens } from '../../../ducks/metamask/metamask'; + +export const ImportTokensModalConfirm = ({ onBackClick, onImportClick }) => { + const t = useI18nContext(); + const pendingTokens = useSelector(getPendingTokens); + + return ( + + {t('likeToImportTokens')} + + + + {t('token')} + + + {t('balance')} + + + + {Object.entries(pendingTokens).map(([address, token]) => { + const { name, symbol } = token; + return ( + + + + + {name} + + {symbol} + + + + + + + + ); + })} + + + + {t('back')} + + + {t('import')} + + + + + ); +}; + +ImportTokensModalConfirm.propTypes = { + onBackClick: PropTypes.func.isRequired, + onImportClick: PropTypes.func.isRequired, +}; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js new file mode 100644 index 000000000..2df36b65d --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal-confirm.stories.js @@ -0,0 +1,100 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { ImportTokensModalConfirm } from './import-tokens-modal-confirm'; + +const createStore = ( + chainId = CHAIN_IDS.MAINNET, + useTokenDetection = true, + tokenRepetition = 1, +) => { + return configureStore({ + ...testData, + metamask: { + ...testData.metamask, + useTokenDetection, + providerConfig: { chainId }, + pendingTokens: { + '0x0000000de40dfa9b17854cbc7869d80f9f98d823': { + address: '0x0000000de40dfa9b17854cbc7869d80f9f98d823', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + fees: {}, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x0000000de40dfa9b17854cbc7869d80f9f98d823.png', + name: 'delta theta'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DLTA', + type: 'erc20', + unlisted: false, + }, + '0x00d8318e44780edeefcf3020a5448f636788883c': { + address: '0x00d8318e44780edeefcf3020a5448f636788883c', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + fees: {}, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x00d8318e44780edeefcf3020a5448f636788883c.png', + name: 'dAppstore'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DAPPX', + type: 'erc20', + unlisted: false, + }, + '0x00e679ba63b509182c349f5614f0a07cdd0ce0c5': { + address: '0x00e679ba63b509182c349f5614f0a07cdd0ce0c5', + aggregators: ['CoinGecko', 'Sonarwatch', 'Coinmarketcap'], + decimals: 18, + iconUrl: + 'https://static.metafi.codefi.network/api/v1/tokenIcons/1/0x00e679ba63b509182c349f5614f0a07cdd0ce0c5.png', + name: 'Damex Token'.repeat(tokenRepetition), + occurrences: 3, + symbol: 'DAMEX', + type: 'erc20', + unlisted: false, + }, + }, + }, + }); +}; + +export default { + title: 'Components/Multichain/ImportTokensModal/ImportTokensModalConfirm', + component: ImportTokensModalConfirm, + argTypes: { + onBackClick: { + action: 'onClose', + }, + onImportClick: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; +DefaultStory.decorators = [ + (Story) => ( + + + + ), +]; + +DefaultStory.storyName = 'Default'; + +export const LongValueStory = (args) => ( +
+ +
+); +LongValueStory.decorators = [ + (Story) => ( + + + + ), +]; + +LongValueStory.storyName = 'LongValueStory'; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js new file mode 100644 index 000000000..2e8ef6b10 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.js @@ -0,0 +1,649 @@ +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { getTokenTrackerLink } from '@metamask/etherscan-link/dist/token-tracker-link'; +import { Tab, Tabs } from '../../ui/tabs'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { + getCurrentChainId, + getIsDynamicTokenListAvailable, + getIsMainnet, + getIsTokenDetectionInactiveOnMainnet, + getIsTokenDetectionSupported, + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + getMetaMaskIdentities, + getRpcPrefsForCurrentProvider, + getSelectedAddress, + getTokenDetectionSupportNetworkByChainId, + getTokenList, +} from '../../../selectors'; +import { + addImportedTokens, + clearPendingTokens, + getTokenStandardAndDetails, + setPendingTokens, + showImportNftsModal, +} from '../../../store/actions'; +import { + BannerAlert, + ButtonLink, + ButtonPrimary, + Text, + FormTextField, + Box, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, +} from '../../component-library'; +import TokenSearch from '../../app/import-token/token-search'; +import TokenList from '../../app/import-token/token-list'; + +import { + FontWeight, + Severity, + Size, + TextAlign, + TextColor, +} from '../../../helpers/constants/design-system'; + +import { ASSET_ROUTE, SECURITY_ROUTE } from '../../../helpers/constants/routes'; +import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; +import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils'; +import { addHexPrefix } from '../../../../app/scripts/lib/util'; +import { STATIC_MAINNET_TOKEN_LIST } from '../../../../shared/constants/tokens'; +import { + AssetType, + TokenStandard, +} from '../../../../shared/constants/transaction'; +import { + checkExistingAddresses, + getURLHostName, +} from '../../../helpers/utils/util'; +import { tokenInfoGetter } from '../../../helpers/utils/token-util'; +import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { getPendingTokens } from '../../../ducks/metamask/metamask'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, + MetaMetricsTokenEventSource, +} from '../../../../shared/constants/metametrics'; +import { ImportTokensModalConfirm } from './import-tokens-modal-confirm'; + +export const ImportTokensModal = ({ onClose }) => { + const t = useI18nContext(); + const history = useHistory(); + const dispatch = useDispatch(); + + const [mode, setMode] = useState(''); + + const [tokenSelectorError, setTokenSelectorError] = useState(null); + const [selectedTokens, setSelectedTokens] = useState({}); + const [searchResults, setSearchResults] = useState([]); + + // Determine if we should show the search tab + const isTokenDetectionSupported = useSelector(getIsTokenDetectionSupported); + const isTokenDetectionInactiveOnMainnet = useSelector( + getIsTokenDetectionInactiveOnMainnet, + ); + const showSearchTab = + isTokenDetectionSupported || + isTokenDetectionInactiveOnMainnet || + Boolean(process.env.IN_TEST); + + const tokenList = useSelector(getTokenList); + const useTokenDetection = useSelector( + ({ metamask }) => metamask.useTokenDetection, + ); + const networkName = useSelector(getTokenDetectionSupportNetworkByChainId); + + // Custom token stuff + const tokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector( + getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, + ); + const isDynamicTokenListAvailable = useSelector( + getIsDynamicTokenListAvailable, + ); + const selectedAddress = useSelector(getSelectedAddress); + const isMainnet = useSelector(getIsMainnet); + const identities = useSelector(getMetaMaskIdentities); + const tokens = useSelector((state) => state.metamask.tokens); + const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); + + const [customAddress, setCustomAddress] = useState(''); + const [customAddressError, setCustomAddressError] = useState(null); + const [nftAddressError, setNftAddressError] = useState(null); + const [symbolAutoFilled, setSymbolAutoFilled] = useState(false); + const [decimalAutoFilled, setDecimalAutoFilled] = useState(false); + const [mainnetTokenWarning, setMainnetTokenWarning] = useState(null); + const [customSymbol, setCustomSymbol] = useState(''); + const [customSymbolError, setCustomSymbolError] = useState(null); + const [customDecimals, setCustomDecimals] = useState(0); + const [customDecimalsError, setCustomDecimalsError] = useState(null); + const [tokenStandard, setTokenStandard] = useState(TokenStandard.none); + const [forceEditSymbol, setForceEditSymbol] = useState(false); + + const chainId = useSelector(getCurrentChainId); + const blockExplorerTokenLink = getTokenTrackerLink( + customAddress, + chainId, + null, + null, + { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null }, + ); + const blockExplorerLabel = rpcPrefs?.blockExplorerUrl + ? getURLHostName(blockExplorerTokenLink) + : t('etherscan'); + + // Min and Max decimal values + const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'; + const MIN_DECIMAL_VALUE = 0; + const MAX_DECIMAL_VALUE = 36; + + const infoGetter = useRef(tokenInfoGetter()); + + // CONFIRMATION MODE + const trackEvent = useContext(MetaMetricsContext); + const pendingTokens = useSelector(getPendingTokens); + + const handleAddTokens = useCallback(async () => { + const addedTokenValues = Object.values(pendingTokens); + await dispatch(addImportedTokens(addedTokenValues)); + + const firstTokenAddress = addedTokenValues?.[0].address?.toLowerCase(); + + addedTokenValues.forEach((pendingToken) => { + trackEvent({ + event: MetaMetricsEventName.TokenAdded, + category: MetaMetricsEventCategory.Wallet, + sensitiveProperties: { + token_symbol: pendingToken.symbol, + token_contract_address: pendingToken.address, + token_decimal_precision: pendingToken.decimals, + unlisted: pendingToken.unlisted, + source_connection_method: pendingToken.isCustom + ? MetaMetricsTokenEventSource.Custom + : MetaMetricsTokenEventSource.List, + token_standard: TokenStandard.ERC20, + asset_type: AssetType.token, + }, + }); + }); + + dispatch(clearPendingTokens()); + + if (firstTokenAddress) { + history.push(`${ASSET_ROUTE}/${firstTokenAddress}`); + } + }, [dispatch, history, pendingTokens, trackEvent]); + + useEffect(() => { + const pendingTokenKeys = Object.keys(pendingTokens); + + if (pendingTokenKeys.length === 0) { + return; + } + + let initialSelectedTokens = {}; + let initialCustomToken = {}; + + pendingTokenKeys.forEach((tokenAddress) => { + const token = pendingTokens[tokenAddress]; + const { isCustom } = token; + + if (isCustom) { + initialCustomToken = { ...token }; + } else { + initialSelectedTokens = { + ...selectedTokens, + [tokenAddress]: { ...token }, + }; + } + }); + + setSelectedTokens(initialSelectedTokens); + setCustomAddress(initialCustomToken.address); + setCustomSymbol(initialCustomToken.symbol); + setCustomDecimals(initialCustomToken.decimals); + }, [pendingTokens]); + + const handleCustomSymbolChange = (value) => { + const symbol = value.trim(); + const symbolLength = symbol.length; + let symbolError = null; + + if (symbolLength <= 0 || symbolLength >= 12) { + symbolError = t('symbolBetweenZeroTwelve'); + } + + setCustomSymbol(symbol); + setCustomSymbolError(symbolError); + }; + + const handleCustomDecimalsChange = (value) => { + let decimals; + let decimalsError = null; + + if (value) { + decimals = Number(value.trim()); + decimalsError = + value < MIN_DECIMAL_VALUE || value > MAX_DECIMAL_VALUE + ? t('decimalsMustZerotoTen') + : null; + } else { + decimals = ''; + decimalsError = t('tokenDecimalFetchFailed'); + } + + setCustomDecimals(decimals); + setCustomDecimalsError(decimalsError); + }; + + const attemptToAutoFillTokenParams = async (address) => { + const { symbol = '', decimals } = await infoGetter.current( + address, + tokenList, + ); + + setSymbolAutoFilled(Boolean(symbol)); + setDecimalAutoFilled(Boolean(decimals)); + + handleCustomSymbolChange(symbol || ''); + handleCustomDecimalsChange(decimals); + }; + + const handleToggleToken = (token) => { + const { address } = token; + const selectedTokensCopy = { ...selectedTokens }; + + if (address in selectedTokensCopy) { + delete selectedTokensCopy[address]; + } else { + selectedTokensCopy[address] = token; + } + + setSelectedTokens(selectedTokensCopy); + setTokenSelectorError(null); + }; + + const hasError = () => { + return ( + tokenSelectorError || + customAddressError || + customSymbolError || + customDecimalsError || + nftAddressError + ); + }; + + const hasSelected = () => { + return customAddress || Object.keys(selectedTokens).length > 0; + }; + + const handleNext = () => { + if (hasError()) { + return; + } + + if (!hasSelected()) { + setTokenSelectorError(t('mustSelectOne')); + return; + } + + const tokenAddressList = Object.keys(tokenList); + const customToken = customAddress + ? { + address: customAddress, + symbol: customSymbol, + decimals: customDecimals, + standard: tokenStandard, + } + : null; + + dispatch( + setPendingTokens({ customToken, selectedTokens, tokenAddressList }), + ); + setMode('confirm'); + }; + + const handleCustomAddressChange = async (value) => { + const address = value.trim(); + + setCustomAddress(address); + setCustomAddressError(null); + setNftAddressError(null); + setSymbolAutoFilled(false); + setDecimalAutoFilled(false); + setMainnetTokenWarning(null); + + const addressIsValid = isValidHexAddress(address, { + allowNonPrefixed: false, + }); + const standardAddress = addHexPrefix(address).toLowerCase(); + + const isMainnetToken = Object.keys(STATIC_MAINNET_TOKEN_LIST).some( + (key) => key.toLowerCase() === address.toLowerCase(), + ); + + let standard; + if (addressIsValid) { + try { + ({ standard } = await getTokenStandardAndDetails( + standardAddress, + selectedAddress, + null, + )); + } catch (error) { + // ignore + } + } + + const addressIsEmpty = address.length === 0 || address === EMPTY_ADDRESS; + + switch (true) { + case !addressIsValid && !addressIsEmpty: + setCustomAddressError(t('invalidAddress')); + setCustomSymbol(''); + setCustomDecimals(0); + setCustomSymbolError(null); + setCustomDecimalsError(null); + break; + + case standard === TokenStandard.ERC1155 || + standard === TokenStandard.ERC721: + setNftAddressError( + t('nftAddressError', [ + { + dispatch(showImportNftsModal()); + onClose(); + }} + color={TextColor.primaryDefault} + key="nftAddressError" + > + {t('importNFTPage')} + , + ]), + ); + break; + + case isMainnetToken && !isMainnet: + setMainnetTokenWarning(t('mainnetToken')); + setCustomSymbol(''); + setCustomDecimals(0); + setCustomSymbolError(null); + setCustomDecimalsError(null); + break; + + case Boolean(identities[standardAddress]): + setCustomAddressError(t('personalAddressDetected')); + break; + + case checkExistingAddresses(address, tokens): + setCustomAddressError(t('tokenAlreadyAdded')); + break; + + default: + if (!addressIsEmpty) { + attemptToAutoFillTokenParams(address); + if (standard) { + setTokenStandard(standard); + } + } + } + }; + + // Determines whether to show the Search/Import or Confirm action + const isConfirming = mode === 'confirm'; + + return ( + { + dispatch(clearPendingTokens()); + onClose(); + }} + className="import-tokens-modal" + > + + + setMode('') : null} + onClose={() => { + dispatch(clearPendingTokens()); + onClose(); + }} + > + {t('importTokensCamelCase')} + + + {isConfirming ? ( + { + dispatch(clearPendingTokens()); + setMode(''); + }} + onImportClick={async () => { + await handleAddTokens(); + onClose(); + }} + /> + ) : ( + <> + + {showSearchTab ? ( + + + {useTokenDetection ? null : ( + + + {t('enhancedTokenDetectionAlertMessage', [ + networkName, + { + history.push( + `${SECURITY_ROUTE}#advanced-settings-autodetect-tokens`, + ); + onClose(); + }} + > + {t('enableFromSettings')} + , + ])} + + + )} + + setSearchResults(results) + } + error={tokenSelectorError} + tokenList={tokenList} + /> + + handleToggleToken(token)} + /> + + + + ) : null} + + + {tokenDetectionInactiveOnNonMainnetSupportedNetwork ? ( + + {t( + 'customTokenWarningInTokenDetectionNetworkWithTDOFF', + [ + + {t('tokenScamSecurityRisk')} + , + + history.push( + `${SECURITY_ROUTE}#advanced-settings-autodetect-tokens`, + ) + } + > + {t('inYourSettings')} + , + ], + )} + + ) : ( + + {t( + isDynamicTokenListAvailable + ? 'customTokenWarningInTokenDetectionNetwork' + : 'customTokenWarningInNonTokenDetectionNetwork', + [ + + {t('learnScamRisk')} + , + ], + )} + + )} + + handleCustomAddressChange(e.target.value) + } + helpText={ + customAddressError || + mainnetTokenWarning || + nftAddressError + } + error={ + customAddressError || + mainnetTokenWarning || + nftAddressError + } + autoFocus + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-address', + }} + /> + + {t('tokenSymbol')} + {symbolAutoFilled && !forceEditSymbol && ( + setForceEditSymbol(true)} + textAlign={TextAlign.End} + paddingInlineEnd={1} + paddingInlineStart={1} + color={TextColor.primaryDefault} + > + {t('edit')} + + )} + + } + value={customSymbol} + onChange={(e) => handleCustomSymbolChange(e.target.value)} + helpText={customSymbolError} + error={customSymbolError} + disabled={symbolAutoFilled && !forceEditSymbol} + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-symbol', + }} + /> + + handleCustomDecimalsChange(e.target.value) + } + helpText={customDecimalsError} + error={customDecimalsError} + disabled={decimalAutoFilled} + min={MIN_DECIMAL_VALUE} + max={MAX_DECIMAL_VALUE} + marginTop={6} + inputProps={{ + 'data-testid': 'import-tokens-modal-custom-decimals', + }} + /> + {customDecimals === '' && ( + + + {t('tokenDecimalFetchFailed')} + + {t('verifyThisTokenDecimalOn', [ + + {blockExplorerLabel} + , + ])} + + )} + + + + + handleNext()} + size={Size.LG} + disabled={Boolean(hasError()) || !hasSelected()} + block + > + {t('next')} + + + + )} + + + + ); +}; + +ImportTokensModal.propTypes = { + onClose: PropTypes.func.isRequired, +}; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss b/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss new file mode 100644 index 000000000..f1240f786 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.scss @@ -0,0 +1,67 @@ +.import-tokens-modal { + $self: &; + + .tabs { + ul { + border-bottom: 0; + } + + li { + width: 50%; + } + } + + &__autodetect { + vertical-align: unset; + } + + &__search-list { + max-width: 100%; + overflow: auto; + max-height: 200px; + } + + &__custom-token-form { + input[type='number']::-webkit-inner-spin-button { + -webkit-appearance: none; + display: none; + } + + input[type='number']:hover::-webkit-inner-spin-button { + -webkit-appearance: none; + display: none; + } + & #{$self}__decimal-warning { + margin-top: 5px; + } + } + + &__token-name { + flex: 1; + } + + &__token-balance { + flex: 0 0 30%; + } + + &__confirm-token-list { + flex-flow: column nowrap; + + &-item { + flex-flow: row nowrap; + + &-wrapper { + flex-grow: 1; + + &__text { + max-width: 130px; + } + } + } + } + + &__nft-address-error-link { + display: contents; + font-size: inherit; + } +} diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js new file mode 100644 index 000000000..04505d00b --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.stories.js @@ -0,0 +1,62 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import configureStore from '../../../store/store'; +import testData from '../../../../.storybook/test-data'; +import { CHAIN_IDS } from '../../../../shared/constants/network'; +import { ImportTokensModal } from './import-tokens-modal'; + +const createStore = (chainId = CHAIN_IDS.MAINNET, useTokenDetection = true) => { + return configureStore({ + ...testData, + metamask: { + ...testData.metamask, + useTokenDetection, + providerConfig: { chainId }, + }, + }); +}; + +export default { + title: 'Components/Multichain/ImportTokensModal', + component: ImportTokensModal, + argTypes: { + onClose: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; +DefaultStory.decorators = [ + (Story) => ( + + + + ), +]; + +DefaultStory.storyName = 'Default'; + +export const CustomImportOnlyStory = (args) => ; +CustomImportOnlyStory.decorators = [ + (Story) => ( + + + + ), +]; + +CustomImportOnlyStory.storyName = 'Custom Import Only'; + +export const TokenDetectionDisabledStory = (args) => ( + +); +TokenDetectionDisabledStory.decorators = [ + (Story) => ( + + + + ), +]; + +TokenDetectionDisabledStory.storyName = 'Token Detection Disabled'; diff --git a/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js b/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js new file mode 100644 index 000000000..e61320662 --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/import-tokens-modal.test.js @@ -0,0 +1,200 @@ +import React from 'react'; +import { fireEvent } from '@testing-library/react'; +import { renderWithProvider } from '../../../../test/lib/render-helpers'; + +import configureStore from '../../../store/store'; +import { + setPendingTokens, + clearPendingTokens, + getTokenStandardAndDetails, +} from '../../../store/actions'; +import mockState from '../../../../test/data/mock-state.json'; +import { TokenStandard } from '../../../../shared/constants/transaction'; +import { ImportTokensModal } from '.'; + +jest.mock('../../../store/actions', () => ({ + getTokenStandardAndDetails: jest + .fn() + .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })), + setPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'SET_PENDING_TOKENS' })), + clearPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'CLEAR_PENDING_TOKENS' })), +})); + +describe('ImportTokensModal', () => { + const render = (metamaskStateChanges = {}, onClose = jest.fn()) => { + const store = configureStore({ + ...mockState, + metamask: { + ...mockState.metamask, + ...metamaskStateChanges, + }, + }); + return renderWithProvider(, store); + }; + + describe('Search', () => { + it('renders expected elements', () => { + const { getByText, getByPlaceholderText } = render(); + expect( + getByText(`Add the tokens you've acquired using MetaMask`), + ).toBeInTheDocument(); + expect(getByText('Next')).toBeDisabled(); + expect(getByPlaceholderText('Search')).toBeInTheDocument(); + }); + + it('shows the token detection notice when setting is off', () => { + const { getByText } = render({ useTokenDetection: false }); + expect(getByText('Enable it from Settings.')).toBeInTheDocument(); + }); + }); + + describe('Custom Token', () => { + it('add custom token button is disabled when no fields are populated', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + const submit = getByText('Next'); + + expect(submit).toBeDisabled(); + }); + + it('edits token address', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + const event = { target: { value: tokenAddress } }; + fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + event, + ); + + expect( + getByTestId('import-tokens-modal-custom-address').value, + ).toStrictEqual(tokenAddress); + }); + + it('edits token symbol', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenSymbol = 'META'; + const event = { target: { value: tokenSymbol } }; + fireEvent.change(getByTestId('import-tokens-modal-custom-symbol'), event); + + expect( + getByTestId('import-tokens-modal-custom-symbol').value, + ).toStrictEqual(tokenSymbol); + }); + + it('edits token decimal precision', () => { + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const tokenPrecision = '2'; + const event = { target: { value: tokenPrecision } }; + fireEvent.change( + getByTestId('import-tokens-modal-custom-decimals'), + event, + ); + + expect( + getByTestId('import-tokens-modal-custom-decimals').value, + ).toStrictEqual(tokenPrecision); + }); + + it('adds custom tokens successfully', async () => { + const { getByText, getByTestId } = render({ tokens: [], tokenList: {} }); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + expect(getByText('Next')).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + await fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + { + target: { value: tokenAddress }, + }, + ); + expect(getByText('Next')).not.toBeDisabled(); + + const tokenSymbol = 'META'; + + fireEvent.change(getByTestId('import-tokens-modal-custom-symbol'), { + target: { value: tokenSymbol }, + }); + + expect(getByTestId('import-tokens-modal-custom-symbol').value).toBe( + 'META', + ); + + const tokenPrecision = '2'; + + fireEvent.change(getByTestId('import-tokens-modal-custom-decimals'), { + target: { value: tokenPrecision }, + }); + + expect(getByText('Next')).not.toBeDisabled(); + + fireEvent.click(getByText('Next')); + + expect(setPendingTokens).toHaveBeenCalledWith({ + customToken: { + address: tokenAddress, + decimals: Number(tokenPrecision), + standard: TokenStandard.ERC20, + symbol: tokenSymbol, + }, + selectedTokens: {}, + tokenAddressList: [], + }); + + expect(getByText('Import')).toBeInTheDocument(); + }); + + it('cancels out of import token flow', () => { + const onClose = jest.fn(); + render({}, onClose); + + fireEvent.click(document.querySelector('button[aria-label="Close"]')); + + expect(clearPendingTokens).toHaveBeenCalled(); + expect(onClose).toHaveBeenCalled(); + }); + + it('sets and error when a token is an NFT', async () => { + getTokenStandardAndDetails.mockImplementation(() => + Promise.resolve({ standard: TokenStandard.ERC721 }), + ); + + const { getByText, getByTestId } = render(); + const customTokenButton = getByText('Custom token'); + fireEvent.click(customTokenButton); + + const submit = getByText('Next'); + expect(submit).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + await fireEvent.change( + getByTestId('import-tokens-modal-custom-address'), + { + target: { value: tokenAddress }, + }, + ); + + expect(submit).toBeDisabled(); + + // The last part of this error message won't be found by getByText because it is wrapped as a link. + const errorMessage = getByText('This token is an NFT. Add on the'); + expect(errorMessage).toBeInTheDocument(); + }); + }); +}); diff --git a/ui/components/multichain/import-tokens-modal/index.js b/ui/components/multichain/import-tokens-modal/index.js new file mode 100644 index 000000000..ecaf2735e --- /dev/null +++ b/ui/components/multichain/import-tokens-modal/index.js @@ -0,0 +1 @@ +export { ImportTokensModal } from './import-tokens-modal'; diff --git a/ui/components/multichain/index.js b/ui/components/multichain/index.js index 95a9b191b..e1e3359fc 100644 --- a/ui/components/multichain/index.js +++ b/ui/components/multichain/index.js @@ -18,3 +18,4 @@ export { CreateAccount } from './create-account'; export { ImportAccount } from './import-account'; export { ImportNftsModal } from './import-nfts-modal'; export { AccountDetailsMenuItem, ViewExplorerMenuItem } from './menu-items'; +export { ImportTokensModal } from './import-tokens-modal'; diff --git a/ui/components/multichain/multichain-components.scss b/ui/components/multichain/multichain-components.scss index c67217a0f..7f6282a35 100644 --- a/ui/components/multichain/multichain-components.scss +++ b/ui/components/multichain/multichain-components.scss @@ -19,3 +19,4 @@ @import 'network-list-menu/'; @import 'product-tour-popover/product-tour-popover'; @import 'nft-item/nft-item'; +@import 'import-tokens-modal/import-tokens-modal' diff --git a/ui/components/ui/page-container/page-container.component.js b/ui/components/ui/page-container/page-container.component.js index d360bbc1b..315a557e6 100644 --- a/ui/components/ui/page-container/page-container.component.js +++ b/ui/components/ui/page-container/page-container.component.js @@ -88,19 +88,6 @@ export default class PageContainer extends PureComponent { return null; } - getTabSubmitText() { - const { tabsComponent } = this.props; - const { activeTabIndex } = this.state; - if (tabsComponent) { - let { children } = tabsComponent.props; - children = children.filter(Boolean); - if (children[activeTabIndex]?.key === 'custom-tab') { - return this.context.t('addCustomToken'); - } - } - return null; - } - render() { const { title, @@ -118,7 +105,6 @@ export default class PageContainer extends PureComponent { headerCloseText, hideCancel, } = this.props; - const tabSubmitText = this.getTabSubmitText(); return (
diff --git a/ui/components/ui/popover/index.scss b/ui/components/ui/popover/index.scss index 2b53cf892..409758812 100644 --- a/ui/components/ui/popover/index.scss +++ b/ui/components/ui/popover/index.scss @@ -90,4 +90,8 @@ transform: rotate(45deg); box-shadow: var(--shadow-size-lg) var(--color-shadow-default); } + + &-container .page-container { + width: auto; + } } diff --git a/ui/ducks/app/app.ts b/ui/ducks/app/app.ts index e34edcf2d..0ad619c0a 100644 --- a/ui/ducks/app/app.ts +++ b/ui/ducks/app/app.ts @@ -28,6 +28,7 @@ interface AppState { networkDropdownOpen: boolean; importNftsModalOpen: boolean; showIpfsModalOpen: boolean; + importTokensModalOpen: boolean; accountDetail: { subview?: string; accountExport?: string; @@ -98,6 +99,7 @@ const initialState: AppState = { networkDropdownOpen: false, importNftsModalOpen: false, showIpfsModalOpen: false, + importTokensModalOpen: false, accountDetail: { privateKey: '', }, @@ -191,6 +193,18 @@ export default function reduceApp( showIpfsModalOpen: false, }; + case actionConstants.IMPORT_TOKENS_POPOVER_OPEN: + return { + ...appState, + importTokensModalOpen: true, + }; + + case actionConstants.IMPORT_TOKENS_POPOVER_CLOSE: + return { + ...appState, + importTokensModalOpen: false, + }; + // alert methods case actionConstants.ALERT_OPEN: return { diff --git a/ui/pages/confirm-import-token/confirm-import-token.js b/ui/pages/confirm-import-token/confirm-import-token.js deleted file mode 100644 index e45ade9da..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.js +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useCallback, useContext, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import { - ASSET_ROUTE, - IMPORT_TOKEN_ROUTE, -} from '../../helpers/constants/routes'; -import Button from '../../components/ui/button'; -import Identicon from '../../components/ui/identicon'; -import TokenBalance from '../../components/ui/token-balance'; -import { I18nContext } from '../../contexts/i18n'; -import { MetaMetricsContext } from '../../contexts/metametrics'; -import { getMostRecentOverviewPage } from '../../ducks/history/history'; -import { getPendingTokens } from '../../ducks/metamask/metamask'; -import { addImportedTokens, clearPendingTokens } from '../../store/actions'; -import { - MetaMetricsEventCategory, - MetaMetricsEventName, - MetaMetricsTokenEventSource, -} from '../../../shared/constants/metametrics'; -import { - AssetType, - TokenStandard, -} from '../../../shared/constants/transaction'; - -const getTokenName = (name, symbol) => { - return name === undefined ? symbol : `${name} (${symbol})`; -}; - -const ConfirmImportToken = () => { - const t = useContext(I18nContext); - const dispatch = useDispatch(); - const history = useHistory(); - const trackEvent = useContext(MetaMetricsContext); - - const mostRecentOverviewPage = useSelector(getMostRecentOverviewPage); - const pendingTokens = useSelector(getPendingTokens); - - const handleAddTokens = useCallback(async () => { - const addedTokenValues = Object.values(pendingTokens); - await dispatch(addImportedTokens(addedTokenValues)); - - const firstTokenAddress = addedTokenValues?.[0].address?.toLowerCase(); - - addedTokenValues.forEach((pendingToken) => { - trackEvent({ - event: MetaMetricsEventName.TokenAdded, - category: MetaMetricsEventCategory.Wallet, - sensitiveProperties: { - token_symbol: pendingToken.symbol, - token_contract_address: pendingToken.address, - token_decimal_precision: pendingToken.decimals, - unlisted: pendingToken.unlisted, - source: pendingToken.isCustom - ? MetaMetricsTokenEventSource.Custom - : MetaMetricsTokenEventSource.List, - token_standard: TokenStandard.ERC20, - asset_type: AssetType.token, - }, - }); - }); - - dispatch(clearPendingTokens()); - - if (firstTokenAddress) { - history.push(`${ASSET_ROUTE}/${firstTokenAddress}`); - } else { - history.push(mostRecentOverviewPage); - } - }, [dispatch, history, mostRecentOverviewPage, pendingTokens, trackEvent]); - - useEffect(() => { - if (Object.keys(pendingTokens).length === 0) { - history.push(mostRecentOverviewPage); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
-
-
- {t('importTokensCamelCase')} -
-
- {t('likeToImportTokens')} -
-
-
-
-
-
{t('token')}
-
{t('balance')}
-
-
- {Object.entries(pendingTokens).map(([address, token]) => { - const { name, symbol } = token; - - return ( -
-
- -
- {getTokenName(name, symbol)} -
-
-
- -
-
- ); - })} -
-
-
-
-
- - -
-
-
- ); -}; - -export default ConfirmImportToken; diff --git a/ui/pages/confirm-import-token/confirm-import-token.stories.js b/ui/pages/confirm-import-token/confirm-import-token.stories.js deleted file mode 100644 index c9bdb091b..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.stories.js +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable react/prop-types */ -import React, { useEffect } from 'react'; - -import { store, getNewState } from '../../../.storybook/preview'; -import { tokens } from '../../../.storybook/initial-states/approval-screens/add-token'; -import { updateMetamaskState } from '../../store/actions'; -import ConfirmAddToken from '.'; - -export default { - title: 'Pages/ConfirmImportToken', - - argTypes: { - pendingTokens: { - control: 'object', - table: { category: 'Data' }, - }, - }, -}; - -const PageSet = ({ children, pendingTokens }) => { - const { metamask: state } = store.getState(); - - useEffect(() => { - store.dispatch( - updateMetamaskState( - getNewState(state, { - pendingTokens, - }), - ), - ); - }, [state, pendingTokens]); - - return children; -}; - -export const DefaultStory = ({ pendingTokens }) => { - return ( - - - - ); -}; -DefaultStory.args = { - pendingTokens: { ...tokens }, -}; -DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirm-import-token/confirm-import-token.test.js b/ui/pages/confirm-import-token/confirm-import-token.test.js deleted file mode 100644 index c756e8983..000000000 --- a/ui/pages/confirm-import-token/confirm-import-token.test.js +++ /dev/null @@ -1,128 +0,0 @@ -import React from 'react'; -import reactRouterDom from 'react-router-dom'; -import { fireEvent, screen } from '@testing-library/react'; -import { - ASSET_ROUTE, - IMPORT_TOKEN_ROUTE, -} from '../../helpers/constants/routes'; -import { addImportedTokens, clearPendingTokens } from '../../store/actions'; -import configureStore from '../../store/store'; -import { renderWithProvider } from '../../../test/jest'; -import ConfirmImportToken from '.'; - -const MOCK_PENDING_TOKENS = { - '0x6b175474e89094c44da98b954eedeac495271d0f': { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - symbol: 'META', - decimals: 18, - image: 'metamark.svg', - }, - '0xB8c77482e45F1F44dE1745F52C74426C631bDD52': { - address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', - symbol: '0X', - decimals: 18, - image: '0x.svg', - }, -}; - -jest.mock('../../store/actions', () => ({ - addImportedTokens: jest.fn().mockReturnValue({ type: 'test' }), - clearPendingTokens: jest - .fn() - .mockReturnValue({ type: 'CLEAR_PENDING_TOKENS' }), -})); - -const renderComponent = (mockPendingTokens = MOCK_PENDING_TOKENS) => { - const store = configureStore({ - metamask: { - pendingTokens: { ...mockPendingTokens }, - providerConfig: { chainId: '0x1' }, - }, - history: { - mostRecentOverviewPage: '/', - }, - }); - - return renderWithProvider(, store); -}; - -describe('ConfirmImportToken Component', () => { - const mockHistoryPush = jest.fn(); - - beforeEach(() => { - jest - .spyOn(reactRouterDom, 'useHistory') - .mockImplementation() - .mockReturnValue({ push: mockHistoryPush }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should render', () => { - renderComponent(); - - const [title, importTokensBtn] = screen.queryAllByText('Import tokens'); - - expect(title).toBeInTheDocument(title); - expect( - screen.getByText('Would you like to import these tokens?'), - ).toBeInTheDocument(); - expect(screen.getByText('Token')).toBeInTheDocument(); - expect(screen.getByText('Balance')).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'Back' })).toBeInTheDocument(); - expect(importTokensBtn).toBeInTheDocument(); - }); - - it('should render the list of tokens', () => { - renderComponent(); - - Object.values(MOCK_PENDING_TOKENS).forEach((token) => { - expect(screen.getByText(token.symbol)).toBeInTheDocument(); - }); - }); - - it('should go to "IMPORT_TOKEN_ROUTE" route when clicking the "Back" button', async () => { - renderComponent(); - - const backBtn = screen.getByRole('button', { name: 'Back' }); - - await fireEvent.click(backBtn); - expect(mockHistoryPush).toHaveBeenCalledTimes(1); - expect(mockHistoryPush).toHaveBeenCalledWith(IMPORT_TOKEN_ROUTE); - }); - - it('should dispatch clearPendingTokens and redirect to the first token page when clicking the "Import tokens" button', async () => { - const mockFirstPendingTokenAddress = - '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1'; - const mockPendingTokens = { - [mockFirstPendingTokenAddress]: { - address: mockFirstPendingTokenAddress, - symbol: 'CVL', - decimals: 18, - image: 'CVL_token.svg', - }, - '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e': { - address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e', - symbol: 'GLA', - decimals: 18, - image: 'gladius.svg', - }, - }; - renderComponent(mockPendingTokens); - - const importTokensBtn = screen.getByRole('button', { - name: 'Import tokens', - }); - - await fireEvent.click(importTokensBtn); - - expect(addImportedTokens).toHaveBeenCalled(); - expect(clearPendingTokens).toHaveBeenCalled(); - expect(mockHistoryPush).toHaveBeenCalledTimes(1); - expect(mockHistoryPush).toHaveBeenCalledWith( - `${ASSET_ROUTE}/${mockFirstPendingTokenAddress}`, - ); - }); -}); diff --git a/ui/pages/confirm-import-token/index.js b/ui/pages/confirm-import-token/index.js deleted file mode 100644 index 4443efa6b..000000000 --- a/ui/pages/confirm-import-token/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ConfirmImportToken from './confirm-import-token'; - -export default ConfirmImportToken; diff --git a/ui/pages/confirm-import-token/index.scss b/ui/pages/confirm-import-token/index.scss deleted file mode 100644 index 4dbc659ec..000000000 --- a/ui/pages/confirm-import-token/index.scss +++ /dev/null @@ -1,50 +0,0 @@ -.confirm-import-token { - padding: 16px; - - &__header { - @include H7; - - display: flex; - } - - &__token { - flex: 1; - min-width: 0; - } - - &__balance { - flex: 0 0 30%; - min-width: 0; - } - - &__token-list { - display: flex; - flex-flow: column nowrap; - } - - &__token-list-item { - display: flex; - flex-flow: row nowrap; - align-items: center; - margin-top: 8px; - box-sizing: border-box; - } - - &__data { - display: flex; - align-items: center; - padding: 8px; - } - - &__name { - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - &__token-icon { - margin-right: 12px; - flex: 0 0 auto; - } -} diff --git a/ui/pages/import-token/README.mdx b/ui/pages/import-token/README.mdx deleted file mode 100644 index 040a0e925..000000000 --- a/ui/pages/import-token/README.mdx +++ /dev/null @@ -1,31 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import ImportToken from './import-token.component'; - -import testData from '../../../.storybook/test-data'; -import configureStore from '../../store/store'; - -export const PersonalAddress = () => {configureStore(testData).getState().metamask.selectedAddress}; - -# ImportToken - -The `ImportToken` component allows a user to import custom tokens in one of two ways: - -1. By searching for one -2. By importing one by `Token Contract Address` - - - - - -## Example inputs - -An example input that works, to enable the `Add custom token` button is `0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`. - -### Personal address error - -To show the personal address detected error, input the address in the `Token Contract Address` field. - -## Props - - diff --git a/ui/pages/import-token/import-token.component.js b/ui/pages/import-token/import-token.component.js deleted file mode 100644 index 20a00d1aa..000000000 --- a/ui/pages/import-token/import-token.component.js +++ /dev/null @@ -1,659 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { getTokenTrackerLink } from '@metamask/etherscan-link'; -import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; -import { - checkExistingAddresses, - getURLHostName, -} from '../../helpers/utils/util'; -import { tokenInfoGetter } from '../../helpers/utils/token-util'; -import { - CONFIRM_IMPORT_TOKEN_ROUTE, - SECURITY_ROUTE, -} from '../../helpers/constants/routes'; -import TextField from '../../components/ui/text-field'; -import PageContainer from '../../components/ui/page-container'; -import { Tabs, Tab } from '../../components/ui/tabs'; -import { addHexPrefix } from '../../../app/scripts/lib/util'; -import { isValidHexAddress } from '../../../shared/modules/hexstring-utils'; -import ActionableMessage from '../../components/ui/actionable-message/actionable-message'; -import Typography from '../../components/ui/typography'; -import { - TypographyVariant, - FONT_WEIGHT, -} from '../../helpers/constants/design-system'; -import Button from '../../components/ui/button'; -import { TokenStandard } from '../../../shared/constants/transaction'; -import { STATIC_MAINNET_TOKEN_LIST } from '../../../shared/constants/tokens'; -import TokenSearch from './token-search'; -import TokenList from './token-list'; - -const emptyAddr = '0x0000000000000000000000000000000000000000'; - -const MIN_DECIMAL_VALUE = 0; -const MAX_DECIMAL_VALUE = 36; - -class ImportToken extends Component { - static contextTypes = { - t: PropTypes.func, - }; - - static propTypes = { - /** - * History object of the router. - */ - history: PropTypes.object, - - /** - * Set the state of `pendingTokens`, called when adding a token. - */ - setPendingTokens: PropTypes.func, - - /** - * The current list of pending tokens to be added. - */ - pendingTokens: PropTypes.object, - - /** - * Clear the list of pending tokens. Called when closing the modal. - */ - clearPendingTokens: PropTypes.func, - - /** - * Clear the list of pending tokens. Called when closing the modal. - */ - showImportNftsModal: PropTypes.func, - - /** - * The list of already added tokens. - */ - tokens: PropTypes.array, - - /** - * The identities/accounts that are currently added to the wallet. - */ - identities: PropTypes.object, - - /** - * Boolean flag that shows/hides the search tab. - */ - showSearchTab: PropTypes.bool.isRequired, - - /** - * The most recent overview page route, which is 'navigated' to when closing the modal. - */ - mostRecentOverviewPage: PropTypes.string.isRequired, - - /** - * The active chainId in use. - */ - chainId: PropTypes.string, - - /** - * The rpc preferences to use for the current provider. - */ - rpcPrefs: PropTypes.object, - - /** - * The list of tokens available for search. - */ - tokenList: PropTypes.object, - - /** - * Boolean flag indicating whether token detection is enabled or not. - * When disabled, shows an information alert in the search tab informing the - * user of the availability of this feature. - */ - useTokenDetection: PropTypes.bool, - - /** - * Function called to fetch information about the token standard and - * details, see `actions.js`. - */ - getTokenStandardAndDetails: PropTypes.func, - - /** - * The currently selected active address. - */ - selectedAddress: PropTypes.string, - isDynamicTokenListAvailable: PropTypes.bool.isRequired, - tokenDetectionInactiveOnNonMainnetSupportedNetwork: - PropTypes.bool.isRequired, - networkName: PropTypes.string.isRequired, - }; - - static defaultProps = { - tokenList: {}, - }; - - state = { - customAddress: '', - customSymbol: '', - customDecimals: 0, - searchResults: [], - selectedTokens: {}, - standard: TokenStandard.NONE, - tokenSelectorError: null, - customAddressError: null, - customSymbolError: null, - customDecimalsError: null, - nftAddressError: null, - forceEditSymbol: false, - symbolAutoFilled: false, - decimalAutoFilled: false, - mainnetTokenWarning: null, - }; - - componentDidMount() { - this.tokenInfoGetter = tokenInfoGetter(); - const { pendingTokens = {} } = this.props; - const pendingTokenKeys = Object.keys(pendingTokens); - - if (pendingTokenKeys.length > 0) { - let selectedTokens = {}; - let customToken = {}; - - pendingTokenKeys.forEach((tokenAddress) => { - const token = pendingTokens[tokenAddress]; - const { isCustom } = token; - - if (isCustom) { - customToken = { ...token }; - } else { - selectedTokens = { ...selectedTokens, [tokenAddress]: { ...token } }; - } - }); - - const { - address: customAddress = '', - symbol: customSymbol = '', - decimals: customDecimals = 0, - } = customToken; - - this.setState({ - selectedTokens, - customAddress, - customSymbol, - customDecimals, - }); - } - } - - handleToggleToken(token) { - const { address } = token; - const { selectedTokens = {} } = this.state; - const selectedTokensCopy = { ...selectedTokens }; - - if (address in selectedTokensCopy) { - delete selectedTokensCopy[address]; - } else { - selectedTokensCopy[address] = token; - } - - this.setState({ - selectedTokens: selectedTokensCopy, - tokenSelectorError: null, - }); - } - - hasError() { - const { - tokenSelectorError, - customAddressError, - customSymbolError, - customDecimalsError, - nftAddressError, - } = this.state; - - return ( - tokenSelectorError || - customAddressError || - customSymbolError || - customDecimalsError || - nftAddressError - ); - } - - hasSelected() { - const { customAddress = '', selectedTokens = {} } = this.state; - return customAddress || Object.keys(selectedTokens).length > 0; - } - - handleNext() { - if (this.hasError()) { - return; - } - - if (!this.hasSelected()) { - this.setState({ tokenSelectorError: this.context.t('mustSelectOne') }); - return; - } - - const { setPendingTokens, history, tokenList } = this.props; - const tokenAddressList = Object.keys(tokenList); - const { - customAddress: address, - customSymbol: symbol, - customDecimals: decimals, - selectedTokens, - standard, - } = this.state; - - const customToken = { - address, - symbol, - decimals, - standard, - }; - - setPendingTokens({ customToken, selectedTokens, tokenAddressList }); - history.push(CONFIRM_IMPORT_TOKEN_ROUTE); - } - - async attemptToAutoFillTokenParams(address) { - const { tokenList } = this.props; - const { symbol = '', decimals } = await this.tokenInfoGetter( - address, - tokenList, - ); - - const symbolAutoFilled = Boolean(symbol); - const decimalAutoFilled = Boolean(decimals); - this.setState({ symbolAutoFilled, decimalAutoFilled }); - this.handleCustomSymbolChange(symbol || ''); - this.handleCustomDecimalsChange(decimals); - } - - async handleCustomAddressChange(value) { - const customAddress = value.trim(); - this.setState({ - customAddress, - customAddressError: null, - nftAddressError: null, - tokenSelectorError: null, - symbolAutoFilled: false, - decimalAutoFilled: false, - mainnetTokenWarning: null, - }); - - const addressIsValid = isValidHexAddress(customAddress, { - allowNonPrefixed: false, - }); - const standardAddress = addHexPrefix(customAddress).toLowerCase(); - - const isMainnetToken = Object.keys(STATIC_MAINNET_TOKEN_LIST).some( - (key) => key.toLowerCase() === customAddress.toLowerCase(), - ); - - const isMainnetNetwork = this.props.chainId === '0x1'; - - let standard; - if (addressIsValid) { - try { - ({ standard } = await this.props.getTokenStandardAndDetails( - standardAddress, - this.props.selectedAddress, - )); - } catch (error) { - // ignore - } - } - - const addressIsEmpty = - customAddress.length === 0 || customAddress === emptyAddr; - - switch (true) { - case !addressIsValid && !addressIsEmpty: - this.setState({ - customAddressError: this.context.t('invalidAddress'), - customSymbol: '', - customDecimals: 0, - customSymbolError: null, - customDecimalsError: null, - }); - - break; - case standard === 'ERC1155' || standard === 'ERC721': - this.setState({ - nftAddressError: this.context.t('nftAddressError', [ - { - this.props.showImportNftsModal(); - }} - key="nftAddressError" - > - {this.context.t('importNFTPage')} - , - ]), - }); - break; - case isMainnetToken && !isMainnetNetwork: - this.setState({ - mainnetTokenWarning: this.context.t('mainnetToken'), - customSymbol: '', - customDecimals: 0, - customSymbolError: null, - customDecimalsError: null, - }); - - break; - case Boolean(this.props.identities[standardAddress]): - this.setState({ - customAddressError: this.context.t('personalAddressDetected'), - }); - - break; - case checkExistingAddresses(customAddress, this.props.tokens): - this.setState({ - customAddressError: this.context.t('tokenAlreadyAdded'), - }); - - break; - default: - if (!addressIsEmpty) { - this.attemptToAutoFillTokenParams(customAddress); - if (standard) { - this.setState({ standard }); - } - } - } - } - - handleCustomSymbolChange(value) { - const customSymbol = value.trim(); - const symbolLength = customSymbol.length; - let customSymbolError = null; - - if (symbolLength <= 0 || symbolLength >= 12) { - customSymbolError = this.context.t('symbolBetweenZeroTwelve'); - } - - this.setState({ customSymbol, customSymbolError }); - } - - handleCustomDecimalsChange(value) { - let customDecimals; - let customDecimalsError = null; - - if (value) { - customDecimals = Number(value.trim()); - customDecimalsError = - value < MIN_DECIMAL_VALUE || value > MAX_DECIMAL_VALUE - ? this.context.t('decimalsMustZerotoTen') - : null; - } else { - customDecimals = ''; - customDecimalsError = this.context.t('tokenDecimalFetchFailed'); - } - - this.setState({ customDecimals, customDecimalsError }); - } - - renderCustomTokenForm() { - const { t } = this.context; - const { - customAddress, - customSymbol, - customDecimals, - customAddressError, - customSymbolError, - customDecimalsError, - forceEditSymbol, - symbolAutoFilled, - decimalAutoFilled, - mainnetTokenWarning, - nftAddressError, - } = this.state; - - const { - chainId, - rpcPrefs, - isDynamicTokenListAvailable, - tokenDetectionInactiveOnNonMainnetSupportedNetwork, - history, - } = this.props; - const blockExplorerTokenLink = getTokenTrackerLink( - customAddress, - chainId, - null, - null, - { blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null }, - ); - const blockExplorerLabel = rpcPrefs?.blockExplorerUrl - ? getURLHostName(blockExplorerTokenLink) - : t('etherscan'); - - return ( -
- {tokenDetectionInactiveOnNonMainnetSupportedNetwork ? ( - - {t('tokenScamSecurityRisk')} - , - , - ])} - withRightButton - useIcon - iconFillColor="var(--color-warning-default)" - /> - ) : ( - - {t('learnScamRisk')} - , - ], - )} - withRightButton - useIcon - iconFillColor={ - isDynamicTokenListAvailable - ? 'var(--color-warning-default)' - : 'var(--color-info-default)' - } - /> - )} - this.handleCustomAddressChange(e.target.value)} - error={customAddressError || mainnetTokenWarning || nftAddressError} - fullWidth - autoFocus - margin="normal" - /> - - - {t('tokenSymbol')} - - {symbolAutoFilled && !forceEditSymbol && ( -
this.setState({ forceEditSymbol: true })} - > - {t('edit')} -
- )} -
- } - type="text" - value={customSymbol} - onChange={(e) => this.handleCustomSymbolChange(e.target.value)} - error={customSymbolError} - fullWidth - margin="normal" - disabled={symbolAutoFilled && !forceEditSymbol} - /> - this.handleCustomDecimalsChange(e.target.value)} - error={customDecimals ? customDecimalsError : null} - fullWidth - margin="normal" - disabled={decimalAutoFilled} - min={MIN_DECIMAL_VALUE} - max={MAX_DECIMAL_VALUE} - /> - {customDecimals === '' && ( - - - {t('tokenDecimalFetchFailed')} - - - {t('verifyThisTokenDecimalOn', [ - , - ])} - - - } - type="warning" - withRightButton - className="import-token__decimal-warning" - /> - )} - - ); - } - - renderSearchToken() { - const { t } = this.context; - const { tokenList, history, useTokenDetection, networkName } = this.props; - const { tokenSelectorError, selectedTokens, searchResults } = this.state; - return ( -
- {!useTokenDetection && ( - - history.push(`${SECURITY_ROUTE}#token-description`) - } - > - {t('enableFromSettings')} - , - ])} - withRightButton - useIcon - iconFillColor="var(--color-primary-default)" - className="import-token__token-detection-announcement" - /> - )} - - this.setState({ searchResults: results }) - } - error={tokenSelectorError} - tokenList={tokenList} - /> -
- this.handleToggleToken(token)} - /> -
-
- ); - } - - renderTabs() { - const { t } = this.context; - const { showSearchTab } = this.props; - const tabs = []; - - if (showSearchTab) { - tabs.push( - - {this.renderSearchToken()} - , - ); - } - tabs.push( - - {this.renderCustomTokenForm()} - , - ); - - return {tabs}; - } - - render() { - const { history, clearPendingTokens, mostRecentOverviewPage } = this.props; - - return ( - this.handleNext()} - hideCancel - disabled={Boolean(this.hasError()) || !this.hasSelected()} - onClose={() => { - clearPendingTokens(); - history.push(mostRecentOverviewPage); - }} - /> - ); - } -} - -export default ImportToken; diff --git a/ui/pages/import-token/import-token.container.js b/ui/pages/import-token/import-token.container.js deleted file mode 100644 index 9d238d646..000000000 --- a/ui/pages/import-token/import-token.container.js +++ /dev/null @@ -1,68 +0,0 @@ -import { connect } from 'react-redux'; - -import { - setPendingTokens, - clearPendingTokens, - getTokenStandardAndDetails, - showImportNftsModal, -} from '../../store/actions'; -import { getMostRecentOverviewPage } from '../../ducks/history/history'; -import { getProviderConfig } from '../../ducks/metamask/metamask'; -import { - getRpcPrefsForCurrentProvider, - getIsTokenDetectionSupported, - getTokenDetectionSupportNetworkByChainId, - getIsTokenDetectionInactiveOnMainnet, - getIsDynamicTokenListAvailable, - getIstokenDetectionInactiveOnNonMainnetSupportedNetwork, - getTokenList, -} from '../../selectors/selectors'; -import ImportToken from './import-token.component'; - -const mapStateToProps = (state) => { - const { - metamask: { - identities, - tokens, - pendingTokens, - useTokenDetection, - selectedAddress, - }, - } = state; - const { chainId } = getProviderConfig(state); - - const isTokenDetectionInactiveOnMainnet = - getIsTokenDetectionInactiveOnMainnet(state); - const showSearchTab = - getIsTokenDetectionSupported(state) || - isTokenDetectionInactiveOnMainnet || - Boolean(process.env.IN_TEST); - - return { - identities, - mostRecentOverviewPage: getMostRecentOverviewPage(state), - tokens, - pendingTokens, - showSearchTab, - chainId, - rpcPrefs: getRpcPrefsForCurrentProvider(state), - tokenList: getTokenList(state), - useTokenDetection, - selectedAddress, - isDynamicTokenListAvailable: getIsDynamicTokenListAvailable(state), - networkName: getTokenDetectionSupportNetworkByChainId(state), - tokenDetectionInactiveOnNonMainnetSupportedNetwork: - getIstokenDetectionInactiveOnNonMainnetSupportedNetwork(state), - }; -}; -const mapDispatchToProps = (dispatch) => { - return { - setPendingTokens: (tokens) => dispatch(setPendingTokens(tokens)), - clearPendingTokens: () => dispatch(clearPendingTokens()), - showImportNftsModal: () => dispatch(showImportNftsModal()), - getTokenStandardAndDetails: (address, selectedAddress) => - getTokenStandardAndDetails(address, selectedAddress, null), - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(ImportToken); diff --git a/ui/pages/import-token/import-token.stories.js b/ui/pages/import-token/import-token.stories.js deleted file mode 100644 index b5368a430..000000000 --- a/ui/pages/import-token/import-token.stories.js +++ /dev/null @@ -1,120 +0,0 @@ -import React from 'react'; - -import { Provider } from 'react-redux'; -import { action } from '@storybook/addon-actions'; -import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; -import configureStore from '../../store/store'; -import testData from '../../../.storybook/test-data'; -import ImportToken from './import-token.component'; -import README from './README.mdx'; - -const store = configureStore(testData); -const { metamask } = store.getState(); - -const { - networkConfigurations, - identities, - pendingTokens, - selectedAddress, - tokenList, - tokens, -} = metamask; - -export default { - title: 'Pages/ImportToken', - - decorators: [(story) => {story()}], - component: ImportToken, - parameters: { - docs: { - page: README, - }, - }, - argTypes: { - history: { - control: { - type: 'object', - }, - }, - setPendingTokens: { - action: 'setPendingTokens', - }, - pendingTokens: { - control: { - type: 'object', - }, - }, - clearPendingTokens: { - action: 'clearPendingTokens', - }, - tokens: { - control: { - type: 'object', - }, - }, - identities: { - control: { - type: 'object', - }, - }, - showSearchTab: { - control: { - type: 'boolean', - }, - }, - mostRecentOverviewPage: { - control: { - type: 'text', - }, - }, - chainId: { - control: { - type: 'text', - }, - }, - rpcPrefs: { - control: { - type: 'object', - }, - }, - tokenList: { - control: { - type: 'object', - }, - }, - useTokenDetection: { - control: { - type: 'boolean', - }, - }, - getTokenStandardAndDetails: { - action: 'getTokenStandardAndDetails', - }, - selectedAddress: { - control: { - type: 'text', - }, - }, - }, - args: { - history: { - push: action('history.push()'), - }, - pendingTokens, - tokens, - identities, - showSearchTab: true, - mostRecentOverviewPage: DEFAULT_ROUTE, - chainId: networkConfigurations['test-networkConfigurationId-1'].chainId, - rpcPrefs: networkConfigurations['test-networkConfigurationId-1'].rpcPrefs, - tokenList, - useTokenDetection: false, - selectedAddress, - }, -}; - -export const DefaultStory = (args) => { - return ; -}; - -DefaultStory.storyName = 'Default'; diff --git a/ui/pages/import-token/import-token.test.js b/ui/pages/import-token/import-token.test.js deleted file mode 100644 index b2fb6b5a2..000000000 --- a/ui/pages/import-token/import-token.test.js +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import { fireEvent } from '@testing-library/react'; -import { renderWithProvider } from '../../../test/lib/render-helpers'; -import configureStore from '../../store/store'; -import { - setPendingTokens, - clearPendingTokens, - getTokenStandardAndDetails, -} from '../../store/actions'; -import ImportToken from './import-token.container'; - -jest.mock('../../store/actions', () => ({ - getTokenStandardAndDetails: jest - .fn() - .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })), - setPendingTokens: jest - .fn() - .mockImplementation(() => ({ type: 'SET_PENDING_TOKENS' })), - clearPendingTokens: jest - .fn() - .mockImplementation(() => ({ type: 'CLEAR_PENDING_TOKENS' })), -})); - -describe('Import Token', () => { - const historyStub = jest.fn(); - const props = { - history: { - push: historyStub, - }, - showSearchTab: true, - tokenList: {}, - }; - - const render = () => { - const baseStore = { - metamask: { - tokens: [], - providerConfig: { chainId: '0x1' }, - networkConfigurations: {}, - identities: {}, - selectedAddress: '0x1231231', - useTokenDetection: true, - }, - history: { - mostRecentOverviewPage: '/', - }, - }; - - const store = configureStore(baseStore); - - return renderWithProvider(, store); - }; - - describe('Import Token', () => { - it('add custom token button is disabled when no fields are populated', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - const submit = getByText('Add custom token'); - - expect(submit).toBeDisabled(); - }); - - it('edits token address', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - const event = { target: { value: tokenAddress } }; - fireEvent.change(document.getElementById('custom-address'), event); - - expect(document.getElementById('custom-address').value).toStrictEqual( - tokenAddress, - ); - }); - - it('edits token symbol', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenSymbol = 'META'; - const event = { target: { value: tokenSymbol } }; - fireEvent.change(document.getElementById('custom-symbol'), event); - - expect(document.getElementById('custom-symbol').value).toStrictEqual( - tokenSymbol, - ); - }); - - it('edits token decimal precision', () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const tokenPrecision = '2'; - const event = { target: { value: tokenPrecision } }; - fireEvent.change(document.getElementById('custom-decimals'), event); - - expect(document.getElementById('custom-decimals').value).toStrictEqual( - tokenPrecision, - ); - }); - - it('adds custom tokens successfully', async () => { - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const submit = getByText('Add custom token'); - expect(submit).toBeDisabled(); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - fireEvent.change(document.getElementById('custom-address'), { - target: { value: tokenAddress }, - }); - expect(submit).not.toBeDisabled(); - - const tokenSymbol = 'META'; - fireEvent.change(document.getElementById('custom-symbol'), { - target: { value: tokenSymbol }, - }); - - const tokenPrecision = '2'; - await fireEvent.change(document.getElementById('custom-decimals'), { - target: { value: tokenPrecision }, - }); - - expect(submit).not.toBeDisabled(); - fireEvent.click(submit); - expect(setPendingTokens).toHaveBeenCalledWith({ - customToken: { - address: tokenAddress, - decimals: Number(tokenPrecision), - standard: 'ERC20', - symbol: tokenSymbol, - }, - selectedTokens: {}, - tokenAddressList: [], - }); - expect(historyStub).toHaveBeenCalledWith('/confirm-import-token'); - }); - - it('cancels out of import token flow', () => { - const { getByRole } = render(); - const closeButton = getByRole('button', { name: 'close' }); - fireEvent.click(closeButton); - - expect(clearPendingTokens).toHaveBeenCalled(); - expect(historyStub).toHaveBeenCalledWith('/'); - }); - - it('sets and error when a token is an NFT', async () => { - getTokenStandardAndDetails.mockImplementation(() => - Promise.resolve({ standard: 'ERC721' }), - ); - - const { getByText } = render(); - const customTokenButton = getByText('Custom token'); - fireEvent.click(customTokenButton); - - const submit = getByText('Add custom token'); - expect(submit).toBeDisabled(); - - const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; - await fireEvent.change(document.getElementById('custom-address'), { - target: { value: tokenAddress }, - }); - - expect(submit).toBeDisabled(); - - // The last part of this error message won't be found by getByText because it is wrapped as a link. - const errorMessage = getByText('This token is an NFT. Add on the'); - expect(errorMessage).toBeInTheDocument(); - }); - }); -}); diff --git a/ui/pages/import-token/index.js b/ui/pages/import-token/index.js deleted file mode 100644 index 8fa4ee8c8..000000000 --- a/ui/pages/import-token/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import ImportToken from './import-token.container'; - -export default ImportToken; diff --git a/ui/pages/import-token/index.scss b/ui/pages/import-token/index.scss deleted file mode 100644 index be2cc1cc5..000000000 --- a/ui/pages/import-token/index.scss +++ /dev/null @@ -1,79 +0,0 @@ -@import 'token-list/index'; - -.import-token { - $self: &; - - &__custom-token-form { - padding: 8px 16px 16px; - - input[type='number']::-webkit-inner-spin-button { - -webkit-appearance: none; - display: none; - } - - input[type='number']:hover::-webkit-inner-spin-button { - -webkit-appearance: none; - display: none; - } - & #{$self}__decimal-warning { - margin-top: 5px; - } - } - - &__search-token { - padding: 16px 16px 16px 16px; - } - - &__token-list { - margin-top: 16px; - } - - &__custom-symbol { - &__label-wrapper { - display: flex; - flex-flow: row nowrap; - } - - &__label { - flex: 0 0 auto; - } - - &__edit { - flex: 1 1 auto; - text-align: right; - color: var(--color-primary-default); - padding-right: 4px; - cursor: pointer; - } - } - - &__link { - @include H7; - - display: inline; - color: var(--color-primary-default); - padding-left: 0; - } - - &__token-detection-announcement { - margin-bottom: 16px; - margin-top: 0; - } - - &__close { - color: var(--color-icon-default); - background: none; - flex: 0; - align-self: flex-start; - padding-right: 0; - } - - &__nft-address-error-link { - color: var(--color-primary-default); - cursor: pointer; - - &:hover { - color: var(--color-primary-default); - } - } -} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/index.scss b/ui/pages/import-token/token-list/token-list-placeholder/index.scss deleted file mode 100644 index e97741621..000000000 --- a/ui/pages/import-token/token-list/token-list-placeholder/index.scss +++ /dev/null @@ -1,27 +0,0 @@ -.token-list-placeholder { - display: flex; - align-items: center; - padding-top: 36px; - flex-direction: column; - line-height: 22px; - - img { - opacity: 0.5; - } - - &__text { - color: var(--color-text-alternative); - width: 50%; - text-align: center; - margin-top: 8px; - opacity: 0.5; - - @include screen-sm-max { - width: 60%; - } - } - - &__link { - margin-top: 0.5rem; - } -} diff --git a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js deleted file mode 100644 index ffc13bbec..000000000 --- a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Button from '../../../../components/ui/button'; -import IconTokenSearch from '../../../../components/ui/icon/icon-token-search'; -import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url'; - -export default class TokenListPlaceholder extends Component { - static contextTypes = { - t: PropTypes.func, - }; - - render() { - return ( -
- -
- {this.context.t('addAcquiredTokens')} -
- -
- ); - } -} diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss index 72c4b2b49..96f1f29c8 100644 --- a/ui/pages/pages.scss +++ b/ui/pages/pages.scss @@ -1,6 +1,5 @@ /** Please import your files in alphabetical order **/ @import 'asset/asset'; -@import 'confirm-import-token/index'; @import 'confirm-add-suggested-token/index'; @import 'confirm-add-suggested-nft/index'; @import 'confirm-approve/index'; @@ -15,7 +14,6 @@ @import 'desktop-pairing/index'; @import 'error/index'; @import 'home/index'; -@import 'import-token/index'; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) @import "institutional/connect-custody/index"; @import "institutional/institutional-entity-done-page/index"; diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index d6280a79c..c66efe967 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -7,6 +7,7 @@ import IdleTimer from 'react-idle-timer'; ///: BEGIN:ONLY_INCLUDE_IN(desktop) import browserAPI from 'webextension-polyfill'; ///: END:ONLY_INCLUDE_IN + import SendTransactionScreen from '../send'; import Swaps from '../swaps'; import ConfirmTransaction from '../confirm-transaction'; @@ -18,8 +19,6 @@ import Lock from '../lock'; import PermissionsConnect from '../permissions-connect'; import RestoreVaultPage from '../keychains/restore-vault'; import RevealSeedConfirmation from '../keychains/reveal-seed'; -import ImportTokenPage from '../import-token'; -import ConfirmImportTokenPage from '../confirm-import-token'; import ConfirmAddSuggestedTokenPage from '../confirm-add-suggested-token'; import CreateAccountPage from '../create-account/create-account.component'; import ConfirmAddSuggestedNftPage from '../confirm-add-suggested-nft'; @@ -33,6 +32,7 @@ import { NetworkListMenu, AccountDetails, ImportNftsModal, + ImportTokensModal, } from '../../components/multichain'; import UnlockPage from '../unlock-page'; import Alerts from '../../components/app/alerts'; @@ -59,7 +59,6 @@ import CustodyPage from '../institutional/custody'; ///: END:ONLY_INCLUDE_IN import { - IMPORT_TOKEN_ROUTE, ASSET_ROUTE, CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE, CONFIRM_ADD_SUGGESTED_NFT_ROUTE, @@ -76,7 +75,6 @@ import { UNLOCK_ROUTE, BUILD_QUOTE_ROUTE, CONFIRMATION_V_NEXT_ROUTE, - CONFIRM_IMPORT_TOKEN_ROUTE, ONBOARDING_ROUTE, ONBOARDING_UNLOCK_ROUTE, TOKEN_DETAILS, @@ -162,6 +160,8 @@ export default class Routes extends Component { hideImportNftsModal: PropTypes.func.isRequired, isIpfsModalOpen: PropTypes.bool.isRequired, hideIpfsModal: PropTypes.func.isRequired, + isImportTokensModalOpen: PropTypes.bool.isRequired, + hideImportTokensModal: PropTypes.func.isRequired, }; static contextTypes = { @@ -279,16 +279,6 @@ export default class Routes extends Component { exact /> - - hideIpfsModal()} /> ) : null} + {isImportTokensModalOpen ? ( + hideImportTokensModal()} /> + ) : null} {isLoading ? : null} {!isLoading && isNetworkLoading ? : null} diff --git a/ui/pages/routes/routes.container.js b/ui/pages/routes/routes.container.js index 03c0e7f46..d964dfc54 100644 --- a/ui/pages/routes/routes.container.js +++ b/ui/pages/routes/routes.container.js @@ -14,6 +14,7 @@ import { isCurrentProviderCustom, } from '../../selectors'; import { + hideImportTokensModal, lockMetamask, hideImportNftsModal, hideIpfsModal, @@ -64,6 +65,7 @@ function mapStateToProps(state) { completedOnboarding, isAccountMenuOpen: state.metamask.isAccountMenuOpen, isNetworkMenuOpen: state.metamask.isNetworkMenuOpen, + isImportTokensModalOpen: state.appState.importTokensModalOpen, accountDetailsAddress: state.appState.accountDetailsAddress, isImportNftsModalOpen: state.appState.importNftsModalOpen, isIpfsModalOpen: state.appState.showIpfsModalOpen, @@ -83,6 +85,7 @@ function mapDispatchToProps(dispatch) { toggleNetworkMenu: () => dispatch(toggleNetworkMenu()), hideImportNftsModal: () => dispatch(hideImportNftsModal()), hideIpfsModal: () => dispatch(hideIpfsModal()), + hideImportTokensModal: () => dispatch(hideImportTokensModal()), }; } diff --git a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap index f9e256234..1190abd2a 100644 --- a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap +++ b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap @@ -627,6 +627,7 @@ exports[`Security Tab should match snapshot 1`] = `
{t('autoDetectTokens')} diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index 3cbc24497..0a48192b5 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -13,6 +13,8 @@ export const IMPORT_NFTS_MODAL_OPEN = 'UI_IMPORT_NFTS_MODAL_OPEN'; export const IMPORT_NFTS_MODAL_CLOSE = 'UI_IMPORT_NFTS_MODAL_CLOSE'; export const SHOW_IPFS_MODAL_OPEN = 'UI_IPFS_MODAL_OPEN'; export const SHOW_IPFS_MODAL_CLOSE = 'UI_IPFS_MODAL_CLOSE'; +export const IMPORT_TOKENS_POPOVER_OPEN = 'UI_IMPORT_TOKENS_POPOVER_OPEN'; +export const IMPORT_TOKENS_POPOVER_CLOSE = 'UI_IMPORT_TOKENS_POPOVER_CLOSE'; // remote state export const UPDATE_METAMASK_STATE = 'UPDATE_METAMASK_STATE'; export const SELECTED_ADDRESS_CHANGED = 'SELECTED_ADDRESS_CHANGED'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index b7751a7ed..6d92e5b1b 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -2381,6 +2381,18 @@ export function hideNetworkDropdown() { }; } +export function showImportTokensModal(): Action { + return { + type: actionConstants.IMPORT_TOKENS_POPOVER_OPEN, + }; +} + +export function hideImportTokensModal(): Action { + return { + type: actionConstants.IMPORT_TOKENS_POPOVER_CLOSE, + }; +} + type ModalPayload = { name: string } & Record; export function showModal(payload: ModalPayload): PayloadAction { From 2550eaa9ac17aa059b33d1818679ef57a25f6eeb Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 14 Aug 2023 21:48:40 +0530 Subject: [PATCH 14/23] PPOM dependency updates (#20342) --- app/scripts/controllers/decrypt-message.ts | 5 +- .../controllers/encryption-public-key.ts | 5 +- builds.yml | 2 +- lavamoat/browserify/beta/policy.json | 102 +++++++-------- lavamoat/browserify/desktop/policy.json | 102 +++++++-------- lavamoat/browserify/flask/policy.json | 117 ++++++++++-------- lavamoat/browserify/main/policy.json | 102 +++++++-------- lavamoat/browserify/mmi/policy.json | 102 +++++++-------- package.json | 6 +- yarn.lock | 32 ++--- 10 files changed, 299 insertions(+), 276 deletions(-) diff --git a/app/scripts/controllers/decrypt-message.ts b/app/scripts/controllers/decrypt-message.ts index fee01257f..a0d180806 100644 --- a/app/scripts/controllers/decrypt-message.ts +++ b/app/scripts/controllers/decrypt-message.ts @@ -45,7 +45,10 @@ export type CoreMessage = AbstractMessage & { }; export type StateMessage = Required< - Omit + Omit< + AbstractMessage, + 'securityAlertResponse' | 'securityProviderResponse' | 'metadata' | 'error' + > >; export type DecryptMessageControllerState = { diff --git a/app/scripts/controllers/encryption-public-key.ts b/app/scripts/controllers/encryption-public-key.ts index 8448fba8a..4c3bcc622 100644 --- a/app/scripts/controllers/encryption-public-key.ts +++ b/app/scripts/controllers/encryption-public-key.ts @@ -44,7 +44,10 @@ export type CoreMessage = AbstractMessage & { }; export type StateMessage = Required< - Omit + Omit< + AbstractMessage, + 'securityAlertResponse' | 'securityProviderResponse' | 'metadata' | 'error' + > > & { msgParams: string; }; diff --git a/builds.yml b/builds.yml index 13b51de6c..4283eb26e 100644 --- a/builds.yml +++ b/builds.yml @@ -47,7 +47,7 @@ buildTypes: - desktop - build-flask - keyring-snaps - # - blockaid + - blockaid env: - INFURA_FLASK_PROJECT_ID - SEGMENT_FLASK_WRITE_KEY diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index a02644c04..3207a448f 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1088,6 +1088,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1095,8 +1096,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1111,11 +1111,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1422,11 +1422,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -1802,6 +1802,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -2853,7 +2880,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -2865,6 +2892,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -2872,15 +2900,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -2903,8 +2930,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -2951,10 +2978,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3315,6 +3342,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3331,7 +3359,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3455,7 +3482,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3496,6 +3523,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3503,8 +3531,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3542,14 +3569,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3642,7 +3669,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3674,7 +3701,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3720,14 +3747,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -3926,33 +3953,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/desktop/policy.json b/lavamoat/browserify/desktop/policy.json index 01e56f8b9..cc3765332 100644 --- a/lavamoat/browserify/desktop/policy.json +++ b/lavamoat/browserify/desktop/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1159,6 +1159,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1166,8 +1167,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1182,11 +1182,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring": { @@ -1550,11 +1550,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2006,6 +2006,33 @@ "@metamask/post-message-stream>readable-stream>safe-buffer": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/providers>@metamask/object-multiplex": { "globals": { "console.warn": true @@ -3388,7 +3415,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3400,6 +3427,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3407,15 +3435,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3438,8 +3465,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3486,10 +3513,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3866,6 +3893,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3882,7 +3910,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -4006,7 +4033,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4047,6 +4074,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -4054,8 +4082,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -4093,14 +4120,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -4193,7 +4220,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -4225,7 +4252,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -4271,14 +4298,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4477,33 +4504,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 01e56f8b9..08100deb2 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1159,6 +1159,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1166,8 +1167,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1182,11 +1182,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring": { @@ -1550,11 +1550,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2006,6 +2006,48 @@ "@metamask/post-message-stream>readable-stream>safe-buffer": true } }, + "@metamask/ppom-validator": { + "globals": { + "clearInterval": true, + "console.error": true, + "setInterval": true + }, + "packages": { + "@metamask/base-controller": true, + "@metamask/controller-utils": true, + "@metamask/ppom-validator>elliptic": true, + "await-semaphore": true, + "browserify>buffer": true, + "eth-query>json-rpc-random-id": true + } + }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/providers>@metamask/object-multiplex": { "globals": { "console.warn": true @@ -3388,7 +3430,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3400,6 +3442,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3407,15 +3450,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3438,8 +3480,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3486,10 +3528,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3866,6 +3908,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3882,7 +3925,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -4006,7 +4048,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -4047,6 +4089,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -4054,8 +4097,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -4093,14 +4135,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -4193,7 +4235,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -4225,7 +4267,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -4271,14 +4313,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4477,33 +4519,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index a02644c04..3207a448f 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1088,6 +1088,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1095,8 +1096,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1111,11 +1111,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1422,11 +1422,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -1802,6 +1802,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -2853,7 +2880,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -2865,6 +2892,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -2872,15 +2900,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -2903,8 +2930,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -2951,10 +2978,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3315,6 +3342,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3331,7 +3359,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3455,7 +3482,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3496,6 +3523,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3503,8 +3531,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3542,14 +3569,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3642,7 +3669,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3674,7 +3701,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3720,14 +3747,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -3926,33 +3953,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 6b526f97a..d8c191d6d 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -380,7 +380,7 @@ "@ethersproject/abi>@ethersproject/bytes": true, "@ethersproject/abi>@ethersproject/logger": true, "@ethersproject/abi>@ethersproject/properties": true, - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "@ethersproject/hdnode>@ethersproject/transactions": { @@ -1316,6 +1316,7 @@ }, "@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -1323,8 +1324,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-ledger-bridge-keyring>hdkey": { @@ -1339,11 +1339,11 @@ "@metamask/eth-ledger-bridge-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-snap-keyring>@metamask/eth-sig-util": { @@ -1650,11 +1650,11 @@ "@metamask/eth-trezor-keyring>hdkey>secp256k1": { "packages": { "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>insert-module-globals>is-buffer": true, "ethereumjs-util>create-hash": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "@metamask/eth-trezor-keyring>hdkey>secp256k1>bip66": { @@ -2030,6 +2030,33 @@ "eslint>optionator>fast-levenshtein": true } }, + "@metamask/ppom-validator>elliptic": { + "packages": { + "@metamask/ppom-validator>elliptic>brorand": true, + "@metamask/ppom-validator>elliptic>hmac-drbg": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "bn.js": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true, + "pumpify>inherits": true + } + }, + "@metamask/ppom-validator>elliptic>brorand": { + "globals": { + "crypto": true, + "msCrypto": true + }, + "packages": { + "browserify>browser-resolve": true + } + }, + "@metamask/ppom-validator>elliptic>hmac-drbg": { + "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-crypto-utils": true, + "ethereumjs-util>ethereum-cryptography>hash.js": true + } + }, "@metamask/rpc-methods": { "packages": { "@metamask/browser-passworder": true, @@ -3081,7 +3108,7 @@ }, "browserify>crypto-browserify>browserify-cipher>browserify-des>des.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3093,6 +3120,7 @@ }, "browserify>crypto-browserify>browserify-sign": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "browserify>crypto-browserify>create-hmac": true, @@ -3100,15 +3128,14 @@ "browserify>crypto-browserify>public-encrypt>parse-asn1": true, "browserify>stream-browserify": true, "ethereumjs-util>create-hash": true, - "ganache>secp256k1>elliptic": true, "pumpify>inherits": true } }, "browserify>crypto-browserify>create-ecdh": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, - "browserify>buffer": true, - "ganache>secp256k1>elliptic": true + "browserify>buffer": true } }, "browserify>crypto-browserify>create-hmac": { @@ -3131,8 +3158,8 @@ }, "browserify>crypto-browserify>diffie-hellman>miller-rabin": { "packages": { - "bn.js": true, - "ganache>secp256k1>elliptic>brorand": true + "@metamask/ppom-validator>elliptic>brorand": true, + "bn.js": true } }, "browserify>crypto-browserify>pbkdf2": { @@ -3179,10 +3206,10 @@ }, "browserify>crypto-browserify>public-encrypt>parse-asn1>asn1.js": { "packages": { + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "bn.js": true, "browserify>buffer": true, "browserify>vm-browserify": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3543,6 +3570,7 @@ "packages": { "@ethereumjs/common>crc-32": true, "@ethersproject/abi": true, + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>buffer": true, "eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true, @@ -3559,7 +3587,6 @@ "ethereumjs-util>ethereum-cryptography>hash.js": true, "ethereumjs-wallet>aes-js": true, "ethereumjs-wallet>bs58check": true, - "ganache>secp256k1>elliptic": true, "lodash": true } }, @@ -3683,7 +3710,7 @@ }, "eth-lattice-keyring>gridplus-sdk>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "eth-lattice-keyring>gridplus-sdk>uuid": { @@ -3724,6 +3751,7 @@ }, "eth-sig-util>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, @@ -3731,8 +3759,7 @@ "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, "ethereumjs-util>rlp": true, - "ethereumjs-wallet>safe-buffer": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-wallet>safe-buffer": true } }, "eth-sig-util>ethereumjs-util>ethjs-util": { @@ -3770,14 +3797,14 @@ }, "ethereumjs-abi>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-util": { @@ -3870,7 +3897,7 @@ }, "ethereumjs-util>ethereum-cryptography>hash.js": { "packages": { - "ganache>secp256k1>elliptic>minimalistic-assert": true, + "@metamask/ppom-validator>elliptic>minimalistic-assert": true, "pumpify>inherits": true } }, @@ -3902,7 +3929,7 @@ }, "ethereumjs-util>ethereum-cryptography>secp256k1": { "packages": { - "ganache>secp256k1>elliptic": true + "@metamask/ppom-validator>elliptic": true } }, "ethereumjs-util>rlp": { @@ -3948,14 +3975,14 @@ }, "ethereumjs-wallet>ethereumjs-util": { "packages": { + "@metamask/ppom-validator>elliptic": true, "bn.js": true, "browserify>assert": true, "browserify>buffer": true, "eth-sig-util>ethereumjs-util>ethjs-util": true, "ethereumjs-util>create-hash": true, "ethereumjs-util>ethereum-cryptography": true, - "ethereumjs-util>rlp": true, - "ganache>secp256k1>elliptic": true + "ethereumjs-util>rlp": true } }, "ethereumjs-wallet>randombytes": { @@ -4154,33 +4181,6 @@ "define": true } }, - "ganache>secp256k1>elliptic": { - "packages": { - "bn.js": true, - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>brorand": true, - "ganache>secp256k1>elliptic>hmac-drbg": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true, - "pumpify>inherits": true - } - }, - "ganache>secp256k1>elliptic>brorand": { - "globals": { - "crypto": true, - "msCrypto": true - }, - "packages": { - "browserify>browser-resolve": true - } - }, - "ganache>secp256k1>elliptic>hmac-drbg": { - "packages": { - "ethereumjs-util>ethereum-cryptography>hash.js": true, - "ganache>secp256k1>elliptic>minimalistic-assert": true, - "ganache>secp256k1>elliptic>minimalistic-crypto-utils": true - } - }, "globalthis>define-properties": { "packages": { "globalthis>define-properties>has-property-descriptors": true, diff --git a/package.json b/package.json index 23f872073..1290980e5 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ }, "dependencies": { "@babel/runtime": "^7.18.9", - "@blockaid/ppom": "^0.1.2", + "@blockaid/ppom": "^1.0.2", "@download/blockies": "^1.0.3", "@ensdomains/content-hash": "^2.5.6", "@ethereumjs/common": "^3.1.1", @@ -250,7 +250,7 @@ "@metamask/key-tree": "^9.0.0", "@metamask/keyring-controller": "^7.0.0", "@metamask/logo": "^3.1.1", - "@metamask/message-manager": "^7.0.2", + "@metamask/message-manager": "^7.3.0", "@metamask/metamask-eth-abis": "^3.0.0", "@metamask/network-controller": "^12.0.0", "@metamask/notification-controller": "^3.0.0", @@ -258,7 +258,7 @@ "@metamask/permission-controller": "^4.0.0", "@metamask/phishing-controller": "^6.0.0", "@metamask/post-message-stream": "^6.0.0", - "@metamask/ppom-validator": "^0.1.2", + "@metamask/ppom-validator": "^0.2.0", "@metamask/providers": "^11.1.0", "@metamask/rate-limit-controller": "^3.0.0", "@metamask/rpc-methods": "^1.0.0-prerelease.1", diff --git a/yarn.lock b/yarn.lock index 23b862842..5f6089519 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1619,10 +1619,10 @@ __metadata: languageName: node linkType: hard -"@blockaid/ppom@npm:^0.1.2": - version: 0.1.2 - resolution: "@blockaid/ppom@npm:0.1.2" - checksum: ff5485db12a11a0b60465faf27b22e365c5a6fdc9b08d932e8a50a50effe1d07059c0e9d3e76f8bf051fbe32f473f6fdac57c5993f6633d2668d3760aa53223b +"@blockaid/ppom@npm:^1.0.2": + version: 1.0.2 + resolution: "@blockaid/ppom@npm:1.0.2" + checksum: d288117f19400108b0177df63d6accc3e06a60f203d36b9e3b1d8cabc55e167674cb000d1fe50971a7fe73662ca55659211682a0d20e676c66ccba112043d9fb languageName: node linkType: hard @@ -4364,9 +4364,9 @@ __metadata: languageName: node linkType: hard -"@metamask/message-manager@npm:^7.0.2, @metamask/message-manager@npm:^7.2.0": - version: 7.2.0 - resolution: "@metamask/message-manager@npm:7.2.0" +"@metamask/message-manager@npm:^7.2.0, @metamask/message-manager@npm:^7.3.0": + version: 7.3.0 + resolution: "@metamask/message-manager@npm:7.3.0" dependencies: "@metamask/base-controller": "npm:^3.2.0" "@metamask/controller-utils": "npm:^4.3.0" @@ -4376,7 +4376,7 @@ __metadata: ethereumjs-util: "npm:^7.0.10" jsonschema: "npm:^1.2.4" uuid: "npm:^8.3.2" - checksum: 2129b2418b8a45234e4e00c1f3aabd386e2b6d5bba7db04b1a19fcd8d4930c8c10de6660a272bab227e9bfed684dc6a37cef26403471ffd5c6fe71921e06e576 + checksum: 2001622731a01a777270d9d4c5f1c7897aa3071bcbc6eb6ccbac819574d0c4786a070ef519fcf11aaa950cedd5a1feba3e1cc55b8641c2a87cb791aab5120903 languageName: node linkType: hard @@ -4566,14 +4566,16 @@ __metadata: languageName: node linkType: hard -"@metamask/ppom-validator@npm:^0.1.2": - version: 0.1.2 - resolution: "@metamask/ppom-validator@npm:0.1.2" +"@metamask/ppom-validator@npm:^0.2.0": + version: 0.2.0 + resolution: "@metamask/ppom-validator@npm:0.2.0" dependencies: "@metamask/base-controller": "npm:^3.0.0" "@metamask/controller-utils": "npm:^4.0.0" await-semaphore: "npm:^0.1.3" - checksum: 1a034882e7040fe66061c956f3736bc6cf164212cc07c9bf520dd64f9df49b6ef51554c1d00ec832e723293d4287e37c1d9ca00112e185bba58aedd6205e27c3 + elliptic: "npm:^6.5.4" + json-rpc-random-id: "npm:^1.0.1" + checksum: c86e3d2aa8321347ffaf45290d9c8b0d4f3a594c88dc2090933b6159a604c2f574360462faba7cbda7fa1f32f730d159f90847bdd47badb0dc0cd027be0db9e8 languageName: node linkType: hard @@ -24213,7 +24215,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.16.7" "@babel/register": "npm:^7.5.5" "@babel/runtime": "npm:^7.18.9" - "@blockaid/ppom": "npm:^0.1.2" + "@blockaid/ppom": "npm:^1.0.2" "@download/blockies": "npm:^1.0.3" "@ensdomains/content-hash": "npm:^2.5.6" "@ethereumjs/common": "npm:^3.1.1" @@ -24267,7 +24269,7 @@ __metadata: "@metamask/key-tree": "npm:^9.0.0" "@metamask/keyring-controller": "npm:^7.0.0" "@metamask/logo": "npm:^3.1.1" - "@metamask/message-manager": "npm:^7.0.2" + "@metamask/message-manager": "npm:^7.3.0" "@metamask/metamask-eth-abis": "npm:^3.0.0" "@metamask/network-controller": "npm:^12.0.0" "@metamask/notification-controller": "npm:^3.0.0" @@ -24276,7 +24278,7 @@ __metadata: "@metamask/phishing-controller": "npm:^6.0.0" "@metamask/phishing-warning": "npm:^2.1.0" "@metamask/post-message-stream": "npm:^6.0.0" - "@metamask/ppom-validator": "npm:^0.1.2" + "@metamask/ppom-validator": "npm:^0.2.0" "@metamask/providers": "npm:^11.1.0" "@metamask/rate-limit-controller": "npm:^3.0.0" "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" From f8187c2538bfb499846f388f8b486b3e17da86d9 Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Mon, 14 Aug 2023 13:55:41 -0230 Subject: [PATCH 15/23] Fix opening of speedup popover in activity list (in the popup view) (#20205) * Set height of activity-list-item rightContent to Blocksize.min, so that the element doesn't overlap buttons * Fix unit test --------- Co-authored-by: Brad Decker --- .../__snapshots__/activity-list-item.test.js.snap | 2 +- .../multichain/activity-list-item/activity-list-item.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap b/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap index 81f7135bd..3bce90a09 100644 --- a/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap +++ b/ui/components/multichain/activity-list-item/__snapshots__/activity-list-item.test.js.snap @@ -86,7 +86,7 @@ exports[`ActivityListItem should match snapshot with props 1`] = `

Content rendered to the right diff --git a/ui/components/multichain/activity-list-item/activity-list-item.js b/ui/components/multichain/activity-list-item/activity-list-item.js index 1cc51fb0e..e2a2f3100 100644 --- a/ui/components/multichain/activity-list-item/activity-list-item.js +++ b/ui/components/multichain/activity-list-item/activity-list-item.js @@ -114,6 +114,7 @@ export const ActivityListItem = ({ Date: Mon, 14 Aug 2023 18:38:03 +0200 Subject: [PATCH 16/23] Add Blockaid / PPOM Failed Request Security Alert (#20362) * WIP blockaid: add request failed UI - proposed to update reason value. planning to update upon update * alphabetize * BlockaidBannerAlert: update tkeys based on reason * SecurityProviderBannerAlert: fix footer alignment * BlockaidBannerAlert: rm footer w failed resultType --------- Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> Co-authored-by: Jyoti Puri --- app/_locales/en/messages.json | 6 ++ shared/constants/security-provider.ts | 2 +- ...ecurity-provider-banner-alert.test.js.snap | 2 +- .../blockaid-banner-alert.test.js.snap | 30 +++++++++- .../blockaid-banner-alert.js | 23 ++++---- .../blockaid-banner-alert.test.js | 49 +++++++++++----- .../security-provider-banner-alert.js | 58 ++++++++++--------- .../security-provider-banner-alert.stories.js | 5 +- 8 files changed, 116 insertions(+), 59 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a5073b434..07d28c054 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -565,6 +565,9 @@ "blockaidDescriptionBlurFarming": { "message": "If you approve this request, someone can steal your assets listed on Blur." }, + "blockaidDescriptionFailed": { + "message": "Because of an error, this request was not verified by the security provider. Proceed with caution." + }, "blockaidDescriptionMaliciousDomain": { "message": "You're interacting with a malicious domain. If you approve this request, you might lose your assets." }, @@ -580,6 +583,9 @@ "blockaidTitleDeceptive": { "message": "This is a deceptive request" }, + "blockaidTitleMayNotBeSafe": { + "message": "Request may not be safe" + }, "blockaidTitleSuspicious": { "message": "This is a suspicious request" }, diff --git a/shared/constants/security-provider.ts b/shared/constants/security-provider.ts index 8f9168321..cb2773d29 100644 --- a/shared/constants/security-provider.ts +++ b/shared/constants/security-provider.ts @@ -49,8 +49,8 @@ export enum BlockaidReason { other = 'other', // Locally defined - notApplicable = 'NotApplicable', failed = 'Failed', + notApplicable = 'NotApplicable', } export enum BlockaidResultType { diff --git a/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap b/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap index 7cf86eeb7..37761b2ba 100644 --- a/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap +++ b/ui/components/app/security-provider-banner-alert/__snapshots__/security-provider-banner-alert.test.js.snap @@ -59,7 +59,7 @@ exports[`Security Provider Banner Alert should match snapshot 1`] = `

`; +exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Failed 1`] = ` +

+ +
+
+ This is a deceptive request +
+

+ If you approve this request, a third party known for scams might take all your assets. +

+
+
+`; + exports[`Blockaid Banner Alert should render 'warning' UI when securityAlertResponse.result_type is 'Warning 1`] = `

); + const isFailedResultType = resultType === BlockaidResultType.Failed; + const severity = resultType === BlockaidResultType.Malicious ? Severity.Danger : Severity.Warning; - const title = - SUSPCIOUS_REASON.indexOf(reason) > -1 - ? t('blockaidTitleSuspicious') - : t('blockaidTitleDeceptive'); + const title = t(REASON_TO_TITLE_TKEY[reason] || 'blockaidTitleDeceptive'); return ( diff --git a/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js b/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js index 7710be531..9e9e3bd07 100644 --- a/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js +++ b/ui/components/app/security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert.test.js @@ -40,7 +40,7 @@ describe('Blockaid Banner Alert', () => { expect(container.querySelector('.mm-banner-alert')).toBeNull(); }); - it(`should not render when securityAlertResponse.result_type is '${BlockaidResultType.Failed}'`, () => { + it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Failed}`, () => { const { container } = renderWithLocalization( { }} />, ); + const warningBannerAlert = container.querySelector( + '.mm-banner-alert--severity-warning', + ); - expect(container.querySelector('.mm-banner-alert')).toBeNull(); + expect(warningBannerAlert).toBeInTheDocument(); + expect(warningBannerAlert).toMatchSnapshot(); + }); + + it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Warning}`, () => { + const { container } = renderWithLocalization( + , + ); + const warningBannerAlert = container.querySelector( + '.mm-banner-alert--severity-warning', + ); + + expect(warningBannerAlert).toBeInTheDocument(); + expect(warningBannerAlert).toMatchSnapshot(); }); it(`should render '${Severity.Danger}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Malicious}`, () => { @@ -70,18 +86,6 @@ describe('Blockaid Banner Alert', () => { expect(dangerBannerAlert).toMatchSnapshot(); }); - it(`should render '${Severity.Warning}' UI when securityAlertResponse.result_type is '${BlockaidResultType.Warning}`, () => { - const { container } = renderWithLocalization( - , - ); - const warningBannerAlert = container.querySelector( - '.mm-banner-alert--severity-warning', - ); - - expect(warningBannerAlert).toBeInTheDocument(); - expect(warningBannerAlert).toMatchSnapshot(); - }); - it('should render title, "This is a deceptive request"', () => { const { getByText } = renderWithLocalization( , @@ -90,7 +94,20 @@ 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 "raw_signature_farming"', () => { + it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.failed}"`, () => { + const { getByText } = renderWithLocalization( + , + ); + + expect(getByText('Request may not be safe')).toBeInTheDocument(); + }); + + it(`should render title, "This is a suspicious request", when the reason is "${BlockaidReason.rawSignatureFarming}"`, () => { const { getByText } = renderWithLocalization( { '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]: + '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.", [BlockaidReason.other]: diff --git a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js index e3b777b8b..5e115c193 100644 --- a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js +++ b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.js @@ -15,6 +15,7 @@ import { I18nContext } from '../../../contexts/i18n'; import { AlignItems, Color, + Display, IconColor, Severity, Size, @@ -45,30 +46,33 @@ function SecurityProviderBannerAlert({ )} - - - {t('securityProviderAdviceBy', [ - - {t(SECURITY_PROVIDER_CONFIG[provider].tKeyName)} - , - ])} - + {provider && ( + + + {t('securityProviderAdviceBy', [ + + {t(SECURITY_PROVIDER_CONFIG[provider].tKeyName)} + , + ])} + + )} ); } @@ -78,9 +82,6 @@ SecurityProviderBannerAlert.propTypes = { description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]) .isRequired, - /** Name of the security provider */ - provider: PropTypes.oneOfType(Object.values(SecurityProvider)).isRequired, - /** Severity level */ severity: PropTypes.oneOfType([Severity.Danger, Severity.Warning]).isRequired, @@ -93,6 +94,9 @@ SecurityProviderBannerAlert.propTypes = { /** Additional details to be displayed under the description */ details: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + + /** Name of the security provider */ + provider: PropTypes.oneOfType(Object.values(SecurityProvider)), }; export default SecurityProviderBannerAlert; diff --git a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js index 2b81f2035..ee2f2a311 100644 --- a/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js +++ b/ui/components/app/security-provider-banner-alert/security-provider-banner-alert.stories.js @@ -53,7 +53,10 @@ export default { control: { type: 'select', }, - options: [Object.values(SecurityProvider)], + options: ['none', ...Object.values(SecurityProvider)], + mapping: { + none: null, + }, }, severity: { control: { From 712a62ed74a6ea7339f682953e0d02fa6952c487 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Mon, 14 Aug 2023 11:53:11 -0500 Subject: [PATCH 17/23] UX: Remove unwanted extra spacing on home screen (#20441) Co-authored-by: Victor Thomas <10986371+vthomas13@users.noreply.github.com> --- ui/pages/home/home.component.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 9c261bfe9..e200027f1 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -988,20 +988,21 @@ export default class Home extends PureComponent { ///: END:ONLY_INCLUDE_IN } -

- { - ///: BEGIN:ONLY_INCLUDE_IN(build-beta) + { + ///: BEGIN:ONLY_INCLUDE_IN(build-beta) +
- ///: END:ONLY_INCLUDE_IN - } - { - ///: BEGIN:ONLY_INCLUDE_IN(build-flask) +
+ ///: END:ONLY_INCLUDE_IN + } + { + ///: BEGIN:ONLY_INCLUDE_IN(build-flask) +
- ///: END:ONLY_INCLUDE_IN - } -
+
+ ///: END:ONLY_INCLUDE_IN + }
- {this.renderNotifications()}
From e0a6435f62365b0cf4edb1e00f9479dd25855a74 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Mon, 14 Aug 2023 21:00:34 +0200 Subject: [PATCH 18/23] Bump SES to fix audit failure (#20434) * Bump SES to fix audit failure * Freeze Symbol --- app/scripts/lockdown-more.js | 2 +- package.json | 2 +- yarn.lock | 19 ++++++++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/scripts/lockdown-more.js b/app/scripts/lockdown-more.js index e6637602a..317052312 100644 --- a/app/scripts/lockdown-more.js +++ b/app/scripts/lockdown-more.js @@ -28,7 +28,7 @@ try { const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis); // These named intrinsics are not automatically hardened by `lockdown` - const shouldHardenManually = new Set(['eval', 'Function']); + const shouldHardenManually = new Set(['eval', 'Function', 'Symbol']); const globalProperties = new Set([ // universalPropertyNames is a constant added by lockdown to global scope diff --git a/package.json b/package.json index 59aedf671..a5696c96a 100644 --- a/package.json +++ b/package.json @@ -358,7 +358,7 @@ "redux-thunk": "^2.3.0", "remove-trailing-slash": "^0.1.1", "reselect": "^3.0.1", - "ses": "^0.18.4", + "ses": "^0.18.7", "single-call-balance-checker-abi": "^1.0.0", "unicode-confusables": "^0.1.1", "uuid": "^8.3.2", diff --git a/yarn.lock b/yarn.lock index 5f6089519..b8a2760b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1864,6 +1864,13 @@ __metadata: languageName: node linkType: hard +"@endo/env-options@npm:^0.1.3": + version: 0.1.3 + resolution: "@endo/env-options@npm:0.1.3" + checksum: dbaa1c6edf2de712b0af76c991f2ffd29e52a27d3adca2292531dbbf42ef0541c03daa4d7f2de3efb223d0cacf303c82fe88c19d44a0f5535dce9ccbe5f66138 + languageName: node + linkType: hard + "@ensdomains/address-encoder@npm:^0.1.7": version: 0.1.9 resolution: "@ensdomains/address-encoder@npm:0.1.9" @@ -24521,7 +24528,7 @@ __metadata: selenium-webdriver: "npm:^4.9.0" semver: "npm:^7.5.4" serve-handler: "npm:^6.1.2" - ses: "npm:^0.18.4" + ses: "npm:^0.18.7" single-call-balance-checker-abi: "npm:^1.0.0" sinon: "npm:^9.0.0" source-map: "npm:^0.7.2" @@ -30935,10 +30942,12 @@ __metadata: languageName: node linkType: hard -"ses@npm:^0.18.1, ses@npm:^0.18.4": - version: 0.18.4 - resolution: "ses@npm:0.18.4" - checksum: 377d2a898816fe2651fe4cff3832f52efb7f0649a9e439b89d0f39f9d15279fb5bb6aadf85759af6768da43e9dbe096d2e39697d66171412c87f3acf09fcbc30 +"ses@npm:^0.18.1, ses@npm:^0.18.7": + version: 0.18.7 + resolution: "ses@npm:0.18.7" + dependencies: + "@endo/env-options": "npm:^0.1.3" + checksum: 601b442426bb8113bb855548878771aacb62f3c4eb3c45ff32f70338320f03eeb23c0d7eb834f5539793fa8ce497f304db50cbd5da51ef3531a4e94d424bb98c languageName: node linkType: hard From e31c9338697d4cb1f11c078810baf46e8ce177e4 Mon Sep 17 00:00:00 2001 From: Dhruv <79097544+dhruvv173@users.noreply.github.com> Date: Tue, 15 Aug 2023 01:55:41 +0530 Subject: [PATCH 19/23] updating SnapSettingsCard (#20296) Co-authored-by: George Marshall --- .../app/snaps/snap-settings-card/README.mdx | 41 ----- .../snap-settings-card/snap-settings-card.js | 31 ++-- .../snap-settings-card.stories.js | 140 ++---------------- 3 files changed, 26 insertions(+), 186 deletions(-) delete mode 100644 ui/components/app/snaps/snap-settings-card/README.mdx diff --git a/ui/components/app/snaps/snap-settings-card/README.mdx b/ui/components/app/snaps/snap-settings-card/README.mdx deleted file mode 100644 index 7ff49feb7..000000000 --- a/ui/components/app/snaps/snap-settings-card/README.mdx +++ /dev/null @@ -1,41 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import SnapSettingsCard from '.'; - -# SnapSettingsCard - -A card component that displays information and the status of a snap. The `SnapSettingsCard` component is made up of the `Card`, `IconBorder`, `IconWithFallback`, `ToggleButton`, `Chip`, `ColorIndicator` and `Button` components - - - - - -## Props - - - -## Usage - -The following describes the props and example usage for this component. - -### Status - -There are 4 statuses the `SnapSettingsCard` can have: `'installing'`,`'running'`,`'stopped'` and `'crashed'`. - - - - - -### isEnabled / onToggle - -Use the `isEnabled` and `onToggle` to control the `ToggleButton` component inside of the `SnapSettingsCard` - -```jsx -const [isEnabled, setIsEnabled] = React.useState(false); - -const handleOnToggle = () => { - setIsEnabled(!isEnabled); -}; - -return ; -``` diff --git a/ui/components/app/snaps/snap-settings-card/snap-settings-card.js b/ui/components/app/snaps/snap-settings-card/snap-settings-card.js index 6f7fc6822..184b042b4 100644 --- a/ui/components/app/snaps/snap-settings-card/snap-settings-card.js +++ b/ui/components/app/snaps/snap-settings-card/snap-settings-card.js @@ -1,42 +1,46 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Box from '../../../ui/box'; - import { Color, AlignItems, JustifyContent, - DISPLAY, - BLOCK_SIZES, + Display, + BlockSize, IconColor, TextVariant, } from '../../../../helpers/constants/design-system'; -import { Icon, IconName, IconSize, Text } from '../../../component-library'; +import { + Icon, + IconName, + IconSize, + Text, + Box, +} from '../../../component-library'; import SnapAvatar from '../snap-avatar'; const SnapSettingsCard = ({ name, packageName, onClick, snapId }) => { return ( - + { SnapSettingsCard.propTypes = { /** - * Name of the snap used for the title of the card and fallback letter for the snap icon + * Name of the snap */ name: PropTypes.string, /** @@ -74,7 +78,7 @@ SnapSettingsCard.propTypes = { */ packageName: PropTypes.string, /** - * onClick function of the "See Details" Button + * onClick event handler */ onClick: PropTypes.func, /** @@ -82,5 +86,4 @@ SnapSettingsCard.propTypes = { */ snapId: PropTypes.string.isRequired, }; - export default SnapSettingsCard; diff --git a/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js b/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js index bf9045142..484deb152 100644 --- a/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js +++ b/ui/components/app/snaps/snap-settings-card/snap-settings-card.stories.js @@ -1,152 +1,30 @@ import React from 'react'; -import { useArgs } from '@storybook/client-api'; - -import README from './README.mdx'; import SnapSettingsCard from '.'; export default { title: 'Components/App/Snaps/SnapSettingsCard', - component: SnapSettingsCard, - parameters: { - docs: { - page: README, - }, - }, argTypes: { name: { control: 'text', }, - description: { + packageName: { control: 'text', }, - icon: { - control: 'text', - }, - dateAdded: { - control: 'text', - }, - version: { - control: 'text', - }, - url: { - control: 'text', - }, - onToggle: { - action: 'onToggle', - }, - isEnabled: { - control: 'boolean', - }, onClick: { action: 'onClick', }, - status: { - control: { - type: 'select', - }, - options: ['installing', 'stopped', 'running', 'crashed'], - }, - className: { - control: 'string', - }, - cardProps: { - control: 'object', - }, - toggleButtonProps: { - control: 'object', - }, - buttonProps: { - control: 'object', - }, - chipProps: { - control: 'object', + snapId: { + control: 'text', }, }, + args: { + name: 'Snap Name', + packageName: 'Snap Package Name', + snapId: 'npm:@metamask/test-snap-bip44', + }, }; -export const DefaultStory = (args) => { - const [{ isEnabled }, updateArgs] = useArgs(); - - const handleOnToggle = () => { - updateArgs({ - isEnabled: !isEnabled, - status: isEnabled ? 'stopped' : 'running', - }); - }; - return ( - - ); -}; +export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; - -let d = new Date(); -d = d.toDateString(); - -DefaultStory.args = { - name: 'Snap name', - description: - 'This snap provides developers everywhere access to an entirely new data storage paradigm, even letting your programs store data autonomously.', - icon: 'AST.png', - dateAdded: d, - version: '10.5.1234', - url: 'https://metamask.io/', - status: 'stopped', -}; - -export const Status = () => ( - <> - - - - - -); From 7c2f7671b0f241ba9c1c32c44c839d10adbbc649 Mon Sep 17 00:00:00 2001 From: Dhruv <79097544+dhruvv173@users.noreply.github.com> Date: Tue, 15 Aug 2023 03:43:15 +0530 Subject: [PATCH 20/23] ButtonSecondary to TS (#20411) * ButtonSecondary to TS * updating components and addressing errors * lint and snapshot updates * using Boolean conversion for className * removing ValidButtonTag type * fix text color when link --------- Co-authored-by: garrettbear --- .../modals/eth-sign-modal/eth-sign-modal.js | 7 ++- .../button-secondary/README.mdx | 21 +++----- ...js.snap => button-secondary.test.tsx.snap} | 0 .../button-secondary.constants.js | 7 --- .../button-secondary/button-secondary.js | 54 ------------------- .../button-secondary/button-secondary.scss | 4 +- ...tories.js => button-secondary.stories.tsx} | 31 +++++------ ...dary.test.js => button-secondary.test.tsx} | 27 +++++----- .../button-secondary/button-secondary.tsx | 40 ++++++++++++++ .../button-secondary.types.ts | 38 +++++++++++++ .../button-secondary/index.js | 2 - .../button-secondary/index.ts | 3 ++ ui/components/component-library/index.js | 2 +- .../account-details-display.js | 4 +- .../import-account/bottom-buttons.js | 6 +-- .../import-nfts-modal/import-nfts-modal.js | 3 +- .../network-list-menu/network-list-menu.js | 4 +- 17 files changed, 133 insertions(+), 120 deletions(-) rename ui/components/component-library/button-secondary/__snapshots__/{button-secondary.test.js.snap => button-secondary.test.tsx.snap} (100%) delete mode 100644 ui/components/component-library/button-secondary/button-secondary.constants.js delete mode 100644 ui/components/component-library/button-secondary/button-secondary.js rename ui/components/component-library/button-secondary/{button-secondary.stories.js => button-secondary.stories.tsx} (77%) rename ui/components/component-library/button-secondary/{button-secondary.test.js => button-secondary.test.tsx} (77%) create mode 100644 ui/components/component-library/button-secondary/button-secondary.tsx create mode 100644 ui/components/component-library/button-secondary/button-secondary.types.ts delete mode 100644 ui/components/component-library/button-secondary/index.js create mode 100644 ui/components/component-library/button-secondary/index.ts diff --git a/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js b/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js index 47b056a7b..41944bb58 100644 --- a/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js +++ b/ui/components/app/modals/eth-sign-modal/eth-sign-modal.js @@ -9,6 +9,7 @@ import { ButtonLink, ButtonPrimary, ButtonSecondary, + ButtonSecondarySize, FormTextField, Icon, IconName, @@ -152,7 +153,11 @@ const EthSignModal = ({ hideModal }) => { gap={4} marginTop={6} > - hideModal()} size={Size.LG} block> + hideModal()} + size={ButtonSecondarySize.Lg} + block + > {t('cancel')} {showTextField ? ( diff --git a/ui/components/component-library/button-secondary/README.mdx b/ui/components/component-library/button-secondary/README.mdx index 177cde8d0..35570acf3 100644 --- a/ui/components/component-library/button-secondary/README.mdx +++ b/ui/components/component-library/button-secondary/README.mdx @@ -12,33 +12,28 @@ The `ButtonSecondary` is an extension of `ButtonBase` to support secondary style ## Props -The `ButtonSecondary` accepts all props below as well as all [Box](/docs/components-ui-box--default-story#props) and [ButtonBase](/docs/components-componentlibrary-buttonbase--default-story#props) component props - ### Size -Use the `size` prop and the `Size` object from `./ui/helpers/constants/design-system.js` to change the size of `ButtonSecondary`. Defaults to `Size.MD` - -Optional: `BUTTON_SIZES` from `./button-base` object can be used instead of `Size`. +Use the `size` prop and the `ButtonSecondarySize` enum from `./ui/components/component-library` to change the size of `ButtonSecondary`. Defaults to `ButtonSecondarySize.Md` Possible sizes include: -- `Size.SM` 32px -- `Size.MD` 40px -- `Size.LG` 48px +- `ButtonSecondarySize.Sm` 32px +- `ButtonSecondarySize.Md` 40px +- `ButtonSecondarySize.Lg` 48px ```jsx -import { Size } from '../../../helpers/constants/design-system'; -import { ButtonSecondary } from '../../component-library'; +import { ButtonSecondary, ButtonSecondarySize } from '../../component-library'; - - - + + + ``` ### Danger diff --git a/ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.js.snap b/ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.tsx.snap similarity index 100% rename from ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.js.snap rename to ui/components/component-library/button-secondary/__snapshots__/button-secondary.test.tsx.snap diff --git a/ui/components/component-library/button-secondary/button-secondary.constants.js b/ui/components/component-library/button-secondary/button-secondary.constants.js deleted file mode 100644 index 28181aa04..000000000 --- a/ui/components/component-library/button-secondary/button-secondary.constants.js +++ /dev/null @@ -1,7 +0,0 @@ -import { Size } from '../../../helpers/constants/design-system'; - -export const BUTTON_SECONDARY_SIZES = { - SM: Size.SM, - MD: Size.MD, - LG: Size.LG, -}; diff --git a/ui/components/component-library/button-secondary/button-secondary.js b/ui/components/component-library/button-secondary/button-secondary.js deleted file mode 100644 index 399ea67fe..000000000 --- a/ui/components/component-library/button-secondary/button-secondary.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - -import { ButtonBase } from '../button-base'; -import { Color } from '../../../helpers/constants/design-system'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; - -export const ButtonSecondary = ({ - className, - danger, - disabled, - size = BUTTON_SECONDARY_SIZES.MD, - ...props -}) => { - const buttonColor = danger ? Color.errorDefault : Color.primaryDefault; - return ( - - ); -}; - -ButtonSecondary.propTypes = { - /** - * An additional className to apply to the ButtonSecondary. - */ - className: PropTypes.string, - /** - * When true, ButtonSecondary color becomes Danger. - */ - danger: PropTypes.bool, - /** - * Boolean to disable button - */ - disabled: PropTypes.bool, - /** - * Possible size values: 'SIZES.SM'(32px), 'SIZES.MD'(40px), 'SIZES.LG'(48px). - * Default value is 'SIZES.MD'. - */ - size: PropTypes.oneOf(Object.values(BUTTON_SECONDARY_SIZES)), - /** - * ButtonSecondary accepts all the props from ButtonBase - */ - ...ButtonBase.propTypes, -}; diff --git a/ui/components/component-library/button-secondary/button-secondary.scss b/ui/components/component-library/button-secondary/button-secondary.scss index f602270bf..c279ce88a 100644 --- a/ui/components/component-library/button-secondary/button-secondary.scss +++ b/ui/components/component-library/button-secondary/button-secondary.scss @@ -1,5 +1,5 @@ .mm-button-secondary { - &:hover { + &:hover:not(&--disabled) { color: var(--color-primary-inverse); background-color: var(--color-primary-default); box-shadow: var(--component-button-primary-shadow); @@ -12,7 +12,7 @@ } // Danger type - &--type-danger { + &--type-danger:not(&--disabled) { color: var(--color-error-default); border: 1px solid var(--color-error-default); background-color: transparent; diff --git a/ui/components/component-library/button-secondary/button-secondary.stories.js b/ui/components/component-library/button-secondary/button-secondary.stories.tsx similarity index 77% rename from ui/components/component-library/button-secondary/button-secondary.stories.js rename to ui/components/component-library/button-secondary/button-secondary.stories.tsx index 94166d359..a7883213f 100644 --- a/ui/components/component-library/button-secondary/button-secondary.stories.js +++ b/ui/components/component-library/button-secondary/button-secondary.stories.tsx @@ -1,14 +1,9 @@ import React from 'react'; -import { - AlignItems, - DISPLAY, - Size, -} from '../../../helpers/constants/design-system'; -import Box from '../../ui/box/box'; -import { IconName } from '..'; -import { ButtonSecondary } from './button-secondary'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; +import { StoryFn, Meta } from '@storybook/react'; +import { AlignItems, Display } from '../../../helpers/constants/design-system'; +import { IconName, Box } from '..'; import README from './README.mdx'; +import { ButtonSecondary, ButtonSecondarySize } from '.'; const marginSizeControlOptions = [ undefined, @@ -84,7 +79,7 @@ export default { }, size: { control: 'select', - options: Object.values(BUTTON_SECONDARY_SIZES), + options: Object.values(ButtonSecondarySize), }, marginTop: { options: marginSizeControlOptions, @@ -110,29 +105,29 @@ export default { args: { children: 'Button Secondary', }, -}; +} as Meta; export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; -export const SizeStory = (args) => ( - - +export const SizeStory: StoryFn = (args) => ( + + Small Button - + Medium (Default) Button - + Large Button ); SizeStory.storyName = 'Size'; -export const Danger = (args) => ( - +export const Danger: StoryFn = (args) => ( + Normal {/* Test Anchor tag to match exactly as button */} diff --git a/ui/components/component-library/button-secondary/button-secondary.test.js b/ui/components/component-library/button-secondary/button-secondary.test.tsx similarity index 77% rename from ui/components/component-library/button-secondary/button-secondary.test.js rename to ui/components/component-library/button-secondary/button-secondary.test.tsx index 0529664cd..f8730d34a 100644 --- a/ui/components/component-library/button-secondary/button-secondary.test.js +++ b/ui/components/component-library/button-secondary/button-secondary.test.tsx @@ -2,8 +2,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { IconName } from '..'; -import { ButtonSecondary } from './button-secondary'; -import { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; +import { ButtonSecondary, ButtonSecondarySize } from '.'; describe('ButtonSecondary', () => { it('should render button element correctly', () => { @@ -45,28 +44,28 @@ describe('ButtonSecondary', () => { const { getByTestId } = render( <> , ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.SM)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.SM}`, + expect(getByTestId(ButtonSecondarySize.Sm)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Sm}`, ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.MD)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.MD}`, + expect(getByTestId(ButtonSecondarySize.Md)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Md}`, ); - expect(getByTestId(BUTTON_SECONDARY_SIZES.LG)).toHaveClass( - `mm-button-base--size-${BUTTON_SECONDARY_SIZES.LG}`, + expect(getByTestId(ButtonSecondarySize.Lg)).toHaveClass( + `mm-button-base--size-${ButtonSecondarySize.Lg}`, ); }); diff --git a/ui/components/component-library/button-secondary/button-secondary.tsx b/ui/components/component-library/button-secondary/button-secondary.tsx new file mode 100644 index 000000000..404b97301 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import classnames from 'classnames'; + +import { ButtonBase, ButtonBaseProps } from '../button-base'; +import { Color } from '../../../helpers/constants/design-system'; +import { PolymorphicRef } from '../box'; +import type { ButtonSecondaryProps } from './button-secondary.types'; +import { + ButtonSecondarySize, + ButtonSecondaryComponent, +} from './button-secondary.types'; + +export const ButtonSecondary: ButtonSecondaryComponent = React.forwardRef( + ( + { + className = '', + danger, + disabled, + size = ButtonSecondarySize.Md, + ...props + }: ButtonSecondaryProps, + ref?: PolymorphicRef, + ) => { + const buttonColor = danger ? Color.errorDefault : Color.primaryDefault; + return ( + ) }} + /> + ); + }, +); diff --git a/ui/components/component-library/button-secondary/button-secondary.types.ts b/ui/components/component-library/button-secondary/button-secondary.types.ts new file mode 100644 index 000000000..e3d73d467 --- /dev/null +++ b/ui/components/component-library/button-secondary/button-secondary.types.ts @@ -0,0 +1,38 @@ +import type { ButtonBaseStyleUtilityProps } from '../button-base/button-base.types'; +import type { PolymorphicComponentPropWithRef } from '../box'; + +export enum ButtonSecondarySize { + Sm = 'sm', + Md = 'md', + Lg = 'lg', +} + +export interface ButtonSecondaryStyleUtilityProps + extends Omit { + /** + * An additional className to apply to the ButtonSecondary. + */ + className?: string; + /** + * When true, ButtonSecondary color becomes Danger. + */ + danger?: boolean; + /** + * Boolean to disable button + */ + disabled?: boolean; + /** + * Possible size values: 'ButtonSecondarySize.Sm'(32px), 'ButtonSecondarySize.Md'(40px), 'ButtonSecondarySize.Lg'(48px). + * Default value is 'ButtonSecondarySize.Md'. + */ + size?: ButtonSecondarySize; +} + +export type ButtonSecondaryProps = + PolymorphicComponentPropWithRef; + +export type ButtonSecondaryComponent = < + C extends React.ElementType = 'button' | 'a', +>( + props: ButtonSecondaryProps, +) => React.ReactElement | null; diff --git a/ui/components/component-library/button-secondary/index.js b/ui/components/component-library/button-secondary/index.js deleted file mode 100644 index bccde44c2..000000000 --- a/ui/components/component-library/button-secondary/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { ButtonSecondary } from './button-secondary'; -export { BUTTON_SECONDARY_SIZES } from './button-secondary.constants'; diff --git a/ui/components/component-library/button-secondary/index.ts b/ui/components/component-library/button-secondary/index.ts new file mode 100644 index 000000000..5f7818004 --- /dev/null +++ b/ui/components/component-library/button-secondary/index.ts @@ -0,0 +1,3 @@ +export { ButtonSecondary } from './button-secondary'; +export { ButtonSecondarySize } from './button-secondary.types'; +export type { ButtonSecondaryProps } from './button-secondary.types'; diff --git a/ui/components/component-library/index.js b/ui/components/component-library/index.js index a993c763a..f7ee14fb1 100644 --- a/ui/components/component-library/index.js +++ b/ui/components/component-library/index.js @@ -20,7 +20,7 @@ export { ButtonBase, ButtonBaseSize } from './button-base'; export { ButtonIcon, ButtonIconSize } from './button-icon'; export { ButtonLink, ButtonLinkSize } from './button-link'; export { ButtonPrimary, ButtonPrimarySize } from './button-primary'; -export { ButtonSecondary, BUTTON_SECONDARY_SIZES } from './button-secondary'; +export { ButtonSecondary, ButtonSecondarySize } from './button-secondary'; export { Checkbox } from './checkbox'; export { FormTextField } from './form-text-field'; export { HeaderBase } from './header-base'; diff --git a/ui/components/multichain/account-details/account-details-display.js b/ui/components/multichain/account-details/account-details-display.js index 7e70bfaae..410515089 100644 --- a/ui/components/multichain/account-details/account-details-display.js +++ b/ui/components/multichain/account-details/account-details-display.js @@ -13,7 +13,7 @@ import { } from '../../../selectors'; import { isAbleToExportAccount } from '../../../helpers/utils/util'; import { - BUTTON_SECONDARY_SIZES, + ButtonSecondarySize, ButtonSecondary, Box, } from '../../component-library'; @@ -74,7 +74,7 @@ export const AccountDetailsDisplay = ({ {exportPrivateKeyFeatureEnabled ? ( { trackEvent({ diff --git a/ui/components/multichain/import-account/bottom-buttons.js b/ui/components/multichain/import-account/bottom-buttons.js index d9b32af61..426f53588 100644 --- a/ui/components/multichain/import-account/bottom-buttons.js +++ b/ui/components/multichain/import-account/bottom-buttons.js @@ -5,7 +5,7 @@ import { ButtonPrimary, ButtonSecondary, Box, - BUTTON_SECONDARY_SIZES, + ButtonSecondarySize, } from '../../component-library'; import { Display } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; @@ -32,7 +32,7 @@ export default function BottomButtons({ dispatch(actions.hideWarning()); onActionComplete(); }} - size={BUTTON_SECONDARY_SIZES.LG} + size={ButtonSecondarySize.Lg} block > {t('cancel')} @@ -49,7 +49,7 @@ export default function BottomButtons({ } }} disabled={isPrimaryDisabled} - size={BUTTON_SECONDARY_SIZES.LG} + size={ButtonSecondarySize.Lg} data-testid="import-account-confirm-button" block > 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 b399c0fb9..0d5544660 100644 --- a/ui/components/multichain/import-nfts-modal/import-nfts-modal.js +++ b/ui/components/multichain/import-nfts-modal/import-nfts-modal.js @@ -44,6 +44,7 @@ import { Modal, ButtonPrimary, ButtonSecondary, + ButtonSecondarySize, Box, FormTextField, Label, @@ -258,7 +259,7 @@ export const ImportNftsModal = ({ onClose }) => { paddingBottom={4} > onClose()} block className="import-nfts-modal__cancel-button" diff --git a/ui/components/multichain/network-list-menu/network-list-menu.js b/ui/components/multichain/network-list-menu/network-list-menu.js index aa410abf8..5cac6e04e 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.js +++ b/ui/components/multichain/network-list-menu/network-list-menu.js @@ -29,7 +29,7 @@ import { TextColor, } from '../../../helpers/constants/design-system'; import { - BUTTON_SECONDARY_SIZES, + ButtonSecondarySize, ButtonSecondary, Modal, ModalContent, @@ -242,7 +242,7 @@ export const NetworkListMenu = ({ onClose }) => { ) : null} { From 63d67f3d2ff287da57f9ce38082d75fd6ea34af7 Mon Sep 17 00:00:00 2001 From: Dhruv <79097544+dhruvv173@users.noreply.github.com> Date: Tue, 15 Aug 2023 04:37:45 +0530 Subject: [PATCH 21/23] replacing deprecated Box component and updating story (#20301) Co-authored-by: George Marshall Co-authored-by: Garrett Bear --- .../update-snap-permission-list.js | 2 +- .../update-snap-permission-list.stories.js | 89 +++++++++++-------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js index 216591489..a3b33f857 100644 --- a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js +++ b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { getWeightedPermissions } from '../../../../helpers/utils/permission'; import { useI18nContext } from '../../../../hooks/useI18nContext'; import PermissionCell from '../../permission-cell'; -import Box from '../../../ui/box'; +import { Box } from '../../../component-library'; export default function UpdateSnapPermissionList({ approvedPermissions, diff --git a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js index 6aab35b5f..2b5b25e6b 100644 --- a/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js +++ b/ui/components/app/snaps/update-snap-permission-list/update-snap-permission-list.stories.js @@ -2,50 +2,65 @@ import React from 'react'; import UpdateSnapPermissionList from './update-snap-permission-list'; export default { - title: 'Components/App/UpdateSnapPermissionList', + title: 'Components/App/Snaps/UpdateSnapPermissionList', component: UpdateSnapPermissionList, - argTypes: { - permissions: { + approvedPermissions: { control: 'object', }, + revokedPermissions: { + control: 'object', + }, + newPermissions: { + control: 'object', + }, + targetSubjectMetadata: { + control: 'object', + }, + }, + args: { + approvedPermissions: { + 'endowment:network-access': { + date: 1620710693178, + }, + snap_getBip32PublicKey: { + date: 1620710693178, + caveats: [ + { + value: [ + { + path: ['m', `44'`, `0'`], + curve: 'secp256k1', + }, + ], + }, + ], + }, + }, + revokedPermissions: { + snap_notify: { + date: 1620710693178, + }, + eth_accounts: { + date: 1620710693178, + }, + }, + newPermissions: { + snap_dialog: { + date: 1620710693178, + }, + }, + targetSubjectMetadata: { + extensionId: null, + iconUrl: null, + name: 'TypeScript Example Snap', + origin: 'local:http://localhost:8080', + subjectType: 'snap', + version: '0.2.2', + }, }, }; export const DefaultStory = (args) => ; DefaultStory.storyName = 'Default'; - -DefaultStory.args = { - approvedPermissions: { - 'endowment:network-access': { - date: 1620710693178, - }, - snap_getBip32PublicKey: { - date: 1620710693178, - caveats: [ - { - value: [ - { - path: ['m', `44'`, `0'`], - curve: 'secp256k1', - }, - ], - }, - ], - }, - }, - revokedPermissions: { - snap_notify: { - date: 1620710693178, - }, - eth_accounts: { - date: 1620710693178, - }, - }, - newPermissions: { - snap_dialog: { - date: 1620710693178, - }, - }, -}; From 260962402b844a60696c5e18cce412f7038b85b2 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 15 Aug 2023 09:38:38 +0200 Subject: [PATCH 22/23] `snaps@1.0.0` (#20450) --- package.json | 8 +++---- yarn.lock | 68 ++++++++++++++++++++++++++-------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index a5696c96a..88a03d3bd 100644 --- a/package.json +++ b/package.json @@ -261,18 +261,18 @@ "@metamask/ppom-validator": "^0.2.0", "@metamask/providers": "^11.1.0", "@metamask/rate-limit-controller": "^3.0.0", - "@metamask/rpc-methods": "^1.0.0-prerelease.1", + "@metamask/rpc-methods": "^1.0.0", "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.37.2-flask.1", "@metamask/safe-event-emitter": "^2.0.0", "@metamask/scure-bip39": "^2.0.3", "@metamask/signature-controller": "^5.3.0", "@metamask/slip44": "^3.0.0", "@metamask/smart-transactions-controller": "^4.0.0", - "@metamask/snaps-controllers": "^1.0.0-prerelease.1", + "@metamask/snaps-controllers": "^1.0.0", "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1", - "@metamask/snaps-ui": "^1.0.0-prerelease.1", + "@metamask/snaps-ui": "^1.0.0", "@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.37.3-flask.1", - "@metamask/snaps-utils": "^1.0.0-prerelease.1", + "@metamask/snaps-utils": "^1.0.0", "@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.38.0-flask.1", "@metamask/subject-metadata-controller": "^2.0.0", "@metamask/utils": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index b8a2760b1..f9a0a9995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4691,22 +4691,22 @@ __metadata: languageName: node linkType: hard -"@metamask/rpc-methods@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/rpc-methods@npm:1.0.0-prerelease.1" +"@metamask/rpc-methods@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/rpc-methods@npm:1.0.0" dependencies: "@metamask/browser-passworder": "npm:^4.0.2" "@metamask/key-tree": "npm:^7.1.1" "@metamask/permission-controller": "npm:^4.0.0" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/types": "npm:^1.1.0" "@metamask/utils": "npm:^6.0.1" "@noble/hashes": "npm:^1.1.3" eth-rpc-errors: "npm:^4.0.2" nanoid: "npm:^3.1.31" superstruct: "npm:^1.0.3" - checksum: ea07ca26eb8b99be108e7d6f790d2ed2e74aa859f228c4e974410663d0a6ddcc4aa1aee87688b0491d70352c96a707931f8364ceddd088065ac04c12577b2b36 + checksum: 7e5f2900f9a54bcc112d9861eeb461de5a7803fdaa4e1bfee1c1c9f68a659dc42f56a7dbbc4f8147f66927c7192d1b5314cc32ca5d8985b969694582127b8fa8 languageName: node linkType: hard @@ -4856,19 +4856,19 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-controllers@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-controllers@npm:1.0.0-prerelease.1" +"@metamask/snaps-controllers@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-controllers@npm:1.0.0" dependencies: "@metamask/approval-controller": "npm:^3.0.0" "@metamask/base-controller": "npm:^3.0.0" "@metamask/object-multiplex": "npm:^1.2.0" "@metamask/permission-controller": "npm:^4.0.0" "@metamask/post-message-stream": "npm:^6.1.2" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-execution-environments": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" + "@metamask/snaps-execution-environments": "npm:^1.0.0" "@metamask/snaps-registry": "npm:^1.2.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" "@xstate/fsm": "npm:^2.0.0" concat-stream: "npm:^2.0.0" @@ -4882,7 +4882,7 @@ __metadata: pump: "npm:^3.0.0" readable-web-to-node-stream: "npm:^3.0.2" tar-stream: "npm:^2.2.0" - checksum: 4977d962019d2f26c1b4487ceeb42690bfaadf8f78bbe6600ae15c5b13da3ddad4ac1eb4632f538c1d912c1de2401005646972ebc371b1b788fdf8faa6fe79aa + checksum: f9ab5a5f593d5d0e971e682d3b32758d30e4bb444ba48f2f66dcf662305ed5c38394fadab32c625d9d171025736637c15765e443d01ef6be247ab75875e0e2e5 languageName: node linkType: hard @@ -4928,15 +4928,15 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-execution-environments@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-execution-environments@npm:1.0.0-prerelease.1" +"@metamask/snaps-execution-environments@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-execution-environments@npm:1.0.0" dependencies: "@metamask/object-multiplex": "npm:^1.2.0" "@metamask/post-message-stream": "npm:^6.1.1" "@metamask/providers": "npm:^10.2.0" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" eth-rpc-errors: "npm:^4.0.3" json-rpc-engine: "npm:^6.1.0" @@ -4944,7 +4944,7 @@ __metadata: ses: "npm:^0.18.1" stream-browserify: "npm:^3.0.0" superstruct: "npm:^1.0.3" - checksum: 209834cf5ffd51013b91cecc287f4a3c73482307481b02e95262653626a23e801bc542ec39557dc84905065ac3fdf5c9f0ab532ca1bb467b2851558548510eca + checksum: 744af06aab2952da69efa6922eb886a6cdbbec0368b35d3d253ecedcc940001e08b2496aa87acfbfe88d7e38955c8e807e942a4c86fc6c01ed86ce44f2106180 languageName: node linkType: hard @@ -4989,13 +4989,13 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-ui@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-ui@npm:1.0.0-prerelease.1" +"@metamask/snaps-ui@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-ui@npm:1.0.0" dependencies: "@metamask/utils": "npm:^6.0.1" superstruct: "npm:^1.0.3" - checksum: 2eca86fdfdfcc5620ec4e6f1ce365e3d694183105c02add8cb390abeae008da38f6626d0172ae0cada6164f9506f03a2cd2cafd625368879a3f97ec89deaf137 + checksum: 805d23c43eb9a5d7ed7d332c9f98187b755142aeb37129d29a5153d2c9bd995beb5508a4d7f26b9d958d403768decded133d8b0c9935d3ac691f6e26fa81c285 languageName: node linkType: hard @@ -5088,9 +5088,9 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^1.0.0-prerelease.1": - version: 1.0.0-prerelease.1 - resolution: "@metamask/snaps-utils@npm:1.0.0-prerelease.1" +"@metamask/snaps-utils@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/snaps-utils@npm:1.0.0" dependencies: "@babel/core": "npm:^7.18.6" "@babel/types": "npm:^7.18.7" @@ -5099,7 +5099,7 @@ __metadata: "@metamask/permission-controller": "npm:^4.0.0" "@metamask/providers": "npm:^10.2.1" "@metamask/snaps-registry": "npm:^1.2.1" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" "@metamask/utils": "npm:^6.0.1" "@noble/hashes": "npm:^1.1.3" "@scure/base": "npm:^1.1.1" @@ -5108,11 +5108,11 @@ __metadata: fast-deep-equal: "npm:^3.1.3" fast-json-stable-stringify: "npm:^2.1.0" rfdc: "npm:^1.3.0" - semver: "npm:^7.3.7" - ses: "npm:^0.18.1" + semver: "npm:^7.5.4" + ses: "npm:^0.18.7" superstruct: "npm:^1.0.3" validate-npm-package-name: "npm:^5.0.0" - checksum: 5adda5d8aa059e6115deb29246e3828200c90dad4e212c872521306e2d9f43c4ea66be93e999a5fedb592744473fc89ba90011ef4dec23f1737a101dd5213a03 + checksum: daf2ff95c7fbd3c68ef47b3816aba9fbbe7363adc780500fe03b3b0b0ba23ca382e16feeb6deb909d458e08c035214e5819a48d8f7456499934299224f980b8f languageName: node linkType: hard @@ -24288,18 +24288,18 @@ __metadata: "@metamask/ppom-validator": "npm:^0.2.0" "@metamask/providers": "npm:^11.1.0" "@metamask/rate-limit-controller": "npm:^3.0.0" - "@metamask/rpc-methods": "npm:^1.0.0-prerelease.1" + "@metamask/rpc-methods": "npm:^1.0.0" "@metamask/rpc-methods-flask": "npm:@metamask/rpc-methods@0.37.2-flask.1" "@metamask/safe-event-emitter": "npm:^2.0.0" "@metamask/scure-bip39": "npm:^2.0.3" "@metamask/signature-controller": "npm:^5.3.0" "@metamask/slip44": "npm:^3.0.0" "@metamask/smart-transactions-controller": "npm:^4.0.0" - "@metamask/snaps-controllers": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-controllers": "npm:^1.0.0" "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1" - "@metamask/snaps-ui": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-ui": "npm:^1.0.0" "@metamask/snaps-ui-flask": "npm:@metamask/snaps-ui@0.37.3-flask.1" - "@metamask/snaps-utils": "npm:^1.0.0-prerelease.1" + "@metamask/snaps-utils": "npm:^1.0.0" "@metamask/snaps-utils-flask": "npm:@metamask/snaps-utils@0.38.0-flask.1" "@metamask/subject-metadata-controller": "npm:^2.0.0" "@metamask/test-dapp": "npm:^7.0.1" From 0ca4a3c45790fd1640d722350c139cf7daca86dc Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Tue, 15 Aug 2023 07:16:13 -0230 Subject: [PATCH 23/23] Fix bad changelog entry for v10.34.1 (#20445) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 583701a69..265973d75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [10.34.1] ### Fixed - Fix bug that could cause a failure in the persistence of network related data ([#20080](https://github.com/MetaMask/metamask-extension/pull/20080)) -- Fix ([#20080](https://github.com/MetaMask/metamask-extension/pull/20080)) +- Fix possible crash when opening the network menu ([#20181](https://github.com/MetaMask/metamask-extension/pull/20181)) ## [10.34.0] ### Added