From bb39806d87ca8004e1bdfda84df81d79b0764126 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 2 Jul 2024 20:09:18 +0530 Subject: [PATCH] fix: decimal places displayed on token value on permit pages (#25410) --- .../confirmations/signatures/permit.spec.ts | 2 +- .../__snapshots__/typed-sign.test.tsx.snap | 2 +- .../permit-simulation.test.tsx.snap | 2 +- .../permit-simulation.test.tsx | 5 +- .../permit-simulation/permit-simulation.tsx | 10 +++- .../info/typed-sign/typed-sign.test.tsx | 6 +++ .../confirm/info/typed-sign/typed-sign.tsx | 21 ++++++++- .../components/confirm/row/dataTree.tsx | 24 +++++++++- .../row/typed-sign-data/typedSignData.tsx | 11 +++-- .../components/confirm/utils.test.ts | 46 +++++++++++-------- .../confirmations/components/confirm/utils.ts | 11 +++++ 11 files changed, 110 insertions(+), 30 deletions(-) diff --git a/test/e2e/tests/confirmations/signatures/permit.spec.ts b/test/e2e/tests/confirmations/signatures/permit.spec.ts index 8320fd748f63..03941dbecc3f 100644 --- a/test/e2e/tests/confirmations/signatures/permit.spec.ts +++ b/test/e2e/tests/confirmations/signatures/permit.spec.ts @@ -83,7 +83,7 @@ async function assertInfoValues(driver: Driver) { css: '.name__value', text: '0x5B38D...eddC4', }); - const value = driver.findElement({ text: '3000' }); + const value = driver.findElement({ text: '3,000' }); const nonce = driver.findElement({ text: '0' }); const deadline = driver.findElement({ text: '02 August 1971, 16:53' }); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap index 328fe4209390..c06db18f9268 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/__snapshots__/typed-sign.test.tsx.snap @@ -401,7 +401,7 @@ exports[`TypedSignInfo correctly renders permit sign type 1`] = ` class="mm-box mm-text mm-text--body-md mm-box--color-inherit" style="white-space: pre-wrap;" > - 3000 + 3,000

diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap index 111bc553477f..9b67671cbb06 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/__snapshots__/permit-simulation.test.tsx.snap @@ -70,7 +70,7 @@ exports[`PermitSimulation renders component correctly 1`] = `

- 3000 + 30.00

diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx index ea0ffcc47bc1..daca28ece848 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.test.tsx @@ -15,7 +15,10 @@ describe('PermitSimulation', () => { }, }; const mockStore = configureMockStore([])(state); - const { container } = renderWithProvider(, mockStore); + const { container } = renderWithProvider( + , + mockStore, + ); expect(container).toMatchSnapshot(); }); }); diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index 04eaba8f9be4..f6a116b0842e 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -21,9 +21,12 @@ import { import { SignatureRequestType } from '../../../../../types/confirm'; import useTokenExchangeRate from '../../../../../../../components/app/currency-input/hooks/useTokenExchangeRate'; import { IndividualFiatDisplay } from '../../../../simulation-details/fiat-display'; +import { formatNumber } from '../../../utils'; import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; -const PermitSimulation: React.FC = () => { +const PermitSimulation: React.FC<{ + tokenDecimals: number; +}> = ({ tokenDecimals }) => { const t = useI18nContext(); const currentConfirmation = useSelector( currentConfirmationSelector, @@ -61,7 +64,10 @@ const PermitSimulation: React.FC = () => { paddingInline={2} textAlign={TextAlign.Center} > - {value} + {formatNumber( + value / Math.pow(10, tokenDecimals), + tokenDecimals, + )} diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx index 332b3f79a6ff..1ac473dd40f8 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.test.tsx @@ -10,6 +10,12 @@ import { } from '../../../../../../../test/data/confirmations/typed_sign'; import TypedSignInfo from './typed-sign'; +jest.mock('../../../../../../store/actions', () => { + return { + getTokenStandardAndDetails: jest.fn().mockResolvedValue({ decimals: 2 }), + }; +}); + describe('TypedSignInfo', () => { it('renders origin for typed sign data request', () => { const state = { diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx index 019dd138f401..88ade5cc2a6f 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/typed-sign.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { isValidAddress } from 'ethereumjs-util'; @@ -11,6 +11,7 @@ import { } from '../../../../../../components/app/confirm/info/row'; import { useI18nContext } from '../../../../../../hooks/useI18nContext'; import { currentConfirmationSelector } from '../../../../../../selectors'; +import { getTokenStandardAndDetails } from '../../../../../../store/actions'; import { SignatureRequestType } from '../../../../types/confirm'; import { isPermitSignatureRequest } from '../../../../utils'; import { selectUseTransactionSimulations } from '../../../../selectors/preferences'; @@ -26,6 +27,7 @@ const TypedSignInfo: React.FC = () => { const useTransactionSimulations = useSelector( selectUseTransactionSimulations, ); + const [decimals, setDecimals] = useState(0); if (!currentConfirmation?.msgParams) { return null; @@ -38,9 +40,23 @@ const TypedSignInfo: React.FC = () => { const isPermit = isPermitSignatureRequest(currentConfirmation); + useEffect(() => { + (async () => { + if (!isPermit) { + return; + } + const { decimals: tokenDecimals } = await getTokenStandardAndDetails( + verifyingContract, + ); + setDecimals(parseInt(tokenDecimals ?? '0', 10)); + })(); + }, [verifyingContract]); + return ( <> - {isPermit && useTransactionSimulations && } + {isPermit && useTransactionSimulations && ( + + )} {isPermit && ( <> @@ -64,6 +80,7 @@ const TypedSignInfo: React.FC = () => { diff --git a/ui/pages/confirmations/components/confirm/row/dataTree.tsx b/ui/pages/confirmations/components/confirm/row/dataTree.tsx index 307b608488d9..9f5511e72446 100644 --- a/ui/pages/confirmations/components/confirm/row/dataTree.tsx +++ b/ui/pages/confirmations/components/confirm/row/dataTree.tsx @@ -11,6 +11,7 @@ import { ConfirmInfoRowDate, ConfirmInfoRowText, } from '../../../../../components/app/confirm/info/row'; +import { formatNumber } from '../utils'; type ValueType = string | Record | TreeData[]; @@ -22,9 +23,11 @@ export type TreeData = { export const DataTree = ({ data, isPermit = false, + tokenDecimals = 0, }: { data: Record | TreeData[]; isPermit?: boolean; + tokenDecimals?: number; }) => ( {Object.entries(data).map(([label, { value, type }], i) => ( @@ -42,6 +45,7 @@ export const DataTree = ({ isPermit={isPermit} value={value} type={type} + tokenDecimals={tokenDecimals} /> } @@ -54,14 +58,32 @@ const DataField = ({ isPermit, type, value, + tokenDecimals, }: { label: string; isPermit: boolean; type: string; value: ValueType; + tokenDecimals: number; }) => { if (typeof value === 'object' && value !== null) { - return ; + return ( + + ); + } + if (isPermit && label === 'value') { + return ( + + ); } if (isPermit && label === 'deadline') { return ; diff --git a/ui/pages/confirmations/components/confirm/row/typed-sign-data/typedSignData.tsx b/ui/pages/confirmations/components/confirm/row/typed-sign-data/typedSignData.tsx index 9b3934cef651..0b60f45b6a38 100644 --- a/ui/pages/confirmations/components/confirm/row/typed-sign-data/typedSignData.tsx +++ b/ui/pages/confirmations/components/confirm/row/typed-sign-data/typedSignData.tsx @@ -7,16 +7,17 @@ import { ConfirmInfoRow, ConfirmInfoRowText, } from '../../../../../../components/app/confirm/info/row'; - -import { DataTree } from '../dataTree'; import { parseSanitizeTypedDataMessage } from '../../../../utils'; +import { DataTree } from '../dataTree'; export const ConfirmInfoRowTypedSignData = ({ data, isPermit, + tokenDecimals, }: { data: string; isPermit?: boolean; + tokenDecimals?: number; }) => { const t = useI18nContext(); @@ -35,7 +36,11 @@ export const ConfirmInfoRowTypedSignData = ({ - + ); diff --git a/ui/pages/confirmations/components/confirm/utils.test.ts b/ui/pages/confirmations/components/confirm/utils.test.ts index 87e6307de7be..b967a81e8f77 100644 --- a/ui/pages/confirmations/components/confirm/utils.test.ts +++ b/ui/pages/confirmations/components/confirm/utils.test.ts @@ -8,29 +8,39 @@ import { unapprovedPersonalSignMsg, } from '../../../../../test/data/confirmations/personal_sign'; import { SignatureRequestType } from '../../types/confirm'; -import { getConfirmationSender } from './utils'; +import { formatNumber, getConfirmationSender } from './utils'; -describe('getConfirmationSender()', () => { - test("returns the sender address from a signature if it's passed", () => { - const testCurrentConfirmation = - genUnapprovedContractInteractionConfirmation() as TransactionMeta; - const { from } = getConfirmationSender(testCurrentConfirmation); +describe('confirm - utils', () => { + describe('getConfirmationSender()', () => { + test("returns the sender address from a signature if it's passed", () => { + const testCurrentConfirmation = + genUnapprovedContractInteractionConfirmation() as TransactionMeta; + const { from } = getConfirmationSender(testCurrentConfirmation); - expect(from).toEqual(CONTRACT_INTERACTION_SENDER_ADDRESS); - }); + expect(from).toEqual(CONTRACT_INTERACTION_SENDER_ADDRESS); + }); - test("returns the sender address from a transaction if it's passed", () => { - const { from } = getConfirmationSender( - unapprovedPersonalSignMsg as SignatureRequestType, - ); + test("returns the sender address from a transaction if it's passed", () => { + const { from } = getConfirmationSender( + unapprovedPersonalSignMsg as SignatureRequestType, + ); - expect(from).toEqual(PERSONAL_SIGN_SENDER_ADDRESS); - }); + expect(from).toEqual(PERSONAL_SIGN_SENDER_ADDRESS); + }); - test('returns no sender address if no confirmation is passed', () => { - const testCurrentConfirmation = undefined; - const { from } = getConfirmationSender(testCurrentConfirmation); + test('returns no sender address if no confirmation is passed', () => { + const testCurrentConfirmation = undefined; + const { from } = getConfirmationSender(testCurrentConfirmation); + + expect(from).toEqual(undefined); + }); + }); - expect(from).toEqual(undefined); + describe('formatNumber()', () => { + test('formats number according to decimal places passed', () => { + expect(formatNumber(123456, 2)).toEqual('123,456.00'); + expect(formatNumber(123456, 0)).toEqual('123,456'); + expect(formatNumber(123456, 7)).toEqual('123,456.0000000'); + }); }); }); diff --git a/ui/pages/confirmations/components/confirm/utils.ts b/ui/pages/confirmations/components/confirm/utils.ts index 21b4aac78998..581bf1a5ccee 100644 --- a/ui/pages/confirmations/components/confirm/utils.ts +++ b/ui/pages/confirmations/components/confirm/utils.ts @@ -17,3 +17,14 @@ export const getConfirmationSender = ( return { from }; }; + +export const formatNumber = (value: number, decimals: number) => { + if (value === undefined) { + return value; + } + const formatter = new Intl.NumberFormat('en-US', { + minimumFractionDigits: decimals, + maximumFractionDigits: decimals, + }); + return formatter.format(value); +};