diff --git a/CHANGELOG.md b/CHANGELOG.md index 743920163..0f31e9855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.6.2] +### Fixed +- [#12770](https://github.com/MetaMask/metamask-extension/pull/12770): Fixed display of best quote in swaps quotes modal +- [#12786](https://github.com/MetaMask/metamask-extension/pull/12786): Ensure there is a single localhost option in network selector and that it is clickable + ## [10.6.1] ### Fixed - [#12573](https://github.com/MetaMask/metamask-extension/pull/12573): Ensure metrics api errors do not impact user experience @@ -2598,7 +2603,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.6.1...HEAD +[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.6.2...HEAD +[10.6.2]: https://github.com/MetaMask/metamask-extension/compare/v10.6.1...v10.6.2 [10.6.1]: https://github.com/MetaMask/metamask-extension/compare/v10.6.0...v10.6.1 [10.6.0]: https://github.com/MetaMask/metamask-extension/compare/v10.5.2...v10.6.0 [10.5.2]: https://github.com/MetaMask/metamask-extension/compare/v10.5.1...v10.5.2 diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 0d69dd77c..df29bc365 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -28,6 +28,7 @@ import { } from '../../../ui/pages/swaps/swaps.util'; import fetchWithCache from '../../../ui/helpers/utils/fetch-with-cache'; import { MINUTE, SECOND } from '../../../shared/constants/time'; +import { isEqualCaseInsensitive } from '../../../ui/helpers/utils/util'; import { NETWORK_EVENTS } from './network'; // The MAX_GAS_LIMIT is a number that is higher than the maximum gas costs we have observed on any aggregator @@ -91,7 +92,7 @@ export default class SwapsController { networkController, provider, getProviderConfig, - tokenRatesStore, + getTokenRatesState, fetchTradesInfo = defaultFetchTradesInfo, getCurrentChainId, getEIP1559GasFeeEstimates, @@ -105,7 +106,7 @@ export default class SwapsController { this._getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates; this.getBufferedGasLimit = getBufferedGasLimit; - this.tokenRatesStore = tokenRatesStore; + this.getTokenRatesState = getTokenRatesState; this.pollCount = 0; this.getProviderConfig = getProviderConfig; @@ -610,7 +611,9 @@ export default class SwapsController { } async _findTopQuoteAndCalculateSavings(quotes = {}) { - const tokenConversionRates = this.tokenRatesStore.contractExchangeRates; + const { + contractExchangeRates: tokenConversionRates, + } = this.getTokenRatesState(); const { swapsState: { customGasPrice, customMaxPriorityFeePerGas }, } = this.store.getState(); @@ -734,7 +737,12 @@ export default class SwapsController { decimalAdjustedDestinationAmount, ); - const tokenConversionRate = tokenConversionRates[destinationToken]; + const tokenConversionRate = + tokenConversionRates[ + Object.keys(tokenConversionRates).find((tokenAddress) => + isEqualCaseInsensitive(tokenAddress, destinationToken), + ) + ]; const conversionRateForSorting = tokenConversionRate || 1; const ethValueOfTokens = decimalAdjustedDestinationAmount.times( @@ -777,7 +785,17 @@ export default class SwapsController { isSwapsDefaultTokenAddress( newQuotes[topAggId].destinationToken, chainId, - ) || Boolean(tokenConversionRates[newQuotes[topAggId]?.destinationToken]); + ) || + Boolean( + tokenConversionRates[ + Object.keys(tokenConversionRates).find((tokenAddress) => + isEqualCaseInsensitive( + tokenAddress, + newQuotes[topAggId]?.destinationToken, + ), + ) + ], + ); let savings = null; diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js index 7bc26a5aa..d0f4556ab 100644 --- a/app/scripts/controllers/swaps.test.js +++ b/app/scripts/controllers/swaps.test.js @@ -82,12 +82,12 @@ const MOCK_FETCH_METADATA = { chainId: MAINNET_CHAIN_ID, }; -const MOCK_TOKEN_RATES_STORE = { +const MOCK_TOKEN_RATES_STORE = () => ({ contractExchangeRates: { '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': 2, '0x1111111111111111111111111111111111111111': 0.1, }, -}; +}); const MOCK_GET_PROVIDER_CONFIG = () => ({ type: 'FAKE_NETWORK' }); @@ -161,7 +161,7 @@ describe('SwapsController', function () { networkController: getMockNetworkController(), provider, getProviderConfig: MOCK_GET_PROVIDER_CONFIG, - tokenRatesStore: MOCK_TOKEN_RATES_STORE, + getTokenRatesState: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, getCurrentChainId: getCurrentChainIdStub, getEIP1559GasFeeEstimates: getEIP1559GasFeeEstimatesStub, @@ -211,7 +211,7 @@ describe('SwapsController', function () { networkController, provider, getProviderConfig: MOCK_GET_PROVIDER_CONFIG, - tokenRatesStore: MOCK_TOKEN_RATES_STORE, + getTokenRatesState: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, getCurrentChainId: getCurrentChainIdStub, }); @@ -235,7 +235,7 @@ describe('SwapsController', function () { networkController, provider, getProviderConfig: MOCK_GET_PROVIDER_CONFIG, - tokenRatesStore: MOCK_TOKEN_RATES_STORE, + getTokenRatesState: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, getCurrentChainId: getCurrentChainIdStub, }); @@ -259,7 +259,7 @@ describe('SwapsController', function () { networkController, provider, getProviderConfig: MOCK_GET_PROVIDER_CONFIG, - tokenRatesStore: MOCK_TOKEN_RATES_STORE, + getTokenRatesState: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, getCurrentChainId: getCurrentChainIdStub, }); @@ -816,9 +816,10 @@ describe('SwapsController', function () { .stub(swapsController, '_getERC20Allowance') .resolves(ethers.BigNumber.from(1)); - swapsController.tokenRatesStore = { + swapsController.getTokenRatesState = () => ({ contractExchangeRates: {}, - }; + }); + const [newQuotes, topAggId] = await swapsController.fetchAndSetQuotes( MOCK_FETCH_PARAMS, MOCK_FETCH_METADATA, diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js index 34969b58f..857921889 100644 --- a/app/scripts/first-time-state.js +++ b/app/scripts/first-time-state.js @@ -10,7 +10,15 @@ const initialState = { config: {}, PreferencesController: { - frequentRpcListDetail: [], + frequentRpcListDetail: [ + { + rpcUrl: 'http://localhost:8545', + chainId: '0x539', + ticker: 'ETH', + nickname: 'Localhost 8545', + rpcPrefs: {}, + }, + ], }, }; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 16b41ec54..563ef40ed 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -545,7 +545,7 @@ export default class MetamaskController extends EventEmitter { getProviderConfig: this.networkController.getProviderConfig.bind( this.networkController, ), - tokenRatesStore: this.tokenRatesController.state, + getTokenRatesState: () => this.tokenRatesController.state, getCurrentChainId: this.networkController.getCurrentChainId.bind( this.networkController, ), diff --git a/package.json b/package.json index 392b1969f..6638b8d18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "metamask-crx", - "version": "10.6.1", + "version": "10.6.2", "private": true, "repository": { "type": "git", @@ -91,7 +91,8 @@ "netmask": "^2.0.1", "pubnub/superagent-proxy": "^3.0.0", "pull-ws": "^3.3.2", - "ws": "^7.4.6" + "ws": "^7.4.6", + "json-schema": "^0.4.0" }, "dependencies": { "3box": "^1.10.2", diff --git a/test/e2e/tests/custom-rpc-history.spec.js b/test/e2e/tests/custom-rpc-history.spec.js index 687b64f96..6868808ac 100644 --- a/test/e2e/tests/custom-rpc-history.spec.js +++ b/test/e2e/tests/custom-rpc-history.spec.js @@ -84,7 +84,7 @@ describe('Stores custom RPC history', function () { await rpcUrlInput.clear(); await rpcUrlInput.sendKeys(duplicateRpcUrl); await driver.findElement({ - text: 'This URL is currently used by the localhost network.', + text: 'This URL is currently used by the Localhost 8545 network.', tag: 'h6', }); }, @@ -123,7 +123,8 @@ describe('Stores custom RPC history', function () { await chainIdInput.clear(); await chainIdInput.sendKeys(duplicateChainId); await driver.findElement({ - text: 'This Chain ID is currently used by the localhost network.', + text: + 'This Chain ID is currently used by the Localhost 8545 network.', tag: 'h6', }); }, diff --git a/ui/components/app/dropdowns/network-dropdown.js b/ui/components/app/dropdowns/network-dropdown.js index 2e2528e47..889ba9fa9 100644 --- a/ui/components/app/dropdowns/network-dropdown.js +++ b/ui/components/app/dropdowns/network-dropdown.js @@ -7,7 +7,10 @@ import classnames from 'classnames'; import Button from '../../ui/button'; import * as actions from '../../../store/actions'; import { openAlert as displayInvalidCustomNetworkAlert } from '../../../ducks/alerts/invalid-custom-network'; -import { NETWORK_TYPE_RPC } from '../../../../shared/constants/network'; +import { + NETWORK_TYPE_RPC, + LOCALHOST_RPC_URL, +} from '../../../../shared/constants/network'; import { isPrefixedFormattedHexString } from '../../../../shared/modules/network.utils'; import ColorIndicator from '../../ui/color-indicator'; @@ -149,7 +152,7 @@ class NetworkDropdown extends Component { ); } - renderCustomRpcList(rpcListDetail, provider) { + renderCustomRpcList(rpcListDetail, provider, opts = {}) { const reversedRpcListDetail = rpcListDetail.slice().reverse(); return reversedRpcListDetail.map((entry) => { @@ -157,6 +160,14 @@ class NetworkDropdown extends Component { const isCurrentRpcTarget = provider.type === NETWORK_TYPE_RPC && rpcUrl === provider.rpcUrl; + let borderColor = COLORS.UI2; + if (isCurrentRpcTarget) { + borderColor = COLORS.WHITE; + } + if (opts.isLocalHost) { + borderColor = 'localhost'; + } + return ( ✓ )} rpc.rpcUrl !== LOCALHOST_RPC_URL, + ); + const rpcListDetailForLocalHost = rpcListDetail.filter( + (rpc) => rpc.rpcUrl === LOCALHOST_RPC_URL, + ); const isOpen = this.props.networkDropdownOpen; const { t } = this.context; @@ -340,7 +357,10 @@ class NetworkDropdown extends Component {
{this.renderNetworkEntry('mainnet')} - {this.renderCustomRpcList(rpcListDetail, this.props.provider)} + {this.renderCustomRpcList( + rpcListDetailWithoutLocalHost, + this.props.provider, + )}
diff --git a/ui/components/app/dropdowns/network-dropdown.test.js b/ui/components/app/dropdowns/network-dropdown.test.js index 263460b85..f8546f8b6 100644 --- a/ui/components/app/dropdowns/network-dropdown.test.js +++ b/ui/components/app/dropdowns/network-dropdown.test.js @@ -4,6 +4,7 @@ import thunk from 'redux-thunk'; import Button from '../../ui/button'; import { mountWithRouter } from '../../../../test/lib/render-helpers'; import ColorIndicator from '../../ui/color-indicator'; +import { LOCALHOST_RPC_URL } from '../../../../shared/constants/network'; import NetworkDropdown from './network-dropdown'; import { DropdownMenuItem } from './dropdown'; @@ -55,6 +56,7 @@ describe('Network Dropdown', () => { frequentRpcListDetail: [ { chainId: '0x1a', rpcUrl: 'http://localhost:7545' }, { rpcUrl: 'http://localhost:7546' }, + { rpcUrl: LOCALHOST_RPC_URL, nickname: 'localhost' }, ], }, appState: { diff --git a/ui/pages/settings/networks-tab/networks-tab.constants.js b/ui/pages/settings/networks-tab/networks-tab.constants.js index d5ad83541..26a57c7bd 100644 --- a/ui/pages/settings/networks-tab/networks-tab.constants.js +++ b/ui/pages/settings/networks-tab/networks-tab.constants.js @@ -5,9 +5,6 @@ import { KOVAN, KOVAN_CHAIN_ID, KOVAN_RPC_URL, - LOCALHOST, - LOCALHOST_CHAIN_ID, - LOCALHOST_RPC_URL, MAINNET, MAINNET_CHAIN_ID, MAINNET_RPC_URL, @@ -65,15 +62,6 @@ const defaultNetworksData = [ ticker: 'ETH', blockExplorerUrl: 'https://kovan.etherscan.io', }, - { - labelKey: LOCALHOST, - iconColor: '#29B6AF', - providerType: LOCALHOST, - rpcUrl: LOCALHOST_RPC_URL, - chainId: LOCALHOST_CHAIN_ID, - ticker: 'ETH', - blockExplorerUrl: '', - }, ]; export { defaultNetworksData }; diff --git a/yarn.lock b/yarn.lock index 90a3eddd7..37fae01dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18564,10 +18564,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.2.3, json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1"