From 4295155e893f9432f8239d1bac6f9644c4b0a7fc Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Thu, 31 Oct 2024 17:51:04 +0000 Subject: [PATCH 1/9] Remove global network usages from transactions Add linting rule. --- .eslintrc.js | 13 ++++++ .../advanced-gas-fee-defaults.js | 6 +-- .../confirm-hexdata/confirm-hexdata.test.js | 18 +++++++- .../confirm-subtitle/confirm-subtitle.test.js | 4 +- .../contract-details-modal.js | 2 +- .../contract-token-values.js | 8 ++-- .../transaction-alerts/transaction-alerts.js | 13 ++++-- .../transaction-alerts.test.js | 40 +++++++++++++++++- .../confirm-approve-content.component.js | 12 +++--- .../confirm-approve/confirm-approve.js | 25 ++++++----- .../confirm-send-token/confirm-send-token.js | 14 ++++--- .../confirm-token-transaction-base.js | 20 +++++---- .../confirm-transaction-base.component.js | 11 +---- .../confirm-transaction-base.container.js | 27 ++++++++---- .../confirm-transaction-base.test.js | 41 ------------------- ui/pages/confirmations/hooks/test-utils.js | 10 +---- .../hooks/useTransactionFunctionType.js | 16 ++++++-- .../hooks/useTransactionFunctionType.test.js | 27 +++++++++--- .../confirmations/hooks/useTransactionInfo.js | 3 +- .../hooks/useTransactionInfo.test.js | 7 +++- .../send/gas-display/gas-display.js | 18 ++++---- .../token-allowance.test.js.snap | 14 +++---- .../token-allowance/token-allowance.js | 18 +++++--- .../token-allowance/token-allowance.test.js | 2 +- 24 files changed, 220 insertions(+), 149 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 846158a741ef..fc66d281c327 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -472,5 +472,18 @@ module.exports = { '@metamask/design-tokens/color-no-hex': 'off', }, }, + { + files: ['ui/pages/confirmations/**/*.{js,ts,tsx}'], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: + 'ImportSpecifier[imported.name=/(getCurrentChainId)|(getNativeCurrency)|(getProviderConfig)|(getRpcPrefsForCurrentProvider)/]', + message: 'Avoid using global network selectors in confirmations', + }, + ], + }, + }, ], }; diff --git a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js index a52b4c16f893..33d4ebd45c5f 100644 --- a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js +++ b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js @@ -11,7 +11,6 @@ import { } from '../../../../../helpers/constants/design-system'; import { getAdvancedGasFeeValues, - getCurrentChainId, getNetworkIdentifier, } from '../../../../../selectors'; import { setAdvancedGasFee } from '../../../../../store/actions'; @@ -35,11 +34,10 @@ const AdvancedGasFeeDefaults = () => { 10, ).toString(); const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); - // This will need to use a different chainId in multinetwork - const chainId = useSelector(getCurrentChainId); const networkIdentifier = useSelector(getNetworkIdentifier); const { updateTransactionEventFragment } = useTransactionEventFragment(); - const { editGasMode } = useGasFeeContext(); + const { editGasMode, transaction } = useGasFeeContext(); + const { chainId } = transaction; const [isDefaultSettingsSelected, setDefaultSettingsSelected] = useState( Boolean(advancedGasFeeValues) && advancedGasFeeValues.maxBaseFee === maxBaseFee && diff --git a/ui/pages/confirmations/components/confirm-hexdata/confirm-hexdata.test.js b/ui/pages/confirmations/components/confirm-hexdata/confirm-hexdata.test.js index d31478775ba2..949b3ed92a86 100644 --- a/ui/pages/confirmations/components/confirm-hexdata/confirm-hexdata.test.js +++ b/ui/pages/confirmations/components/confirm-hexdata/confirm-hexdata.test.js @@ -1,20 +1,33 @@ import React from 'react'; +import { CHAIN_IDS } from '@metamask/transaction-controller'; import mockState from '../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../test/jest'; import configureStore from '../../../../store/store'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import ConfirmHexData from './confirm-hexdata'; jest.mock('../../../../../shared/lib/fetch-with-cache'); +const CHAIN_ID_MOCK = CHAIN_IDS.GOERLI; + +const STATE_MOCK = { + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ chainId: CHAIN_ID_MOCK }), + }, +}; + describe('ConfirmHexData', () => { - const store = configureStore(mockState); + const store = configureStore(STATE_MOCK); it('should render function type', async () => { const { findByText } = renderWithProvider( { const { container } = renderWithProvider( { const { container } = renderWithProvider( { const { getByText } = renderWithProvider( { mockState.metamask.preferences.showFiatInTestnets = false; mockState.metamask.allNftContracts = { [mockSelectedInternalAccount.address]: { - [getProviderConfig(mockState).chainId]: [{ address: '0x9' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x9' }], }, }; store = configureStore(mockState); diff --git a/ui/pages/confirmations/components/contract-details-modal/contract-details-modal.js b/ui/pages/confirmations/components/contract-details-modal/contract-details-modal.js index 795401673691..f433bf4caee3 100644 --- a/ui/pages/confirmations/components/contract-details-modal/contract-details-modal.js +++ b/ui/pages/confirmations/components/contract-details-modal/contract-details-modal.js @@ -338,7 +338,7 @@ ContractDetailsModal.propTypes = { */ chainId: PropTypes.string, /** - * Block explorer URL of the current network + * Block explorer URL for the contract chain */ blockExplorerUrl: PropTypes.string, /** diff --git a/ui/pages/confirmations/components/contract-token-values/contract-token-values.js b/ui/pages/confirmations/components/contract-token-values/contract-token-values.js index 6858689f77d0..0e8890b607ec 100644 --- a/ui/pages/confirmations/components/contract-token-values/contract-token-values.js +++ b/ui/pages/confirmations/components/contract-token-values/contract-token-values.js @@ -25,7 +25,7 @@ export default function ContractTokenValues({ address, tokenName, chainId, - rpcPrefs, + blockExplorerUrl, }) { const t = useI18nContext(); const [copied, handleCopy] = useCopyToClipboard(); @@ -69,7 +69,7 @@ export default function ContractTokenValues({ address, chainId, { - blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null, + blockExplorerUrl: blockExplorerUrl ?? null, }, null, ); @@ -98,7 +98,7 @@ ContractTokenValues.propTypes = { */ chainId: PropTypes.string, /** - * RPC prefs + * URL for the block explorer */ - rpcPrefs: PropTypes.object, + blockExplorerUrl: PropTypes.string, }; diff --git a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js index a641ecbab93a..e1002dcebdf8 100644 --- a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js +++ b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js @@ -15,14 +15,16 @@ import { import SimulationErrorMessage from '../simulation-error-message'; import { SEVERITIES } from '../../../../helpers/constants/design-system'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) -import { submittedPendingTransactionsSelector } from '../../../../selectors'; +import { + selectNetworkConfigurationByChainId, + submittedPendingTransactionsSelector, +} from '../../../../selectors'; import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url'; ///: END:ONLY_INCLUDE_IF import { isSuspiciousResponse } from '../../../../../shared/modules/security-provider.utils'; import BlockaidBannerAlert from '../security-provider-banner-alert/blockaid-banner-alert/blockaid-banner-alert'; import SecurityProviderBannerMessage from '../security-provider-banner-message/security-provider-banner-message'; -import { getNativeCurrency } from '../../../../ducks/metamask/metamask'; import { parseStandardTokenTransactionData } from '../../../../../shared/modules/transaction.utils'; import { getTokenValueParam } from '../../../../../shared/lib/metamask-controller-utils'; import { QueuedRequestsBannerAlert } from '../../confirmation/components/queued-requests-banner-alert'; @@ -47,7 +49,12 @@ const TransactionAlerts = ({ ///: END:ONLY_INCLUDE_IF const t = useI18nContext(); - const nativeCurrency = useSelector(getNativeCurrency); + const { chainId } = txData; + + const { nativeCurrency } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const transactionData = txData.txParams.data; const currentTokenSymbol = tokenSymbol || nativeCurrency; let currentTokenAmount; diff --git a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.test.js b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.test.js index 6ae1a59591dd..4695120fbae7 100644 --- a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.test.js +++ b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import sinon from 'sinon'; +import { CHAIN_IDS } from '@metamask/transaction-controller'; import { SECURITY_PROVIDER_MESSAGE_SEVERITY } from '../../../../../shared/constants/security-provider'; import { renderWithProvider } from '../../../../../test/jest'; import { submittedPendingTransactionsSelector } from '../../../../selectors/transactions'; @@ -9,6 +10,7 @@ import configureStore from '../../../../store/store'; import mockState from '../../../../../test/data/mock-state.json'; import * as txUtil from '../../../../../shared/modules/transaction.utils'; import * as metamaskControllerUtils from '../../../../../shared/lib/metamask-controller-utils'; +import { mockNetworkState } from '../../../../../test/stub/networks'; import TransactionAlerts from './transaction-alerts'; jest.mock('../../../../selectors/transactions', () => { @@ -22,17 +24,28 @@ jest.mock('../../../../contexts/gasFee'); jest.mock('../../../../selectors/account-abstraction'); +const CHAIN_ID_MOCK = CHAIN_IDS.MAINNET; + +const STATE_MOCK = { + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ + chainId: CHAIN_ID_MOCK, + }), + }, +}; + function render({ componentProps = {}, useGasFeeContextValue = {}, submittedPendingTransactionsSelectorValue = null, - mockedStore = mockState, }) { useGasFeeContext.mockReturnValue(useGasFeeContextValue); submittedPendingTransactionsSelector.mockReturnValue( submittedPendingTransactionsSelectorValue, ); - const store = configureStore(mockedStore); + const store = configureStore(STATE_MOCK); return renderWithProvider(, store); } @@ -41,6 +54,7 @@ describe('TransactionAlerts', () => { const { getByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, securityAlertResponse: { resultType: 'Malicious', reason: 'blur_farming', @@ -64,6 +78,7 @@ describe('TransactionAlerts', () => { const { queryByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, securityProviderResponse: { flagAsDangerous: '?', reason: 'Some reason...', @@ -89,6 +104,7 @@ describe('TransactionAlerts', () => { const { queryByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, securityProviderResponse: { flagAsDangerous: SECURITY_PROVIDER_MESSAGE_SEVERITY.NOT_MALICIOUS, }, @@ -118,6 +134,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -141,6 +158,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -160,6 +178,7 @@ describe('TransactionAlerts', () => { componentProps: { setUserAcknowledgedGasMissing, txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -181,6 +200,7 @@ describe('TransactionAlerts', () => { componentProps: { userAcknowledgedGasMissing: true, txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -199,6 +219,7 @@ describe('TransactionAlerts', () => { const { queryByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -220,6 +241,7 @@ describe('TransactionAlerts', () => { submittedPendingTransactionsSelectorValue: [{ some: 'transaction' }], componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -242,6 +264,7 @@ describe('TransactionAlerts', () => { ], componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -261,6 +284,7 @@ describe('TransactionAlerts', () => { submittedPendingTransactionsSelectorValue: [], componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -282,6 +306,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -301,6 +326,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -322,6 +348,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -345,6 +372,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -367,6 +395,7 @@ describe('TransactionAlerts', () => { submittedPendingTransactionsSelectorValue: [{ some: 'transaction' }], componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -388,6 +417,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -407,6 +437,7 @@ describe('TransactionAlerts', () => { }, componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -445,6 +476,7 @@ describe('TransactionAlerts', () => { const { getByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x0', }, @@ -461,6 +493,7 @@ describe('TransactionAlerts', () => { const { getByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x0', }, @@ -478,6 +511,7 @@ describe('TransactionAlerts', () => { const { queryByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x5af3107a4000', }, @@ -492,6 +526,7 @@ describe('TransactionAlerts', () => { const { queryByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x0', }, @@ -508,6 +543,7 @@ describe('TransactionAlerts', () => { const { getByText } = render({ componentProps: { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, diff --git a/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index ebd57c35a141..007aec372c62 100644 --- a/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/pages/confirmations/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -70,7 +70,7 @@ export default class ConfirmApproveContent extends Component { fromAddressIsLedger: PropTypes.bool, chainId: PropTypes.string, tokenAddress: PropTypes.string, - rpcPrefs: PropTypes.object, + blockExplorerUrl: PropTypes.string, isContract: PropTypes.bool, hexTransactionTotal: PropTypes.string, hexMinimumTransactionFee: PropTypes.string, @@ -375,10 +375,10 @@ export default class ConfirmApproveContent extends Component { } getTitleTokenDescription() { - const { tokenId, tokenAddress, rpcPrefs, chainId, userAddress } = + const { tokenId, tokenAddress, blockExplorerUrl, chainId, userAddress } = this.props; const useBlockExplorer = - rpcPrefs?.blockExplorerUrl || + blockExplorerUrl || [...TEST_CHAINS, CHAIN_IDS.MAINNET, CHAIN_IDS.LINEA_MAINNET].includes( chainId, ); @@ -393,7 +393,7 @@ export default class ConfirmApproveContent extends Component { null, userAddress, { - blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null, + blockExplorerUrl: blockExplorerUrl ?? null, }, ); const blockExplorerElement = ( @@ -529,7 +529,7 @@ export default class ConfirmApproveContent extends Component { fromAddressIsLedger, toAddress, chainId, - rpcPrefs, + blockExplorerUrl, assetStandard, tokenId, tokenAddress, @@ -612,7 +612,7 @@ export default class ConfirmApproveContent extends Component { tokenAddress={tokenAddress} toAddress={toAddress} chainId={chainId} - rpcPrefs={rpcPrefs} + blockExplorerUrl={blockExplorerUrl} tokenId={tokenId} assetName={assetName} assetStandard={assetStandard} diff --git a/ui/pages/confirmations/confirm-approve/confirm-approve.js b/ui/pages/confirmations/confirm-approve/confirm-approve.js index a5dcaeb6202d..f835a77a70bd 100644 --- a/ui/pages/confirmations/confirm-approve/confirm-approve.js +++ b/ui/pages/confirmations/confirm-approve/confirm-approve.js @@ -12,10 +12,7 @@ import { getTokenApprovedParam } from '../../../helpers/utils/token-util'; import { readAddressAsContract } from '../../../../shared/modules/contract-utils'; import { GasFeeContextProvider } from '../../../contexts/gasFee'; import { TransactionModalContextProvider } from '../../../contexts/transaction-modal'; -import { - getNativeCurrency, - isAddressLedger, -} from '../../../ducks/metamask/metamask'; +import { isAddressLedger } from '../../../ducks/metamask/metamask'; import ConfirmContractInteraction from '../confirm-contract-interaction'; import { getCurrentCurrency, @@ -23,10 +20,9 @@ import { getUseNonceField, getCustomNonceValue, getNextSuggestedNonce, - getCurrentChainId, - getRpcPrefsForCurrentProvider, checkNetworkAndAccountSupports1559, getUseCurrencyRateCheck, + selectNetworkConfigurationByChainId, } from '../../../selectors'; import { useApproveTransaction } from '../hooks/useApproveTransaction'; import { useSimulationFailureWarning } from '../hooks/useSimulationFailureWarning'; @@ -66,16 +62,22 @@ export default function ConfirmApprove({ isSetApproveForAll, }) { const dispatch = useDispatch(); - const { txParams: { data: transactionData } = {} } = transaction; + const { chainId, txParams: { data: transactionData } = {} } = transaction; const currentCurrency = useSelector(getCurrentCurrency); - const nativeCurrency = useSelector(getNativeCurrency); + + const { nativeCurrency } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const subjectMetadata = useSelector(getSubjectMetadata); const useNonceField = useSelector(getUseNonceField); const nextNonce = useSelector(getNextSuggestedNonce); const customNonceValue = useSelector(getCustomNonceValue); - const chainId = useSelector(getCurrentChainId); - const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); + const { blockExplorerUrls } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const blockExplorerUrl = blockExplorerUrls?.[0]; const networkAndAccountSupports1559 = useSelector( checkNetworkAndAccountSupports1559, ); @@ -291,7 +293,7 @@ export default function ConfirmApprove({ txData={transaction} fromAddressIsLedger={fromAddressIsLedger} chainId={chainId} - rpcPrefs={rpcPrefs} + blockExplorerUrl={blockExplorerUrl} isContract={isContract} hasLayer1GasFee={layer1GasFee !== undefined} supportsEIP1559={supportsEIP1559} @@ -334,6 +336,7 @@ ConfirmApprove.propTypes = { userAddress: PropTypes.string, toAddress: PropTypes.string, transaction: PropTypes.shape({ + chainId: PropTypes.string, layer1GasFee: PropTypes.string, origin: PropTypes.string, txParams: PropTypes.shape({ diff --git a/ui/pages/confirmations/confirm-send-token/confirm-send-token.js b/ui/pages/confirmations/confirm-send-token/confirm-send-token.js index 3e24c72541d5..0fe088063959 100644 --- a/ui/pages/confirmations/confirm-send-token/confirm-send-token.js +++ b/ui/pages/confirmations/confirm-send-token/confirm-send-token.js @@ -8,11 +8,9 @@ import { editExistingTransaction } from '../../../ducks/send'; import { contractExchangeRateSelector, getCurrentCurrency, + selectNetworkConfigurationByChainId, } from '../../../selectors'; -import { - getConversionRate, - getNativeCurrency, -} from '../../../ducks/metamask/metamask'; +import { getConversionRate } from '../../../ducks/metamask/metamask'; import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'; import { showSendTokenPage } from '../../../store/actions'; import { @@ -50,7 +48,12 @@ export default function ConfirmSendToken({ }); }; const conversionRate = useSelector(getConversionRate); - const nativeCurrency = useSelector(getNativeCurrency); + const { chainId } = transaction; + + const { nativeCurrency } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const currentCurrency = useSelector(getCurrentCurrency); const contractExchangeRate = useSelector(contractExchangeRateSelector); @@ -98,6 +101,7 @@ ConfirmSendToken.propTypes = { toAddress: PropTypes.string, tokenAddress: PropTypes.string, transaction: PropTypes.shape({ + chainId: PropTypes.string, origin: PropTypes.string, txParams: PropTypes.shape({ data: PropTypes.string, diff --git a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js index 7b60bf9ab4fe..e361a5d0311f 100644 --- a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js +++ b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js @@ -15,14 +15,12 @@ import { import { PRIMARY } from '../../../helpers/constants/common'; import { contractExchangeRateSelector, - getCurrentChainId, getCurrentCurrency, - getRpcPrefsForCurrentProvider, getSelectedInternalAccount, + selectNetworkConfigurationByChainId, } from '../../../selectors'; import { getConversionRate, - getNativeCurrency, getNftContracts, } from '../../../ducks/metamask/metamask'; import { TokenStandard } from '../../../../shared/constants/transaction'; @@ -47,14 +45,19 @@ export default function ConfirmTokenTransactionBase({ ethTransactionTotal, fiatTransactionTotal, hexMaximumTransactionFee, + transaction, }) { const t = useContext(I18nContext); const contractExchangeRate = useSelector(contractExchangeRateSelector); - const nativeCurrency = useSelector(getNativeCurrency); + const { chainId } = transaction; + + const { blockExplorerUrls, nativeCurrency } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + + const blockExplorerUrl = blockExplorerUrls?.[0]; const currentCurrency = useSelector(getCurrentCurrency); const conversionRate = useSelector(getConversionRate); - const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider); - const chainId = useSelector(getCurrentChainId); const { address: userAddress } = useSelector(getSelectedInternalAccount); const nftCollections = useSelector(getNftContracts); @@ -64,7 +67,7 @@ export default function ConfirmTokenTransactionBase({ const getTitleTokenDescription = (renderType) => { const useBlockExplorer = - rpcPrefs?.blockExplorerUrl || + blockExplorerUrl || [...TEST_CHAINS, CHAIN_IDS.MAINNET, CHAIN_IDS.LINEA_MAINNET].includes( chainId, ); @@ -87,7 +90,7 @@ export default function ConfirmTokenTransactionBase({ null, userAddress, { - blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null, + blockExplorerUrl: blockExplorerUrl ?? null, }, ); const blockExplorerElement = ( @@ -219,4 +222,5 @@ ConfirmTokenTransactionBase.propTypes = { ethTransactionTotal: PropTypes.string, fiatTransactionTotal: PropTypes.string, hexMaximumTransactionFee: PropTypes.string, + transaction: PropTypes.string, }; diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js index c07b8a263f8a..be3b3882d81e 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js @@ -1012,24 +1012,15 @@ export default class ConfirmTransactionBase extends Component { this._isMounted = true; const { toAddress, - txData: { origin, chainId: txChainId } = {}, + txData: { origin } = {}, getNextNonce, tryReverseResolveAddress, smartTransactionsPreferenceEnabled, currentChainSupportsSmartTransactions, setSwapsFeatureFlags, fetchSmartTransactionsLiveness, - chainId, } = this.props; - // If the user somehow finds themselves seeing a confirmation - // on a network which is not presently selected, throw - if (txChainId === undefined || txChainId !== chainId) { - throw new Error( - `Currently selected chainId (${chainId}) does not match chainId (${txChainId}) on which the transaction was proposed.`, - ); - } - const { trackEvent } = this.context; trackEvent({ category: MetaMetricsEventCategory.Transactions, diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js index e06090f48e75..554e06f05cd1 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js @@ -69,9 +69,7 @@ import { isAddressLedger, updateGasFees, getIsGasEstimatesLoading, - getNativeCurrency, getSendToAccounts, - getProviderConfig, findKeyringForAddress, getConversionRate, } from '../../../ducks/metamask/metamask'; @@ -97,10 +95,14 @@ import { CUSTOM_GAS_ESTIMATE } from '../../../../shared/constants/gas'; // eslint-disable-next-line import/no-duplicates import { getIsUsingPaymaster } from '../../../selectors/account-abstraction'; +import { + getAccountType, + selectDefaultRpcEndpointByChainId, + selectNetworkConfigurationByChainId, +} from '../../../selectors/selectors'; + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) // eslint-disable-next-line import/no-duplicates -import { getAccountType } from '../../../selectors/selectors'; - import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../shared/constants/app'; import { getIsNoteToTraderSupported, @@ -172,11 +174,12 @@ const mapStateToProps = (state, ownProps) => { const { addressBook, nextNonce } = metamask; const unapprovedTxs = getUnapprovedTransactions(state); - const { chainId } = getProviderConfig(state); const { tokenData, txData, tokenProps, nonce } = confirmTransaction; const { txParams = {}, id: transactionId, type } = txData; const txId = transactionId || paramsTransactionId; const transaction = getUnapprovedTransaction(state, txId) ?? {}; + const { chainId } = txData; + const { from: fromAddress, to: txParamsToAddress, @@ -184,6 +187,7 @@ const mapStateToProps = (state, ownProps) => { gas: gasLimit, data, } = (transaction && transaction.txParams) || txParams; + const accounts = getMetaMaskAccounts(state); const smartTransactionsPreferenceEnabled = getSmartTransactionsPreferenceEnabled(state); @@ -270,7 +274,17 @@ const mapStateToProps = (state, ownProps) => { fullTxData.userFeeLevel === CUSTOM_GAS_ESTIMATE || txParamsAreDappSuggested(fullTxData); const fromAddressIsLedger = isAddressLedger(state, fromAddress); - const nativeCurrency = getNativeCurrency(state); + + const { nativeCurrency } = selectNetworkConfigurationByChainId( + state, + chainId, + ); + + const { url: customRpcUrl } = selectDefaultRpcEndpointByChainId( + state, + chainId, + ); + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const accountType = getAccountType(state, fromAddress); const fromChecksumHexAddress = toChecksumHexAddress(fromAddress); @@ -281,7 +295,6 @@ const mapStateToProps = (state, ownProps) => { const custodianPublishesTransaction = getIsCustodianPublishesTransactionSupported(state, fromChecksumHexAddress); const builtinRpcUrl = CHAIN_ID_TO_RPC_URL_MAP[chainId]; - const { rpcUrl: customRpcUrl } = getProviderConfig(state); const rpcUrl = customRpcUrl || builtinRpcUrl; diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js index bea6aef1d84d..5a846874fed3 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.test.js @@ -969,45 +969,4 @@ describe('Confirm Transaction Base', () => { expect(confirmButton).toBeDisabled(); }); }); - - describe('Preventing transaction submission', () => { - it('should throw error when on wrong chain', async () => { - const txParams = { - ...mockTxParams, - to: undefined, - data: '0xa22cb46500000000000000', - chainId: '0x5', - }; - const state = { - ...baseStore, - metamask: { - ...baseStore.metamask, - transactions: [ - { - id: baseStore.confirmTransaction.txData.id, - chainId: '0x5', - status: 'unapproved', - txParams, - }, - ], - ...mockNetworkState({ chainId: CHAIN_IDS.SEPOLIA }), - }, - confirmTransaction: { - ...baseStore.confirmTransaction, - txData: { - ...baseStore.confirmTransaction.txData, - value: '0x0', - isUserOperation: true, - txParams, - chainId: '0x5', - }, - }, - }; - - // Error will be triggered by componentDidMount - await expect(render({ state })).rejects.toThrow( - 'Currently selected chainId (0xaa36a7) does not match chainId (0x5) on which the transaction was proposed.', - ); - }); - }); }); diff --git a/ui/pages/confirmations/hooks/test-utils.js b/ui/pages/confirmations/hooks/test-utils.js index 908f600564f8..d8d94926e703 100644 --- a/ui/pages/confirmations/hooks/test-utils.js +++ b/ui/pages/confirmations/hooks/test-utils.js @@ -3,10 +3,7 @@ import { useSelector } from 'react-redux'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; import { GasEstimateTypes } from '../../../../shared/constants/gas'; -import { - getConversionRate, - getNativeCurrency, -} from '../../../ducks/metamask/metamask'; +import { getConversionRate } from '../../../ducks/metamask/metamask'; import { getCurrentCurrency, getShouldShowFiat, @@ -110,10 +107,7 @@ export const generateUseSelectorRouter = if (selector === getConversionRate) { return MOCK_ETH_USD_CONVERSION_RATE; } - if ( - selector === getMultichainNativeCurrency || - selector === getNativeCurrency - ) { + if (selector === getMultichainNativeCurrency) { return EtherDenomination.ETH; } if (selector === getPreferences) { diff --git a/ui/pages/confirmations/hooks/useTransactionFunctionType.js b/ui/pages/confirmations/hooks/useTransactionFunctionType.js index 559367fff066..3bb6b12e1c61 100644 --- a/ui/pages/confirmations/hooks/useTransactionFunctionType.js +++ b/ui/pages/confirmations/hooks/useTransactionFunctionType.js @@ -2,8 +2,10 @@ import { useSelector } from 'react-redux'; import { TransactionType } from '@metamask/transaction-controller'; import { ORIGIN_METAMASK } from '../../../../shared/constants/app'; -import { getKnownMethodData } from '../../../selectors'; -import { getNativeCurrency } from '../../../ducks/metamask/metamask'; +import { + getKnownMethodData, + selectNetworkConfigurationByChainId, +} from '../../../selectors'; import { getTransactionTypeTitle } from '../../../helpers/utils/transactions.util'; import { getMethodName } from '../../../helpers/utils/metrics'; @@ -11,8 +13,12 @@ import { useI18nContext } from '../../../hooks/useI18nContext'; export const useTransactionFunctionType = (txData = {}) => { const t = useI18nContext(); - const nativeCurrency = useSelector(getNativeCurrency); - const { txParams } = txData; + const { chainId, txParams } = txData; + + const networkConfiguration = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const methodData = useSelector( (state) => getKnownMethodData(state, txParams?.data) || {}, ); @@ -21,6 +27,8 @@ export const useTransactionFunctionType = (txData = {}) => { return {}; } + const { nativeCurrency } = networkConfiguration; + const isTokenApproval = txData.type === TransactionType.tokenMethodSetApprovalForAll || txData.type === TransactionType.tokenMethodApprove || diff --git a/ui/pages/confirmations/hooks/useTransactionFunctionType.test.js b/ui/pages/confirmations/hooks/useTransactionFunctionType.test.js index 2bdc008e05dd..da625ad68267 100644 --- a/ui/pages/confirmations/hooks/useTransactionFunctionType.test.js +++ b/ui/pages/confirmations/hooks/useTransactionFunctionType.test.js @@ -1,47 +1,64 @@ -import { TransactionType } from '@metamask/transaction-controller'; +import { CHAIN_IDS, TransactionType } from '@metamask/transaction-controller'; import { renderHookWithProvider } from '../../../../test/lib/render-helpers'; import mockState from '../../../../test/data/mock-state.json'; +import { mockNetworkState } from '../../../../test/stub/networks'; import { useTransactionFunctionType } from './useTransactionFunctionType'; +const CHAIN_ID_MOCK = CHAIN_IDS.GOERLI; + +const STATE_MOCK = { + ...mockState, + metamask: { + ...mockState.metamask, + ...mockNetworkState({ chainId: CHAIN_ID_MOCK }), + }, +}; + describe('useTransactionFunctionType', () => { it('should return functionType depending on transaction data if present', () => { const { result } = renderHookWithProvider( () => useTransactionFunctionType({ + chainId: CHAIN_ID_MOCK, txParams: { data: '0x095ea7b30000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c9700000000000000000000000000000000000000000000000000000000000011170', }, type: TransactionType.tokenMethodApprove, }), - mockState, + STATE_MOCK, ); expect(result.current.functionType).toStrictEqual('Approve spend limit'); }); + it('should return functionType depending on transaction type if method not present in transaction data', () => { const { result } = renderHookWithProvider( () => useTransactionFunctionType({ + chainId: CHAIN_ID_MOCK, txParams: {}, type: TransactionType.tokenMethodTransfer, }), - mockState, + STATE_MOCK, ); expect(result.current.functionType).toStrictEqual('Transfer'); }); + it('should return functionType Contract interaction by default', () => { const { result } = renderHookWithProvider( () => useTransactionFunctionType({ + chainId: CHAIN_ID_MOCK, txParams: {}, }), - mockState, + STATE_MOCK, ); expect(result.current.functionType).toStrictEqual('Contract interaction'); }); + it('should return undefined is txData is not present', () => { const { result } = renderHookWithProvider( () => useTransactionFunctionType(), - mockState, + STATE_MOCK, ); expect(result.current.functionType).toBeUndefined(); }); diff --git a/ui/pages/confirmations/hooks/useTransactionInfo.js b/ui/pages/confirmations/hooks/useTransactionInfo.js index 452e44c83dfb..ef709de50486 100644 --- a/ui/pages/confirmations/hooks/useTransactionInfo.js +++ b/ui/pages/confirmations/hooks/useTransactionInfo.js @@ -1,5 +1,4 @@ import { useSelector } from 'react-redux'; -import { getProviderConfig } from '../../../ducks/metamask/metamask'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { getSelectedInternalAccount } from '../../../selectors'; @@ -7,7 +6,7 @@ import { getSelectedInternalAccount } from '../../../selectors'; export const useTransactionInfo = (txData = {}) => { const { allNftContracts } = useSelector((state) => state.metamask); const selectedInternalAccount = useSelector(getSelectedInternalAccount); - const { chainId } = useSelector(getProviderConfig); + const { chainId } = txData; const isNftTransfer = Boolean( allNftContracts?.[selectedInternalAccount.address]?.[chainId]?.find( diff --git a/ui/pages/confirmations/hooks/useTransactionInfo.test.js b/ui/pages/confirmations/hooks/useTransactionInfo.test.js index 61ae036c4000..272c27abbfa4 100644 --- a/ui/pages/confirmations/hooks/useTransactionInfo.test.js +++ b/ui/pages/confirmations/hooks/useTransactionInfo.test.js @@ -1,7 +1,7 @@ +import { CHAIN_IDS } from '@metamask/transaction-controller'; import { renderHookWithProvider } from '../../../../test/lib/render-helpers'; import mockState from '../../../../test/data/mock-state.json'; import { getSelectedInternalAccountFromMockState } from '../../../../test/jest/mocks'; -import { getCurrentChainId } from '../../../selectors'; import { useTransactionInfo } from './useTransactionInfo'; const mockSelectedInternalAccount = @@ -13,22 +13,25 @@ describe('useTransactionInfo', () => { const { result } = renderHookWithProvider( () => useTransactionInfo({ + chainId: CHAIN_IDS.GOERLI, txParams: {}, }), mockState, ); expect(result.current.isNftTransfer).toStrictEqual(false); }); + it('should return true if transaction is NFT transfer', () => { mockState.metamask.allNftContracts = { [mockSelectedInternalAccount.address]: { - [getCurrentChainId(mockState)]: [{ address: '0x9' }], + [CHAIN_IDS.GOERLI]: [{ address: '0x9' }], }, }; const { result } = renderHookWithProvider( () => useTransactionInfo({ + chainId: CHAIN_IDS.GOERLI, txParams: { to: '0x9', }, diff --git a/ui/pages/confirmations/send/gas-display/gas-display.js b/ui/pages/confirmations/send/gas-display/gas-display.js index 33a011c2966a..312854d168a7 100644 --- a/ui/pages/confirmations/send/gas-display/gas-display.js +++ b/ui/pages/confirmations/send/gas-display/gas-display.js @@ -27,14 +27,11 @@ import { getIsTestnet, getUseCurrencyRateCheck, getUnapprovedTransactions, + selectNetworkConfigurationByChainId, } from '../../../../selectors'; import { INSUFFICIENT_TOKENS_ERROR } from '../send.constants'; import { getCurrentDraftTransaction } from '../../../../ducks/send'; -import { - getNativeCurrency, - getProviderConfig, -} from '../../../../ducks/metamask/metamask'; import { showModal } from '../../../../store/actions'; import { addHexes, @@ -52,25 +49,26 @@ import { getIsNativeTokenBuyable } from '../../../../ducks/ramps'; export default function GasDisplay({ gasError }) { const t = useContext(I18nContext); const dispatch = useDispatch(); - const { estimateUsed } = useGasFeeContext(); + const { estimateUsed, transaction } = useGasFeeContext(); + const { chainId } = transaction; const trackEvent = useContext(MetaMetricsContext); - const { openBuyCryptoInPdapp } = useRamps(); - const providerConfig = useSelector(getProviderConfig); + const { name: networkNickname, nativeCurrency } = useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ); + const isTestnet = useSelector(getIsTestnet); const isBuyableChain = useSelector(getIsNativeTokenBuyable); const draftTransaction = useSelector(getCurrentDraftTransaction); const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck); const { showFiatInTestnets } = useSelector(getPreferences); const unapprovedTxs = useSelector(getUnapprovedTransactions); - const nativeCurrency = useSelector(getNativeCurrency); - const { chainId } = providerConfig; const networkName = NETWORK_TO_NAME_MAP[chainId]; const isInsufficientTokenError = draftTransaction?.amount?.error === INSUFFICIENT_TOKENS_ERROR; const editingTransaction = unapprovedTxs[draftTransaction.id]; - const currentNetworkName = networkName || providerConfig.nickname; + const currentNetworkName = networkName || networkNickname; const transactionData = { txParams: { diff --git a/ui/pages/confirmations/token-allowance/__snapshots__/token-allowance.test.js.snap b/ui/pages/confirmations/token-allowance/__snapshots__/token-allowance.test.js.snap index 91ac3c735f84..bc44e728df86 100644 --- a/ui/pages/confirmations/token-allowance/__snapshots__/token-allowance.test.js.snap +++ b/ui/pages/confirmations/token-allowance/__snapshots__/token-allowance.test.js.snap @@ -148,14 +148,14 @@ exports[`TokenAllowancePage when mounted should match snapshot 1`] = `
- mainnet + Ethereum Mainnet
+ selectNetworkConfigurationByChainId(state, chainId), + ); + + const blockExplorerUrl = blockExplorerUrls?.[0]; const unapprovedTxCount = useSelector(getUnapprovedTxCount); const unapprovedTxs = useSelector(getUnapprovedTransactions); const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck); @@ -387,7 +395,7 @@ export default function TokenAllowance({ tokenName={tokenSymbol} address={tokenAddress} chainId={fullTxData.chainId} - rpcPrefs={rpcPrefs} + blockExplorerUrl={blockExplorerUrl} /> ); @@ -710,7 +718,7 @@ export default function TokenAllowance({ tokenAddress={tokenAddress} toAddress={toAddress} chainId={fullTxData.chainId} - rpcPrefs={rpcPrefs} + blockExplorerUrl={blockExplorerUrl} /> )} diff --git a/ui/pages/confirmations/token-allowance/token-allowance.test.js b/ui/pages/confirmations/token-allowance/token-allowance.test.js index ddfc48a2cfaa..68df89777c7e 100644 --- a/ui/pages/confirmations/token-allowance/token-allowance.test.js +++ b/ui/pages/confirmations/token-allowance/token-allowance.test.js @@ -203,7 +203,7 @@ describe('TokenAllowancePage', () => { status: 'unapproved', originalGasEstimate: '0xea60', userEditedGasLimit: false, - chainId: '0x3', + chainId: CHAIN_IDS.MAINNET, loadingDefaults: false, dappSuggestedGasFees: { gasPrice: '0x4a817c800', From 200866f42d04eb80ab8ea47cad6d775c760bde96 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Thu, 31 Oct 2024 17:53:44 +0000 Subject: [PATCH 2/9] Fix unit test --- .../components/confirm-subtitle/confirm-subtitle.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js index 5456706631b2..fa00f0275350 100644 --- a/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js +++ b/ui/pages/confirmations/components/confirm-subtitle/confirm-subtitle.test.js @@ -59,6 +59,7 @@ describe('ConfirmSubTitle', () => { const { findByText } = renderWithProvider( Date: Fri, 1 Nov 2024 16:43:55 +0000 Subject: [PATCH 3/9] Fix imports --- .../confirm-transaction-base.container.js | 18 +++++++++++------- .../token-allowance/token-allowance.js | 9 +++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js index 554e06f05cd1..e9558480cb7b 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js @@ -96,12 +96,16 @@ import { CUSTOM_GAS_ESTIMATE } from '../../../../shared/constants/gas'; import { getIsUsingPaymaster } from '../../../selectors/account-abstraction'; import { - getAccountType, - selectDefaultRpcEndpointByChainId, selectNetworkConfigurationByChainId, + // eslint-disable-next-line import/no-duplicates } from '../../../selectors/selectors'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) +import { + getAccountType, + selectDefaultRpcEndpointByChainId, + // eslint-disable-next-line import/no-duplicates +} from '../../../selectors/selectors'; // eslint-disable-next-line import/no-duplicates import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../shared/constants/app'; import { @@ -280,11 +284,6 @@ const mapStateToProps = (state, ownProps) => { chainId, ); - const { url: customRpcUrl } = selectDefaultRpcEndpointByChainId( - state, - chainId, - ); - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const accountType = getAccountType(state, fromAddress); const fromChecksumHexAddress = toChecksumHexAddress(fromAddress); @@ -296,6 +295,11 @@ const mapStateToProps = (state, ownProps) => { getIsCustodianPublishesTransactionSupported(state, fromChecksumHexAddress); const builtinRpcUrl = CHAIN_ID_TO_RPC_URL_MAP[chainId]; + const { url: customRpcUrl } = selectDefaultRpcEndpointByChainId( + state, + chainId, + ); + const rpcUrl = customRpcUrl || builtinRpcUrl; ///: END:ONLY_INCLUDE_IF diff --git a/ui/pages/confirmations/token-allowance/token-allowance.js b/ui/pages/confirmations/token-allowance/token-allowance.js index 0c2a9103baa5..3d092e91cd82 100644 --- a/ui/pages/confirmations/token-allowance/token-allowance.js +++ b/ui/pages/confirmations/token-allowance/token-allowance.js @@ -76,11 +76,12 @@ import FeeDetailsComponent from '../components/fee-details-component/fee-details import { BlockaidResultType } from '../../../../shared/constants/security-provider'; import { QueuedRequestsBannerAlert } from '../confirmation/components/queued-requests-banner-alert/queued-requests-banner-alert'; +// eslint-disable-next-line import/no-duplicates +import { selectNetworkConfigurationByChainId } from '../../../selectors/selectors'; + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) -import { - getAccountType, - selectNetworkConfigurationByChainId, -} from '../../../selectors/selectors'; +// eslint-disable-next-line import/no-duplicates +import { getAccountType } from '../../../selectors/selectors'; import { mmiActionsFactory } from '../../../store/institutional/institution-background'; import { showCustodyConfirmLink } from '../../../store/institutional/institution-actions'; import { From 48a451181f32e9f1a5ce51ae1b973b3a326d38d9 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Fri, 1 Nov 2024 20:43:42 +0000 Subject: [PATCH 4/9] Fix linting --- .../components/transaction-alerts/transaction-alerts.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js index e1002dcebdf8..5127003a81e8 100644 --- a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js +++ b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.js @@ -14,11 +14,11 @@ import { } from '../../../../components/component-library'; import SimulationErrorMessage from '../simulation-error-message'; import { SEVERITIES } from '../../../../helpers/constants/design-system'; +// eslint-disable-next-line import/no-duplicates +import { selectNetworkConfigurationByChainId } from '../../../../selectors'; ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) -import { - selectNetworkConfigurationByChainId, - submittedPendingTransactionsSelector, -} from '../../../../selectors'; +// eslint-disable-next-line import/no-duplicates +import { submittedPendingTransactionsSelector } from '../../../../selectors'; import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url'; ///: END:ONLY_INCLUDE_IF From 0e23f15862ed8a030296c07c7cc25c7ec8b6933b Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 4 Nov 2024 10:54:13 +0000 Subject: [PATCH 5/9] Fix E2E tests --- .../confirm-transaction-base.container.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js index e9558480cb7b..4ab28dc5a136 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js @@ -182,7 +182,7 @@ const mapStateToProps = (state, ownProps) => { const { txParams = {}, id: transactionId, type } = txData; const txId = transactionId || paramsTransactionId; const transaction = getUnapprovedTransaction(state, txId) ?? {}; - const { chainId } = txData; + const { chainId } = transaction; const { from: fromAddress, @@ -279,10 +279,8 @@ const mapStateToProps = (state, ownProps) => { txParamsAreDappSuggested(fullTxData); const fromAddressIsLedger = isAddressLedger(state, fromAddress); - const { nativeCurrency } = selectNetworkConfigurationByChainId( - state, - chainId, - ); + const { nativeCurrency } = + selectNetworkConfigurationByChainId(state, chainId) ?? {}; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) const accountType = getAccountType(state, fromAddress); @@ -295,10 +293,8 @@ const mapStateToProps = (state, ownProps) => { getIsCustodianPublishesTransactionSupported(state, fromChecksumHexAddress); const builtinRpcUrl = CHAIN_ID_TO_RPC_URL_MAP[chainId]; - const { url: customRpcUrl } = selectDefaultRpcEndpointByChainId( - state, - chainId, - ); + const { url: customRpcUrl } = + selectDefaultRpcEndpointByChainId(state, chainId) ?? {}; const rpcUrl = customRpcUrl || builtinRpcUrl; From 1537289d49d2866b34f1c8577d097f27a4790ced Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 4 Nov 2024 14:51:30 +0000 Subject: [PATCH 6/9] Fix stories --- .storybook/preview.js | 5 ++- .../confirmations/contract-interaction.ts | 3 +- test/data/confirmations/helper.ts | 1 + .../transaction-data.stories.tsx | 1 - .../transaction-details.stories.tsx | 3 -- .../transaction-alerts.stories.js | 42 ++++++------------- .../contract-interaction.stories.tsx | 19 +++++---- .../confirmations/confirm/stories/utils.tsx | 21 +--------- 8 files changed, 31 insertions(+), 64 deletions(-) diff --git a/.storybook/preview.js b/.storybook/preview.js index b46c91339273..525c364f2072 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -15,6 +15,7 @@ import MetaMetricsProviderStorybook from './metametrics'; import testData from './test-data.js'; import { Router } from 'react-router-dom'; import { createBrowserHistory } from 'history'; +import { MemoryRouter } from 'react-router-dom'; import { setBackgroundConnection } from '../ui/store/background-connection'; import { metamaskStorybookTheme } from './metamask-storybook-theme'; import { DocsContainer } from '@storybook/addon-docs'; @@ -147,7 +148,7 @@ const metamaskDecorator = (story, context) => { return ( - + { - + ); }; diff --git a/test/data/confirmations/contract-interaction.ts b/test/data/confirmations/contract-interaction.ts index 49a6e1aad1ab..cbe5dd2bd2ba 100644 --- a/test/data/confirmations/contract-interaction.ts +++ b/test/data/confirmations/contract-interaction.ts @@ -1,4 +1,5 @@ import { + CHAIN_IDS, SimulationData, TransactionMeta, TransactionStatus, @@ -18,7 +19,7 @@ export const CONTRACT_INTERACTION_SENDER_ADDRESS = export const DEPOSIT_METHOD_DATA = '0xd0e30db0'; -export const CHAIN_ID = '0xaa36a7'; +export const CHAIN_ID = CHAIN_IDS.GOERLI; export const genUnapprovedContractInteractionConfirmation = ({ address = CONTRACT_INTERACTION_SENDER_ADDRESS, diff --git a/test/data/confirmations/helper.ts b/test/data/confirmations/helper.ts index 6669c043d0ea..b8bd8a634588 100644 --- a/test/data/confirmations/helper.ts +++ b/test/data/confirmations/helper.ts @@ -133,6 +133,7 @@ export const getMockConfirmState = (args: RootState = { metamask: {} }) => ({ ...args.metamask, preferences: { ...mockState.metamask.preferences, + ...(args.metamask?.preferences as Record), redesignedTransactionsEnabled: true, redesignedConfirmationsEnabled: true, isRedesignedConfirmationsDeveloperEnabled: true, diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-data/transaction-data.stories.tsx b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/transaction-data.stories.tsx index 091185ae23f3..105838af78c2 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/transaction-data/transaction-data.stories.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-data/transaction-data.stories.tsx @@ -24,7 +24,6 @@ function getStore(transactionData?: string, to?: string) { const confirmation = { ...confirmationTemplate, - chainId: '0x1', txParams: { ...confirmationTemplate.txParams, to: to ?? confirmationTemplate.txParams.to, diff --git a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.stories.tsx b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.stories.tsx index dfec9c8ef4d0..f37317c230c4 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.stories.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/transaction-details/transaction-details.stories.tsx @@ -5,7 +5,6 @@ import { PAYMASTER_AND_DATA, genUnapprovedContractInteractionConfirmation, } from '../../../../../../../../test/data/confirmations/contract-interaction'; -import mockState from '../../../../../../../../test/data/mock-state.json'; import { getMockConfirmStateForTransaction } from '../../../../../../../../test/data/confirmations/helper'; import configureStore from '../../../../../../../store/store'; import { ConfirmContextProvider } from '../../../../../context/confirm'; @@ -20,9 +19,7 @@ function getStore() { return configureStore( getMockConfirmStateForTransaction(confirmation, { metamask: { - ...mockState.metamask, preferences: { - ...mockState.metamask.preferences, petnamesEnabled: true, }, userOperations: { diff --git a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js index ae46a4a83903..99a60a94e25d 100644 --- a/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js +++ b/ui/pages/confirmations/components/transaction-alerts/transaction-alerts.stories.js @@ -10,6 +10,8 @@ import { CHAIN_IDS } from '../../../../../shared/constants/network'; import { mockNetworkState } from '../../../../../test/stub/networks'; import TransactionAlerts from '.'; +const CHAIN_ID_MOCK = CHAIN_IDS.MAINNET; + const mockSelectedInternalAccount = getSelectedInternalAccountFromMockState(testData); @@ -24,7 +26,7 @@ const customTransaction = ({ userFeeLevel: estimateUsed ? 'low' : 'medium', blockNumber: `${10902987 + i}`, id: 4678200543090545 + i, - chainId: '0x1', + chainId: CHAIN_ID_MOCK, status: 'confirmed', time: 1600654021000, txParams: { @@ -48,23 +50,14 @@ const customTransaction = ({ }; // simulate gas fee state -const customStore = ({ - supportsEIP1559, - isNetworkBusy, - pendingCount = 0, -} = {}) => { +const customStore = ({ supportsEIP1559, pendingCount = 0 } = {}) => { const data = cloneDeep({ ...testData, metamask: { ...testData?.metamask, - // isNetworkBusy - gasFeeEstimates: { - ...testData?.metamask?.gasFeeEstimates, - networkCongestion: isNetworkBusy ? 1 : 0.1, - }, // supportsEIP1559 ...mockNetworkState({ - chainId: CHAIN_IDS.MAINNET, + chainId: CHAIN_ID_MOCK, metadata: { EIPS: { 1559: Boolean(supportsEIP1559), @@ -75,15 +68,12 @@ const customStore = ({ featureFlags: { ...testData?.metamask?.featureFlags, }, - incomingTransactions: { - ...testData?.metamask?.incomingTransactions, - ...Object.fromEntries( - Array.from({ length: pendingCount }).map((_, i) => { - const transaction = customTransaction({ i, status: 'submitted' }); - return [transaction?.hash, transaction]; - }), + transactions: [ + ...testData.metamask.transactions, + ...Array.from({ length: pendingCount }).map((_, i) => + customTransaction({ i, status: 'submitted' }), ), - }, + ], }, }); return configureStore(data); @@ -99,6 +89,7 @@ export default { args: { userAcknowledgedGasMissing: false, txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x1', }, @@ -129,6 +120,7 @@ DefaultStory.storyName = 'Default'; DefaultStory.args = { ...DefaultStory.args, txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x0', }, @@ -176,15 +168,6 @@ export const LowPriority = (args) => ( ); LowPriority.storyName = 'LowPriority'; -export const BusyNetwork = (args) => ( - - - - - -); -BusyNetwork.storyName = 'BusyNetwork'; - export const SendingZeroAmount = (args) => ( @@ -195,6 +178,7 @@ export const SendingZeroAmount = (args) => ( SendingZeroAmount.storyName = 'SendingZeroAmount'; SendingZeroAmount.args = { txData: { + chainId: CHAIN_ID_MOCK, txParams: { value: '0x0', }, diff --git a/ui/pages/confirmations/confirm/stories/transactions/contract-interaction.stories.tsx b/ui/pages/confirmations/confirm/stories/transactions/contract-interaction.stories.tsx index 33a8c2e67f0b..5a531a97787d 100644 --- a/ui/pages/confirmations/confirm/stories/transactions/contract-interaction.stories.tsx +++ b/ui/pages/confirmations/confirm/stories/transactions/contract-interaction.stories.tsx @@ -39,15 +39,16 @@ export const UserOperationStory = () => { }; const confirmState = getMockConfirmStateForTransaction(confirmation, { - metamask: {}, - preferences: { - ...mockState.metamask.preferences, - petnamesEnabled: true, - }, - userOperations: { - [confirmation.id]: { - userOperation: { - paymasterAndData: PAYMASTER_AND_DATA, + metamask: { + preferences: { + ...mockState.metamask.preferences, + petnamesEnabled: true, + }, + userOperations: { + [confirmation.id]: { + userOperation: { + paymasterAndData: PAYMASTER_AND_DATA, + }, }, }, }, diff --git a/ui/pages/confirmations/confirm/stories/utils.tsx b/ui/pages/confirmations/confirm/stories/utils.tsx index 9c68a392cbd7..18f9ae734e31 100644 --- a/ui/pages/confirmations/confirm/stories/utils.tsx +++ b/ui/pages/confirmations/confirm/stories/utils.tsx @@ -1,17 +1,11 @@ import React from 'react'; import { Provider } from 'react-redux'; -import { MemoryRouter, Route } from 'react-router-dom'; import configureStore from '../../../../store/store'; -import { ConfirmContextProvider } from '../../context/confirm'; import ConfirmPage from '../confirm'; export const CONFIRM_PAGE_DECORATOR = [ (story: () => React.ReactFragment) => { - return ( - -
{story()}
-
- ); + return
{story()}
; }, ]; @@ -36,18 +30,7 @@ export function ConfirmStoryTemplate( return ( - {/* Adding the MemoryRouter and Route is a workaround to bypass a 404 error in storybook that - is caused when the 'ui/pages/confirmations/hooks/syncConfirmPath.ts' hook calls - history.replace. To avoid history.replace, we can provide a param id. */} - - } /> - + ); } From 4751ebfb9b4f17290117b59106dfd64692410df3 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 4 Nov 2024 23:06:35 +0000 Subject: [PATCH 7/9] Fix storybook --- .../shared/advanced-details/advanced-details.stories.tsx | 2 +- ui/pages/confirmations/hooks/useTransactionFunctionType.js | 2 +- ui/pages/confirmations/token-allowance/token-allowance.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.stories.tsx b/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.stories.tsx index 8654400a0bfa..aa9ec6cdc87c 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.stories.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/advanced-details/advanced-details.stories.tsx @@ -22,6 +22,6 @@ const Story = { export default Story; -export const DefaultStory = () => ; +export const DefaultStory = () => ; DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirmations/hooks/useTransactionFunctionType.js b/ui/pages/confirmations/hooks/useTransactionFunctionType.js index 3bb6b12e1c61..10255149eda7 100644 --- a/ui/pages/confirmations/hooks/useTransactionFunctionType.js +++ b/ui/pages/confirmations/hooks/useTransactionFunctionType.js @@ -27,7 +27,7 @@ export const useTransactionFunctionType = (txData = {}) => { return {}; } - const { nativeCurrency } = networkConfiguration; + const { nativeCurrency } = networkConfiguration ?? {}; const isTokenApproval = txData.type === TransactionType.tokenMethodSetApprovalForAll || diff --git a/ui/pages/confirmations/token-allowance/token-allowance.js b/ui/pages/confirmations/token-allowance/token-allowance.js index 3d092e91cd82..82fcd91756fa 100644 --- a/ui/pages/confirmations/token-allowance/token-allowance.js +++ b/ui/pages/confirmations/token-allowance/token-allowance.js @@ -148,9 +148,10 @@ export default function TokenAllowance({ const networkIdentifier = useSelector(getNetworkIdentifier); const { chainId } = txData; - const { blockExplorerUrls } = useSelector((state) => - selectNetworkConfigurationByChainId(state, chainId), - ); + const { blockExplorerUrls } = + useSelector((state) => + selectNetworkConfigurationByChainId(state, chainId), + ) ?? {}; const blockExplorerUrl = blockExplorerUrls?.[0]; const unapprovedTxCount = useSelector(getUnapprovedTxCount); From 64e464e13195941833616e0a06a5e9a016350b0d Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Tue, 5 Nov 2024 13:20:44 +0000 Subject: [PATCH 8/9] Remove getConversionRate usages --- .eslintrc.js | 7 ++++++- .../confirm/info/hooks/useFeeCalculations.ts | 12 +++++++++--- .../native-send-heading/native-send-heading.tsx | 9 +++++++-- .../useBalanceChanges.test.ts | 17 ++++++++--------- .../simulation-details/useBalanceChanges.ts | 11 ++++++++--- .../confirm-send-token/confirm-send-token.js | 8 ++++++-- .../confirm-token-transaction-base.js | 12 +++++++----- .../confirm-transaction-base.container.js | 4 ++-- ui/pages/confirmations/hooks/test-utils.js | 4 ++-- ui/selectors/selectors.js | 16 ++++++++++++++++ 10 files changed, 71 insertions(+), 29 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fc66d281c327..bf001c58bc49 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -479,7 +479,12 @@ module.exports = { 'error', { selector: - 'ImportSpecifier[imported.name=/(getCurrentChainId)|(getNativeCurrency)|(getProviderConfig)|(getRpcPrefsForCurrentProvider)/]', + 'ImportSpecifier[imported.name=/' + + '(getCurrentChainId)|' + + '(getNativeCurrency)|' + + '(getProviderConfig)|' + + '(getRpcPrefsForCurrentProvider)|' + + '(getConversionRate)/]', message: 'Avoid using global network selectors in confirmations', }, ], diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useFeeCalculations.ts b/ui/pages/confirmations/components/confirm/info/hooks/useFeeCalculations.ts index 70bd2c0e3af2..5b54dcc710b8 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/useFeeCalculations.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/useFeeCalculations.ts @@ -12,10 +12,12 @@ import { multiplyHexes, } from '../../../../../../../shared/modules/conversion.utils'; import { Numeric } from '../../../../../../../shared/modules/Numeric'; -import { getConversionRate } from '../../../../../../ducks/metamask/metamask'; import { useFiatFormatter } from '../../../../../../hooks/useFiatFormatter'; import { useGasFeeEstimates } from '../../../../../../hooks/useGasFeeEstimates'; -import { getCurrentCurrency } from '../../../../../../selectors'; +import { + getCurrentCurrency, + selectConversionRateByChainId, +} from '../../../../../../selectors'; import { getMultichainNetwork } from '../../../../../../selectors/multichain'; import { HEX_ZERO } from '../shared/constants'; import { useEIP1559TxFees } from './useEIP1559TxFees'; @@ -30,9 +32,13 @@ const EMPTY_FEES = { export function useFeeCalculations(transactionMeta: TransactionMeta) { const currentCurrency = useSelector(getCurrentCurrency); - const conversionRate = useSelector(getConversionRate); + const { chainId } = transactionMeta; const fiatFormatter = useFiatFormatter(); + const conversionRate = useSelector((state) => + selectConversionRateByChainId(state, chainId), + ); + const multichainNetwork = useSelector(getMultichainNetwork); const ticker = multichainNetwork?.network?.ticker; diff --git a/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx b/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx index a3c2b91c9f8e..bb1897484b2d 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx @@ -11,7 +11,6 @@ import { } from '../../../../../../../components/component-library'; import Tooltip from '../../../../../../../components/ui/tooltip'; import { getIntlLocale } from '../../../../../../../ducks/locale/locale'; -import { getConversionRate } from '../../../../../../../ducks/metamask/metamask'; import { AlignItems, Display, @@ -29,16 +28,22 @@ import { formatAmountMaxPrecision, } from '../../../../simulation-details/formatAmount'; import { toNonScientificString } from '../../hooks/use-token-values'; +import { selectConversionRateByChainId } from '../../../../../../../selectors'; const NativeSendHeading = () => { const { currentConfirmation: transactionMeta } = useConfirmContext(); + const { chainId } = transactionMeta; + const nativeAssetTransferValue = new BigNumber( transactionMeta.txParams.value as string, ).dividedBy(new BigNumber(10).pow(18)); - const conversionRate = useSelector(getConversionRate); + const conversionRate = useSelector((state) => + selectConversionRateByChainId(state, chainId), + ); + const fiatValue = conversionRate && nativeAssetTransferValue && diff --git a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts index f77774c6ec4a..5f2e1dcbfd16 100644 --- a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts +++ b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.test.ts @@ -6,10 +6,10 @@ import { } from '@metamask/transaction-controller'; import { BigNumber } from 'bignumber.js'; import { TokenStandard } from '../../../../../shared/constants/transaction'; -import { getConversionRate } from '../../../../ducks/metamask/metamask'; import { getTokenStandardAndDetails } from '../../../../store/actions'; import { fetchTokenExchangeRates } from '../../../../helpers/utils/util'; import { memoizedGetTokenStandardAndDetails } from '../../utils/token'; +import { selectConversionRateByChainId } from '../../../../selectors'; import { useBalanceChanges } from './useBalanceChanges'; import { FIAT_UNAVAILABLE } from './types'; @@ -17,13 +17,10 @@ jest.mock('react-redux', () => ({ useSelector: jest.fn((selector) => selector()), })); -jest.mock('../../../../ducks/metamask/metamask', () => ({ - getConversionRate: jest.fn(), -})); - jest.mock('../../../../selectors', () => ({ getCurrentChainId: jest.fn(), getCurrentCurrency: jest.fn(), + selectConversionRateByChainId: jest.fn(), })); jest.mock('../../../../helpers/utils/util', () => ({ @@ -34,7 +31,9 @@ jest.mock('../../../../store/actions', () => ({ getTokenStandardAndDetails: jest.fn(), })); -const mockGetConversionRate = getConversionRate as jest.Mock; +const mockSelectConversionRateByChainId = jest.mocked( + selectConversionRateByChainId, +); const mockGetTokenStandardAndDetails = getTokenStandardAndDetails as jest.Mock; const mockFetchTokenExchangeRates = fetchTokenExchangeRates as jest.Mock; @@ -85,7 +84,7 @@ describe('useBalanceChanges', () => { } return Promise.reject(new Error('Unable to determine token standard')); }); - mockGetConversionRate.mockReturnValue(ETH_TO_FIAT_RATE); + mockSelectConversionRateByChainId.mockReturnValue(ETH_TO_FIAT_RATE); mockFetchTokenExchangeRates.mockResolvedValue({ [ERC20_TOKEN_ADDRESS_1_MOCK]: ERC20_TO_FIAT_RATE_1_MOCK, [ERC20_TOKEN_ADDRESS_2_MOCK]: ERC20_TO_FIAT_RATE_2_MOCK, @@ -344,7 +343,7 @@ describe('useBalanceChanges', () => { }); it('handles native fiat rate with more than 15 significant digits', async () => { - mockGetConversionRate.mockReturnValue(0.1234567890123456); + mockSelectConversionRateByChainId.mockReturnValue(0.1234567890123456); const { result, waitForNextUpdate } = setupHook({ ...dummyBalanceChange, difference: DIFFERENCE_ETH_MOCK, @@ -357,7 +356,7 @@ describe('useBalanceChanges', () => { }); it('handles unavailable native fiat rate', async () => { - mockGetConversionRate.mockReturnValue(null); + mockSelectConversionRateByChainId.mockReturnValue(null); const { result, waitForNextUpdate } = setupHook({ ...dummyBalanceChange, difference: DIFFERENCE_ETH_MOCK, diff --git a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts index 1bbc9cb5eec8..666682c95c76 100644 --- a/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts +++ b/ui/pages/confirmations/components/simulation-details/useBalanceChanges.ts @@ -10,8 +10,10 @@ import { BigNumber } from 'bignumber.js'; import { ContractExchangeRates } from '@metamask/assets-controllers'; import { useAsyncResultOrThrow } from '../../../../hooks/useAsyncResult'; import { TokenStandard } from '../../../../../shared/constants/transaction'; -import { getConversionRate } from '../../../../ducks/metamask/metamask'; -import { getCurrentCurrency } from '../../../../selectors'; +import { + getCurrentCurrency, + selectConversionRateByChainId, +} from '../../../../selectors'; import { fetchTokenExchangeRates } from '../../../../helpers/utils/util'; import { ERC20_DEFAULT_DECIMALS, fetchErc20Decimals } from '../../utils/token'; @@ -158,7 +160,10 @@ export const useBalanceChanges = ({ simulationData?: SimulationData; }): { pending: boolean; value: BalanceChange[] } => { const fiatCurrency = useSelector(getCurrentCurrency); - const nativeFiatRate = useSelector(getConversionRate); + + const nativeFiatRate = useSelector((state) => + selectConversionRateByChainId(state, chainId), + ); const { nativeBalanceChange, tokenBalanceChanges = [] } = simulationData ?? {}; diff --git a/ui/pages/confirmations/confirm-send-token/confirm-send-token.js b/ui/pages/confirmations/confirm-send-token/confirm-send-token.js index 0fe088063959..4e68783e9be7 100644 --- a/ui/pages/confirmations/confirm-send-token/confirm-send-token.js +++ b/ui/pages/confirmations/confirm-send-token/confirm-send-token.js @@ -8,9 +8,9 @@ import { editExistingTransaction } from '../../../ducks/send'; import { contractExchangeRateSelector, getCurrentCurrency, + selectConversionRateByChainId, selectNetworkConfigurationByChainId, } from '../../../selectors'; -import { getConversionRate } from '../../../ducks/metamask/metamask'; import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'; import { showSendTokenPage } from '../../../store/actions'; import { @@ -47,9 +47,13 @@ export default function ConfirmSendToken({ history.push(SEND_ROUTE); }); }; - const conversionRate = useSelector(getConversionRate); + const { chainId } = transaction; + const conversionRate = useSelector((state) => + selectConversionRateByChainId(state, chainId), + ); + const { nativeCurrency } = useSelector((state) => selectNetworkConfigurationByChainId(state, chainId), ); diff --git a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js index e361a5d0311f..c15c6bdc251f 100644 --- a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js +++ b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js @@ -17,12 +17,10 @@ import { contractExchangeRateSelector, getCurrentCurrency, getSelectedInternalAccount, + selectConversionRateByChainId, selectNetworkConfigurationByChainId, } from '../../../selectors'; -import { - getConversionRate, - getNftContracts, -} from '../../../ducks/metamask/metamask'; +import { getNftContracts } from '../../../ducks/metamask/metamask'; import { TokenStandard } from '../../../../shared/constants/transaction'; import { getWeiHexFromDecimalValue, @@ -57,7 +55,11 @@ export default function ConfirmTokenTransactionBase({ const blockExplorerUrl = blockExplorerUrls?.[0]; const currentCurrency = useSelector(getCurrentCurrency); - const conversionRate = useSelector(getConversionRate); + + const conversionRate = useSelector((state) => + selectConversionRateByChainId(state, chainId), + ); + const { address: userAddress } = useSelector(getSelectedInternalAccount); const nftCollections = useSelector(getNftContracts); diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js index 4ab28dc5a136..37f150caa658 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.container.js @@ -71,7 +71,6 @@ import { getIsGasEstimatesLoading, getSendToAccounts, findKeyringForAddress, - getConversionRate, } from '../../../ducks/metamask/metamask'; import { addHexPrefix, @@ -96,6 +95,7 @@ import { CUSTOM_GAS_ESTIMATE } from '../../../../shared/constants/gas'; import { getIsUsingPaymaster } from '../../../selectors/account-abstraction'; import { + selectConversionRateByChainId, selectNetworkConfigurationByChainId, // eslint-disable-next-line import/no-duplicates } from '../../../selectors/selectors'; @@ -174,7 +174,6 @@ const mapStateToProps = (state, ownProps) => { const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state); const isBuyableChain = getIsNativeTokenBuyable(state); const { confirmTransaction, metamask } = state; - const conversionRate = getConversionRate(state); const { addressBook, nextNonce } = metamask; const unapprovedTxs = getUnapprovedTransactions(state); @@ -183,6 +182,7 @@ const mapStateToProps = (state, ownProps) => { const txId = transactionId || paramsTransactionId; const transaction = getUnapprovedTransaction(state, txId) ?? {}; const { chainId } = transaction; + const conversionRate = selectConversionRateByChainId(state, chainId); const { from: fromAddress, diff --git a/ui/pages/confirmations/hooks/test-utils.js b/ui/pages/confirmations/hooks/test-utils.js index d8d94926e703..5e327b0467c5 100644 --- a/ui/pages/confirmations/hooks/test-utils.js +++ b/ui/pages/confirmations/hooks/test-utils.js @@ -3,7 +3,6 @@ import { useSelector } from 'react-redux'; import { useMultichainSelector } from '../../../hooks/useMultichainSelector'; import { GasEstimateTypes } from '../../../../shared/constants/gas'; -import { getConversionRate } from '../../../ducks/metamask/metamask'; import { getCurrentCurrency, getShouldShowFiat, @@ -11,6 +10,7 @@ import { getCurrentKeyring, getTokenExchangeRates, getPreferences, + selectConversionRateByChainId, } from '../../../selectors'; import { @@ -104,7 +104,7 @@ export const generateUseSelectorRouter = if (selector === getMultichainIsEvm) { return true; } - if (selector === getConversionRate) { + if (selector === selectConversionRateByChainId) { return MOCK_ETH_USD_CONVERSION_RATE; } if (selector === getMultichainNativeCurrency) { diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 8b44dd715aba..d4eeb4b65f4b 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -734,6 +734,22 @@ export const selectDefaultRpcEndpointByChainId = createSelector( }, ); +/** + * @type (state: any, chainId: string) => number | undefined + */ +export const selectConversionRateByChainId = createSelector( + selectNetworkConfigurationByChainId, + (state) => state, + (networkConfiguration, state) => { + if (!networkConfiguration) { + return undefined; + } + + const { nativeCurrency } = networkConfiguration; + return state.metamask.currencyRates[nativeCurrency]?.conversionRate; + }, +); + export function getRequestingNetworkInfo(state, chainIds) { // If chainIds is undefined, set it to an empty array let processedChainIds = chainIds === undefined ? [] : chainIds; From 43317f9a766e8475708eeeb204698c3734d183c9 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Tue, 5 Nov 2024 16:00:45 +0000 Subject: [PATCH 9/9] Remove additional selector usages --- .eslintrc.js | 21 +++++++++----- .../advanced-gas-fee-defaults.js | 8 +++-- .../advanced-gas-fee-defaults.test.js | 4 ++- .../confirm-page-container.component.js | 12 +++++--- .../confirm-page-container.container.js | 3 -- .../approve-static-simulation.tsx | 1 + .../confirm/info/approve/approve.tsx | 1 + .../edit-spending-cap-modal.tsx | 1 + .../approve/spending-cap/spending-cap.tsx | 1 + .../confirm/info/hooks/use-token-values.ts | 1 + .../nft-send-heading/nft-send-heading.tsx | 2 ++ .../title/hooks/useCurrentSpendingCap.ts | 8 ++++- .../confirm-token-transaction-base.js | 7 +++-- .../confirm-token-transaction-switch.js | 4 ++- .../confirmations/hooks/useAssetDetails.js | 13 +++++++-- .../token-allowance/token-allowance.js | 14 ++++++--- ui/selectors/selectors.js | 29 +++++++++++++++++++ 17 files changed, 102 insertions(+), 28 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index bf001c58bc49..4aa9ef688592 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -478,13 +478,20 @@ module.exports = { 'no-restricted-syntax': [ 'error', { - selector: - 'ImportSpecifier[imported.name=/' + - '(getCurrentChainId)|' + - '(getNativeCurrency)|' + - '(getProviderConfig)|' + - '(getRpcPrefsForCurrentProvider)|' + - '(getConversionRate)/]', + selector: `ImportSpecifier[imported.name=/${[ + 'getConversionRate', + 'getCurrentChainId', + 'getNativeCurrency', + 'getNetworkIdentifier', + 'getNftContracts', + 'getNfts', + 'getProviderConfig', + 'getRpcPrefsForCurrentProvider', + 'getUSDConversionRate', + 'isCurrentProviderCustom', + ] + .map((method) => `(${method})`) + .join('|')}/]`, message: 'Avoid using global network selectors in confirmations', }, ], diff --git a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js index 33d4ebd45c5f..106984363699 100644 --- a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js +++ b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js @@ -11,7 +11,7 @@ import { } from '../../../../../helpers/constants/design-system'; import { getAdvancedGasFeeValues, - getNetworkIdentifier, + selectNetworkIdentifierByChainId, } from '../../../../../selectors'; import { setAdvancedGasFee } from '../../../../../store/actions'; import { useGasFeeContext } from '../../../../../contexts/gasFee'; @@ -34,10 +34,14 @@ const AdvancedGasFeeDefaults = () => { 10, ).toString(); const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); - const networkIdentifier = useSelector(getNetworkIdentifier); const { updateTransactionEventFragment } = useTransactionEventFragment(); const { editGasMode, transaction } = useGasFeeContext(); const { chainId } = transaction; + + const networkIdentifier = useSelector((state) => + selectNetworkIdentifierByChainId(state, chainId), + ); + const [isDefaultSettingsSelected, setDefaultSettingsSelected] = useState( Boolean(advancedGasFeeValues) && advancedGasFeeValues.maxBaseFee === maxBaseFee && diff --git a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js index 5e330a8b90c7..07c7fc8faaab 100644 --- a/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js +++ b/ui/pages/confirmations/components/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js @@ -21,6 +21,7 @@ import { mockNetworkState } from '../../../../../../test/stub/networks'; import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults'; const TEXT_SELECTOR = 'Save these values as my default for the Goerli network.'; +const CHAIN_ID_MOCK = CHAIN_IDS.GOERLI; jest.mock('../../../../../store/actions', () => ({ gasFeeStartPollingByNetworkClientId: jest @@ -43,7 +44,7 @@ const render = async (defaultGasParams, contextParams) => { metamask: { ...mockState.metamask, ...defaultGasParams, - ...mockNetworkState({ chainId: CHAIN_IDS.GOERLI }), + ...mockNetworkState({ chainId: CHAIN_ID_MOCK }), accounts: { [mockSelectedInternalAccount.address]: { address: mockSelectedInternalAccount.address, @@ -71,6 +72,7 @@ const render = async (defaultGasParams, contextParams) => { (result = renderWithProvider( { useState(false); const isBuyableChain = useSelector(getIsNativeTokenBuyable); const contact = useSelector((state) => getAddressBookEntry(state, toAddress)); - const networkIdentifier = useSelector(getNetworkIdentifier); const defaultToken = useSelector(getSwapsDefaultToken); const accountBalance = defaultToken.string; const internalAccounts = useSelector(getInternalAccounts); @@ -139,8 +138,13 @@ const ConfirmPageContainer = (props) => { const shouldDisplayWarning = contentComponent && disabled && (errorKey || errorMessage); - const networkName = - NETWORK_TO_NAME_MAP[currentTransaction.chainId] || networkIdentifier; + const { chainId } = currentTransaction; + + const networkIdentifier = useSelector((state) => + selectNetworkIdentifierByChainId(state, chainId), + ); + + const networkName = NETWORK_TO_NAME_MAP[chainId] || networkIdentifier; const fetchCollectionBalance = useCallback(async () => { const tokenBalance = await fetchTokenBalance( diff --git a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js index db07ad4117e7..23eaa43d44ab 100644 --- a/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js +++ b/ui/pages/confirmations/components/confirm-page-container/confirm-page-container.container.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import { getAddressBookEntry, - getNetworkIdentifier, getSwapsDefaultToken, getMetadataContractName, getAccountName, @@ -12,7 +11,6 @@ import ConfirmPageContainer from './confirm-page-container.component'; function mapStateToProps(state, ownProps) { const to = ownProps.toAddress; const contact = getAddressBookEntry(state, to); - const networkIdentifier = getNetworkIdentifier(state); const defaultToken = getSwapsDefaultToken(state); const accountBalance = defaultToken.string; const internalAccounts = getInternalAccounts(state); @@ -26,7 +24,6 @@ function mapStateToProps(state, ownProps) { toMetadataName, recipientIsOwnedAccount: Boolean(ownedAccountName), to, - networkIdentifier, accountBalance, }; } diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx index f1117e2a22b2..eab973d0a37c 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx @@ -33,6 +33,7 @@ export const ApproveStaticSimulation = () => { transactionMeta?.txParams?.to, transactionMeta?.txParams?.from, transactionMeta?.txParams?.data, + transactionMeta?.chainId, ); const decimals = initialDecimals || '0'; diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx index fed03a75e17e..8f4ec8db31d7 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx @@ -31,6 +31,7 @@ const ApproveInfo = () => { transactionMeta.txParams.to, transactionMeta.txParams.from, transactionMeta.txParams.data, + transactionMeta.chainId, ); const { spendingCap, pending } = useApproveTokenSimulation( diff --git a/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx b/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx index f908333e4f25..0b08bbe13068 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/edit-spending-cap-modal/edit-spending-cap-modal.tsx @@ -55,6 +55,7 @@ export const EditSpendingCapModal = ({ transactionMeta.txParams.to, transactionMeta.txParams.from, transactionMeta.txParams.data, + transactionMeta.chainId, ); const accountBalance = calcTokenAmount( diff --git a/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx b/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx index 2ebd9d8e7e4e..afe43e6f9e72 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/spending-cap/spending-cap.tsx @@ -89,6 +89,7 @@ export const SpendingCap = ({ transactionMeta.txParams.to, transactionMeta.txParams.from, transactionMeta.txParams.data, + transactionMeta.chainId, ); const accountBalance = calcTokenAmount( diff --git a/ui/pages/confirmations/components/confirm/info/hooks/use-token-values.ts b/ui/pages/confirmations/components/confirm/info/hooks/use-token-values.ts index f416282ee4ef..087a17c473c6 100644 --- a/ui/pages/confirmations/components/confirm/info/hooks/use-token-values.ts +++ b/ui/pages/confirmations/components/confirm/info/hooks/use-token-values.ts @@ -17,6 +17,7 @@ export const useTokenValues = (transactionMeta: TransactionMeta) => { transactionMeta.txParams.to, transactionMeta.txParams.from, transactionMeta.txParams.data, + transactionMeta.chainId, ); const decodedResponse = useDecodedTransactionData(); diff --git a/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/nft-send-heading.tsx b/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/nft-send-heading.tsx index 006613e206c9..2f36d10ce42c 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/nft-send-heading.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/nft-send-heading.tsx @@ -24,11 +24,13 @@ const NFTSendHeading = () => { const tokenAddress = transactionMeta.txParams.to; const userAddress = transactionMeta.txParams.from; const { data } = transactionMeta.txParams; + const { chainId } = transactionMeta; const { assetName, tokenImage, tokenId } = useAssetDetails( tokenAddress, userAddress, data, + chainId, ); const TokenImage = ; diff --git a/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts b/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts index 5f588a971561..b50948259734 100644 --- a/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts +++ b/ui/pages/confirmations/components/confirm/title/hooks/useCurrentSpendingCap.ts @@ -32,8 +32,14 @@ export function useCurrentSpendingCap(currentConfirmation: Confirmation) { const txParamsData = isTxWithSpendingCap ? currentConfirmation.txParams.data : null; + const chainId = isTxWithSpendingCap ? currentConfirmation.chainId : null; - const { decimals } = useAssetDetails(txParamsTo, txParamsFrom, txParamsData); + const { decimals } = useAssetDetails( + txParamsTo, + txParamsFrom, + txParamsData, + chainId, + ); const { spendingCap, pending } = useApproveTokenSimulation( currentConfirmation as TransactionMeta, diff --git a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js index c15c6bdc251f..077d5c757dcb 100644 --- a/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js +++ b/ui/pages/confirmations/confirm-token-transaction-base/confirm-token-transaction-base.js @@ -19,8 +19,8 @@ import { getSelectedInternalAccount, selectConversionRateByChainId, selectNetworkConfigurationByChainId, + selectNftContractsByChainId, } from '../../../selectors'; -import { getNftContracts } from '../../../ducks/metamask/metamask'; import { TokenStandard } from '../../../../shared/constants/transaction'; import { getWeiHexFromDecimalValue, @@ -61,7 +61,10 @@ export default function ConfirmTokenTransactionBase({ ); const { address: userAddress } = useSelector(getSelectedInternalAccount); - const nftCollections = useSelector(getNftContracts); + + const nftCollections = useSelector((state) => + selectNftContractsByChainId(state, chainId), + ); const ethTransactionTotalMaxAmount = Number( hexWEIToDecETH(hexMaximumTransactionFee), diff --git a/ui/pages/confirmations/confirm-transaction/confirm-token-transaction-switch.js b/ui/pages/confirmations/confirm-transaction/confirm-token-transaction-switch.js index 1bf5c8771b4f..72f570c5b98b 100644 --- a/ui/pages/confirmations/confirm-transaction/confirm-token-transaction-switch.js +++ b/ui/pages/confirmations/confirm-transaction/confirm-token-transaction-switch.js @@ -27,6 +27,7 @@ import { useAssetDetails } from '../hooks/useAssetDetails'; export default function ConfirmTokenTransactionSwitch({ transaction }) { const { + chainId, txParams: { data, to: tokenAddress, from: userAddress } = {}, layer1GasFee, } = transaction; @@ -44,7 +45,7 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) { tokenAmount, tokenId, toAddress, - } = useAssetDetails(tokenAddress, userAddress, data); + } = useAssetDetails(tokenAddress, userAddress, data, chainId); const { ethTransactionTotal, @@ -221,6 +222,7 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) { ConfirmTokenTransactionSwitch.propTypes = { transaction: PropTypes.shape({ + chainId: PropTypes.string, origin: PropTypes.string, txParams: PropTypes.shape({ data: PropTypes.string, diff --git a/ui/pages/confirmations/hooks/useAssetDetails.js b/ui/pages/confirmations/hooks/useAssetDetails.js index 4a9afaf05468..bea2f6da89a8 100644 --- a/ui/pages/confirmations/hooks/useAssetDetails.js +++ b/ui/pages/confirmations/hooks/useAssetDetails.js @@ -1,7 +1,7 @@ import { isEqual } from 'lodash'; import { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { getNfts, getTokens } from '../../../ducks/metamask/metamask'; +import { getTokens } from '../../../ducks/metamask/metamask'; import { getAssetDetails } from '../../../helpers/utils/token-util'; import { hideLoadingIndication, @@ -10,11 +10,18 @@ import { import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { usePrevious } from '../../../hooks/usePrevious'; import { useTokenTracker } from '../../../hooks/useTokenTracker'; +import { selectNftsByChainId } from '../../../selectors'; -export function useAssetDetails(tokenAddress, userAddress, transactionData) { +export function useAssetDetails( + tokenAddress, + userAddress, + transactionData, + chainId, +) { const dispatch = useDispatch(); + // state selectors - const nfts = useSelector(getNfts); + const nfts = useSelector((state) => selectNftsByChainId(state, chainId)); const tokens = useSelector(getTokens, isEqual); const currentToken = tokens.find((token) => isEqualCaseInsensitive(token.address, tokenAddress), diff --git a/ui/pages/confirmations/token-allowance/token-allowance.js b/ui/pages/confirmations/token-allowance/token-allowance.js index 82fcd91756fa..be732e749afa 100644 --- a/ui/pages/confirmations/token-allowance/token-allowance.js +++ b/ui/pages/confirmations/token-allowance/token-allowance.js @@ -29,7 +29,6 @@ import { PageContainerFooter } from '../../../components/ui/page-container'; import ContractDetailsModal from '../components/contract-details-modal/contract-details-modal'; import { getCustomTokenAmount, - getNetworkIdentifier, transactionFeeSelector, getKnownMethodData, getUnapprovedTxCount, @@ -76,8 +75,11 @@ import FeeDetailsComponent from '../components/fee-details-component/fee-details import { BlockaidResultType } from '../../../../shared/constants/security-provider'; import { QueuedRequestsBannerAlert } from '../confirmation/components/queued-requests-banner-alert/queued-requests-banner-alert'; -// eslint-disable-next-line import/no-duplicates -import { selectNetworkConfigurationByChainId } from '../../../selectors/selectors'; +import { + selectNetworkConfigurationByChainId, + selectNetworkIdentifierByChainId, + // eslint-disable-next-line import/no-duplicates +} from '../../../selectors/selectors'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) // eslint-disable-next-line import/no-duplicates @@ -145,9 +147,13 @@ export default function TokenAllowance({ const fromAccount = useSelector((state) => getTargetAccountWithSendEtherInfo(state, userAddress), ); - const networkIdentifier = useSelector(getNetworkIdentifier); + const { chainId } = txData; + const networkIdentifier = useSelector((state) => + selectNetworkIdentifierByChainId(state, chainId), + ); + const { blockExplorerUrls } = useSelector((state) => selectNetworkConfigurationByChainId(state, chainId), diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index d4eeb4b65f4b..d09d31b82f18 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -750,6 +750,35 @@ export const selectConversionRateByChainId = createSelector( }, ); +export const selectNftsByChainId = createSelector( + getSelectedInternalAccount, + (state) => state.metamask.allNfts, + (_state, chainId) => chainId, + (selectedAccount, nfts, chainId) => { + return nfts?.[selectedAccount.address]?.[chainId] ?? []; + }, +); + +export const selectNftContractsByChainId = createSelector( + getSelectedInternalAccount, + (state) => state.metamask.allNftContracts, + (_state, chainId) => chainId, + (selectedAccount, nftContracts, chainId) => { + return nftContracts?.[selectedAccount.address]?.[chainId] ?? []; + }, +); + +export const selectNetworkIdentifierByChainId = createSelector( + selectNetworkConfigurationByChainId, + selectDefaultRpcEndpointByChainId, + (networkConfiguration, defaultRpcEndpoint) => { + const { name: nickname } = networkConfiguration ?? {}; + const { url: rpcUrl, networkClientId } = defaultRpcEndpoint ?? {}; + + return nickname || rpcUrl || networkClientId; + }, +); + export function getRequestingNetworkInfo(state, chainIds) { // If chainIds is undefined, set it to an empty array let processedChainIds = chainIds === undefined ? [] : chainIds;