diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 63ff92a11ccc..67f2764af3a4 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -338,6 +338,15 @@ export const getNfts = (state) => { return allNfts?.[selectedAddress]?.[chainId] ?? []; }; +export const getNFTsByChainId = (state, chainId) => { + const { + metamask: { allNfts }, + } = state; + const { address: selectedAddress } = getSelectedInternalAccount(state); + + return allNfts?.[selectedAddress]?.[chainId] ?? []; +}; + export const getNftContracts = (state) => { const { metamask: { allNftContracts }, 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 c860556a359a..8d66b17a1f46 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 @@ -14,7 +14,6 @@ import { TextAlign, } from '../../../../../../../helpers/constants/design-system'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; -import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants'; import { useConfirmContext } from '../../../../../context/confirm'; import { useAssetDetails } from '../../../../../hooks/useAssetDetails'; import StaticSimulation from '../../shared/static-simulation/static-simulation'; @@ -37,8 +36,13 @@ export const ApproveStaticSimulation = () => { const decimals = initialDecimals || '0'; - const { spendingCap, formattedSpendingCap, value, pending } = - useApproveTokenSimulation(transactionMeta, decimals); + const { + spendingCap, + isUnlimitedSpendingCap, + formattedSpendingCap, + value, + pending, + } = useApproveTokenSimulation(transactionMeta, decimals); const { isNFT } = useIsNFT(transactionMeta); @@ -61,9 +65,7 @@ export const ApproveStaticSimulation = () => { textAlign={TextAlign.Center} alignItems={AlignItems.center} > - {spendingCap === SPENDING_CAP_UNLIMITED_MSG - ? t('unlimited') - : spendingCap} + {isUnlimitedSpendingCap ? t('unlimited') : formattedSpendingCap} ); @@ -78,10 +80,9 @@ export const ApproveStaticSimulation = () => { marginInlineEnd={1} minWidth={BlockSize.Zero} > - {spendingCap === SPENDING_CAP_UNLIMITED_MSG ? ( - - {formattedTokenText} - + {Boolean(isUnlimitedSpendingCap) || + spendingCap !== formattedSpendingCap ? ( + {formattedTokenText} ) : ( formattedTokenText )} 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 1eb0ac2cde05..1cd080de96d4 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 @@ -62,41 +62,38 @@ export const EditSpendingCapModal = ({ Number(decimals ?? '0'), ).toFixed(); - const { formattedSpendingCap } = useApproveTokenSimulation( + const { formattedSpendingCap, spendingCap } = useApproveTokenSimulation( transactionMeta, decimals || '0', ); const [customSpendingCapInputValue, setCustomSpendingCapInputValue] = - useState(formattedSpendingCap.toString()); + useState(spendingCap); useEffect(() => { - if (formattedSpendingCap) { - setCustomSpendingCapInputValue(formattedSpendingCap.toString()); + if (spendingCap) { + setCustomSpendingCapInputValue(spendingCap); } - }, [formattedSpendingCap]); + }, [spendingCap]); const handleCancel = useCallback(() => { setIsOpenEditSpendingCapModal(false); - setCustomSpendingCapInputValue(formattedSpendingCap.toString()); + setCustomSpendingCapInputValue(spendingCap); }, [ setIsOpenEditSpendingCapModal, setCustomSpendingCapInputValue, - formattedSpendingCap, + spendingCap, ]); const [isModalSaving, setIsModalSaving] = useState(false); const handleSubmit = useCallback(async () => { setIsModalSaving(true); - const parsedValue = parseInt(String(customSpendingCapInputValue), 10); const customTxParamsData = getCustomTxParamsData( transactionMeta?.txParams?.data, { - customPermissionAmount: - // coerce negative numbers to zero - parsedValue < 0 ? '0' : customSpendingCapInputValue || '0', + customPermissionAmount: customSpendingCapInputValue || '0', decimals: decimals || '0', }, ); @@ -117,8 +114,8 @@ export const EditSpendingCapModal = ({ setIsModalSaving(false); setIsOpenEditSpendingCapModal(false); - setCustomSpendingCapInputValue(formattedSpendingCap.toString()); - }, [customSpendingCapInputValue, formattedSpendingCap]); + setCustomSpendingCapInputValue(spendingCap); + }, [customSpendingCapInputValue, spendingCap]); const showDecimalError = decimals && diff --git a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts index 0178e2ffff62..e0a5a8165dfb 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts +++ b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts @@ -65,7 +65,8 @@ describe('useApproveTokenSimulation', () => { expect(result.current).toMatchInlineSnapshot(` { - "formattedSpendingCap": "7", + "formattedSpendingCap": "#7", + "isUnlimitedSpendingCap": false, "pending": undefined, "spendingCap": "#7", "value": { @@ -132,8 +133,9 @@ describe('useApproveTokenSimulation', () => { expect(result.current).toMatchInlineSnapshot(` { "formattedSpendingCap": "1,000,000,000,000,000", + "isUnlimitedSpendingCap": true, "pending": undefined, - "spendingCap": "UNLIMITED MESSAGE", + "spendingCap": "1000000000000000", "value": { "data": [ { @@ -197,7 +199,8 @@ describe('useApproveTokenSimulation', () => { expect(result.current).toMatchInlineSnapshot(` { - "formattedSpendingCap": "0.0000000000001", + "formattedSpendingCap": "<0.000001", + "isUnlimitedSpendingCap": false, "pending": undefined, "spendingCap": "0.0000000000001", "value": { diff --git a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts index 8a95fdc3e35b..4944effcd17b 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts +++ b/ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts @@ -1,11 +1,12 @@ import { TransactionMeta } from '@metamask/transaction-controller'; import { isHexString } from '@metamask/utils'; +import BigNumber from 'bignumber.js'; import { isBoolean } from 'lodash'; import { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { calcTokenAmount } from '../../../../../../../../shared/lib/transactions-controller-utils'; import { getIntlLocale } from '../../../../../../../ducks/locale/locale'; -import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants'; +import { formatAmount } from '../../../../simulation-details/formatAmount'; import { useDecodedTransactionData } from '../../hooks/useDecodedTransactionData'; import { useIsNFT } from './use-is-nft'; @@ -46,22 +47,26 @@ export const useApproveTokenSimulation = ( ).toFixed(); }, [value, decimals]); + const tokenPrefix = isNFT ? '#' : ''; + const formattedSpendingCap = useMemo(() => { - // formatting coerces small numbers to 0 - return isNFT || parseInt(decodedSpendingCap, 10) < 1 - ? decodedSpendingCap - : new Intl.NumberFormat(locale).format(parseInt(decodedSpendingCap, 10)); + return isNFT + ? `${tokenPrefix}${decodedSpendingCap}` + : formatAmount(locale, new BigNumber(decodedSpendingCap)); }, [decodedSpendingCap, isNFT, locale]); - const spendingCap = useMemo(() => { + const { spendingCap, isUnlimitedSpendingCap } = useMemo(() => { if (!isNFT && isSpendingCapUnlimited(parseInt(decodedSpendingCap, 10))) { - return SPENDING_CAP_UNLIMITED_MSG; + return { spendingCap: decodedSpendingCap, isUnlimitedSpendingCap: true }; } - const tokenPrefix = isNFT ? '#' : ''; - return `${tokenPrefix}${formattedSpendingCap}`; + return { + spendingCap: `${tokenPrefix}${decodedSpendingCap}`, + isUnlimitedSpendingCap: false, + }; }, [decodedSpendingCap, formattedSpendingCap, isNFT]); return { + isUnlimitedSpendingCap, spendingCap, formattedSpendingCap, value, 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 8e7b522f050a..29d009c5b810 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 @@ -9,7 +9,6 @@ import { import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; import Tooltip from '../../../../../../../components/ui/tooltip'; import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; -import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants'; import { useConfirmContext } from '../../../../../context/confirm'; import { useAssetDetails } from '../../../../../hooks/useAssetDetails'; import { Container } from '../../shared/transaction-data/transaction-data'; @@ -19,30 +18,25 @@ const SpendingCapGroup = ({ tokenSymbol, decimals, setIsOpenEditSpendingCapModal, - customSpendingCap, }: { tokenSymbol: string; decimals: string; setIsOpenEditSpendingCapModal: (newValue: boolean) => void; - customSpendingCap: string; }) => { const t = useI18nContext(); const { currentConfirmation: transactionMeta } = useConfirmContext(); - const { spendingCap, formattedSpendingCap, value } = + const { spendingCap, isUnlimitedSpendingCap, formattedSpendingCap, value } = useApproveTokenSimulation(transactionMeta, decimals); - const spendingCapValue = - customSpendingCap === '' ? formattedSpendingCap : customSpendingCap; - const SpendingCapElement = ( setIsOpenEditSpendingCapModal(true)} editIconClassName="edit-spending-cap-btn" @@ -63,8 +57,9 @@ const SpendingCapGroup = ({ tooltip={t('spendingCapTooltipDesc')} data-testid="confirmation__approve-spending-cap-group" > - {spendingCap === SPENDING_CAP_UNLIMITED_MSG ? ( - {SpendingCapElement} + {Boolean(isUnlimitedSpendingCap) || + spendingCap !== formattedSpendingCap ? ( + {SpendingCapElement} ) : ( SpendingCapElement )} @@ -95,7 +90,7 @@ export const SpendingCap = ({ Number(decimals ?? '0'), ).toFixed(); - const { pending, spendingCap } = useApproveTokenSimulation( + const { pending } = useApproveTokenSimulation( transactionMeta, decimals || '0', ); @@ -114,7 +109,6 @@ export const SpendingCap = ({ tokenSymbol={tokenSymbol || ''} decimals={decimals || '0'} setIsOpenEditSpendingCapModal={setIsOpenEditSpendingCapModal} - customSpendingCap={spendingCap} /> ); diff --git a/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap index a17a5f37578a..7a488072fd42 100644 --- a/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap @@ -6,7 +6,7 @@ exports[`NativeTransferInfo renders correctly 1`] = ` class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--flex-direction-column mm-box--justify-content-center mm-box--align-items-center" >
G
diff --git a/ui/pages/confirmations/components/confirm/info/nft-token-transfer/__snapshots__/nft-token-transfer.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/nft-token-transfer/__snapshots__/nft-token-transfer.test.tsx.snap index ca9af0539f6e..3ff774c6770b 100644 --- a/ui/pages/confirmations/components/confirm/info/nft-token-transfer/__snapshots__/nft-token-transfer.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/nft-token-transfer/__snapshots__/nft-token-transfer.test.tsx.snap @@ -6,16 +6,43 @@ exports[`NFTTokenTransferInfo renders correctly 1`] = ` class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--flex-direction-column mm-box--justify-content-center mm-box--align-items-center" >
- ? +

+ > + #undefined +

{ const { currentConfirmation: transactionMeta } = useConfirmContext(); - const isWalletInitiated = transactionMeta.origin === 'metamask'; + // const isWalletInitiated = transactionMeta.origin === 'metamask'; + const isWalletInitiated = false; return ( <> 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 486ba863f92d..f3f1f292fa2e 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 @@ -17,6 +17,7 @@ import Tooltip from '../../../../../../../components/ui/tooltip'; import { getIntlLocale } from '../../../../../../../ducks/locale/locale'; import { AlignItems, + BackgroundColor, Display, FlexDirection, JustifyContent, @@ -81,6 +82,7 @@ const NativeSendHeading = () => { } name={multichainNetwork?.nickname} size={AvatarTokenSize.Xl} + backgroundColor={BackgroundColor.backgroundDefault} /> ); diff --git a/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/__snapshots__/nft-send-heading.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/__snapshots__/nft-send-heading.test.tsx.snap index 2e98c4e5ce13..5559f00fa01f 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/__snapshots__/nft-send-heading.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/shared/nft-send-heading/__snapshots__/nft-send-heading.test.tsx.snap @@ -6,16 +6,43 @@ exports[` renders component 1`] = ` class="mm-box mm-box--padding-4 mm-box--display-flex mm-box--flex-direction-column mm-box--justify-content-center mm-box--align-items-center" >
- ? +

+ > + #undefined +

`; 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 2f36d10ce42c..405236fe66da 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 @@ -1,19 +1,23 @@ +import { Nft } from '@metamask/assets-controllers'; import { TransactionMeta } from '@metamask/transaction-controller'; import React from 'react'; -import { - AvatarToken, - AvatarTokenSize, - Box, - Text, -} from '../../../../../../../components/component-library'; +import { useSelector } from 'react-redux'; +import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../../../../shared/constants/network'; +import { isEqualCaseInsensitive } from '../../../../../../../../shared/modules/string-utils'; +import { Box, Text } from '../../../../../../../components/component-library'; +import { NftItem } from '../../../../../../../components/multichain/nft-item'; +import { getNFTsByChainId } from '../../../../../../../ducks/metamask/metamask'; import { AlignItems, Display, FlexDirection, JustifyContent, + TextAlign, TextColor, TextVariant, } from '../../../../../../../helpers/constants/design-system'; +import { getNftImageAlt } from '../../../../../../../helpers/utils/nfts'; +import { getNetworkConfigurationsByChainId } from '../../../../../../../selectors'; import { useConfirmContext } from '../../../../../context/confirm'; import { useAssetDetails } from '../../../../../hooks/useAssetDetails'; @@ -25,21 +29,53 @@ const NFTSendHeading = () => { const userAddress = transactionMeta.txParams.from; const { data } = transactionMeta.txParams; const { chainId } = transactionMeta; + const { + assetName, + tokenImage, + tokenId: assetTokenId, + } = useAssetDetails(tokenAddress, userAddress, data, chainId); + const nfts: Nft[] = useSelector((state) => + getNFTsByChainId(state, chainId), + ) as Nft[]; + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); + const nft: Nft | undefined = + assetTokenId && + nfts.find( + ({ address, tokenId }: Nft) => + isEqualCaseInsensitive(address, tokenAddress as string) && + assetTokenId === tokenId.toString(), + ); + const imageOriginal = (nft as Nft | undefined)?.imageOriginal; + const image = (nft as Nft | undefined)?.image; + const nftImageAlt = nft && getNftImageAlt(nft); + const nftSrcUrl = imageOriginal ?? (image || ''); + const isIpfsURL = nftSrcUrl?.startsWith('ipfs:'); + const currentChain = networkConfigurations[chainId]; - const { assetName, tokenImage, tokenId } = useAssetDetails( - tokenAddress, - userAddress, - data, - chainId, + const TokenImage = ( + + + ); - const TokenImage = ; - const TokenName = ( {assetName} @@ -47,7 +83,7 @@ const NFTSendHeading = () => { const TokenID = ( - {tokenId} + {`#${assetTokenId}`} );