Skip to content

Refine Money Sweepstakes campaign UX - #34852

Draft
jasonculbertson wants to merge 14 commits into
MetaMask:mainfrom
jasonculbertson:codex/pr-33887-money-sweepstakes
Draft

Refine Money Sweepstakes campaign UX#34852
jasonculbertson wants to merge 14 commits into
MetaMask:mainfrom
jasonculbertson:codex/pr-33887-money-sweepstakes

Conversation

@jasonculbertson

Copy link
Copy Markdown
Contributor

Summary

  • Rework the Money Sweepstakes entry card, opt-in flow, and post-opt-in balance experience.
  • Replace notification and onboarding detours with campaign opt-in and action-oriented eligibility guidance.
  • Simplify rules, draw schedule, and supporting learning links; add campaign artwork and a change log.

Validation

  • Focused Money Sweepstakes Jest tests
  • Prettier and ESLint checks for updated components
  • Simulator inspection of the post-opt-in balance card

Follow-up

  • The development Rewards API must return qualifyingThresholdUsd: 100 to match the PRD. The client intentionally renders the backend value.

sophieqgu and others added 14 commits July 28, 2026 16:43
Wire series-aware details, stats, draw schedule, prize pool, opt-in CTA, and winning flow using backend-resolved localizedText.

Co-authored-by: Cursor <cursoragent@cursor.com>
Assert Money Account binding before campaign opt-in, surface binding conflicts in CTA/details UI, and mock the new outcome toast hook in dashboard/campaigns tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
…etaMask#34024)

## Summary
- Align Money Account Sweepstakes scorecard UI with the backend collapse
of `lost_today` into `below_threshold`.
- Warning icon / copy now only keys off `below_threshold`.
- Test fixtures update `belowThresholdDescription` to point at
**tomorrow's** entry.

Companion backend PR: consensys-vertical-apps/va-mmcx-rewards (branch
`fix/collapse-today-status-below-threshold`).

## Finding
`lost_today` was misleading: the API only had today's minimum, so a flat
`$50 → $50` day looked like a dip. Under the strict intra-day-minimum
rule both cases are unrecoverable for today, so one status +
tomorrow-facing copy is the honest UX.

## Test plan
- [ ] `MoneyAccountSweepstakesStatsSummary` unit tests pass
- [ ] Warning Danger icon shows for `below_threshold`
- [ ] Info sheet for `below_threshold` uses the tomorrow's-entry
description
- [ ] No TypeScript references to `lost_today` remain


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Matches the API collapsing lost_today into below_threshold; mobile no longer reads this key.

Co-authored-by: Cursor <cursoragent@cursor.com>
…n rule (MetaMask#34067)

## **Description**

Client side of
[va-mmcx-rewards#748](consensys-vertical-apps/va-mmcx-rewards#748),
which changes what qualifies a sweepstakes day: **net new deposits since
the participant joined**, rather than an intra-day balance minimum.
Targets `feat/money-account-sweepstakes` (MetaMask#33887), so review that first.

**Draft** — the backend PR and its spec
([va-mmcx-rewards#747](consensys-vertical-apps/va-mmcx-rewards#747))
are still awaiting product sign-off. If the rule changes, this changes
with it.

- `todayMinUsd` → `qualifyingDepositsUsd`, plus a new
`qualifyingThresholdUsd` carrying the campaign's threshold so progress
can be rendered. The threshold is per-campaign Contentful config, so it
can't be hardcoded client-side — it did not previously reach the client
at all, which is why the API now returns it.
- `todayStatus` splits `below_threshold` back into `not_yet_qualified`
and `lost_today`. These are **not** interchangeable: the first is still
winnable today by depositing the shortfall, the second is forfeit. Only
`lost_today` warns; `not_yet_qualified` gets a neutral add affordance.
Reverses the collapse in MetaMask#34024.
- The tile shows the qualifying figure **against the threshold** (`$50 /
$100`) rather than a bare number. The qualifying figure counts only
deposits, never accrued yield, so it is normally **lower** than the
account balance — someone holding $100 who deposited $50 since joining
is legitimately at $50, and a lone figure gave them no way to understand
that.
- The figure is clamped at zero for display. An outflow is valued when
it happens and so carries the yield earned on the position, which can
leave the total a few cents below zero after a full withdrawal.
- Copy keys follow the API: `belowThresholdDescription` →
`notYetQualifiedDescription` + `lostTodayDescription`, and
`currentBalanceTitle` / `currentBalanceDescription` are gone. Those two
described the account balance, which no longer decides a day and is
rendered nowhere, so the DTO stops requiring strings the API no longer
sends.
- The localized-text fixtures now hold the strings the API actually
returns, with the threshold already substituted. The backend resolves
`{threshold}` from campaign config before responding, so a fixture
carrying the raw token was asserting on copy no client can receive.

**Known remaining gap, left for design:** "Current balance" is still
absent from the tiles, so the total balance appears nowhere on screen.
Showing it as context beside the qualifying figure is the piece that
makes the two numbers coherent, and it needs a design call rather than a
guess.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Refs:
consensys-vertical-apps/va-mmcx-rewards#748

## **Manual testing steps**

Not performed — this is a draft against an unmerged base, and the API
fields it consumes are not deployed to any environment yet. Verification
so far is type-level and unit-level only:

1. `npx tsc --noEmit -p tsconfig.json` — clean.
2. `npx jest
app/components/UI/Rewards/components/Campaigns/MoneyAccountSweepstakes
app/components/UI/Rewards/hooks/useGetMoneyAccountSweepstakesStatsMe.test.ts
app/components/UI/Rewards/Views/MoneyAccountSweepstakesCampaignDetailsView.test.tsx`
— 7 suites, 54 tests passing.
3. `npx jest app/core/Engine/controllers/rewards-controller` — 9 suites,
888 tests passing.

Once MetaMask#748 is on DEV: open the campaign, confirm the tile reads
`qualifying / threshold`, that a participant below the threshold shows
the neutral state rather than a warning, and that a participant who
dipped after crossing shows the warning.

## **Screenshots/Recordings**

### **Before**

N/A — not captured. Draft against an unmerged base with undeployed API
fields, so the screen cannot render real data yet.

### **After**

N/A — see above. To be added before this leaves draft.

## **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)

- [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**

- [ ] 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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the rewards campaign stats contract and user-facing
qualification messaging; scope is limited to MAS with unit tests, but
behavior depends on an unmerged backend API.
> 
> **Overview**
> Aligns the Money Account Sweepstakes client with the backend rule that
**net new deposits since join** (not intra-day balance minimum)
determine daily entries.
> 
> **Stats API & types:** Replaces `todayMinUsd` with
`qualifyingDepositsUsd` and `qualifyingThresholdUsd`. Splits
`todayStatus` from `below_threshold` into `not_yet_qualified` (still
winnable today) and `lost_today` (forfeit). Drops unused
`currentBalance*` localized strings and swaps
`belowThresholdDescription` for `notYetQualifiedDescription` /
`lostTodayDescription`. `RewardsController` disabled-feature defaults
and cache mapping follow the new shape.
> 
> **Stats tile (`MoneyAccountSweepstakesStatsSummary`):** Shows progress
as **`$qualifying / $threshold`** with qualifying amount clamped at
zero. Warning styling and danger icon apply only to `lost_today`;
`not_yet_qualified` uses a neutral add icon. Info-sheet copy picks the
status-specific description.
> 
> **Tests:** MAS fixtures and expectations updated for the new copy and
fields (including CTA “no balance” toast strings).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
16d79de. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: sophieqgu <sophieqgu@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants