Skip to content

Commit e159f48

Browse files
authored
fix(money): show projected 1-year balance in USD (MUSD-1173) cp-8.3.0 (#33333)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> <!-- mms-check directive vocabulary — read by .github/scripts/shared/pr-template-checks.ts at module load to build the validation plan. Directives are invisible in rendered markdown and must NOT be removed or edited without updating the validator registry. type=text Section must contain non-placeholder prose. type=changelog Section must have a valid CHANGELOG entry: line. type=issue-link Section must have a Fixes:/Closes:/Refs: line with a value. type=manual-testing Section must have real testing steps or an explicit N/A. type=screenshot Section must have evidence (image/URL) or an explicit N/A. type=checklist Section must have all checkboxes consciously checked. required=true|false Whether a missing/invalid section runs the validator at all. blocking=true|false Whether a failure of this check fails the CI workflow. Default: false — failures are shown as warnings in the sticky comment but do not block the PR. Sections without a directive are checked for structural presence only. --> ## **Description** <!-- mms-check: type=text required=true --> <!-- 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? --> The "Projected 1-year balance" text on the Money crypto deposit screen now always displays the amount in USD with the `$` symbol, matching every other Money account surface. Bug: with a non-USD preferred display currency selected (e.g. EUR), the projection rendered with that currency's symbol (`1.040,00 €`) even though the underlying value is USD-denominated — the deposit amount on this screen is a dollar input (the same value feeds `ReceiveRow` as `inputAmountUsd`), so the number was correct but mislabelled. Fix: `BalanceProjection` no longer formats via `useFiatFormatter` (which stamps the user's selected currency onto the raw number); it renders through `moneyFormatUsd` (en-US, USD) — the same pattern used by Money activity rows and the Money toasts (#33330). This also removes the component's only Redux currency dependency, so it is structurally incapable of rendering a non-USD symbol. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Fixed the projected 1-year balance on the Money deposit screen to always display in USD ($) instead of the user's preferred fiat currency ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/MUSD-1173 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Projected 1-year balance displays USD Scenario: user views the balance projection with a non-USD preferred currency Given the user's preferred fiat currency is set to EUR And the user has a Money account When user opens the Money crypto deposit screen and enters an amount Then the "Projected 1-year balance" text displays the amount in USD (e.g. "$1,040.00"), not in EUR ``` ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> <!-- 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** <!-- mms-check: type=checklist required=true --> <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [x] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [x] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [x] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] 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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Localized display-only change in one Money component with updated unit tests; no payment, auth, or balance calculation logic changes. > > **Overview** > **BalanceProjection** on the Money crypto deposit screen now formats the projected balance with **`moneyFormatUsd`** instead of **`useFiatFormatter`**, so the label always shows **USD with `$`** (e.g. `$1,040.00`) even when the user’s preferred display currency is something else. > > Tests drop the fiat-formatter mock, update expected strings for en-US grouping, and add coverage that euro symbols are not shown for the projection. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 05613a5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent c16d9c7 commit e159f48

2 files changed

Lines changed: 11 additions & 21 deletions

File tree

app/components/UI/Money/components/BalanceProjection/BalanceProjection.test.tsx

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import React from 'react';
22
import { fireEvent, render } from '@testing-library/react-native';
3-
import BigNumber from 'bignumber.js';
43
import { BalanceProjection } from './BalanceProjection';
54
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
6-
import useFiatFormatter from '../../../SimulationDetails/FiatDisplay/useFiatFormatter';
75
import { strings } from '../../../../../../locales/i18n';
86
import {
97
MONEY_TOOLTIP_NAMES,
@@ -12,7 +10,6 @@ import {
1210
import Routes from '../../../../../constants/navigation/Routes';
1311

1412
jest.mock('../../hooks/useMoneyAccountBalance');
15-
jest.mock('../../../SimulationDetails/FiatDisplay/useFiatFormatter');
1613

1714
const mockTrackTooltipClicked = jest.fn();
1815

@@ -35,7 +32,6 @@ jest.mock('@react-navigation/native', () => {
3532
});
3633

3734
const useMoneyAccountBalanceMock = jest.mocked(useMoneyAccountBalance);
38-
const useFiatFormatterMock = jest.mocked(useFiatFormatter);
3935

4036
const ONE_YEAR_LABEL = strings('confirm.custom_amount.projected_balance', {
4137
projectedYears: 1,
@@ -58,13 +54,8 @@ function mockBalance({
5854
}
5955

6056
describe('BalanceProjection', () => {
61-
const formatFiat = jest.fn(
62-
(value: BigNumber) => `$${value.toFixed(2, BigNumber.ROUND_HALF_UP)}`,
63-
);
64-
6557
beforeEach(() => {
6658
jest.clearAllMocks();
67-
useFiatFormatterMock.mockReturnValue(formatFiat);
6859
});
6960

7061
it('renders label and projected balance for $1,000 at apyDecimal 0.04 over 1 year (~$1,040.00)', () => {
@@ -76,7 +67,7 @@ describe('BalanceProjection', () => {
7667

7768
expect(getByTestId('balance-projection')).toBeOnTheScreen();
7869
expect(getByText(ONE_YEAR_LABEL, { exact: false })).toBeOnTheScreen();
79-
expect(getByText('$1040.00')).toBeOnTheScreen();
70+
expect(getByText('$1,040.00')).toBeOnTheScreen();
8071
});
8172

8273
it('compounds the projection over multiple years', () => {
@@ -86,7 +77,7 @@ describe('BalanceProjection', () => {
8677
<BalanceProjection amountFiat="1000" projectedYears={5} />,
8778
);
8879

89-
expect(getByText('$1216.65')).toBeOnTheScreen();
80+
expect(getByText('$1,216.65')).toBeOnTheScreen();
9081
});
9182

9283
it('renders the APY pitch when amountFiat is "0"', () => {
@@ -303,14 +294,14 @@ describe('BalanceProjection', () => {
303294
expect(queryByTestId('balance-projection-apy-pitch')).toBeNull();
304295
});
305296

306-
it('passes a BigNumber to the fiat formatter when projecting', () => {
307-
mockBalance({ apyDecimal: 0.04, apyPercent: 4 });
297+
it('renders the projected balance in dollars regardless of the preferred currency', () => {
298+
mockBalance({ apyDecimal: 0.05, apyPercent: 5 });
308299

309-
render(<BalanceProjection amountFiat="1000" projectedYears={1} />);
300+
const { getByText, queryByText } = render(
301+
<BalanceProjection amountFiat="100" projectedYears={1} />,
302+
);
310303

311-
expect(formatFiat).toHaveBeenCalledTimes(1);
312-
const passed = formatFiat.mock.calls[0][0];
313-
expect(BigNumber.isBigNumber(passed)).toBe(true);
314-
expect(passed.toFixed(2)).toBe('1040.00');
304+
expect(getByText('$105.00')).toBeOnTheScreen();
305+
expect(queryByText(//)).toBeNull();
315306
});
316307
});

app/components/UI/Money/components/BalanceProjection/BalanceProjection.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import {
1616
TextVariant,
1717
} from '@metamask/design-system-react-native';
1818
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
19-
import useFiatFormatter from '../../../SimulationDetails/FiatDisplay/useFiatFormatter';
2019
import { strings } from '../../../../../../locales/i18n';
2120
import { Skeleton } from '../../../../../component-library/components-temp/Skeleton';
2221
import { isPositiveNumberOrZero } from '../../utils/number';
22+
import { moneyFormatUsd } from '../../utils/moneyFormatFiat';
2323
import { useMoneyAnalytics } from '../../hooks/useMoneyAnalytics';
2424
import {
2525
MONEY_TOOLTIP_NAMES,
@@ -39,7 +39,6 @@ export function BalanceProjection({
3939
}: BalanceProjectionProps) {
4040
const navigation = useNavigation();
4141
const { vaultApyQuery, apyDecimal, apyPercent } = useMoneyAccountBalance();
42-
const formatFiat = useFiatFormatter();
4342
const { trackTooltipClicked } = useMoneyAnalytics({
4443
screen_name: SCREEN_NAMES.MONEY_DEPOSIT,
4544
});
@@ -112,7 +111,7 @@ export function BalanceProjection({
112111
projectedYears,
113112
})}{' '}
114113
<Text variant={TextVariant.BodyMd} color={TextColor.SuccessDefault}>
115-
{formatFiat(projected)}
114+
{moneyFormatUsd(projected)}
116115
</Text>
117116
</Text>
118117
<ButtonIcon

0 commit comments

Comments
 (0)