Skip to content

feat: add Merkl rewards functionality and localization#39901

Merged
Jwhiles merged 37 commits intomainfrom
claim-musd
Feb 20, 2026
Merged

feat: add Merkl rewards functionality and localization#39901
Jwhiles merged 37 commits intomainfrom
claim-musd

Conversation

@PatrykLucka
Copy link
Contributor

@PatrykLucka PatrykLucka commented Feb 9, 2026

Description

Adds mUSD Merkl rewards claiming support to the extension, allowing users to view and claim mUSD conversion bonuses directly from the asset details page. This mirrors the feature recently added in metamask-mobile, adapted to the extension's architecture and patterns.

Why: Users holding mUSD earn conversion bonuses distributed via Merkl on Linea. They currently have no way to discover or claim these rewards within the extension.

What's included:

  • "Claim bonus" badge in the token list for eligible tokens (mUSD on Mainnet/Linea), gated behind the earnMerklCampaignClaiming remote feature flag
  • Merkl rewards section on the asset details page showing claimable bonus amount with a claim button
  • Claim flow: modal explaining bonuses are on Linea → generates a claim() transaction on the Merkl Distributor contract → routes to the transaction confirmation page
  • On-chain read of the claimed amount from the Merkl Distributor contract for instant balance updates after claiming (the Merkl API can lag)
  • Clicking the "Claim bonus" badge scrolls to the rewards section on the asset page
  • Pending claim detection to disable the claim button while a transaction is in-flight

Key architectural decisions:

  • All new UI components use @metamask/design-system-react (non-deprecated)
  • Feature is fully gated behind the earnMerklCampaignClaiming remote feature flag

Changelog

CHANGELOG entry: Added the ability to view and claim mUSD Merkl conversion bonuses from the token list and asset details page (behind feature flag)

Related issues

Fixes:
https://consensyssoftware.atlassian.net/browse/MUSD-299
https://consensyssoftware.atlassian.net/browse/MUSD-300
https://consensyssoftware.atlassian.net/browse/MUSD-301

Manual testing steps

  1. Enable the earnMerklCampaignClaiming remote feature flag (e.g. via .manifest-overrides.json)
  2. Ensure you have an account holding mUSD on Mainnet or Linea with unclaimed Merkl rewards
  3. Click "Claim bonus" — verify the asset detail page opens and scrolls to the Merkl Rewards section
  4. Click elsewhere on the mUSD row — verify the asset detail page opens but does NOT scroll to the Merkl section
  5. On the asset detail page, verify the "Claimable bonus" amount and "3% bonus" label are displayed
  6. Click "Claim" — verify the "Claim bonuses on Linea" modal appears with the mUSD icon
  7. Click "Continue" — verify a transaction confirmation page appears for the Merkl Distributor claim() call on Linea

Screenshots/Recordings

Before

After

claim-extension-1.mov

Pre-merge author checklist

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
Adds a new transaction creation path (external API + on-chain read) and wires it into the confirmation flow for TransactionType.musdClaim, which could impact user transaction routing and displayed claim amounts if incorrect.

Overview
Adds a Merkl rewards “Claim bonus” entry point to token cells (token list, asset page, DeFi details) that appears only when the remote flag earnMerklCampaignClaiming is enabled and the token is in an eligible allowlist.

Introduces a new Merkl claim flow: fetches Merkl proof data from api.merkl.xyz, builds/dispatches a TransactionType.musdClaim distributor claim() transaction on Linea, and adds a dedicated redesigned confirmation/info UI (title/subtitle, account row, receive summary handling). The claim confirmation computes the actual unclaimed amount by reading the distributor’s claimed() value on-chain via a JSON-RPC eth_call (Infura URL from FEATURED_RPCS) and formats token/fiat amounts.

Written by Cursor Bugbot for commit cf911d4. This will update automatically on new commits. Configure here.

@PatrykLucka PatrykLucka self-assigned this Feb 9, 2026
@PatrykLucka PatrykLucka requested a review from a team as a code owner February 9, 2026 09:06
@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

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.

@metamaskbot metamaskbot added the team-mobile-ux Mobile UX team label Feb 9, 2026
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 9, 2026

✨ Files requiring CODEOWNER review ✨

@MetaMask/confirmations (12 files, +417 -1)
  • 📁 ui/
    • 📁 pages/
      • 📁 confirmations/
        • 📁 components/
          • 📁 activity/
            • 📁 transaction-details-modal/
              • 📄 transaction-details-modal.tsx +2 -0
            • 📁 transaction-details-summary/
              • 📄 transaction-details-summary.tsx +1 -0
          • 📁 confirm/
            • 📁 info/
              • 📄 info.tsx +3 -1
            • 📁 title/
              • 📄 title.test.tsx +1 -0
              • 📄 title.tsx +6 -0
          • 📁 info/
            • 📁 musd-claim-info/
              • 📄 index.ts +1 -0
              • 📄 musd-claim-heading.tsx +93 -0
              • 📄 musd-claim-info.test.tsx +127 -0
              • 📄 musd-claim-info.tsx +35 -0
          • 📁 rows/
            • 📁 account-row/
              • 📄 account-row.test.tsx +122 -0
              • 📄 account-row.tsx +25 -0
              • 📄 index.ts +1 -0

💎 @MetaMask/metamask-assets (3 files, +32 -2)
  • 📁 ui/
    • 📁 components/
      • 📁 app/
        • 📁 assets/
          • 📁 token-cell/
            • 📄 token-cell.test.tsx +2 -0
            • 📄 token-cell.tsx +28 -2
          • 📁 token-list/
            • 📄 token-list.tsx +2 -0

👨‍🔧 @MetaMask/metamask-earn (9 files, +1317 -0)
  • 📁 ui/
    • 📁 components/
      • 📁 app/
        • 📁 musd/
          • 📁 hooks/
            • 📄 useMerklClaim.test.ts +224 -0
            • 📄 useMerklClaim.ts +162 -0
          • 📁 selectors/
            • 📄 getMerklRewardsEnabled.ts +25 -0
            • 📄 index.ts +1 -0
            • 📄 claim-bonus-badge.tsx +86 -0
            • 📄 constants.ts +46 -0
            • 📄 index.ts +6 -0
            • 📄 merkl-client.test.ts +467 -0
            • 📄 merkl-client.ts +300 -0

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 9, 2026

Builds ready [552723f]
UI Startup Metrics (1422 ± 90 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1422121816109014831561
load1213100213888912701350
domContentLoaded1207100013828912651342
domInteractive281796192479
firstPaint171661322164206307
backgroundConnect24121933317246271
firstReactRender16113041823
initialActions109113
loadScripts98177711488810361116
setupStore1363651620
numNetworkReqs221588201579
BrowserifyPower User HomeuiStartup17871464263517718452082
load11781050208618211531668
domContentLoaded11641042207617711411618
domInteractive37191752934110
firstPaint203781669227239395
backgroundConnect32528344928337366
firstReactRender24155482639
initialActions104111
loadScripts92180317811668961349
setupStore1693961831
numNetworkReqs70441833464175
WebpackStandard HomeuiStartup86869411701059331072
load732608990100822904
domContentLoaded72660498399816899
domInteractive2716111212382
firstPaint1105831253137213
backgroundConnect3718137253889
firstReactRender16104571732
initialActions107112
loadScripts72360298198810897
setupStore1163041221
numNetworkReqs231595201581
WebpackPower User HomeuiStartup1274910205222014111746
load71962511791327011073
domContentLoaded71061811691326931063
domInteractive3419132233597
firstPaint155641156122182278
backgroundConnect17715139339174278
firstReactRender24184542631
initialActions102111
loadScripts70761611611306911056
setupStore1245461318
numNetworkReqs1174929053139261
FirefoxBrowserifyStandard HomeuiStartup16261333228621317462069
load13741155195117514421762
domContentLoaded13721155194517514421762
domInteractive74322914795141
firstPaint------
backgroundConnect68262134162155
firstReactRender1392221316
initialActions103122
loadScripts13351128192316013911636
setupStore134119141336
numNetworkReqs251298231892
BrowserifyPower User HomeuiStartup28022062453954129133931
load16191241261932616912372
domContentLoaded16181236261932716902372
domInteractive1063150588106350
firstPaint------
backgroundConnect3161221321252320878
firstReactRender18153131922
initialActions213122
loadScripts15631225259529916292245
setupStore1348712190144616
numNetworkReqs65361303193124
WebpackStandard HomeuiStartup16421336204016417711934
load13911146163911214731571
domContentLoaded13911145163911214731570
domInteractive922929448129160
firstPaint------
backgroundConnect75252214690176
firstReactRender14112631520
initialActions103122
loadScripts13491115160810014161497
setupStore2342114013114
numNetworkReqs241288181873
WebpackPower User HomeuiStartup27381954855981727613750
load15721224727863617082150
domContentLoaded15711222727863617082150
domInteractive1203073214498474
firstPaint------
backgroundConnect38211514313434391032
firstReactRender22156272429
initialActions217123
loadScripts15101155725262516361917
setupStore17583689404136678
numNetworkReqs63351373188120
📊 Page Load Benchmark Results

Current Commit: 552723f | Date: 2/9/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±40ms) 🟡 | historical mean value: 1.05s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 733ms (±38ms) 🟢 | historical mean value: 731ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 78ms (±11ms) 🟢 | historical mean value: 78ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 40ms 1.02s 1.34s 1.09s 1.34s
domContentLoaded 733ms 38ms 707ms 1.01s 766ms 1.01s
firstPaint 78ms 11ms 68ms 172ms 88ms 172ms
firstContentfulPaint 78ms 11ms 68ms 172ms 88ms 172ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 3.86 KiB (0.1%)
  • ui: 24.38 KiB (0.3%)
  • common: 35.47 KiB (0.33%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 9, 2026

Builds ready [45df3e2]
UI Startup Metrics (1410 ± 95 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1410118616979514711568
load1210999148410012721385
domContentLoaded1202975147610212661377
domInteractive2716118182375
firstPaint1316636666183237
backgroundConnect24122231618243278
firstReactRender18104162028
initialActions105113
loadScripts978760123910410451158
setupStore1364461624
numNetworkReqs231587211583
BrowserifyPower User HomeuiStartup288014549791177440016694
load12611045211718013361647
domContentLoaded12421031210818113051640
domInteractive3820243313691
firstPaint2108556390265393
backgroundConnect11162818089151316473086
firstReactRender25155282741
initialActions103112
loadScripts998804181716810451364
setupStore1564061928
numNetworkReqs1245227544149194
WebpackStandard HomeuiStartup8506661091979191006
load712598101599794877
domContentLoaded707594100799790871
domInteractive2617163222179
firstPaint996130750102198
backgroundConnect3918130264592
firstReactRender15104361627
initialActions104112
loadScripts704592100598786869
setupStore1162841222
numNetworkReqs231589201583
WebpackPower User HomeuiStartup1232901276224713241676
load73464011121217161067
domContentLoaded72563311061207071051
domInteractive34161412630100
firstPaint162681032125206287
backgroundConnect1614949362161271
firstReactRender21175652227
initialActions102111
loadScripts72263110971187051049
setupStore1243951316
numNetworkReqs1361228346158222
FirefoxBrowserifyStandard HomeuiStartup15951310210118516721924
load13401120184414913891630
domContentLoaded13391114184314913851630
domInteractive65312123886135
firstPaint------
backgroundConnect68272063963158
firstReactRender1292221214
initialActions102012
loadScripts13041095172313113491531
setupStore163155241159
numNetworkReqs2511102222088
BrowserifyPower User HomeuiStartup27541975499345829043698
load15421280343433315532021
domContentLoaded15411280343333315522021
domInteractive158521742193145369
firstPaint------
backgroundConnect40911113133085931091
firstReactRender18146661822
initialActions2049522
loadScripts14921263337831714991869
setupStore16110759191249570
numNetworkReqs893718335110159
WebpackStandard HomeuiStartup15911273211418516611973
load13561135166511714251568
domContentLoaded13561130166511714241567
domInteractive822724346119143
firstPaint------
backgroundConnect67242334479161
firstReactRender14112831519
initialActions103122
loadScripts13211119160810513871505
setupStore184261321249
numNetworkReqs2312102191878
WebpackPower User HomeuiStartup27071945896475828963544
load15501281723560415871944
domContentLoaded15501281723560415861944
domInteractive17136869158242574
firstPaint------
backgroundConnect3501231473281405950
firstReactRender20164142328
initialActions203122
loadScripts15181261717860015441896
setupStore15012721162181545
numNetworkReqs86352304288184
📊 Page Load Benchmark Results

Current Commit: 45df3e2 | Date: 2/9/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.03s (±37ms) 🟡 | historical mean value: 1.05s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 714ms (±35ms) 🟢 | historical mean value: 731ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±12ms) 🟢 | historical mean value: 78ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.03s 37ms 998ms 1.30s 1.05s 1.30s
domContentLoaded 714ms 35ms 690ms 970ms 730ms 970ms
firstPaint 76ms 12ms 56ms 184ms 84ms 184ms
firstContentfulPaint 76ms 12ms 56ms 184ms 84ms 184ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 3.86 KiB (0.1%)
  • ui: 24.31 KiB (0.3%)
  • common: 35.47 KiB (0.33%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 11, 2026

Builds ready [e3f2a69]
UI Startup Metrics (1366 ± 110 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup13661152183211014321533
load117498915489912351318
domContentLoaded116698515339812311308
domInteractive271696182473
firstPaint169681275191199344
backgroundConnect23521727613239265
firstReactRender1810164151826
initialActions107113
loadScripts94576712939710091089
setupStore1363251521
numNetworkReqs231598211581
BrowserifyPower User HomeuiStartup309316679344167038166314
load13151119184314313751624
domContentLoaded12911109175213113411614
domInteractive38191432538112
firstPaint249801775282275427
backgroundConnect91031244759279773138
firstReactRender24165872633
initialActions104112
loadScripts1036865150412710781352
setupStore1564871731
numNetworkReqs1024420735118160
WebpackStandard HomeuiStartup83469411271018961023
load719613100493770900
domContentLoaded71460999793765890
domInteractive2717123202384
firstPaint1196240767137244
backgroundConnect26177282837
firstReactRender15103451724
initialActions104112
loadScripts71160798691763884
setupStore1265561217
numNetworkReqs2315100211584
WebpackPower User HomeuiStartup1263903278730713241855
load728615109499734998
domContentLoaded717610106199723993
domInteractive38182033136127
firstPaint1457444871191267
backgroundConnect16812964073161315
firstReactRender22174042232
initialActions104111
loadScripts714608104697721985
setupStore1243151421
numNetworkReqs1356423438156201
FirefoxBrowserifyStandard HomeuiStartup15131310214316015401832
load13081144193212713471563
domContentLoaded13071143192712713471563
domInteractive65311913685136
firstPaint------
backgroundConnect5026146165379
firstReactRender1192121213
initialActions103012
loadScripts12841127190412413221535
setupStore156173231239
numNetworkReqs241296221689
BrowserifyPower User HomeuiStartup30252004808694630824066
load17551312667286516712315
domContentLoaded17551306667286516712315
domInteractive188511715211179466
firstPaint------
backgroundConnect3951221355277492934
firstReactRender20148292127
initialActions213122
loadScripts17141291662386216352244
setupStore17813773203262664
numNetworkReqs903717533106152
WebpackStandard HomeuiStartup15411323228415115791855
load13421138207813213951581
domContentLoaded13401138207713213951581
domInteractive882726750128163
firstPaint------
backgroundConnect50221342453108
firstReactRender13102521418
initialActions103112
loadScripts13161124205212813631545
setupStore1265191132
numNetworkReqs221285171766
WebpackPower User HomeuiStartup27142017471740728833391
load15121281260024315912064
domContentLoaded15111280260024315912064
domInteractive15350808141166537
firstPaint------
backgroundConnect46612113903446981100
firstReactRender22157582332
initialActions203123
loadScripts14571257257623115201966
setupStore1509804168200520
numNetworkReqs87352163197141
📊 Page Load Benchmark Results

Current Commit: e3f2a69 | Date: 2/11/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.04s (±42ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 722ms (±38ms) 🟢 | historical mean value: 724ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±12ms) 🟢 | historical mean value: 78ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.04s 42ms 1.01s 1.33s 1.06s 1.33s
domContentLoaded 722ms 38ms 702ms 986ms 736ms 986ms
firstPaint 76ms 12ms 56ms 184ms 84ms 184ms
firstContentfulPaint 76ms 12ms 56ms 184ms 84ms 184ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -47.84 KiB (-1.18%)
  • ui: 90.04 KiB (1.09%)
  • common: 206.84 KiB (1.94%)

Copy link

@cursor cursor bot left a comment

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 2 potential issues.

>
{label}
</Text>
</span>
Copy link

Choose a reason for hiding this comment

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

Non-accessible span used as clickable interactive element

Medium Severity

The clickable "Claim bonus" badge uses a raw <span> with an onClick handler. This element is not focusable via keyboard, won't respond to Enter/Space key presses, and screen readers won't announce it as interactive. A <button> (with appropriate styling) would provide built-in keyboard and accessibility support. This was also flagged in the PR discussion as "mess left by cursor."

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

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 2 potential issues.

Copy link

@cursor cursor bot left a comment

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 2 potential issues.

Copy link

@cursor cursor bot left a comment

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 2 potential issues.

Matt561
Matt561 previously approved these changes Feb 19, 2026
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 19, 2026

Builds ready [b2fa23b]
UI Startup Metrics (1430 ± 102 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup14301235171710214711605
load1217103714819112611382
domContentLoaded1208102314558812531362
domInteractive271795162470
firstPaint176671350176209339
backgroundConnect25423337518259273
firstReactRender18133442027
initialActions106113
loadScripts97179012178810141124
setupStore1163241417
numNetworkReqs312294202286
BrowserifyPower User HomeuiStartup2523132210055177724734871
load1134984191016111571500
domContentLoaded1117972188415311441436
domInteractive3616141253599
firstPaint1927437670233300
backgroundConnect782257730210887012758
firstReactRender22164972338
initialActions103112
loadScripts90776816001449241221
setupStore1354161424
numNetworkReqs75381572587128
WebpackStandard HomeuiStartup9017261197949541050
load77063595290843910
domContentLoaded76463094689838902
domInteractive3017117202779
firstPaint1226831050143208
backgroundConnect29195383347
firstReactRender19123862131
initialActions104113
loadScripts76062893788836894
setupStore1366071326
numNetworkReqs312291202588
WebpackPower User HomeuiStartup1333898271432313822159
load78366316491367791052
domContentLoaded77265316401367691043
domInteractive44212373640122
firstPaint1577243977194305
backgroundConnect17313449655176275
firstReactRender23173842530
initialActions102011
loadScripts76865116301347661036
setupStore1354661521
numNetworkReqs1134425043140184
FirefoxBrowserifyStandard HomeuiStartup16851412256023616782207
load14181190230420714351807
domContentLoaded14181189230420714351807
domInteractive843328541104148
firstPaint------
backgroundConnect61312592963107
firstReactRender14122831416
initialActions103122
loadScripts13891173227820114091695
setupStore188136211647
numNetworkReqs321997202590
BrowserifyPower User HomeuiStartup28702056745160730353611
load16521345584647116552053
domContentLoaded16521344584547116542053
domInteractive15940608123188420
firstPaint------
backgroundConnect373891613319458967
firstReactRender20157391927
initialActions213122
loadScripts16081321555444616031967
setupStore1559859203150641
numNetworkReqs79311933592142
WebpackStandard HomeuiStartup18651491342538618403156
load15441272308830815401721
domContentLoaded15441266308730815401721
domInteractive143321613252136204
firstPaint------
backgroundConnect10129153320977192
firstReactRender17135251825
initialActions103122
loadScripts15161255305730615081687
setupStore277165322390
numNetworkReqs312093172777
WebpackPower User HomeuiStartup28482060852476129543575
load16811302725568316772141
domContentLoaded16811302725568416762141
domInteractive17049780169177678
firstPaint------
backgroundConnect41612034674394521096
firstReactRender24168092632
initialActions203123
loadScripts16091273722160016102113
setupStore16910781197256594
numNetworkReqs76312164383192
📊 Page Load Benchmark Results

Current Commit: b2fa23b | Date: 2/19/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.04s (±40ms) 🟡 | historical mean value: 1.03s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 732ms (±38ms) 🟢 | historical mean value: 726ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±12ms) 🟢 | historical mean value: 84ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.04s 40ms 1.02s 1.33s 1.07s 1.33s
domContentLoaded 732ms 38ms 708ms 1.01s 750ms 1.01s
firstPaint 80ms 12ms 64ms 180ms 88ms 180ms
firstContentfulPaint 80ms 12ms 64ms 180ms 88ms 180ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 95.41 KiB (2.21%)
  • ui: 2 KiB (0.02%)
  • common: 13.29 KiB (0.12%)

Copy link

@cursor cursor bot left a comment

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 3 potential issues.

{t('merklRewardsUnexpectedError')}
</Text>
);
}
Copy link

Choose a reason for hiding this comment

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

Error state renders non-interactive element blocking retry

Medium Severity

When useMerklClaim sets error, ClaimBonusBadge renders a non-interactive <Text> element displaying "Unexpected error. Please try again." — but the clickable <button> is not rendered in this branch, so the user has no way to actually retry. The error state is only cleared inside claimRewards() (line 71 of useMerklClaim.ts), which can only be invoked via the button's onClick. This creates an irrecoverable dead-end state within the component. The error message explicitly tells the user to "try again" but the UI provides no mechanism to do so without navigating away and remounting the component.

Fix in Cursor Fix in Web

networkClientId,
type: TransactionType.musdClaim,
}),
);
Copy link

Choose a reason for hiding this comment

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

Reward cache not cleared after successful claim dispatch

Medium Severity

After addTransactionAndRouteToConfirmationPage successfully dispatches, the 5-minute rewardCache in merkl-client.ts is not invalidated. If the user completes a claim, navigates back, and clicks the badge again within 5 minutes, stale cached proof data is used to build a new transaction. The on-chain contract would compute a zero payout (totalAmount − alreadyClaimed = 0), leading to a confusing confirmation screen and wasted gas if confirmed. Calling clearRewardCache() after successful dispatch would prevent this.

Additional Locations (1)

Fix in Cursor Fix in Web

* Transaction types that use the Pay flow (TransactionDetailsModal instead of TransactionListItemDetails)
*/
export const PAY_TRANSACTION_TYPES = [
TransactionType.musdClaim,
Copy link

Choose a reason for hiding this comment

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

Activity list shows wrong title for claim transactions

Medium Severity

Adding TransactionType.musdClaim to PAY_TRANSACTION_TYPES causes it to fall into the existing ternary in useTransactionDisplayData, which only distinguishes perpsDeposit from everything else. As a result, musdClaim transactions display the title musdConversionActivityTitle ("mUSD Conversion") in the activity list instead of a claim-specific label like "Claim bonus". No musdClaimActivityTitle locale key was added, and the branching logic was not updated to handle the new transaction type.

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

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 2 potential issues.

{t('merklRewardsUnexpectedError')}
</Text>
);
}
Copy link

Choose a reason for hiding this comment

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

Error state permanently replaces clickable badge with no retry

Medium Severity

When claimRewards() fails, useMerklClaim sets the error state, which causes ClaimBonusBadge to render a non-interactive <Text> element saying "Please try again." However, the clickable button is now gone, and the hook only clears error inside claimRewards() (line 71), which can no longer be invoked. The user is stuck in a dead-end error state with no retry path until the component unmounts.

Additional Locations (1)

Fix in Cursor Fix in Web

export const ELIGIBLE_TOKENS: Record<string, string[]> = {
[CHAIN_IDS.MAINNET]: [AGLAMERKL_ADDRESS_MAINNET, MUSD_TOKEN_ADDRESS],
[CHAIN_IDS.LINEA_MAINNET]: [AGLAMERKL_ADDRESS_LINEA, MUSD_TOKEN_ADDRESS],
'0xe709': [AGLAMERKL_ADDRESS_LINEA, MUSD_TOKEN_ADDRESS],
Copy link

Choose a reason for hiding this comment

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

Unknown chain ID 0xe709 in eligible tokens map

Medium Severity

The ELIGIBLE_TOKENS map contains a hardcoded '0xe709' (decimal 59145) key, which is not a recognized chain ID. Linea Mainnet is 0xe708 (59144), already present in the map via CHAIN_IDS.LINEA_MAINNET. Linea Sepolia is 0xe705. The entry uses Linea-specific addresses, suggesting it was intended for a Linea chain but is off by one or is leftover development/test code.

Fix in Cursor Fix in Web

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 19, 2026

Builds ready [cf911d4]
UI Startup Metrics (1439 ± 92 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1439126216789214701615
load1217106614258212521393
domContentLoaded1210105714128112471386
domInteractive281792172575
firstPaint170721095116216270
backgroundConnect25923344026262281
firstReactRender18114252026
initialActions1015214
loadScripts97082311658110071142
setupStore1263041422
numNetworkReqs322290193184
BrowserifyPower User HomeuiStartup2388142510427158521825140
load12231076169515212251597
domContentLoaded12061069164214612201575
domInteractive3720179273597
firstPaint1848050186254334
backgroundConnect66329844178593742736
firstReactRender24156592644
initialActions105112
loadScripts95080913871429661317
setupStore1664781731
numNetworkReqs69371452683122
WebpackStandard HomeuiStartup88970611781049601086
load7596211025101836940
domContentLoaded7536161019100826932
domInteractive281799172571
firstPaint1246632254144227
backgroundConnect28195693144
firstReactRender18124062130
initialActions105112
loadScripts750614101799824924
setupStore1264751321
numNetworkReqs3122103212588
WebpackPower User HomeuiStartup1249877323827112941630
load73864113061107241002
domContentLoaded7276351299111708997
domInteractive39181733037128
firstPaint1457339571165293
backgroundConnect17113378175166277
firstReactRender22174542327
initialActions102011
loadScripts7256331289109707988
setupStore1245761421
numNetworkReqs76361613389152
FirefoxBrowserifyStandard HomeuiStartup16131364270421516442108
load13531161210814814101627
domContentLoaded13521159210214814091626
domInteractive73333825488154
firstPaint------
backgroundConnect57262313059101
firstReactRender14115341416
initialActions102012
loadScripts13261141196613713821598
setupStore2071623013111
numNetworkReqs3219100212594
BrowserifyPower User HomeuiStartup28152026782067329643591
load16241273640758316222157
domContentLoaded16231268640658316222157
domInteractive13335770135114444
firstPaint------
backgroundConnect3671071067293450977
firstReactRender19146971924
initialActions203122
loadScripts15871249637158015722123
setupStore1548954209152624
numNetworkReqs69301893686136
WebpackStandard HomeuiStartup18351462359635318282200
load15581245313132415521835
domContentLoaded15581244313132415521834
domInteractive119321506151133212
firstPaint------
backgroundConnect63302083170132
firstReactRender16133441723
initialActions103122
loadScripts15281222302431915251808
setupStore287231412988
numNetworkReqs302085162774
WebpackPower User HomeuiStartup27721940790863529223487
load16071272621452117102172
domContentLoaded16071272621452117102171
domInteractive14033874149122438
firstPaint------
backgroundConnect3971221513304514961
firstReactRender21166662328
initialActions207122
loadScripts15661244618552016842103
setupStore15510829225106722
numNetworkReqs68301973490131
📊 Page Load Benchmark Results

Current Commit: cf911d4 | Date: 2/19/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 958ms (±38ms) 🟢 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 673ms (±35ms) 🟢 | historical mean value: 734ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±10ms) 🟢 | historical mean value: 84ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 958ms 38ms 927ms 1.23s 986ms 1.23s
domContentLoaded 673ms 35ms 653ms 928ms 701ms 928ms
firstPaint 76ms 10ms 64ms 160ms 84ms 160ms
firstContentfulPaint 76ms 10ms 64ms 160ms 84ms 160ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 95.41 KiB (2.21%)
  • ui: 2.29 KiB (0.03%)
  • common: 13.29 KiB (0.12%)

privacyMode={privacyMode}
onClick={isNonEvmTestnet ? undefined : handleTokenClick(token)}
safeChains={safeChains}
showMerklBadge
Copy link
Contributor

Choose a reason for hiding this comment

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

(non-blocking nit) - if this is always true, maybe we can just kill this prop 😅

Not blocking, maybe we can clean up separately.

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

Labels

release-13.21.0 Issue or pull request that will be included in release 13.21.0 size-XL team-mobile-ux Mobile UX team

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

7 participants