1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 09:57:02 +01:00

Merge pull request #20389 from MetaMask/Version-v10.34.3

Version v10.34.3
This commit is contained in:
Dan J Miller 2023-08-03 19:17:13 -02:30 committed by GitHub
commit a042f90db1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 228 additions and 39 deletions

View File

@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [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] ## [10.34.2]
### Added ### Added
- Add Address Details and View on Explorer to Global Menu ([#20013](https://github.com/MetaMask/metamask-extension/pull/20013)) - 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 ### Uncategorized
- Added the ability to restore accounts from seed words. - 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.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.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 [10.34.0]: https://github.com/MetaMask/metamask-extension/compare/v10.33.1...v10.34.0

View File

@ -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: [],
},
});
});
});

View File

@ -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<string, unknown>;
}) {
const versionedData = cloneDeep(originalVersionedData);
versionedData.meta.version = version;
versionedData.data = transformState(versionedData.data);
return versionedData;
}
function transformState(state: Record<string, unknown>) {
if (
hasProperty(state, 'PhishingController') &&
isObject(state.PhishingController)
) {
delete state.PhishingController.stalelistLastFetched;
delete state.PhishingController.hotlistLastFetched;
}
return state;
}

View File

@ -95,6 +95,7 @@ import * as m088 from './088';
import * as m089 from './089'; import * as m089 from './089';
import * as m090 from './090'; import * as m090 from './090';
import * as m091 from './091'; import * as m091 from './091';
import * as m092 from './092';
const migrations = [ const migrations = [
m002, m002,
@ -187,6 +188,7 @@ const migrations = [
m089, m089,
m090, m090,
m091, m091,
m092,
]; ];
export default migrations; export default migrations;

View File

@ -1,6 +1,6 @@
{ {
"name": "metamask-crx", "name": "metamask-crx",
"version": "10.34.2", "version": "10.34.3",
"private": true, "private": true,
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -119,7 +119,6 @@ export const UI_NOTIFICATIONS = {
date: null, date: null,
image: { image: {
src: 'images/global-menu-block-explorer.svg', src: 'images/global-menu-block-explorer.svg',
width: '100%',
}, },
}, },
}; };

View File

@ -111,7 +111,7 @@ describe('Swaps - notifications', function () {
}); });
await checkNotification(driver, { await checkNotification(driver, {
title: 'Insufficient balance', 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, { await reviewQuote(driver, {
swapFrom: 'TESTETH', swapFrom: 'TESTETH',

View File

@ -100,6 +100,9 @@ function getActionFunctionById(id, history) {
updateViewedNotifications({ 21: true }); updateViewedNotifications({ 21: true });
history.push(PREPARE_SWAP_ROUTE); history.push(PREPARE_SWAP_ROUTE);
}, },
22: () => {
updateViewedNotifications({ 22: true });
},
}; };
return actionFunctions[id]; return actionFunctions[id];
@ -360,6 +363,7 @@ export default function WhatsNewPopup({
18: renderFirstNotification, 18: renderFirstNotification,
19: renderFirstNotification, 19: renderFirstNotification,
21: renderFirstNotification, 21: renderFirstNotification,
22: renderFirstNotification,
}; };
return ( return (

View File

@ -121,6 +121,7 @@ const initialState = {
currentSmartTransactionsError: '', currentSmartTransactionsError: '',
swapsSTXLoading: false, swapsSTXLoading: false,
transactionSettingsOpened: false, transactionSettingsOpened: false,
latestAddedTokenTo: '',
}; };
const slice = createSlice({ const slice = createSlice({
@ -150,6 +151,9 @@ const slice = createSlice({
setFetchingQuotes: (state, action) => { setFetchingQuotes: (state, action) => {
state.fetchingQuotes = action.payload; state.fetchingQuotes = action.payload;
}, },
setLatestAddedTokenTo: (state, action) => {
state.latestAddedTokenTo = action.payload;
},
setFromToken: (state, action) => { setFromToken: (state, action) => {
state.fromToken = action.payload; state.fromToken = action.payload;
}, },
@ -245,6 +249,8 @@ export const getToToken = (state) => state.swaps.toToken;
export const getFetchingQuotes = (state) => state.swaps.fetchingQuotes; export const getFetchingQuotes = (state) => state.swaps.fetchingQuotes;
export const getLatestAddedTokenTo = (state) => state.swaps.latestAddedTokenTo;
export const getQuotesFetchStartTime = (state) => export const getQuotesFetchStartTime = (state) =>
state.swaps.quotesFetchStartTime; state.swaps.quotesFetchStartTime;
@ -479,6 +485,7 @@ const {
setAggregatorMetadata, setAggregatorMetadata,
setBalanceError, setBalanceError,
setFetchingQuotes, setFetchingQuotes,
setLatestAddedTokenTo,
setFromToken, setFromToken,
setFromTokenError, setFromTokenError,
setFromTokenInputValue, setFromTokenInputValue,
@ -502,6 +509,7 @@ export {
setAggregatorMetadata, setAggregatorMetadata,
setBalanceError, setBalanceError,
setFetchingQuotes, setFetchingQuotes,
setLatestAddedTokenTo,
setFromToken as setSwapsFromToken, setFromToken as setSwapsFromToken,
setFromTokenError, setFromTokenError,
setFromTokenInputValue, setFromTokenInputValue,
@ -665,7 +673,12 @@ export const fetchQuotesAndSetQuoteState = (
iconUrl: fromTokenIconUrl, iconUrl: fromTokenIconUrl,
balance: fromTokenBalance, balance: fromTokenBalance,
} = selectedFromToken; } = 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). // 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. // In that case we just want to silently prefetch quotes without redirecting to the quotes loading page.
if (!pageRedirectionDisabled) { if (!pageRedirectionDisabled) {
@ -676,6 +689,30 @@ export const fetchQuotesAndSetQuoteState = (
const contractExchangeRates = getTokenExchangeRates(state); 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 ( if (
fromTokenAddress && fromTokenAddress &&
fromTokenSymbol !== swapsDefaultToken.symbol && 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 = ({ export const signAndSendSwapsSmartTransaction = ({
unsignedTransaction, unsignedTransaction,
trackEvent, trackEvent,
@ -960,7 +967,6 @@ export const signAndSendSwapsSmartTransaction = ({
dispatch(setCurrentSmartTransactionsError(StxErrorTypes.unavailable)); dispatch(setCurrentSmartTransactionsError(StxErrorTypes.unavailable));
return; return;
} }
addTokenTo(dispatch, state);
if (approveTxParams) { if (approveTxParams) {
updatedApproveTxParams.gas = `0x${decimalToHex( updatedApproveTxParams.gas = `0x${decimalToHex(
fees.approvalTxFees?.gasLimit || 0, fees.approvalTxFees?.gasLimit || 0,
@ -1204,7 +1210,6 @@ export const signAndSendTransactions = (
history.push(AWAITING_SIGNATURES_ROUTE); history.push(AWAITING_SIGNATURES_ROUTE);
} }
addTokenTo(dispatch, state);
if (approveTxParams) { if (approveTxParams) {
if (networkAndAccountSupports1559) { if (networkAndAccountSupports1559) {
approveTxParams.maxFeePerGas = maxFeePerGas; approveTxParams.maxFeePerGas = maxFeePerGas;

View File

@ -48,6 +48,7 @@ import {
getIsFeatureFlagLoaded, getIsFeatureFlagLoaded,
getCurrentSmartTransactionsError, getCurrentSmartTransactionsError,
getSmartTransactionFees, getSmartTransactionFees,
getLatestAddedTokenTo,
} from '../../../ducks/swaps/swaps'; } from '../../../ducks/swaps/swaps';
import { import {
getSwapsDefaultToken, getSwapsDefaultToken,
@ -84,6 +85,7 @@ import {
import { import {
resetSwapsPostFetchState, resetSwapsPostFetchState,
ignoreTokens,
setBackgroundSwapRouteState, setBackgroundSwapRouteState,
clearSwapsQuotes, clearSwapsQuotes,
stopPollingForQuotes, stopPollingForQuotes,
@ -144,6 +146,7 @@ export default function BuildQuote({
const tokenList = useSelector(getTokenList, isEqual); const tokenList = useSelector(getTokenList, isEqual);
const quotes = useSelector(getQuotes, isEqual); const quotes = useSelector(getQuotes, isEqual);
const areQuotesPresent = Object.keys(quotes).length > 0; const areQuotesPresent = Object.keys(quotes).length > 0;
const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual);
const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual); const tokenConversionRates = useSelector(getTokenExchangeRates, isEqual);
const conversionRate = useSelector(getConversionRate); const conversionRate = useSelector(getConversionRate);
@ -347,12 +350,21 @@ export default function BuildQuote({
? getURLHostName(blockExplorerTokenLink) ? getURLHostName(blockExplorerTokenLink)
: t('etherscan'); : t('etherscan');
const { address: toAddress } = toToken || {};
const onToSelect = useCallback( const onToSelect = useCallback(
(token) => { (token) => {
if (latestAddedTokenTo && token.address !== toAddress) {
dispatch(
ignoreTokens({
tokensToIgnore: toAddress,
dontShowLoadingIndicator: true,
}),
);
}
dispatch(setSwapToToken(token)); dispatch(setSwapToToken(token));
setVerificationClicked(false); setVerificationClicked(false);
}, },
[dispatch], [dispatch, latestAddedTokenTo, toAddress],
); );
const hideDropdownItemIf = useCallback( const hideDropdownItemIf = useCallback(

View File

@ -29,6 +29,7 @@ const createProps = (customProps = {}) => {
setBackgroundConnection({ setBackgroundConnection({
resetPostFetchState: jest.fn(), resetPostFetchState: jest.fn(),
ignoreTokens: jest.fn(),
setBackgroundSwapRouteState: jest.fn(), setBackgroundSwapRouteState: jest.fn(),
clearSwapsQuotes: jest.fn(), clearSwapsQuotes: jest.fn(),
stopPollingForQuotes: jest.fn(), stopPollingForQuotes: jest.fn(),

View File

@ -50,6 +50,7 @@ import {
navigateBackToBuildQuote, navigateBackToBuildQuote,
getSwapRedesignEnabled, getSwapRedesignEnabled,
setTransactionSettingsOpened, setTransactionSettingsOpened,
getLatestAddedTokenTo,
} from '../../ducks/swaps/swaps'; } from '../../ducks/swaps/swaps';
import { import {
checkNetworkAndAccountSupports1559, checkNetworkAndAccountSupports1559,
@ -79,6 +80,7 @@ import {
import { import {
resetBackgroundSwapsState, resetBackgroundSwapsState,
setSwapsTokens, setSwapsTokens,
ignoreTokens,
setBackgroundSwapRouteState, setBackgroundSwapRouteState,
setSwapsErrorKey, setSwapsErrorKey,
} from '../../store/actions'; } from '../../store/actions';
@ -134,6 +136,7 @@ export default function Swap() {
const routeState = useSelector(getBackgroundSwapRouteState); const routeState = useSelector(getBackgroundSwapRouteState);
const selectedAccount = useSelector(getSelectedAccount, shallowEqual); const selectedAccount = useSelector(getSelectedAccount, shallowEqual);
const quotes = useSelector(getQuotes, isEqual); const quotes = useSelector(getQuotes, isEqual);
const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual);
const txList = useSelector(currentNetworkTxListSelector, shallowEqual); const txList = useSelector(currentNetworkTxListSelector, shallowEqual);
const tradeTxId = useSelector(getTradeTxId); const tradeTxId = useSelector(getTradeTxId);
const approveTxId = useSelector(getApproveTxId); const approveTxId = useSelector(getApproveTxId);
@ -209,6 +212,32 @@ export default function Swap() {
swapsErrorKey = SWAP_FAILED_ERROR; 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 // eslint-disable-next-line
useEffect(() => { useEffect(() => {
if (!isSwapsChain) { if (!isSwapsChain) {
@ -283,6 +312,7 @@ export default function Swap() {
const beforeUnloadEventAddedRef = useRef(); const beforeUnloadEventAddedRef = useRef();
useEffect(() => { useEffect(() => {
const fn = () => { const fn = () => {
clearTemporaryTokenRef.current();
if (isLoadingQuotesRoute) { if (isLoadingQuotesRoute) {
dispatch(prepareToLeaveSwaps()); dispatch(prepareToLeaveSwaps());
} }
@ -349,6 +379,7 @@ export default function Swap() {
} }
const redirectToDefaultRoute = async () => { const redirectToDefaultRoute = async () => {
clearTemporaryTokenRef.current();
dispatch(clearSwapsState()); dispatch(clearSwapsState());
await dispatch(resetBackgroundSwapsState()); await dispatch(resetBackgroundSwapsState());
history.push(DEFAULT_ROUTE); history.push(DEFAULT_ROUTE);
@ -400,6 +431,7 @@ export default function Swap() {
<div <div
className="swaps__header-cancel" className="swaps__header-cancel"
onClick={async () => { onClick={async () => {
clearTemporaryTokenRef.current();
dispatch(clearSwapsState()); dispatch(clearSwapsState());
await dispatch(resetBackgroundSwapsState()); await dispatch(resetBackgroundSwapsState());
history.push(DEFAULT_ROUTE); history.push(DEFAULT_ROUTE);

View File

@ -54,6 +54,7 @@ import {
getAggregatorMetadata, getAggregatorMetadata,
getTransactionSettingsOpened, getTransactionSettingsOpened,
setTransactionSettingsOpened, setTransactionSettingsOpened,
getLatestAddedTokenTo,
} from '../../../ducks/swaps/swaps'; } from '../../../ducks/swaps/swaps';
import { import {
getSwapsDefaultToken, getSwapsDefaultToken,
@ -92,6 +93,7 @@ import {
} from '../../../../shared/constants/swaps'; } from '../../../../shared/constants/swaps';
import { import {
resetSwapsPostFetchState, resetSwapsPostFetchState,
ignoreTokens,
clearSwapsQuotes, clearSwapsQuotes,
stopPollingForQuotes, stopPollingForQuotes,
setSmartTransactionsOptInStatus, setSmartTransactionsOptInStatus,
@ -182,6 +184,7 @@ export default function PrepareSwapPage({
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider, shallowEqual); const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider, shallowEqual);
const tokenList = useSelector(getTokenList, isEqual); const tokenList = useSelector(getTokenList, isEqual);
const quotes = useSelector(getQuotes, isEqual); const quotes = useSelector(getQuotes, isEqual);
const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual);
const numberOfQuotes = Object.keys(quotes).length; const numberOfQuotes = Object.keys(quotes).length;
const areQuotesPresent = numberOfQuotes > 0; const areQuotesPresent = numberOfQuotes > 0;
const swapsErrorKey = useSelector(getSwapsErrorKey); const swapsErrorKey = useSelector(getSwapsErrorKey);
@ -449,12 +452,21 @@ export default function PrepareSwapPage({
? getURLHostName(blockExplorerTokenLink) ? getURLHostName(blockExplorerTokenLink)
: t('etherscan'); : t('etherscan');
const { address: toAddress } = toToken || {};
const onToSelect = useCallback( const onToSelect = useCallback(
(token) => { (token) => {
if (latestAddedTokenTo && token.address !== toAddress) {
dispatch(
ignoreTokens({
tokensToIgnore: toAddress,
dontShowLoadingIndicator: true,
}),
);
}
dispatch(setSwapToToken(token)); dispatch(setSwapToToken(token));
setVerificationClicked(false); setVerificationClicked(false);
}, },
[dispatch], [dispatch, latestAddedTokenTo, toAddress],
); );
const tokensWithBalancesFromToken = tokensWithBalances.find((token) => const tokensWithBalancesFromToken = tokensWithBalances.find((token) =>

View File

@ -27,6 +27,7 @@ const createProps = (customProps = {}) => {
setBackgroundConnection({ setBackgroundConnection({
resetPostFetchState: jest.fn(), resetPostFetchState: jest.fn(),
ignoreTokens: jest.fn(),
setBackgroundSwapRouteState: jest.fn(), setBackgroundSwapRouteState: jest.fn(),
clearSwapsQuotes: jest.fn(), clearSwapsQuotes: jest.fn(),
stopPollingForQuotes: jest.fn(), stopPollingForQuotes: jest.fn(),

View File

@ -995,6 +995,7 @@ function getAllowedAnnouncementIds(state) {
19: false, 19: false,
20: currentKeyringIsLedger && isFirefox, 20: currentKeyringIsLedger && isFirefox,
21: isSwapsChain, 21: isSwapsChain,
22: true,
}; };
} }