Skip to content

feat(rewards): add VIP badge and discounted fee display to Bridge and Perps#30439

Merged
michalconsensys merged 27 commits into
mainfrom
feat/perps-vip
May 21, 2026
Merged

feat(rewards): add VIP badge and discounted fee display to Bridge and Perps#30439
michalconsensys merged 27 commits into
mainfrom
feat/perps-vip

Conversation

@michalconsensys

@michalconsensys michalconsensys commented May 20, 2026

Copy link
Copy Markdown
Contributor

Description

Adds VIP tier badge and discounted fee display across Bridge and Perps features. When a user has an active VIP tier from the Rewards program, the UI now shows:

  • A VIP badge (e.g. "VIP 1") next to fee displays in both Bridge and Perps views
  • The original fee struck through alongside the discounted fee, making the discount visually clear
  • A refactored PerpsFeesDisplay component that accepts raw fee/originalFee numbers instead of pre-formatted text, enabling it to handle discount presentation internally

On the controller side, the VIP fees fetch logic (#getVipFeesForSubscriptionId) is extracted into a reusable private method, and a new public getVipTierForAccount method is exposed via the messenger so any feature can query the user's VIP tier.

Changelog

CHANGELOG entry: Added VIP badge and discounted fee display for Bridge and Perps views when user has an active Rewards VIP tier

Related issues

Fixes:

Manual testing steps

Feature: VIP fee discount display in Bridge

  Scenario: VIP user sees discounted fee in Bridge footer
    Given the user has an active Rewards VIP tier
    And the user navigates to the Bridge view with an active quote

    When the quote returns a discounted quoteBpsFee lower than baseBpsFee
    Then a VIP badge is displayed next to the fee disclaimer
    And the original (base) fee percentage is shown with a strikethrough
    And the discounted fee percentage is shown alongside it

  Scenario: Non-VIP user sees standard fee disclaimer
    Given the user does not have a VIP tier
    And the user navigates to the Bridge view with an active quote

    When the quote fee data is returned
    Then no VIP badge is displayed
    And the standard "Includes X% MetaMask fee." disclaimer is shown

Feature: VIP fee discount display in Perps

  Scenario: VIP user sees discounted fee in Perps order view
    Given the user has an active Rewards VIP tier with a perps fee discount
    And the user opens a Perps order or close-position view

    When fees are calculated and displayed
    Then a VIP badge is shown next to the fee amount
    And the original (undiscounted) fee is shown with a strikethrough
    And the discounted fee is displayed beside it

Screenshots/Recordings

Before

After

Pre-merge author checklist

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 to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

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 fee calculation/formatting and display across Perps order/close flows and Bridge footer, and gates discount retrieval behind a new remote feature flag; mistakes could lead to incorrect fee presentation. Behavior is mostly UI-level with added tests and a flag fallback to 0 discount when disabled.

Overview
Adds VIP fee-discount presentation across Bridge and Perps: fee rows now show a RewardsVipBadge and, when discounted, a struck-through original fee/rate alongside the discounted value.

Refactors PerpsFeesDisplay to take numeric fee/originalFee (plus placeholder) and handle formatting/discount visuals internally, and extends perps fee hooks/results to return undiscountedTotalFee for UI use.

Introduces a new version-gated remote flag vipProgramEnabled (selectVipProgramEnabled) and uses it to short-circuit rewards discount/tier fetching (returning 0/none when disabled), plus updates tooltip copy/styles and broadens mocks/tests to cover the new VIP badge/discount behaviors.

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

micaelae and others added 13 commits May 19, 2026 14:59
Replace the inline -X% discount badge in PerpsFeesDisplay with the
RewardsVipBadge component and a struck-through original fee, matching
the extension's perps-vip-feature implementation.

- Update PerpsFeesDisplay to accept raw fee/originalFee numbers instead
  of pre-formatted text, show VIP badge when discount is active
- Add undiscountedTotalFee to usePerpsOrderFees hook return value
- Create usePerpsAccountId hook for CAIP-10 account ID in perps views
- Update all consumers (PerpsOrderView, PerpsClosePositionView,
  PerpsCloseSummary, PerpsFlipPositionConfirmSheet) to pass originalFee
  and accountId to PerpsFeesDisplay
- Update tests and mocks for the new component API
…t ID lookup

Extract useVipTier hook that derives the CAIP-10 account ID from Redux
selectors and fetches the VIP tier internally, removing the need for
consumers to pass accountId as a prop to RewardsVipBadge.
@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.

@metamaskbotv2 metamaskbotv2 Bot added the team-perps Perps team label May 20, 2026
@github-actions github-actions Bot added size-XL and removed size-L labels May 21, 2026
The useVipTier hook inside RewardsVipBadge calls
Engine.context.RewardsController which is unavailable in the test
environment, causing two Rewards Points Row tests to crash.
# Conflicts:
#	app/components/UI/Bridge/Views/BridgeView/BridgeViewFooter.tsx
#	app/components/UI/Rewards/components/RewardsVipBadge/RewardsVipBadge.test.tsx
#	app/components/UI/Rewards/components/RewardsVipBadge/RewardsVipBadge.tsx
@michalconsensys michalconsensys marked this pull request as ready for review May 21, 2026 09:20
@michalconsensys michalconsensys requested review from a team as code owners May 21, 2026 09:20
@github-actions github-actions Bot added size-L and removed size-XL labels May 21, 2026
Comment thread app/components/UI/Rewards/hooks/useVipTier.ts Outdated
The VIP fee discount was previously always fetched from the backend
regardless of any feature flag. This gates it behind the remote
`vipProgramEnabled` flag at three levels: UI preview hooks
(usePerpsOrderFees, usePerpsCloseAllCalculations) and the execution
layer adapter (mobileInfrastructure rewards bridge).
Switch from a plain boolean to the { enabled, minimumVersion }
VersionGatedFeatureFlag shape so the flag matches the LaunchDarkly
payload and supports minimum-version gating. Register the flag in
the feature-flag registry and document it in the perps feature-flags
reference.
@michalconsensys michalconsensys requested a review from a team as a code owner May 21, 2026 12:01
…elected network

useVipTier was using the deprecated selectChainId selector to construct
the CAIP-10 account ID, which breaks VIP badge lookups when the user's
selected network differs from the account's registration chain or when
a non-EVM network is selected. Derive the CAIP ID from the internal
account's own scopes instead, matching the pattern used by the
RewardsController's convertInternalAccountToCaipAccountId.
Comment thread app/components/UI/Rewards/hooks/useVipTier.ts Outdated
Add cleanup function with cancelled flag to the useVipTier useEffect
to prevent stale promises from overwriting state when the user switches
accounts. Also reset vipTier to null immediately when a new fetch begins
so the old badge doesn't flash while the new tier loads.

@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.

There are 2 total unresolved issues (including 1 from previous review).

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 c97f348. Configure here.

Comment thread app/components/UI/Rewards/hooks/useVipTier.ts
The hook was unconditionally calling RewardsController.getVipTierForAccount
without checking the remote feature flag, inconsistent with all other
VIP-related code paths. This could fire unnecessary API calls and display
a badge when the VIP program is disabled.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

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

E2E Test Selection:

The PR introduces VIP program feature flag gating and refactors the fee display system across Perps and Bridge components:

  1. SmokePerps (primary): Core Perps fee display is refactored - PerpsFeesDisplay now accepts fee/originalFee props instead of formatFeeText, showing struck-through pre-discount fees for VIP users. usePerpsOrderFees adds undiscountedTotalFee, usePerpsCloseAllCalculations gates discount fetching on vipProgramEnabled feature flag. PerpsClosePositionView, PerpsOrderView, PerpsFlipPositionConfirmSheet, and PerpsCloseSummary all updated. These are direct changes to Perps trading flows.

  2. SmokeWalletPlatform (required by SmokePerps): Per tag description, when selecting SmokePerps, also select SmokeWalletPlatform (Trending section). Perps is embedded in Trending.

  3. SmokeConfirmations (required by SmokePerps): Per tag description, when selecting SmokePerps, also select SmokeConfirmations (Add Funds deposits are on-chain transactions).

  4. SmokeSwap: BridgeViewFooter.tsx is modified - the RewardsVipBadge component no longer requires accountId prop (simplified interface). This affects the Bridge/Swap flow UI. The change is minor (prop removal) but touches the swap/bridge confirmation footer.

The RewardsVipBadge refactoring (removing accountId prop, using useVipTier hook internally) and the new vipProgramEnabled feature flag selector are infrastructure changes that support the VIP discount display. The feature flag is disabled in production (inProd: false), reducing risk of regressions in production flows.

The localization change updates the discount message text which is a low-risk string change.

Risk is medium because the fee display refactoring touches multiple Perps views and the Bridge footer, but the changes are well-contained and the VIP feature flag is disabled by default.

Performance Test Selection:
The changes are focused on fee display logic (UI text/formatting), VIP discount calculation gating, and a feature flag selector. While there are new hooks (useVipTier) and selector computations, these are lightweight operations (a single async call gated by a feature flag that is disabled in production). No significant rendering performance impact is expected - the changes don't affect list rendering, app startup, account loading, or other performance-sensitive paths.

View GitHub Actions results

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.75000% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.13%. Comparing base (66f0cb2) to head (f85265c).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
app/components/UI/Rewards/hooks/useVipTier.ts 33.33% 14 Missing and 2 partials ⚠️
...components/UI/Perps/components/FoxIcon/FoxIcon.tsx 0.00% 2 Missing ⚠️
...nts/UI/Perps/hooks/usePerpsCloseAllCalculations.ts 85.71% 0 Missing and 1 partial ⚠️
app/components/UI/Perps/hooks/usePerpsOrderFees.ts 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #30439      +/-   ##
==========================================
- Coverage   82.13%   82.13%   -0.01%     
==========================================
  Files        5488     5492       +4     
  Lines      147743   147857     +114     
  Branches    33969    34012      +43     
==========================================
+ Hits       121353   121437      +84     
- Misses      18084    18114      +30     
  Partials     8306     8306              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud

Copy link
Copy Markdown

@michalconsensys michalconsensys enabled auto-merge May 21, 2026 14:14

@racitores racitores 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.

would it be possible to use component view tests for these PR instead of unit? we are trying to standardise how unit test are written to avoid too much mock and have test a bit more controlled of what is been tested.
There is a skill that helps a lot adding these tests and I reckon it could migrate most of them into cv test.
I don't think you need to do it now but it would be nice to have it done in a subsequent PR. Is it ok?

@racitores racitores 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.

LGTM

@michalconsensys michalconsensys added this pull request to the merge queue May 21, 2026
Merged via the queue into main with commit 0888744 May 21, 2026
204 of 207 checks passed
@michalconsensys michalconsensys deleted the feat/perps-vip branch May 21, 2026 15:11
@github-actions github-actions Bot locked and limited conversation to collaborators May 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants