Skip to content

Commit 3d4bab3

Browse files
fix: Prevent coercing small spending caps to zero (#28179)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** Previously there was a bug that affected the approve screen. When users had a small spending cap (between 0.001 and 0.0001 or smaller), it was coerced to 0. This was caused by the method `new Intl.NumberFormat(locale).format(spendingCap)` that applied the `1,000` large number formatting, so the fix is to bypass it entirely for values smaller than 1. Additionally, these unformatted small numbers are presented in scientific notation, so we leverage `toNonScientificString(spendingCap)` to prevent that. <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28179?quickstart=1) ## **Related issues** Fixes: [#28117](#28117) ## **Manual testing steps** See original bug report. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
1 parent 9cc7b41 commit 3d4bab3

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.test.ts

+67-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('useApproveTokenSimulation', () => {
6565

6666
expect(result.current).toMatchInlineSnapshot(`
6767
{
68-
"formattedSpendingCap": 7,
68+
"formattedSpendingCap": "7",
6969
"pending": undefined,
7070
"spendingCap": "#7",
7171
"value": {
@@ -155,4 +155,70 @@ describe('useApproveTokenSimulation', () => {
155155
}
156156
`);
157157
});
158+
159+
it('returns correct small decimal number token amount for fungible tokens', async () => {
160+
const useIsNFTMock = jest.fn().mockImplementation(() => ({ isNFT: false }));
161+
162+
const useDecodedTransactionDataMock = jest.fn().mockImplementation(() => ({
163+
pending: false,
164+
value: {
165+
data: [
166+
{
167+
name: 'approve',
168+
params: [
169+
{
170+
type: 'address',
171+
value: '0x9bc5baF874d2DA8D216aE9f137804184EE5AfEF4',
172+
},
173+
{
174+
type: 'uint256',
175+
value: 10 ** 5,
176+
},
177+
],
178+
},
179+
],
180+
source: 'FourByte',
181+
},
182+
}));
183+
184+
(useIsNFT as jest.Mock).mockImplementation(useIsNFTMock);
185+
(useDecodedTransactionData as jest.Mock).mockImplementation(
186+
useDecodedTransactionDataMock,
187+
);
188+
189+
const transactionMeta = genUnapprovedContractInteractionConfirmation({
190+
address: CONTRACT_INTERACTION_SENDER_ADDRESS,
191+
}) as TransactionMeta;
192+
193+
const { result } = renderHookWithProvider(
194+
() => useApproveTokenSimulation(transactionMeta, '18'),
195+
mockState,
196+
);
197+
198+
expect(result.current).toMatchInlineSnapshot(`
199+
{
200+
"formattedSpendingCap": "0.0000000000001",
201+
"pending": undefined,
202+
"spendingCap": "0.0000000000001",
203+
"value": {
204+
"data": [
205+
{
206+
"name": "approve",
207+
"params": [
208+
{
209+
"type": "address",
210+
"value": "0x9bc5baF874d2DA8D216aE9f137804184EE5AfEF4",
211+
},
212+
{
213+
"type": "uint256",
214+
"value": 100000,
215+
},
216+
],
217+
},
218+
],
219+
"source": "FourByte",
220+
},
221+
}
222+
`);
223+
});
158224
});

ui/pages/confirmations/components/confirm/info/approve/hooks/use-approve-token-simulation.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useMemo } from 'react';
66
import { useSelector } from 'react-redux';
77
import { getIntlLocale } from '../../../../../../../ducks/locale/locale';
88
import { SPENDING_CAP_UNLIMITED_MSG } from '../../../../../constants';
9+
import { toNonScientificString } from '../../hooks/use-token-values';
910
import { useDecodedTransactionData } from '../../hooks/useDecodedTransactionData';
1011
import { useIsNFT } from './use-is-nft';
1112

@@ -46,8 +47,9 @@ export const useApproveTokenSimulation = (
4647
}, [value, decimals]);
4748

4849
const formattedSpendingCap = useMemo(() => {
49-
return isNFT
50-
? decodedSpendingCap
50+
// formatting coerces small numbers to 0
51+
return isNFT || decodedSpendingCap < 1
52+
? toNonScientificString(decodedSpendingCap)
5153
: new Intl.NumberFormat(locale).format(decodedSpendingCap);
5254
}, [decodedSpendingCap, isNFT, locale]);
5355

0 commit comments

Comments
 (0)