diff --git a/test/jest/console-baseline-unit.json b/test/jest/console-baseline-unit.json index ec507e61e253..e1eabf433fad 100644 --- a/test/jest/console-baseline-unit.json +++ b/test/jest/console-baseline-unit.json @@ -787,6 +787,10 @@ "ui/pages/confirmations/hooks/transactions/dapp-swap-comparison/useDappSwapComparisonInfo.test.ts": { "MetaMask: Background connection not initialized": 2 }, + "ui/pages/confirmations/hooks/transactions/useTransactionConfirm.test.ts": { + "error: Money Account withdraw: amount encode": 3, + "error: Money Account withdraw: no committed": 1 + }, "ui/pages/confirmations/hooks/useConfirmationAlerts.test.ts": { "MetaMask: Background connection not initialized": 5, "React: Act warnings (component updates not wrapped)": 3, diff --git a/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.test.ts b/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.test.ts index 5c13f7a312b8..50f3b42d4996 100644 --- a/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.test.ts +++ b/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.test.ts @@ -11,6 +11,7 @@ import { getMockConfirmStateForTransaction } from '../../../../../../test/data/c import { genUnapprovedContractInteractionConfirmation } from '../../../../../../test/data/confirmations/contract-interaction'; import { renderHookWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; import { useTransactionPayPrimaryRequiredToken } from '../../pay/useTransactionPayData'; +import { useLastMoneyAccountWithdrawAmount } from '../../transactions/useLastMoneyAccountWithdrawAmount'; import { AlertsName } from '../constants'; import { RowAlertKey } from '../../../../../components/app/confirm/info/row/constants'; import { Severity } from '../../../../../helpers/constants/design-system'; @@ -20,11 +21,15 @@ jest.mock('@metamask/react-data-query', () => ({ useQuery: jest.fn(), })); jest.mock('../../pay/useTransactionPayData'); +jest.mock('../../transactions/useLastMoneyAccountWithdrawAmount'); const useQueryMock = jest.mocked(useQuery); const usePrimaryRequiredTokenMock = jest.mocked( useTransactionPayPrimaryRequiredToken, ); +const useLastMoneyAccountWithdrawAmountMock = jest.mocked( + useLastMoneyAccountWithdrawAmount, +); const EXPECTED_ALERT = { field: RowAlertKey.Amount, @@ -92,6 +97,7 @@ describe('useInsufficientMoneyAccountBalanceAlert', () => { typeof useTransactionPayPrimaryRequiredToken >, ); + useLastMoneyAccountWithdrawAmountMock.mockReturnValue(undefined); }); it('returns alert when pending amount exceeds available balance', () => { @@ -202,4 +208,36 @@ describe('useInsufficientMoneyAccountBalanceAlert', () => { expect(result.current).toStrictEqual([]); }); + + it('returns alert when the live typed amount exceeds the balance', () => { + // Mirrors mobile: the alert reacts to the current input, not just the + // debounced calldata commit. + useLastMoneyAccountWithdrawAmountMock.mockReturnValue('150'); + + const { result } = runHook(); + + expect(result.current).toEqual([EXPECTED_ALERT]); + }); + + it('prefers the live typed amount over the stale required token amount', () => { + // The user reduced the amount below the balance; the committed calldata + // still carries the previous over-balance amount until the debounce + // re-encodes. The alert must clear immediately. + useLastMoneyAccountWithdrawAmountMock.mockReturnValue('50'); + usePrimaryRequiredTokenMock.mockReturnValue({ + amountHuman: '150', + } as ReturnType); + + const { result } = runHook(); + + expect(result.current).toStrictEqual([]); + }); + + it('prefers pendingAmount over the live typed amount', () => { + useLastMoneyAccountWithdrawAmountMock.mockReturnValue('50'); + + const { result } = runHook({ pendingAmount: '150' }); + + expect(result.current).toEqual([EXPECTED_ALERT]); + }); }); diff --git a/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.ts b/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.ts index 69f71ed62ef9..9ee78118664a 100644 --- a/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.ts +++ b/ui/pages/confirmations/hooks/alerts/transactions/useInsufficientMoneyAccountBalanceAlert.ts @@ -17,6 +17,7 @@ import { hasTransactionType } from '../../../../../../shared/lib/transactions.ut import { MoneyAccountBalanceServiceQueryKeys } from '../../../../../../shared/lib/money/query-keys'; import { useConfirmContext } from '../../../context/confirm'; import { useTransactionPayPrimaryRequiredToken } from '../../pay/useTransactionPayData'; +import { useLastMoneyAccountWithdrawAmount } from '../../transactions/useLastMoneyAccountWithdrawAmount'; import { AlertsName } from '../constants'; const MUSD_UNIT = 10 ** MUSD_DECIMALS; @@ -35,7 +36,11 @@ const NON_WITHDRAW_QUERY_KEY = 'money-account-withdraw-alert:disabled'; * withdraw info messenger) so this can run in `useConfirmationAlerts` without * requiring a money-account route messenger. * - * Mirrors mobile `useInsufficientMoneyAccountBalanceAlert`. + * Mirrors mobile `useInsufficientMoneyAccountBalanceAlert`, including its + * live-input responsiveness: mobile evaluates the pending typed amount, so + * the extension prefers the synchronously-recorded last withdraw amount over + * `primaryRequiredToken.amountHuman`, which lags typing by the debounced + * calldata encode. * * @param options * @param options.pendingAmount - Optional in-progress human mUSD amount. @@ -48,6 +53,9 @@ export function useInsufficientMoneyAccountBalanceAlert({ const t = useI18nContext(); const { currentConfirmation } = useConfirmContext(); const primaryRequiredToken = useTransactionPayPrimaryRequiredToken(); + const lastWithdrawAmount = useLastMoneyAccountWithdrawAmount( + currentConfirmation?.id ?? '', + ); const isMoneyAccountWithdraw = hasTransactionType(currentConfirmation, [ TransactionType.moneyAccountWithdraw, @@ -71,7 +79,11 @@ export function useInsufficientMoneyAccountBalanceAlert({ enabled: false, }); - const amountHuman = pendingAmount ?? primaryRequiredToken?.amountHuman ?? '0'; + const amountHuman = + pendingAmount ?? + lastWithdrawAmount ?? + primaryRequiredToken?.amountHuman ?? + '0'; const withdrawableMusd = useMemo(() => { if (