Skip to content

feat(rewards): hardware wallet support#40202

Merged
VGR-GIT merged 6 commits intomainfrom
rwds-feat-hardware-wallet-support
Feb 25, 2026
Merged

feat(rewards): hardware wallet support#40202
VGR-GIT merged 6 commits intomainfrom
rwds-feat-hardware-wallet-support

Conversation

@VGR-GIT
Copy link
Copy Markdown
Contributor

@VGR-GIT VGR-GIT commented Feb 18, 2026

Description

Support for hardware wallets in rewards feature for extension.

Changelog

CHANGELOG entry: support for hardware wallets in rewards feature for extension.

Screenshots

  • Message shown to users when we know their hardware wallet is associated with a subscription but can't authenticate with it unless they explicitly sign a message:
sign-message-to-authenticate-existing-sub
  • Multi subscription balance (hardware wallet = subscription B). Hot wallet was already opted in

multi-subscription

  • Fresh wallet & hardware wallet without existing subscriptions & opt in + link account(s) via swap flow

new-subscription-hardware-wallet-and-link-via-swap

  • Opting in while ledger is not connected/paired (message from existing thrown error)
image image

Note

High Risk
High: introduces new SIWE auth + challenge endpoints and changes rewards authentication/subscription selection logic, which can affect opt-in/linking and access to balances (especially across mixed hardware/software account groups).

Overview
Adds hardware-wallet support to Rewards by introducing a SIWE-based authentication path: the controller can now generateChallenge, request SIWE-aware signPersonalMessage, and call new data-service endpoints siweLogin/siweJoin for opt-in and account linking.

Refines subscription-id handling and caching so hardware accounts don’t persist/use a subscriptionId unless a usable session token exists, skips hardware accounts during silent auth attempts, and extends season discovery/metadata to include previous seasons (with a shorter metadata cache TTL).

Updates the UI to handle the new “hardware wallet needs explicit sign-in” state (error-existing-subscription-hardware-wallet-explicit-sign) by showing a sign-in / signing-in badge, adjusts onboarding to no longer block hardware wallets, and ensures the onboarding modal stays behind QR signing modals via z-index/outside-click tweaks. Locale strings for the removed hardware-wallet onboarding error are deleted and new sign-in strings are added.

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

@VGR-GIT VGR-GIT requested a review from a team as a code owner February 18, 2026 13:24
@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.

@metamaskbot metamaskbot added team-rewards Rewards team INVALID-PR-TEMPLATE PR's body doesn't match template labels Feb 18, 2026
@VGR-GIT VGR-GIT force-pushed the rwds-feat-hardware-wallet-support branch from a440d3a to c69df6f Compare February 23, 2026 09:02
@VGR-GIT
Copy link
Copy Markdown
Contributor Author

VGR-GIT commented Feb 23, 2026

Code review

Found 1 issue:

  1. getHasAccountOptedIn silently changed to a compound condition — hardware wallets that have opted in but not yet completed SIWE authentication (subscriptionId === null) now return false, even though they are opted in. The method name and JSDoc still document only the hasOptedIn check, making this a silent behavioral break for any caller relying on the original semantics to distinguish consent from authentication state.

async getHasAccountOptedIn(account: CaipAccountId): Promise<boolean> {
const rewardsEnabled = this.isRewardsFeatureEnabled();
if (!rewardsEnabled) {
return false;
}
return (
(this.#getAccountState(account)?.hasOptedIn &&
this.#getAccountState(account)?.subscriptionId !== null) ??
false
);

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [2feb193]
⚡ Performance Benchmarks (1355 ± 98 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account2995304304
total2995304304
Confirm Txconfirm_tx60301160366049
total60301160366049
Bridge User Actionsbridge_load_page1862189189
bridge_load_asset_picker1981199199
bridge_search_token6991699700
total1074710791082
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup1355114816519814101550
load114495413858911961311
domContentLoaded113595013448711921290
domInteractive2615102202383
firstPaint192581346198212314
backgroundConnect20418224414212236
firstReactRender17113851925
initialActions103112
loadScripts94976211588610011102
setupStore1272441318
numNetworkReqs312193202289
Chrome Browserify Startup Power User HomeuiStartup19801304615973920003721
load1154992184315911911515
domContentLoaded1133988171914511751440
domInteractive35171642632100
firstPaint243721535183266364
backgroundConnect42026125954073351062
firstReactRender24145882541
initialActions104111
loadScripts92878015051399561217
setupStore1565671725
numNetworkReqs72371582679133
Chrome Webpack Startup Standard HomeuiStartup85368911281008911076
load727614104586780870
domContentLoaded722610103885775863
domInteractive2716111202383
firstPaint1086132451126203
backgroundConnect26195373040
firstReactRender18124362033
initialActions104112
loadScripts719608103684773854
setupStore1164561123
numNetworkReqs312298212589
Chrome Webpack Startup Power User HomeuiStartup1198988203019112691583
load6895991065101675942
domContentLoaded6795951059101666935
domInteractive35171532636104
firstPaint1456170493188297
backgroundConnect17914170575168282
firstReactRender22173842330
initialActions103111
loadScripts677593104899664926
setupStore1043341115
numNetworkReqs1233827450149200
Firefox Browserify Startup Standard HomeuiStartup16461419253621816392108
load13931174225418314021656
domContentLoaded13921173225418314001655
domInteractive943289891123145
firstPaint------
backgroundConnect55291812457100
firstReactRender13112111415
initialActions102112
loadScripts13691144223818213811623
setupStore2271803515127
numNetworkReqs3120101202588
Firefox Browserify Startup Power User HomeuiStartup27382125414835428853359
load15561235262722916372067
domContentLoaded15551229262722916362067
domInteractive16036668131184445
firstPaint------
backgroundConnect3211091151246362847
firstReactRender18146271827
initialActions104122
loadScripts15111203260122215771994
setupStore1328815187113608
numNetworkReqs77282063885146
Firefox Webpack Startup Standard HomeuiStartup17371431322429817512135
load14531211294523314811729
domContentLoaded14531211294523314811729
domInteractive882925649126166
firstPaint------
backgroundConnect59252173067118
firstReactRender16123031624
initialActions103112
loadScripts14301195291523114551707
setupStore366140214115122
numNetworkReqs312091182783
Firefox Webpack Startup Power User HomeuiStartup26842037378436128253448
load15351295237123316431960
domContentLoaded15341295237123316421960
domInteractive13935735143123496
firstPaint------
backgroundConnect3551211348256506859
firstReactRender231599122334
initialActions103122
loadScripts14861273200619216001888
setupStore1686738189194604
numNetworkReqs72292133984145
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2171217218
srpButtonToSrpForm9019090
confirmSrpToPwForm2102121
pwFormToMetricsScreen1501515
metricsToWalletReadyScreen1501516
doneButtonToHomeScreen707141765912
openAccountMenuToAccountListLoaded70609070167215
total840049389069081
Onboarding New WalletcreateWalletToSocialScreen2170217217
srpButtonToPwForm11411125129
createPwToRecoveryScreen8199
skipBackupToMetricsScreen3403434
agreeButtonToOnboardingSuccess1601616
doneButtonToAssetList52673489652
total918778831051
Asset DetailsassetClickToPriceChart49145572
total49145572
Solana Asset DetailsassetClickToPriceChart4614748
total4614748
Import Srp HomeloginToHomeScreen18462518621878
openAccountMenuAfterLogin4024143
homeAfterImportWithNewWallet244724725672869
total422715942134483
Send TransactionsopenSendPageFromHome2693637
selectTokenToSendFormLoaded2983740
reviewTransactionToConfirmationPage8507846863
total9079903922
SwapopenSwapPageFromHome11819123150
fetchAndDisplaySwapQuotes536176662246371
total547976963746468
🌐 Dapp Page Load Benchmarks

Current Commit: 2feb193 | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.04s (±39ms) 🟡 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 722ms (±37ms) 🟢 | historical mean value: 725ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 77ms (±12ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.04s 39ms 1.01s 1.31s 1.06s 1.31s
domContentLoaded 722ms 37ms 703ms 979ms 735ms 979ms
firstPaint 77ms 12ms 60ms 188ms 84ms 188ms
firstContentfulPaint 77ms 12ms 60ms 188ms 84ms 188ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 4.45 KiB (0.1%)
  • ui: 3.82 KiB (0.05%)
  • common: 162 Bytes (0%)

…ontroller

- Remove unused `useMemo` import from useOptIn
- Fix import order: move `isHardwareAccount` before local imports
- Delete extra blank lines (prettier)
- Wrap long lines to satisfy prettier print width
- Fix `no-param-reassign` in rewards-controller by using local variable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add app/scripts/controllers/rewards/rewards-data-service.test.ts to the
unit test console baseline to allow its expected console.error output
(RewardsDataService: Failed to fetch geolocation) during CI runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@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 1 potential issue.

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [1bea1ec]
⚡ Performance Benchmarks (1391 ± 106 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account28932296338
total28932296338
Confirm Txconfirm_tx60644960816149
total60644960816149
Bridge User Actionsbridge_load_page23746256311
bridge_load_asset_picker21543243281
bridge_search_token72114731736
total11735512091256
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup13911151165310614441584
load117396314209512381329
domContentLoaded116695814129312311322
domInteractive2916136242392
firstPaint186681427205210330
backgroundConnect20318425112205228
firstReactRender18114652024
initialActions105113
loadScripts97976912179310411133
setupStore146123121622
numNetworkReqs312291202286
Chrome Browserify Startup Power User HomeuiStartup3141143010753201740387296
load12531076197314312861511
domContentLoaded12361063196614012701488
domInteractive3619185263472
firstPaint227791678198270343
backgroundConnect13132578895176121143859
firstReactRender24164962636
initialActions103112
loadScripts1026863168713410501277
setupStore1564781628
numNetworkReqs844013921101114
Chrome Webpack Startup Standard HomeuiStartup8666891202909301000
load73861492687807882
domContentLoaded73260991987802876
domInteractive2916123232389
firstPaint1166031260129239
backgroundConnect28196593248
firstReactRender18124062131
initialActions105112
loadScripts72960691786799874
setupStore1162731217
numNetworkReqs312297212588
Chrome Webpack Startup Power User HomeuiStartup1276930301029113131839
load7456341398114748983
domContentLoaded7346281383114738977
domInteractive40182273836134
firstPaint1506442283178373
backgroundConnect18113470289173286
firstReactRender22153232427
initialActions102111
loadScripts7316261369112736968
setupStore1344261521
numNetworkReqs1024720134118169
Firefox Browserify Startup Standard HomeuiStartup15921354246220616182041
load13451157223017513771711
domContentLoaded13441153223017513731710
domInteractive65302143787137
firstPaint------
backgroundConnect58263203657118
firstReactRender13113121315
initialActions106112
loadScripts13171135220616713541581
setupStore186168281433
numNetworkReqs311996212692
Firefox Browserify Startup Power User HomeuiStartup307420898456131629937649
load184413207088121516285718
domContentLoaded184313207088121516285718
domInteractive156461752193145364
firstPaint------
backgroundConnect40910513273005241031
firstReactRender19147082028
initialActions213122
loadScripts180112967053121015835677
setupStore14614745180139610
numNetworkReqs85301853699173
Firefox Webpack Startup Standard HomeuiStartup17891416342533917732182
load15101271301130814991686
domContentLoaded15101266301130814991680
domInteractive115301441141133179
firstPaint------
backgroundConnect67262583672128
firstReactRender18128491726
initialActions103122
loadScripts14831249297630614721651
setupStore278161323282
numNetworkReqs302085162775
Firefox Webpack Startup Power User HomeuiStartup28072009789063728723648
load15901201622152715912176
domContentLoaded15901200622152815912175
domInteractive16247791155170582
firstPaint------
backgroundConnect47711515833427611044
firstReactRender22156282332
initialActions217122
loadScripts15481185618552415202006
setupStore1409656169127580
numNetworkReqs80292474088164
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2190219219
srpButtonToSrpForm985102104
confirmSrpToPwForm2212323
pwFormToMetricsScreen1511616
metricsToWalletReadyScreen1611617
doneButtonToHomeScreen72992736852
openAccountMenuToAccountListLoaded732966878738056
total855244789699025
Onboarding New WalletcreateWalletToSocialScreen2191219220
srpButtonToPwForm1052105108
createPwToRecoveryScreen9099
skipBackupToMetricsScreen3713839
agreeButtonToOnboardingSuccess1701717
doneButtonToAssetList60885627727
total9938510161109
Asset DetailsassetClickToPriceChart53166278
total53166278
Solana Asset DetailsassetClickToPriceChart4835052
total4835052
Import Srp HomeloginToHomeScreen2018320222022
openAccountMenuAfterLogin4464754
homeAfterImportWithNewWallet24128224242518
total44781144894489
Send TransactionsopenSendPageFromHome27113843
selectTokenToSendFormLoaded1911921
reviewTransactionToConfirmationPage8534855860
total9038910914
SwapopenSwapPageFromHome1207117132
fetchAndDisplaySwapQuotes530583162536386
total543384063696545
🌐 Dapp Page Load Benchmarks

Current Commit: 1bea1ec | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±42ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 734ms (±39ms) 🟢 | historical mean value: 727ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±11ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 42ms 1.03s 1.34s 1.08s 1.34s
domContentLoaded 734ms 39ms 713ms 1.00s 761ms 1.00s
firstPaint 80ms 11ms 64ms 172ms 88ms 172ms
firstContentfulPaint 80ms 11ms 64ms 172ms 88ms 172ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 4.45 KiB (0.1%)
  • ui: -8.08 KiB (-0.1%)
  • common: 123 Bytes (0%)

Delete extra blank line before closing describe block to satisfy
prettier/prettier formatting rule (Delete ⏎ at line 672:1).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@metamaskbotv2
Copy link
Copy Markdown
Contributor

metamaskbotv2 bot commented Feb 25, 2026

Builds ready [02a3cdc]
⚡ Performance Benchmarks (1394 ± 108 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account2757276284
total2757276284
Confirm Txconfirm_tx60643160896106
total60643160896106
Bridge User Actionsbridge_load_page1958201204
bridge_load_asset_picker1995204204
bridge_search_token6983700703
total10931011001104
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup13941161168710814271589
load117497914299712061355
domContentLoaded116697014239611981347
domInteractive271793172378
firstPaint1496639675198296
backgroundConnect20418926514207238
firstReactRender18123651929
initialActions106114
loadScripts97978512239610131146
setupStore156215211522
numNetworkReqs312297202285
Chrome Browserify Startup Power User HomeuiStartup16971396225115317462007
load11221010159812811081513
domContentLoaded11081003155912311001491
domInteractive3517147253397
firstPaint177701454151222311
backgroundConnect28425835421297325
firstReactRender24156882741
initialActions106113
loadScripts90381013541218891277
setupStore1684061928
numNetworkReqs60391462462128
Chrome Webpack Startup Standard HomeuiStartup87371911561009461054
load740626100198791936
domContentLoaded73462199497786924
domInteractive3016117242498
firstPaint1106339160121208
backgroundConnect302086103444
firstReactRender20133972334
initialActions104112
loadScripts73161999296784920
setupStore1264761419
numNetworkReqs312297202588
Chrome Webpack Startup Power User HomeuiStartup1200857168717412871561
load7066121147109701982
domContentLoaded6976071134108688970
domInteractive34171432731114
firstPaint146631144124176266
backgroundConnect16613035344164267
firstReactRender22183232428
initialActions102111
loadScripts6946051125106686961
setupStore1244771328
numNetworkReqs1093925049139220
Firefox Browserify Startup Standard HomeuiStartup16441377312424416442026
load13901156287722213971718
domContentLoaded13881150287722313951718
domInteractive903592294101153
firstPaint------
backgroundConnect58272562863114
firstReactRender13112111416
initialActions103112
loadScripts13631133284421813711669
setupStore187135211646
numNetworkReqs312097202588
Firefox Browserify Startup Power User HomeuiStartup27282036376638928343529
load15621239241327616452137
domContentLoaded15611239241227616442137
domInteractive12035627112111424
firstPaint------
backgroundConnect3051061179275254924
firstReactRender18136271823
initialActions103122
loadScripts15281214235326716202094
setupStore1528778204173592
numNetworkReqs61331393084119
Firefox Webpack Startup Standard HomeuiStartup16961356314324917201977
load14231132284022114501612
domContentLoaded14231131283922114491612
domInteractive1032929955132199
firstPaint------
backgroundConnect63253104567183
firstReactRender15122531521
initialActions102112
loadScripts13991119281921914171586
setupStore237181321584
numNetworkReqs312094192783
Firefox Webpack Startup Power User HomeuiStartup27031920378647628113627
load15601194245931917332226
domContentLoaded15591194245931917332225
domInteractive13231762161104540
firstPaint------
backgroundConnect2911021033249238896
firstReactRender20153142228
initialActions203122
loadScripts15221178243430416852179
setupStore16381212239148684
numNetworkReqs60311463585140
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2191220220
srpButtonToSrpForm94697104
confirmSrpToPwForm2112122
pwFormToMetricsScreen1501515
metricsToWalletReadyScreen1501515
doneButtonToHomeScreen7491668001039
openAccountMenuToAccountListLoaded734047378317866
total845343788238828
Onboarding New WalletcreateWalletToSocialScreen2171217218
srpButtonToPwForm1042105105
createPwToRecoveryScreen8089
skipBackupToMetricsScreen3413535
agreeButtonToOnboardingSuccess1611617
doneButtonToAssetList654127709815
total103613110841209
Asset DetailsassetClickToPriceChart61208385
total61208385
Solana Asset DetailsassetClickToPriceChart5475766
total5475766
Import Srp HomeloginToHomeScreen19488120282048
openAccountMenuAfterLogin4024143
homeAfterImportWithNewWallet228815324012460
total428020944674473
Send TransactionsopenSendPageFromHome27103839
selectTokenToSendFormLoaded1911920
reviewTransactionToConfirmationPage8594861863
total9174918922
SwapopenSwapPageFromHome1323135135
fetchAndDisplaySwapQuotes564884063756383
total577683464966504
🌐 Dapp Page Load Benchmarks

Current Commit: 02a3cdc | Date: 2/25/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 971ms (±35ms) 🟢 | historical mean value: 1.04s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 673ms (±33ms) 🟢 | historical mean value: 725ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 74ms (±8ms) 🟢 | historical mean value: 77ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 971ms 35ms 950ms 1.23s 986ms 1.23s
domContentLoaded 673ms 33ms 656ms 922ms 681ms 922ms
firstPaint 74ms 8ms 60ms 140ms 84ms 140ms
firstContentfulPaint 74ms 8ms 60ms 140ms 84ms 140ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -258.31 KiB (-5.95%)
  • ui: 1.18 KiB (0.01%)
  • common: 189.65 KiB (1.7%)

@VGR-GIT VGR-GIT enabled auto-merge February 25, 2026 12:13
@VGR-GIT VGR-GIT added this pull request to the merge queue Feb 25, 2026
Merged via the queue into main with commit 4a97299 Feb 25, 2026
178 of 179 checks passed
@VGR-GIT VGR-GIT deleted the rwds-feat-hardware-wallet-support branch February 25, 2026 12:50
@github-actions github-actions bot locked and limited conversation to collaborators Feb 25, 2026
@metamaskbot metamaskbot added the release-13.21.0 Issue or pull request that will be included in release 13.21.0 label Feb 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

INVALID-PR-TEMPLATE PR's body doesn't match template release-13.21.0 Issue or pull request that will be included in release 13.21.0 size-XL team-rewards Rewards team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants