Skip to content

feat: Display Unlimited for really large spending caps on Permit #29102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import { calcTokenAmount } from '../../../../../../../../shared/lib/transactions
import { getIntlLocale } from '../../../../../../../ducks/locale/locale';
import { formatAmount } from '../../../../simulation-details/formatAmount';
import { useDecodedTransactionData } from '../../hooks/useDecodedTransactionData';
import { TOKEN_VALUE_UNLIMITED_THRESHOLD } from '../../shared/constants';
import { useIsNFT } from './use-is-nft';

const UNLIMITED_THRESHOLD = 10 ** 15;

function isSpendingCapUnlimited(decodedSpendingCap: number) {
return decodedSpendingCap >= UNLIMITED_THRESHOLD;
return decodedSpendingCap >= TOKEN_VALUE_UNLIMITED_THRESHOLD;
}

export const useApproveTokenSimulation = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const HEX_ZERO = '0x0';

export const TOKEN_VALUE_UNLIMITED_THRESHOLD = 10 ** 15;
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,35 @@ const decodingDataBidding: DecodingDataStateChanges = [

describe('DecodedSimulation', () => {
it('renders component correctly', async () => {
const state = getMockTypedSignConfirmStateForRequest({
...permitSignatureMsg,
decodingLoading: false,
decodingData: {
...decodingData,
stateChanges: decodingData.stateChanges
? [
{
...decodingData.stateChanges[0],
amount: '12345',
},
]
: [],
},
});

const mockStore = configureMockStore([])(state);

const { findByText } = renderWithConfirmContextProvider(
<PermitSimulation />,
mockStore,
);

expect(await findByText('Estimated changes')).toBeInTheDocument();
expect(await findByText('Spending cap')).toBeInTheDocument();
expect(await findByText('12,345')).toBeInTheDocument();
});

it('renders component correctly for a very large amount', async () => {
const state = getMockTypedSignConfirmStateForRequest({
...permitSignatureMsg,
decodingLoading: false,
Expand All @@ -91,7 +120,7 @@ describe('DecodedSimulation', () => {

expect(await findByText('Estimated changes')).toBeInTheDocument();
expect(await findByText('Spending cap')).toBeInTheDocument();
expect(await findByText('1,461,501,637,3...')).toBeInTheDocument();
expect(await findByText('Unlimited')).toBeInTheDocument();
});

it('render correctly for ERC712 token', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ const StateChangeRow = ({
const { assetType, changeType, amount, contractAddress, tokenID } =
stateChange;
const tooltip = getStateChangeToolip(stateChangeList, stateChange, t);
const canDisplayValueAsUnlimited =
assetType === TokenStandard.ERC20 &&
(changeType === DecodingDataChangeType.Approve ||
changeType === DecodingDataChangeType.Revoke);
return (
<ConfirmInfoRow
label={shouldDisplayLabel ? getStateChangeLabelMap(t, changeType) : ''}
Expand All @@ -86,6 +90,7 @@ const StateChangeRow = ({
tokenId={tokenID}
credit={changeType === DecodingDataChangeType.Receive}
debit={changeType === DecodingDataChangeType.Transfer}
canDisplayValueAsUnlimited={canDisplayValueAsUnlimited}
/>
)}
{assetType === 'NATIVE' && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import React, { useMemo } from 'react';
import { NameType } from '@metamask/name-controller';
import { Hex } from '@metamask/utils';
import { captureException } from '@sentry/browser';

import React, { useMemo } from 'react';
import { MetaMetricsEventLocation } from '../../../../../../../../../shared/constants/metametrics';
import { shortenString } from '../../../../../../../../helpers/utils/util';
import { calcTokenAmount } from '../../../../../../../../../shared/lib/transactions-controller-utils';
import useTokenExchangeRate from '../../../../../../../../components/app/currency-input/hooks/useTokenExchangeRate';
import { IndividualFiatDisplay } from '../../../../../simulation-details/fiat-display';
import {
formatAmount,
formatAmountMaxPrecision,
} from '../../../../../simulation-details/formatAmount';
import { useGetTokenStandardAndDetails } from '../../../../../../hooks/useGetTokenStandardAndDetails';
import useTrackERC20WithoutDecimalInformation from '../../../../../../hooks/useTrackERC20WithoutDecimalInformation';

import Name from '../../../../../../../../components/app/name/name';
import {
Box,
Text,
Expand All @@ -27,8 +18,17 @@ import {
JustifyContent,
TextAlign,
} from '../../../../../../../../helpers/constants/design-system';
import Name from '../../../../../../../../components/app/name/name';
import { shortenString } from '../../../../../../../../helpers/utils/util';
import { useI18nContext } from '../../../../../../../../hooks/useI18nContext';
import { useGetTokenStandardAndDetails } from '../../../../../../hooks/useGetTokenStandardAndDetails';
import useTrackERC20WithoutDecimalInformation from '../../../../../../hooks/useTrackERC20WithoutDecimalInformation';
import { TokenDetailsERC20 } from '../../../../../../utils/token';
import { IndividualFiatDisplay } from '../../../../../simulation-details/fiat-display';
import {
formatAmount,
formatAmountMaxPrecision,
} from '../../../../../simulation-details/formatAmount';
import { TOKEN_VALUE_UNLIMITED_THRESHOLD } from '../../../shared/constants';
import { getAmountColors } from '../../../utils';

type PermitSimulationValueDisplayParams = {
Expand Down Expand Up @@ -56,6 +56,9 @@ type PermitSimulationValueDisplayParams = {

/** True if value is being debited to wallet */
debit?: boolean;

/** Whether a large amount can be substituted by "Unlimited" */
canDisplayValueAsUnlimited?: boolean;
};

const PermitSimulationValueDisplay: React.FC<
Expand All @@ -68,7 +71,10 @@ const PermitSimulationValueDisplay: React.FC<
value,
credit,
debit,
canDisplayValueAsUnlimited,
}) => {
const t = useI18nContext();

const exchangeRate = useTokenExchangeRate(tokenContract);

const tokenDetails = useGetTokenStandardAndDetails(tokenContract);
Expand All @@ -88,18 +94,26 @@ const PermitSimulationValueDisplay: React.FC<
return undefined;
}, [exchangeRate, tokenDecimals, value]);

const { tokenValue, tokenValueMaxPrecision } = useMemo(() => {
if (!value || tokenId) {
return { tokenValue: null, tokenValueMaxPrecision: null };
}
const { tokenValue, tokenValueMaxPrecision, shouldShowUnlimitedValue } =
useMemo(() => {
if (!value || tokenId) {
return {
tokenValue: null,
tokenValueMaxPrecision: null,
shouldShowUnlimitedValue: false,
};
}

const tokenAmount = calcTokenAmount(value, tokenDecimals);
const tokenAmount = calcTokenAmount(value, tokenDecimals);

return {
tokenValue: formatAmount('en-US', tokenAmount),
tokenValueMaxPrecision: formatAmountMaxPrecision('en-US', tokenAmount),
};
}, [tokenDecimals, value]);
return {
tokenValue: formatAmount('en-US', tokenAmount),
tokenValueMaxPrecision: formatAmountMaxPrecision('en-US', tokenAmount),
shouldShowUnlimitedValue:
canDisplayValueAsUnlimited &&
Number(value) > TOKEN_VALUE_UNLIMITED_THRESHOLD,
};
}, [tokenDecimals, value]);

/** Temporary error capturing as we are building out Permit Simulations */
if (!tokenContract) {
Expand Down Expand Up @@ -138,13 +152,15 @@ const PermitSimulationValueDisplay: React.FC<
>
{credit && '+ '}
{debit && '- '}
{tokenValue !== null &&
shortenString(tokenValue || '', {
truncatedCharLimit: 15,
truncatedStartChars: 15,
truncatedEndChars: 0,
skipCharacterInEnd: true,
})}
{shouldShowUnlimitedValue
? t('unlimited')
: tokenValue !== null &&
shortenString(tokenValue || '', {
truncatedCharLimit: 15,
truncatedStartChars: 15,
truncatedEndChars: 0,
skipCharacterInEnd: true,
})}
{tokenId && `#${tokenId}`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pedronfigueiredo : not all decoded simulation are Spending Cap some are even amount that user will receive. Can you plz confirm with if this change is ok for all state change types in decoded simulations.

</Text>
</Tooltip>
Expand Down
Loading