Skip to content

feat: increase swap submission friction when price impact is high#40586

Merged
micaelae merged 32 commits intomainfrom
swaps4023-price-impact
Mar 6, 2026
Merged

feat: increase swap submission friction when price impact is high#40586
micaelae merged 32 commits intomainfrom
swaps4023-price-impact

Conversation

@micaelae
Copy link
Member

@micaelae micaelae commented Mar 3, 2026

Description

Changes

  • Show a price impact error modal before submitting a high-PI transaction to make sure the user agrees with the quoted values
  • Always show the price impact and indicate warning and error states
  • Show 0% when price impact is negative

Open in GitHub Codespaces

Changelog

CHANGELOG entry: feat: increase swap submission friction when price impact is high

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-4023

Manual testing steps

See acceptance criteria: https://docs.google.com/document/d/1bCKM-dJgJOYBfzPYdcsjDncvr0fL01IWbaWJGzRv8Is/edit?tab=t.0

Screenshots/Recordings

Before

After

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
Updates bridge transaction submission flow (including hardware-wallet routing) to add a new high price-impact confirmation step, which could affect when/if transactions are sent. Risk is mitigated by added selectors and extensive unit/snapshot test coverage, but mistakes could still block valid swaps or allow unintended submissions.

Overview
Bridge now computes and surfaces price impact as a first-class validation signal. New selectors parse quote.priceData.priceImpact, format it for display, and derive isPriceImpactWarning/isPriceImpactError using feature-flag thresholds (with defaults for warning/error). Negative/invalid price impact is now treated as 0%.

High price impact submissions add friction via a new modal. BridgeCTAButton opens BridgePriceImpactWarningModal instead of submitting when isPriceImpactError, and the quote card always shows a Price impact row with warning/error styling plus a details button that opens the modal.

Submission logic is centralized and hardened. useSubmitBridgeTransaction now owns isSubmitting, performs hardware-wallet ensureDeviceReady() checks, routes hardware wallets to the awaiting-signatures page, and prevents duplicate submits; tests and mocks were updated accordingly.

Localization and tests updated. Adds new bridgePriceImpact* strings (and proceed) in en/en_GB, removes the old bridgePriceImpactNormalWarning string from many locales, updates mock quote/store data to include priceData.priceImpact and priceImpactThreshold, and adds/updates unit and snapshot tests (including baseline adjustments).

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

@metamaskbot metamaskbot added the team-swaps-and-bridge Swaps and Bridge team label Mar 3, 2026
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 3, 2026

✨ Files requiring CODEOWNER review ✨

🔄 @MetaMask/swaps-engineers (11 files, +1015 -113)
  • 📁 test/
    • 📁 data/
      • 📁 bridge/
        • 📄 mock-bridge-store.ts +6 -0
        • 📄 mock-quotes-native-erc20.json +6 -0
  • 📁 ui/
    • 📁 ducks/
      • 📁 bridge/
        • 📄 selectors.test.ts +72 -0
        • 📄 selectors.ts +45 -1
    • 📁 pages/
      • 📁 bridge/
        • 📁 hooks/
          • 📁 __snapshots__/
            • 📄 useSubmitBridgeTransaction.test.tsx.snap +2 -2
            • 📄 useSubmitBridgeTransaction.test.tsx +122 -23
            • 📄 useSubmitBridgeTransaction.ts +47 -46
        • 📁 prepare/
          • 📁 __snapshots__/
            • 📄 bridge-price-impact-modal.test.tsx.snap +563 -0
            • 📄 bridge-cta-button.test.tsx +137 -10
            • 📄 bridge-cta-button.tsx +11 -30
          • 📄 index.test.tsx +4 -1

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 3, 2026

Builds ready [6bf0778]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account2792692908278290
total2792692908278290
Confirm Txconfirm_tx6059601360932860806093
total6059601360932860806093
Bridge User Actionsbridge_load_page24321625917257259
bridge_load_asset_picker24718333151265331
bridge_search_token73371374814744748
total1200113612564412211256
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14881239193112015341710
load12601045159710513131421
domContentLoaded12521038158410313081415
domInteractive3017123222787
firstPaint209701390220247394
backgroundConnect21919627517225254
firstReactRender19124872035
initialActions108225
loadScripts1052833137210311071209
setupStore157107111728
numNetworkReqs312294202287
Power User HomeuiStartup247214289976148123124922
load12121051186516212201652
domContentLoaded11921042171314812031574
domInteractive40192043236121
firstPaint2098647595276397
backgroundConnect69725839938594762789
firstReactRender24165472635
initialActions106112
loadScripts978825143814010011345
setupStore1554471632
numNetworkReqs67331392482115
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2172172180218218
srpButtonToSrpForm91899319293
confirmSrpToPwForm21212212222
pwFormToMetricsScreen14141501415
metricsToWalletReadyScreen16151601516
doneButtonToHomeScreen5935846089590608
openAccountMenuToAccountListLoaded2940291129722129482972
total3924386239994739483999
Onboarding New WalletcreateWalletToSocialScreen2182162201219220
srpButtonToPwForm1081031165113116
createPwToRecoveryScreen888088
skipBackupToMetricsScreen36353603636
agreeButtonToOnboardingSuccess16151601616
doneButtonToAssetList608474740109698740
total996856114611510791146
Asset DetailsassetClickToPriceChart37353923839
total37353923839
Solana Asset DetailsassetClickToPriceChart71677537375
total71677537375
Import Srp HomeloginToHomeScreen1998195020684520062068
openAccountMenuAfterLogin50426495864
homeAfterImportWithNewWallet27092580287611227752876
total4837480948682448684868
Send TransactionsopenSendPageFromHome17171801818
selectTokenToSendFormLoaded18152021920
reviewTransactionToConfirmationPage958844116512810511165
total994881120012810841200
SwapopenSwapPageFromHome1201181222121122
fetchAndDisplaySwapQuotes288528822890328862890
total300430023006230063006
🌐 Dapp Page Load Benchmarks

Current Commit: 6bf0778 | Date: 3/3/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±41ms) 🟡 | historical mean value: 1.06s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 740ms (±38ms) 🟢 | historical mean value: 742ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 81ms (±12ms) 🟢 | historical mean value: 83ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 41ms 1.02s 1.35s 1.08s 1.35s
domContentLoaded 740ms 38ms 716ms 1.02s 772ms 1.02s
firstPaint 81ms 12ms 64ms 192ms 96ms 192ms
firstContentfulPaint 81ms 12ms 64ms 192ms 96ms 192ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: -25 Bytes (0%)
  • common: 20 Bytes (0%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 3, 2026

Builds ready [369df73]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account30727233723324337
total30727233723324337
Confirm Txconfirm_tx6065605360821160656082
total6065605360821160656082
Bridge User Actionsbridge_load_page2432372474245247
bridge_load_asset_picker23117030445248304
bridge_search_token72870474414739744
total1196111512604912181260
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14201185179810914771620
load1191974145710012441388
domContentLoaded118297014529812401378
domInteractive3017102192682
firstPaint193681284201216346
backgroundConnect20518025514208234
firstReactRender19114452131
initialActions206124
loadScripts99778912409610571193
setupStore1374151622
numNetworkReqs312198202287
Power User HomeuiStartup16761289220616517371993
load11471016167213911371517
domContentLoaded11321008165713511221510
domInteractive3219106143453
firstPaint1667341379215329
backgroundConnect29326134919301328
firstReactRender23154272439
initialActions107113
loadScripts92080714211329111254
setupStore1574771725
numNetworkReqs56361552455110
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2212202221222222
srpButtonToSrpForm9796100198100
confirmSrpToPwForm22222302323
pwFormToMetricsScreen16151701617
metricsToWalletReadyScreen16151711717
doneButtonToHomeScreen61960964515613645
openAccountMenuToAccountListLoaded291629142917129172917
total3903387539312039053931
Onboarding New WalletcreateWalletToSocialScreen2202182221219222
srpButtonToPwForm11510713411119134
createPwToRecoveryScreen989099
skipBackupToMetricsScreen36353713637
agreeButtonToOnboardingSuccess16161701617
doneButtonToAssetList61460363313633633
total100398910251610251025
Asset DetailsassetClickToPriceChart573886186686
total573886186686
Solana Asset DetailsassetClickToPriceChart78668888288
total78668888288
Import Srp HomeloginToHomeScreen20511897235317621502353
openAccountMenuAfterLogin513364116064
homeAfterImportWithNewWallet28002571299316429612993
total49024571540630049955406
Send TransactionsopenSendPageFromHome27243132831
selectTokenToSendFormLoaded19162121921
reviewTransactionToConfirmationPage8468468460846846
total8918898963890896
SwapopenSwapPageFromHome104991073106107
fetchAndDisplaySwapQuotes289428902899428982899
total299729913006629963006
🌐 Dapp Page Load Benchmarks

Current Commit: 369df73 | Date: 3/3/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±45ms) 🟡 | historical mean value: 1.06s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 740ms (±37ms) 🟢 | historical mean value: 742ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 82ms (±11ms) 🟢 | historical mean value: 83ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 45ms 1.02s 1.34s 1.10s 1.34s
domContentLoaded 740ms 37ms 711ms 1.01s 759ms 1.01s
firstPaint 82ms 11ms 72ms 172ms 92ms 172ms
firstContentfulPaint 82ms 11ms 72ms 172ms 92ms 172ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: 162 Bytes (0%)
  • common: 156 Bytes (0%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 4, 2026

Builds ready [3312657]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account416268714178529714
total416268714178529714
Confirm Txconfirm_tx607460616082960826082
total607460616082960826082
Bridge User Actionsbridge_load_page26825029919270299
bridge_load_asset_picker18412423747233237
bridge_search_token72270475719728757
total1162109312647612441264
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14631249189510515161639
load1226100615889812731402
domContentLoaded1220100015829812671395
domInteractive3018113212592
firstPaint183751368143227316
backgroundConnect21819926815225251
firstReactRender21125272135
initialActions2014226
loadScripts101881513849610701182
setupStore1463961726
numNetworkReqs312292202283
Power User HomeuiStartup2587142410044137936974162
load12441090177714112781570
domContentLoaded12251074176014012511545
domInteractive3520173233477
firstPaint214891271142272374
backgroundConnect9082578177119613352566
firstReactRender23164662437
initialActions1010113
loadScripts1009869151713410351338
setupStore1674171833
numNetworkReqs79431542487123
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2192192201219220
srpButtonToSrpForm92929309393
confirmSrpToPwForm21212202122
pwFormToMetricsScreen15151501515
metricsToWalletReadyScreen16151711617
doneButtonToHomeScreen64159870640660706
openAccountMenuToAccountListLoaded293229272937429362937
total3914389139372039303937
Onboarding New WalletcreateWalletToSocialScreen2222172285227228
srpButtonToPwForm1061051081107108
createPwToRecoveryScreen888088
skipBackupToMetricsScreen36353613636
agreeButtonToOnboardingSuccess16151601616
doneButtonToAssetList613493886154682886
total1001879127015110651270
Asset DetailsassetClickToPriceChart46434924649
total46434924649
Solana Asset DetailsassetClickToPriceChart946713430127134
total946713430127134
Import Srp HomeloginToHomeScreen203820312051920512051
openAccountMenuAfterLogin61526766667
homeAfterImportWithNewWallet30112818331719131423317
total49974830524115249905241
Send TransactionsopenSendPageFromHome22182732427
selectTokenToSendFormLoaded24192842828
reviewTransactionToConfirmationPage8508458637847863
total89688591511893915
SwapopenSwapPageFromHome33313523535
fetchAndDisplaySwapQuotes2904289429151029132915
total2934292429471029472947
🌐 Dapp Page Load Benchmarks

Current Commit: 3312657 | Date: 3/4/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.02s (±70ms) 🟡 | historical mean value: 1.05s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 716ms (±85ms) 🟢 | historical mean value: 740ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 96ms (±148ms) 🟢 | historical mean value: 82ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.02s 70ms 976ms 1.60s 1.08s 1.60s
domContentLoaded 716ms 85ms 681ms 1.50s 749ms 1.50s
firstPaint 96ms 148ms 68ms 1.56s 96ms 1.56s
firstContentfulPaint 96ms 148ms 68ms 1.56s 96ms 1.56s
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: 162 Bytes (0%)
  • common: 156 Bytes (0%)

Comment on lines +91 to +93
try {
if (isHardwareWalletAccount) {
const isDeviceReady = await ensureDeviceReady();
Copy link
Member Author

Choose a reason for hiding this comment

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

This try-catch block was moved over from bridge-cta-button so that the same submission flow can be used by the price impact modal Proceed button

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

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

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 6, 2026

Builds ready [b651eb7]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account28827531013285310
total28827531013285310
Confirm Txconfirm_tx607260666076460766076
total607260666076460766076
Bridge User Actionsbridge_load_page2402372443244244
bridge_load_asset_picker17911123846215238
bridge_search_token71770574616724746
total1137108912065311981206
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14451211194712215021631
load12221024162611312781402
domContentLoaded12141018161711212721394
domInteractive3018114212690
firstPaint1547143580209301
backgroundConnect21719529716222248
firstReactRender20134462136
initialActions109124
loadScripts1015811142211210731186
setupStore1374061525
numNetworkReqs322290192583
Power User HomeuiStartup19641657259815220262228
load11611035179514111551525
domContentLoaded11451023172813411411485
domInteractive3519176253564
firstPaint1747544677225289
backgroundConnect30126141428314355
firstReactRender23164362437
initialActions107112
loadScripts92981914901319281271
setupStore1665571728
numNetworkReqs68351784355169
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2222202241223224
srpButtonToSrpForm101981063102106
confirmSrpToPwForm25242602526
pwFormToMetricsScreen18162011820
metricsToWalletReadyScreen19182011920
doneButtonToHomeScreen63460966822638668
openAccountMenuToAccountListLoaded292329152934829282934
total3939390939632039493963
Onboarding New WalletcreateWalletToSocialScreen2192172212220221
srpButtonToPwForm1071051081108108
createPwToRecoveryScreen888088
skipBackupToMetricsScreen37353923839
agreeButtonToOnboardingSuccess16151701617
doneButtonToAssetList64858377373688773
total103596611647510761164
Asset DetailsassetClickToPriceChart13010515019149150
total13010515019149150
Solana Asset DetailsassetClickToPriceChart73707527475
total73707527475
Import Srp HomeloginToHomeScreen2007186821239320982123
openAccountMenuAfterLogin42384534545
homeAfterImportWithNewWallet2419238924452324452445
total44924320481017945564810
Send TransactionsopenSendPageFromHome21182632226
selectTokenToSendFormLoaded18171911919
reviewTransactionToConfirmationPage960846112512010851125
total1013885123314011221233
SwapopenSwapPageFromHome39285184251
fetchAndDisplaySwapQuotes2700268427311927132731
total2749271527983227732798
🌐 Dapp Page Load Benchmarks

Current Commit: b651eb7 | Date: 3/6/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±41ms) 🟡 | historical mean value: 1.05s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 735ms (±38ms) 🟢 | historical mean value: 734ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±12ms) 🟢 | historical mean value: 83ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 41ms 1.02s 1.36s 1.07s 1.36s
domContentLoaded 735ms 38ms 713ms 1.02s 758ms 1.02s
firstPaint 80ms 12ms 64ms 184ms 88ms 184ms
firstContentfulPaint 80ms 12ms 64ms 184ms 88ms 184ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 6.03 KiB (0.07%)
  • common: 581 Bytes (0.01%)

@micaelae micaelae added this pull request to the merge queue Mar 6, 2026
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 6, 2026

Merged via the queue into main with commit 2c650eb Mar 6, 2026
498 of 506 checks passed
@micaelae micaelae deleted the swaps4023-price-impact branch March 6, 2026 21:20
@github-actions github-actions bot locked and limited conversation to collaborators Mar 6, 2026
@metamaskbot metamaskbot added the release-13.23.0 Issue or pull request that will be included in release 13.23.0 label Mar 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-13.23.0 Issue or pull request that will be included in release 13.23.0 size-XL team-swaps-and-bridge Swaps and Bridge team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants