Skip to content

Commit aa4f3d9

Browse files
authored
feat: MUSD-434 add real money account balance and apy to the money home screen (#28889)
## **Description** <!-- 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? --> Integrates `@metamask/money-account-balance-service` into the Engine so the Money Account homepage can display live mUSD and Veda vault USD balances instead of hardcoded placeholder values. - Registers `MoneyAccountBalanceService` in the Engine (init, messenger, types, constants) - Added `useMoneyAccountBalance` hook to consume the `MoneyAccountBalanceService` and expose queries to components. - `useMoneyAccountBalance` refetches account balances at a set interval. Currently this is set to 30 seconds but can be easily changed. - `MoneyHomeView` now consumes the hook. - The aggregated balance (mUSD + musdSHFvd) is now displayed. - `MoneyHomeView` components dependent on the APY will only render certain UI elements when the APY is number and > 0. ## **Changelog** <!-- 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: Added live mUSD and Veda vault USD balance display on the Money Account homepage ## **Related issues** Fixes: - [MUSD-434: As a Money Account user I want to see my mUSD + veda vault USD balance on the money account homepage](https://consensyssoftware.atlassian.net/browse/MUSD-434) ## **Manual testing steps** ```gherkin Feature: Money Account live balance and APY display Scenario: user views the Money Account homepage with an active mUSD balance Given the user has a Money Account with mUSD and/or Veda vault deposits And the app is connected to a network When user navigates to the Money Account homepage Then the balance summary shows skeleton loaders while data is fetching And once loaded, the total USD balance reflects the mUSD + vault equivalent value And the APY percentage is displayed in MoneyBalanceSummary, MoneyHowItWorks, and MoneyWhatYouGet Scenario: user views the Money Account homepage with no balance Given the user has a Money Account with zero mUSD and zero vault deposits When user navigates to the Money Account homepage Then the balance displays as $0.00 (or the formatted zero fiat value) And the APY is still displayed once the vault APY query resolves ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** N/A - No `MoneyAccountBalanceService` and the Money home screen was using placeholder data. <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> 1. Loading states https://github.com/user-attachments/assets/de21f761-962e-41c4-b5ab-95d332088191 3. Money home when apy > 0 https://github.com/user-attachments/assets/68f59435-633a-4a44-a92e-feaf11408986 4. Money hom when apy < 0 or undefined https://github.com/user-attachments/assets/db35542c-ddbc-4816-8575-4442eb59dcc5 ## **Pre-merge author checklist** - [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) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] 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 - [ ] 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** - [ ] 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. <!-- Generated with the help of the pr-description AI skill --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds a new Engine-registered data service and periodic balance/APY querying, which could impact correctness or performance if the service/messaging configuration is wrong. UI changes are guarded with loading/undefined handling but touch core Money home display logic. > > **Overview** > Money home now pulls **live aggregated balance (mUSD + vault equivalent) and vault APY** via a new `useMoneyAccountBalance` hook, replacing hardcoded APY and wiring loading state into `MoneyBalanceSummary`/`MoneyHowItWorks`. > > Introduces `MoneyAccountBalanceService` into the Engine (init, restricted messenger, registration in constants/types, and data-services list) and adds query keys plus a helper (`getLiveVedaVaultExchangeRate`). Money UI components now treat `apy` as `number | undefined`, add skeleton/conditional rendering for APY and projected earnings, and centralize checks with `isPositiveNumber`; tests and a small locale string tweak were updated accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 0c46411. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 64f79bb commit aa4f3d9

28 files changed

Lines changed: 1054 additions & 81 deletions

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.test.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { MoneyActivityListTestIds } from '../../components/MoneyActivityList/Mon
1717
import Routes from '../../../../../constants/navigation/Routes';
1818
import { useMoneyAccountTransactions } from '../../hooks/useMoneyAccountTransactions';
1919
import MOCK_MONEY_TRANSACTIONS from '../../constants/mockActivityData';
20+
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
2021

2122
const mockGoBack = jest.fn();
2223
const mockNavigate = jest.fn();
@@ -55,10 +56,17 @@ jest.mock('../../hooks/useMoneyAccountTransactions', () => ({
5556
useMoneyAccountTransactions: jest.fn(),
5657
}));
5758

59+
jest.mock('../../hooks/useMoneyAccountBalance', () => ({
60+
__esModule: true,
61+
default: jest.fn(),
62+
}));
63+
5864
const mockUseMoneyAccountTransactions = jest.mocked(
5965
useMoneyAccountTransactions,
6066
);
6167

68+
const mockUseMoneyAccountBalance = jest.mocked(useMoneyAccountBalance);
69+
6270
jest.mock(
6371
'../../../../UI/Assets/components/AssetLogo/AssetLogo',
6472
() => 'AssetLogo',
@@ -97,6 +105,32 @@ jest.mock('../../../../UI/AssetOverview/Balance/Balance', () => ({
97105
describe('MoneyHomeView', () => {
98106
beforeEach(() => {
99107
jest.clearAllMocks();
108+
109+
mockUseMoneyAccountBalance.mockReturnValue({
110+
totalFiatFormatted: '$3.00',
111+
musdFiatFormatted: '$1.00',
112+
musdSHFvdFiatFormatted: '$2.00',
113+
totalFiatRaw: '3',
114+
tokenTotal: undefined,
115+
isAggregatedBalanceLoading: false,
116+
vaultApyQuery: {
117+
data: { apy: 5.5, timestamp: '2026-01-01T00:00:00Z' },
118+
isLoading: false,
119+
},
120+
musdBalanceQuery: {
121+
data: { balance: '1000000' },
122+
isLoading: false,
123+
},
124+
musdEquivalentBalanceQuery: {
125+
data: {
126+
musdEquivalentValue: '2000000',
127+
musdSHFvdBalance: '2000000',
128+
exchangeRate: '1000000',
129+
},
130+
isLoading: false,
131+
},
132+
} as ReturnType<typeof useMoneyAccountBalance>);
133+
100134
// Activity list renders when there are at least 10 transactions; pad the
101135
// mock set so the activity-related assertions below find the View all button.
102136
const paddedTransactions = Array.from({ length: 10 }, (_, index) => ({

app/components/UI/Money/Views/MoneyHomeView/MoneyHomeView.tsx

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,45 +20,49 @@ import MoneyFooter from '../../components/MoneyFooter';
2020
import Routes from '../../../../../constants/navigation/Routes';
2121
import { MoneyHomeViewTestIds } from './MoneyHomeView.testIds';
2222
import styleSheet from './MoneyHomeView.styles';
23-
import { MUSD_CONVERSION_APY } from '../../../Earn/constants/musd';
2423
import { useMusdConversionTokens } from '../../../Earn/hooks/useMusdConversionTokens';
2524
import { useMoneyAccountTransactions } from '../../hooks/useMoneyAccountTransactions';
2625
import { showMoneyActivityUnderConstructionAlert } from '../../constants/showMoneyActivityUnderConstructionAlert';
26+
import useMoneyAccountBalance from '../../hooks/useMoneyAccountBalance';
2727

2828
const Divider = () => <Box twClassName="h-px bg-border-muted my-5" />;
2929

3030
/** Placeholder until Money home actions are implemented */
31-
const noopHandler = () => undefined;
31+
// eslint-disable-next-line no-alert
32+
const displayUnderConstructionAlert = () => alert('Under construction 🚧');
3233

3334
const MoneyHomeView = () => {
3435
const navigation = useNavigation();
3536
const insets = useSafeAreaInsets();
3637
const { styles } = useStyles(styleSheet, {});
3738

39+
const { totalFiatFormatted, vaultApyQuery, isAggregatedBalanceLoading } =
40+
useMoneyAccountBalance();
41+
3842
const { tokens: conversionTokens } = useMusdConversionTokens();
3943
const { allTransactions, moneyAddress } = useMoneyAccountTransactions();
4044

4145
const handleBackPress = useCallback(() => {
4246
navigation.goBack();
4347
}, [navigation]);
4448

45-
const handleMenuPress = noopHandler;
49+
const handleMenuPress = displayUnderConstructionAlert;
4650

47-
const handleAddPress = noopHandler;
48-
const handleTransferPress = noopHandler;
49-
const handleCardPress = noopHandler;
50-
const handleApyInfoPress = noopHandler;
51-
const handleProjectedEarningsPress = noopHandler;
52-
const handleGetNowPress = noopHandler;
53-
const handleMusdRowPress = noopHandler;
54-
const handleHeaderPress = noopHandler;
51+
const handleAddPress = displayUnderConstructionAlert;
52+
const handleTransferPress = displayUnderConstructionAlert;
53+
const handleCardPress = displayUnderConstructionAlert;
54+
const handleApyInfoPress = displayUnderConstructionAlert;
55+
const handleProjectedEarningsPress = displayUnderConstructionAlert;
56+
const handleGetNowPress = displayUnderConstructionAlert;
57+
const handleMusdRowPress = displayUnderConstructionAlert;
58+
const handleHeaderPress = displayUnderConstructionAlert;
5559

56-
const handleTokenConvertPress = noopHandler;
60+
const handleTokenConvertPress = displayUnderConstructionAlert;
5761

58-
const handleEarnCryptoPress = noopHandler;
59-
const handleLearnMorePress = noopHandler;
60-
const handleAddMoneyPress = noopHandler;
61-
const handleHowItWorksHeaderPress = noopHandler;
62+
const handleEarnCryptoPress = displayUnderConstructionAlert;
63+
const handleLearnMorePress = displayUnderConstructionAlert;
64+
const handleAddMoneyPress = displayUnderConstructionAlert;
65+
const handleHowItWorksHeaderPress = displayUnderConstructionAlert;
6266

6367
const handleViewAllActivityPress = useCallback(() => {
6468
navigation.navigate(Routes.MONEY.ACTIVITY as never);
@@ -69,6 +73,10 @@ const MoneyHomeView = () => {
6973
showMoneyActivityUnderConstructionAlert();
7074
}, []);
7175

76+
// TODO: Remove before launch
77+
// Useful for testing how zero and non-zero APYs are handled quickly.
78+
const DEV_APY = __DEV__ ? 4 : vaultApyQuery.data?.apy;
79+
7280
return (
7381
<Box
7482
style={[styles.safeArea, { paddingTop: insets.top }]}
@@ -85,8 +93,10 @@ const MoneyHomeView = () => {
8593
showsVerticalScrollIndicator={false}
8694
>
8795
<MoneyBalanceSummary
88-
apy={String(MUSD_CONVERSION_APY)}
96+
apy={DEV_APY}
97+
balance={totalFiatFormatted ?? '--.--'}
8998
onApyInfoPress={handleApyInfoPress}
99+
isLoading={vaultApyQuery.isLoading || isAggregatedBalanceLoading}
90100
/>
91101
<MoneyActionButtonRow
92102
onAddPress={handleAddPress}
@@ -98,8 +108,9 @@ const MoneyHomeView = () => {
98108
<MoneyEarnings onProjectedPress={handleProjectedEarningsPress} />
99109
<Divider />
100110
<MoneyHowItWorks
101-
apy={MUSD_CONVERSION_APY}
111+
apy={DEV_APY}
102112
onHeaderPress={handleHowItWorksHeaderPress}
113+
isLoading={vaultApyQuery.isLoading}
103114
/>
104115
<MoneyMusdTokenRow
105116
onPress={handleMusdRowPress}
@@ -110,7 +121,7 @@ const MoneyHomeView = () => {
110121
<>
111122
<MoneyPotentialEarnings
112123
tokens={conversionTokens}
113-
apy={MUSD_CONVERSION_APY}
124+
apy={DEV_APY}
114125
onTokenPress={handleTokenConvertPress}
115126
onViewAllPress={handleEarnCryptoPress}
116127
onHeaderPress={handleEarnCryptoPress}
@@ -136,7 +147,7 @@ const MoneyHomeView = () => {
136147
</>
137148
)}
138149
<MoneyWhatYouGet
139-
apy={MUSD_CONVERSION_APY}
150+
apy={DEV_APY}
140151
onLearnMorePress={handleLearnMorePress}
141152
/>
142153
</ScrollView>

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

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ import { strings } from '../../../../../../locales/i18n';
66

77
describe('MoneyBalanceSummary', () => {
88
it('renders the title', () => {
9-
const { getByTestId } = render(<MoneyBalanceSummary apy="4" />);
9+
const { getByTestId } = render(<MoneyBalanceSummary apy={4} />);
1010

1111
expect(getByTestId(MoneyBalanceSummaryTestIds.TITLE)).toBeOnTheScreen();
1212
});
1313

1414
it('renders the APY label inside a tag', () => {
15-
const { getByTestId } = render(<MoneyBalanceSummary apy="5.5" />);
15+
const { getByTestId } = render(<MoneyBalanceSummary apy={5.5} />);
1616

1717
expect(getByTestId(MoneyBalanceSummaryTestIds.APY_TAG)).toBeOnTheScreen();
1818
expect(getByTestId(MoneyBalanceSummaryTestIds.APY)).toHaveTextContent(
19-
strings('money.apy_label', { percentage: '5.5' }),
19+
strings('money.apy_label', { percentage: 5.5 }),
2020
);
2121
});
2222

2323
it('renders the default zero balance when no balance prop is provided', () => {
24-
const { getByTestId } = render(<MoneyBalanceSummary apy="4" />);
24+
const { getByTestId } = render(<MoneyBalanceSummary apy={4} />);
2525

2626
expect(getByTestId(MoneyBalanceSummaryTestIds.BALANCE)).toHaveTextContent(
2727
'$0.00',
@@ -30,17 +30,17 @@ describe('MoneyBalanceSummary', () => {
3030

3131
it('renders the provided balance value', () => {
3232
const { getByTestId } = render(
33-
<MoneyBalanceSummary apy="4" balance="$123.45" />,
33+
<MoneyBalanceSummary apy={4} balance="$123.45" />,
3434
);
3535

3636
expect(getByTestId(MoneyBalanceSummaryTestIds.BALANCE)).toHaveTextContent(
3737
'$123.45',
3838
);
3939
});
4040

41-
it('renders a skeleton instead of the balance when loading', () => {
41+
it('renders the balance skeleton instead of the balance value when loading', () => {
4242
const { getByTestId, queryByTestId } = render(
43-
<MoneyBalanceSummary apy="4" isLoading />,
43+
<MoneyBalanceSummary apy={4} isLoading />,
4444
);
4545

4646
expect(
@@ -51,8 +51,21 @@ describe('MoneyBalanceSummary', () => {
5151
).not.toBeOnTheScreen();
5252
});
5353

54+
it('renders the APY skeleton instead of the APY tag when loading', () => {
55+
const { getByTestId, queryByTestId } = render(
56+
<MoneyBalanceSummary apy={4} isLoading />,
57+
);
58+
59+
expect(
60+
getByTestId(MoneyBalanceSummaryTestIds.APY_SKELETON),
61+
).toBeOnTheScreen();
62+
expect(
63+
queryByTestId(MoneyBalanceSummaryTestIds.APY_TAG),
64+
).not.toBeOnTheScreen();
65+
});
66+
5467
it('does not render the info button when no handler is provided', () => {
55-
const { queryByTestId } = render(<MoneyBalanceSummary apy="4" />);
68+
const { queryByTestId } = render(<MoneyBalanceSummary apy={4} />);
5669

5770
expect(
5871
queryByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON),
@@ -62,11 +75,64 @@ describe('MoneyBalanceSummary', () => {
6275
it('calls onApyInfoPress when the info button is pressed', () => {
6376
const mockInfoPress = jest.fn();
6477
const { getByTestId } = render(
65-
<MoneyBalanceSummary apy="4" onApyInfoPress={mockInfoPress} />,
78+
<MoneyBalanceSummary apy={4} onApyInfoPress={mockInfoPress} />,
6679
);
6780

6881
fireEvent.press(getByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON));
6982

7083
expect(mockInfoPress).toHaveBeenCalledTimes(1);
7184
});
85+
86+
it('hides the APY tag and tooltip button when apy is undefined', () => {
87+
const mockInfoPress = jest.fn();
88+
const { queryByTestId } = render(
89+
<MoneyBalanceSummary apy={undefined} onApyInfoPress={mockInfoPress} />,
90+
);
91+
92+
expect(
93+
queryByTestId(MoneyBalanceSummaryTestIds.APY_TAG),
94+
).not.toBeOnTheScreen();
95+
expect(
96+
queryByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON),
97+
).not.toBeOnTheScreen();
98+
});
99+
100+
it('hides the APY tag and tooltip button when apy is zero', () => {
101+
const mockInfoPress = jest.fn();
102+
const { queryByTestId } = render(
103+
<MoneyBalanceSummary apy={0} onApyInfoPress={mockInfoPress} />,
104+
);
105+
106+
expect(
107+
queryByTestId(MoneyBalanceSummaryTestIds.APY_TAG),
108+
).not.toBeOnTheScreen();
109+
expect(
110+
queryByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON),
111+
).not.toBeOnTheScreen();
112+
});
113+
114+
it('hides the APY tooltip button when isLoading is true', () => {
115+
const mockInfoPress = jest.fn();
116+
const { queryByTestId } = render(
117+
<MoneyBalanceSummary apy={4} isLoading onApyInfoPress={mockInfoPress} />,
118+
);
119+
120+
expect(
121+
queryByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON),
122+
).not.toBeOnTheScreen();
123+
});
124+
125+
it('hides the APY tag and info button when apy is negative', () => {
126+
const mockInfoPress = jest.fn();
127+
const { queryByTestId } = render(
128+
<MoneyBalanceSummary apy={-1} onApyInfoPress={mockInfoPress} />,
129+
);
130+
131+
expect(
132+
queryByTestId(MoneyBalanceSummaryTestIds.APY_TAG),
133+
).not.toBeOnTheScreen();
134+
expect(
135+
queryByTestId(MoneyBalanceSummaryTestIds.APY_INFO_BUTTON),
136+
).not.toBeOnTheScreen();
137+
});
72138
});

app/components/UI/Money/components/MoneyBalanceSummary/MoneyBalanceSummary.testIds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export const MoneyBalanceSummaryTestIds = {
66
APY: 'money-balance-summary-apy',
77
APY_TAG: 'money-balance-summary-apy-tag',
88
APY_INFO_BUTTON: 'money-balance-summary-apy-info-button',
9+
APY_SKELETON: 'money-balance-summary-apy-skeleton',
910
} as const;

app/components/UI/Money/components/MoneyBalanceSummary/MoneyBalanceSummary.tsx

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from '@metamask/design-system-react-native';
1616
import { strings } from '../../../../../../locales/i18n';
1717
import { MoneyBalanceSummaryTestIds } from './MoneyBalanceSummary.testIds';
18+
import { isPositiveNumber } from '../../utils/number';
1819

1920
const DEFAULT_BALANCE = '$0.00';
2021

@@ -24,9 +25,9 @@ interface MoneyBalanceSummaryProps {
2425
*/
2526
balance?: string;
2627
/**
27-
* APY percentage string (e.g. "4")
28+
* APY expressed as a percentage (e.g. 3 for 3%).
2829
*/
29-
apy: string;
30+
apy: number | undefined;
3031
/**
3132
* Render a loading skeleton in place of the balance value.
3233
*/
@@ -59,7 +60,7 @@ const MoneyBalanceSummary = ({
5960
<Skeleton
6061
height={48}
6162
width={160}
62-
twClassName="mb-2"
63+
twClassName="mb-2 rounded-md"
6364
testID={MoneyBalanceSummaryTestIds.BALANCE_SKELETON}
6465
/>
6566
) : (
@@ -77,20 +78,31 @@ const MoneyBalanceSummary = ({
7778
alignItems={BoxAlignItems.Center}
7879
twClassName="gap-1"
7980
>
80-
<Box
81-
twClassName="self-start rounded-md bg-success-muted px-2 py-0.5"
82-
testID={MoneyBalanceSummaryTestIds.APY_TAG}
83-
>
84-
<Text
85-
variant={TextVariant.BodySm}
86-
fontWeight={FontWeight.Medium}
87-
color={TextColor.SuccessDefault}
88-
testID={MoneyBalanceSummaryTestIds.APY}
89-
>
90-
{strings('money.apy_label', { percentage: apy })}
91-
</Text>
92-
</Box>
93-
{onApyInfoPress && (
81+
{isLoading ? (
82+
<Skeleton
83+
height={24}
84+
width={94}
85+
twClassName="rounded-md"
86+
testID={MoneyBalanceSummaryTestIds.APY_SKELETON}
87+
/>
88+
) : (
89+
isPositiveNumber(apy) && (
90+
<Box
91+
twClassName="self-start rounded-md bg-success-muted px-2 py-0.5"
92+
testID={MoneyBalanceSummaryTestIds.APY_TAG}
93+
>
94+
<Text
95+
variant={TextVariant.BodySm}
96+
fontWeight={FontWeight.Medium}
97+
color={TextColor.SuccessDefault}
98+
testID={MoneyBalanceSummaryTestIds.APY}
99+
>
100+
{strings('money.apy_label', { percentage: apy })}
101+
</Text>
102+
</Box>
103+
)
104+
)}
105+
{onApyInfoPress && isPositiveNumber(apy) && !isLoading && (
94106
<ButtonIcon
95107
iconName={IconName.Info}
96108
iconProps={{ color: IconColor.IconAlternative }}

0 commit comments

Comments
 (0)