Skip to content

feat(money): show Paid by MetaMask on sponsored mUSD conversions#30120

Merged
Kureev merged 15 commits into
mainfrom
kureev/MUSD-794
May 14, 2026
Merged

feat(money): show Paid by MetaMask on sponsored mUSD conversions#30120
Kureev merged 15 commits into
mainfrom
kureev/MUSD-794

Conversation

@Kureev

@Kureev Kureev commented May 13, 2026

Copy link
Copy Markdown
Contributor

Description

When MM Pay returns a fully sponsored mUSD conversion quote (network, provider, and MetaMask fees all zero), the confirmation screen now surfaces the sponsorship explicitly: the "Transaction fee" row renders a green check + "Paid by MetaMask" label instead of "$0", and the redundant fee-breakdown tooltip is hidden. Polishes the same screen to match design: 8px row gap, "Confirm" CTA, icon-primary header (i), the Total row is hidden for mUSD conversion, and the screen overlays the bottom tab bar when launched from Money Home / Potential Earnings.

Changelog

CHANGELOG entry: Added a "Paid by MetaMask" treatment on the mUSD conversion confirmation screen when MetaMask fully sponsors the network, provider, and gas fees.

Related issues

Fixes: MUSD-794

Manual testing steps

Feature: Paid by MetaMask on sponsored mUSD conversion

  Scenario: user converts a small amount of USDC to mUSD
    Given the user is on Money Home with a USDC balance
    And the conversion quote returns zero network, provider, and MetaMask fees

    When the user taps a token's "Get 3% mUSD bonus" entry, enters an amount, and waits for the quote
    Then the confirmation screen overlays the bottom tab bar
    And the Transaction fee row shows a green check + "Paid by MetaMask" with no tooltip
    And the Total row is hidden
    And the Claimable bonus row shows 3%
    And the primary CTA reads "Confirm"

Screenshots/Recordings

After

image image image

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
  • I've tested with a power user scenario
  • I've instrumented key operations with Sentry traces for production performance metrics

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.

Note

Medium Risk
Updates the transaction confirmation fee/total presentation for musdConversion and adjusts navigation/tooltip behavior, which could affect conversion confirmation UX and fee transparency if edge cases (missing fee fields/quotes) are mishandled.

Overview
mUSD conversion confirmations now explicitly show sponsorship: when MM Pay returns quotes and all fee components are zero, the Transaction fee row renders a green check + Paid by MetaMask label and suppresses the fee-breakdown tooltip via the new useIsPaidByMetaMask hook.

The confirmation screen is further aligned to the sponsored design by hiding the TotalRow for musdConversion, changing the CTA label to earn.musd_conversion.confirm, and replacing the old useTooltipModal navbar info flow with an inline TooltipModal (TooltipNode) rendered by MusdConversionInfo (including updated tooltip copy and analytics for the terms link).

Navigation into redesigned confirmations from the Earn stack is adjusted to present as a card, and Money conversion initiation calls drop the navigationStack parameter. Tests, smoke specs, and English strings are updated accordingly.

Reviewed by Cursor Bugbot for commit 7c32351. Bugbot is set up for automated code reviews on this repo. Configure here.

…USD-794)

- bridge-fee-row: add isPaidByMetaMask predicate gated to musdConversion +
  all-zero fees; render green check + "Paid by MetaMask" label inline and
  hide the tooltip icon
- total-row: hide for musdConversion via HIDE_TYPES (mirrors bridge-time-row)
- custom-amount-info: 8px gap between post-quote rows; CTA label uses new
  earn.musd_conversion.confirm i18n key
- useMusdConversionNavbar: header info icon now uses IconColor.IconDefault
- Money routes: register RedesignedConfirmations on MoneyModalStack and
  route Money Home / Potential Earnings entry points through the modal
  stack so the convert screen overlays the bottom tab bar
- locales/en.json: add earn.musd_conversion.confirm
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@github-actions github-actions Bot added the pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. label May 13, 2026
@Kureev Kureev marked this pull request as ready for review May 13, 2026 15:26
@Kureev Kureev requested review from a team as code owners May 13, 2026 15:26
@Kureev Kureev self-assigned this May 13, 2026
…ader (MUSD-794)

- Add MoneyClaimableBonusInfoSheet and MoneyConvertInfoSheet that mirror the
  existing MoneyApyInfoSheet pattern (BottomSheet + BottomSheetHeader + body,
  no "Got it" button), per design
- Register both on MoneyModalStack so they overlay the bottom tab bar
- PercentageRow: render a plain Icon + TouchableOpacity in place of InfoRow's
  built-in inline Tooltip; opens the new sheet on press at 16x16 IconAlternative
- useMusdConversionNavbar: header (i) navigates to the new sheet; analytics
  and terms-link handling moved into the sheets themselves
- locales/en.json: add earn.musd_conversion.convert_tooltip_description
@github-actions github-actions Bot added size-XL and removed size-M labels May 13, 2026
@Kureev Kureev added size-M and removed size-XL pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. labels May 13, 2026
@github-actions github-actions Bot added size-XL and removed size-M labels May 13, 2026

@Matt561 Matt561 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Paid by MetaMask" message is testing well but I have some concerns with the implementation.

@@ -0,0 +1,77 @@
import React, { useCallback, useRef } from 'react';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the better approach here would be to update the Tooltip component that all info-rows use. This way all MM Pay tooltip are uniform. We want to avoid creating bespoke bottom sheets for MM Pay since it's meant to be feature agnostic.

@@ -0,0 +1,82 @@
import React, { useCallback, useRef } from 'react';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a very similar bottom sheet. Could we instead update the existing tooltip?

Image

Comment on lines +82 to +100
@@ -97,7 +97,7 @@ const MoneyPotentialEarningsView = () => {
address: token.address as Hex,
chainId: token.chainId as Hex,
},
navigationStack: Routes.MONEY.ROOT,
navigationStack: Routes.MONEY.MODALS.ROOT,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changes required? The confirmation screen (Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS) should be in the EarnScreenStack.

)}
{isResultReady && (
<Box>
<Box gap={8}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthewwalsh0 are you okay with this styling change? I want to double-check to ensure that Earn isn't suggesting design changes to shared components without reaching out to confirmations first.

return new BigNumber(sourceNetworkUsd).plus(targetNetworkUsd);
}

function isPaidByMetaMask({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a good candidate for a new hook.

Comment on lines +184 to +185
if (!hasTransactionType(transactionMeta, [TransactionType.musdConversion]))
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we allow this "Paid by MetaMask" message to display for any transaction type where the fees are zero?

Comment on lines +55 to +67
labelChildren={
<TouchableOpacity
onPress={handleInfoPress}
testID="percentage-row-tooltip-open-btn"
style={styles.tooltipButton}
hitSlop={8}
>
<Icon
name={IconName.Info}
size={IconSize.Sm}
color={IconColor.IconAlternative}
/>
</TouchableOpacity>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's stick to using the tooltip prop and try to avoid one off implementations to ease maintenance of MM Pay tooltip styles.

trackEvent(
createEventBuilder(MetaMetricsEvents.MUSD_BONUS_TERMS_OF_USE_PRESSED)
.addProperties({
location: EVENT_LOCATIONS.PERCENTAGE_ROW,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is using a hardcoded location of PERCENTAGE_ROW for a component that isn't aware of where it's used. This could lead to event pollution if this component is used outside the percentage row.

chainId: token.chainId as Hex,
},
navigationStack: Routes.MONEY.ROOT,
navigationStack: Routes.MONEY.MODALS.ROOT,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required for the redirect to work? The confirmation screen is in the EarnScreenStack

chainId: defaultToken.chainId as Hex,
},
navigationStack: Routes.MONEY.ROOT,
navigationStack: Routes.MONEY.MODALS.ROOT,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required for the redirect to work? The confirmation screen is in the EarnScreenStack

Kureev added 3 commits May 13, 2026 19:40
…(MUSD-794)

Replace the bespoke MoneyClaimableBonusInfoSheet and MoneyConvertInfoSheet
with the existing Tooltip/TooltipModal pattern already used across the
confirmations UI. The bespoke sheets duplicated styling and registered
themselves as navigation routes, which fragmented MM Pay tooltip styling
and coupled shared confirmation components to Money-owned modal screens.

- Delete MoneyClaimableBonusInfoSheet and MoneyConvertInfoSheet components
- Drop CLAIMABLE_BONUS_INFO_SHEET and CONVERT_INFO_SHEET route constants
- Remove the two ModalStack.Screen registrations
- PercentageRow: pass tooltip + tooltipTitle to InfoRow so the standard
  auto-rendered Tooltip handles open/close; terms link content moves inline
- useMusdConversionNavbar: owns local modal state via useState and returns
  a TooltipNode for the consumer to render alongside the page body
- musd-conversion-info: render the returned TooltipNode in a Fragment
…tries (MUSD-794)

Routing the conversion confirmation through MONEY.MODALS.ROOT was only
needed when the bespoke info sheets lived under that modal stack. Now that
the tooltips are self-contained inline, the default screen-stack route is
correct again.

Reverts MoneyHomeView and MoneyPotentialEarningsView to the pre-PR value
of Routes.MONEY.ROOT.
The sponsored-conversion detection in BridgeFeeRow was an inline helper
that read three transaction-pay selectors. Extract it into a hook so the
predicate is colocated with its data dependencies and can be reused.

Scope is intentionally kept to TransactionType.musdConversion - broadening
to any zero-fee transaction is a product call left for follow-up.
@github-actions github-actions Bot added size-L and removed size-XL labels May 13, 2026
Kureev added 2 commits May 13, 2026 20:09
…USD-794)

The hook signature now types TooltipNode as a JSX Element. The mock was
returning null, which CI tsc rejected with TS2322.
…moke (MUSD-794)

mUSD conversions are sponsored, so TotalRow returns null for that
transaction type and the bridge-fee row renders a "Paid by MetaMask"
pill in place of the total. The smoke test was still asserting the
old "total" element which no longer exists on the mUSD confirmation.

- Tag the bridge-fee-row pill with the existing PAID_BY_METAMASK testID
- Expose a paidByMetaMask getter on TransactionPayConfirmation
- Update the two mUSD happy-path scenarios to assert the new surface
@Kureev Kureev requested a review from a team as a code owner May 13, 2026 19:44
Comment thread app/components/UI/Money/routes/index.tsx Outdated
…(MUSD-794)

Routes.FULL_SCREEN_CONFIRMATIONS.REDESIGNED_CONFIRMATIONS was registered
in both MoneyScreenStack and MoneyModalStack with different header options,
which made navigate() resolution to the Confirm screen ambiguous. After
reverting the navigationStack overrides to MONEY.ROOT, the MoneyModalStack
copy is unreachable - remove it so the screen has a single source of truth
in MoneyScreenStack.
Kureev added 2 commits May 13, 2026 23:25
mUSD conversions are sponsored by MetaMask, so the "Paid by MetaMask"
pill renders only when useIsPaidByMetaMask returns true - which requires
all fee components on the resolved TransactionPayTotals to be zero. The
mUSD-specific relay quote mock was stubbing a non-zero relayer fee,
which made the smoke test fail to find the pill after the assertions
were updated to match production design.
…ke (MUSD-794)

The intermediate "is the confirmation screen ready" assertion relied on
useIsPaidByMetaMask returning true, which depends on the controller
normalizing the relayer fee to zero - that path isn't reachable from the
current mock setup. The activity-view check at the end of each scenario
already verifies the conversion completed successfully, and the
tapConfirmButton call waits for the footer button before tapping.
@Kureev Kureev requested a review from Matt561 May 13, 2026 23:06
Comment thread app/components/Views/confirmations/hooks/pay/useIsPaidByMetaMask.ts
Comment thread app/components/Views/confirmations/hooks/pay/useIsPaidByMetaMask.ts Outdated
Comment thread app/components/Views/confirmations/hooks/pay/useIsPaidByMetaMask.ts
Kureev added 3 commits May 14, 2026 13:44
- combine the two early-return guards into a single conditional
- extract SUPPORTED_TYPES constant for transaction-type extensibility
- extract PaidByLabel local component in bridge-fee-row
- revert percentage-row.tsx + test to main; main already uses the `tooltip`
  prop as Matt561 requested, so the in-PR refactor was a no-op
- revert custom-amount-info.tsx 8px gap; the change touched a
  confirmations-owned shared component without justification
- drop cosmetic renames and import reorders in useMusdConversionNavbar
  (test + impl); keep only the functional changes for the inline
  TooltipModal and IconDefault header (i) per design
- remove the unused TransactionPayConfirmation.paidByMetaMask page-object
  getter; no spec references it after the intermediate smoke assertion
  was dropped
…D-794)

Money Home and Potential Earnings entry points were forcing
initiateCustomConversion to push REDESIGNED_CONFIRMATIONS onto
MoneyScreenStack via navigationStack: Routes.MONEY.ROOT. That stack is
mounted as a Tab.Screen inside the bottom-tab navigator, so the convert
screen rendered with the tab bar still visible.

useMusdConversion already defaults navigationStack to Routes.EARN.ROOT,
which is mounted at the root Stack level with EarnScreenStack —
fullscreen, overlapping the tab bar — and REDESIGNED_CONFIRMATIONS is
already registered there. Dropping the override is enough; no new route
registrations or modal-stack plumbing needed (also addresses Matt561's
review point that the screen should live in EarnScreenStack).

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1b2af29. Configure here.

const sourceNetwork = new BigNumber(totals.fees.sourceNetwork.estimate.usd);
const targetNetwork = new BigNumber(totals.fees.targetNetwork.usd);
const provider = new BigNumber(totals.fees.provider.usd);
const metaMask = new BigNumber(totals.fees.metaMask.usd ?? 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing optional chaining causes crash on undefined fee properties

High Severity

The hook accesses totals.fees.sourceNetwork.estimate.usd, totals.fees.targetNetwork.usd, totals.fees.provider.usd, and totals.fees.metaMask.usd without optional chaining after only guarding !totals?.fees. Throughout the codebase, every other consumer of these same fee properties uses optional chaining and ?? 0 fallbacks (e.g., receive-row.tsx, useInsufficientPerpsBalanceAlert.ts, PerpsOrderView.tsx), confirming sub-properties like sourceNetwork, targetNetwork, provider, and metaMask can be undefined even when fees is defined. This will throw a TypeError at runtime if any intermediate object is missing.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1b2af29. Configure here.

…ack (MUSD-794)

EarnScreenStack defaults all screens to `presentation: 'transparentModal'`,
which gives the screen a transparent card background. For Confirm that
means the screen underneath stays visible — when launched from inside the
bottom-tab navigator (Money Home / Potential Earnings), the tab bar bled
through the Confirm screen.

Override the presentation on the Confirm registration to 'card' so it
renders with the default opaque background and fully covers the tab bar.
Other consumers (e.g. MusdConversionAssetOverviewCta) are unaffected
because they don't depend on the screen being a transparent overlay.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeWalletPlatform, SmokeStake, SmokeMoney, SmokeConfirmations
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 88%
click to see 🤖 AI reasoning details

E2E Test Selection:

The PR introduces changes focused on the mUSD conversion flow (Earn/Money feature):

  1. SmokeWalletPlatform: The E2E test file tests/smoke/wallet/musd-conversion-happy-path.spec.ts is explicitly tagged with SmokeWalletPlatform. The mUSD conversion flow is part of the wallet platform. This is the primary tag for these changes.

  2. SmokeStake: Changes to useMusdConversionNavbar.tsx, musd-conversion-info.tsx, Earn/routes/index.tsx, and the new useIsPaidByMetaMask.ts hook all relate to the Earn/staking/lending feature area. The mUSD conversion is a lending/earn product. The Earn routes navigation change (presentation: 'card') could affect how the confirmation screen appears in the staking flow.

  3. SmokeMoney: MoneyHomeView.tsx and MoneyPotentialEarningsView.tsx both had navigationStack: Routes.MONEY.ROOT removed from navigation calls. This is a direct change to the Money feature's navigation behavior that could affect how users navigate from the Money home to mUSD conversion flows.

  4. SmokeConfirmations: Multiple confirmation components were modified:

    • bridge-fee-row.tsx: Added "Paid by MetaMask" label for sponsored transactions
    • total-row.tsx: Hides total row for musdConversion transaction type
    • custom-amount-info.tsx: Changed button label from "Convert" to "Confirm"
    • musd-conversion-info.tsx: Refactored tooltip rendering
      These are shared confirmation UI components. Per tag descriptions, SmokeConfirmations should be selected when SmokeStake or SmokeMoney are selected.

The changes are medium risk - they modify UI components in the confirmation flow and navigation parameters, but are scoped to the mUSD/Earn/Money feature area. The confirmation row components (bridge-fee-row, total-row) are used in broader transaction flows, warranting SmokeConfirmations coverage.

Performance Test Selection:
The changes are focused on UI component logic (tooltip refactoring, fee display, button label changes) and navigation parameter adjustments. None of these changes affect rendering performance, data loading pipelines, account/network list components, or app startup/initialization in a way that would meaningfully impact performance metrics. No performance tests are warranted.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ E2E Fixture Validation — Structural changes detected

Category Count
New keys 0
Missing keys 15
Type mismatches 0
Value mismatches 12 (informational)

The committed fixture schema is out of date. To update, comment:

@metamaskbot update-mobile-fixture

View full details | Download diff report

@Kureev Kureev requested a review from matthewwalsh0 May 14, 2026 13:59
@Kureev Kureev enabled auto-merge May 14, 2026 14:05
@Kureev Kureev removed the request for review from a team May 14, 2026 14:05
@Kureev Kureev added this pull request to the merge queue May 14, 2026
Merged via the queue into main with commit e3a3c5f May 14, 2026
101 of 102 checks passed
@Kureev Kureev deleted the kureev/MUSD-794 branch May 14, 2026 14:49
@github-actions github-actions Bot locked and limited conversation to collaborators May 14, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.78.0 Issue or pull request that will be included in release 7.78.0 label May 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.78.0 Issue or pull request that will be included in release 7.78.0 size-L team-earn

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants