Skip to content

Conversation

@OGPoyraz
Copy link
Member

@OGPoyraz OGPoyraz commented Dec 8, 2025

Description

This PR implements the first step of https://github.com/MetaMask/MetaMask-planning/issues/6067

Note that new gas modal UI is not enabled yet until very last step. Hence no changelog included.

The recording is manually enabled on local.

Open in GitHub Codespaces

Changelog

CHANGELOG entry:

Related issues

Fixes: Step I of https://github.com/MetaMask/MetaMask-planning/issues/6067

Manual testing steps

N/A

Screenshots/Recordings

Before

After

Step.I.gas.modals.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

Adds a new gas fee modal stack (Estimates, Advanced EIP-1559/Legacy) with option hooks, UI list items, time utils, and a calculateGasEstimate API, plus i18n and tests.

  • Gas Modals (UI):
    • New GasFeeModal orchestrator with EstimatesModal, AdvancedEIP1559Modal, and AdvancedGasPriceModal.
    • NetworkStatistics updated to support a redesigned header via useRedesigned.
  • Gas Option Hooks:
    • Add useGasOptions to compose options from:
      • useGasFeeEstimateLevelOptions (low/medium/high; EIP-1559/legacy aware)
      • useGasPriceEstimateOption (network suggested gas price)
      • useDappSuggestedGasFeeOption (site-suggested)
      • useAdvancedGasFeeOption (opens advanced modals)
  • Fee Calculations:
    • useFeeCalculations now exposes calculateGasEstimate for computing totals (incl. optional L1 fees) and precise native amounts.
  • UI Components:
    • New GasEstimateListHeader and GasEstimateListItem with optional tooltip details.
  • Utils & Hooks:
    • Add time helpers toHumanEstimatedTimeRange/toHumanSeconds.
    • Add useTransactionNativeTicker to resolve native ticker by chain.
  • i18n:
    • Add string networkSuggested to en and en_GB locales.
  • Tests:
    • Comprehensive tests for new modals, hooks, list items, time utils, and useFeeCalculations updates.

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

@metamaskbot metamaskbot added the team-confirmations Push issues to confirmations team label Dec 8, 2025
@metamaskbot
Copy link
Collaborator

metamaskbot commented Dec 8, 2025

✨ Files requiring CODEOWNER review ✨

@MetaMask/confirmations (28 files, +2179 -11)
  • 📁 ui/
    • 📁 pages/
      • 📁 confirmations/
        • 📁 components/
          • 📁 confirm/
            • 📁 info/
              • 📁 hooks/
                • 📄 useFeeCalculations.test.ts +3 -0
                • 📄 useFeeCalculations.ts +58 -0
          • 📁 edit-gas-fee-popover/
            • 📁 network-statistics/
              • 📄 network-statistics.js +27 -11
          • 📁 gas-estimate-list-item/
            • 📄 gas-estimate-list-item.test.tsx +94 -0
            • 📄 gas-estimate-list-item.tsx +201 -0
          • 📁 modals/
            • 📁 advanced-eip1559-modal/
              • 📄 advanced-eip1559-modal.test.tsx +23 -0
              • 📄 advanced-eip1559-modal.tsx +26 -0
            • 📁 advanced-gas-price-modal/
              • 📄 advanced-gas-price-modal.test.tsx +23 -0
              • 📄 advanced-gas-price-modal.tsx +26 -0
            • 📁 estimates-modal/
              • 📄 estimates-modal.test.tsx +90 -0
              • 📄 estimates-modal.tsx +85 -0
            • 📁 gas-fee-modal/
              • 📄 gas-fee-modal.test.tsx +55 -0
              • 📄 gas-fee-modal.tsx +44 -0
        • 📁 constants/
          • 📄 gas.ts +16 -0
        • 📁 hooks/
          • 📁 gas/
            • 📄 useAdvancedGasFeeOption.test.ts +147 -0
            • 📄 useAdvancedGasFeeOption.ts +160 -0
            • 📄 useDappSuggestedGasFeeOption.test.ts +115 -0
            • 📄 useDappSuggestedGasFeeOption.ts +104 -0
            • 📄 useGasFeeEstimateLevelOptions.test.ts +137 -0
            • 📄 useGasFeeEstimateLevelOptions.ts +135 -0
            • 📄 useGasOptions.test.ts +185 -0
            • 📄 useGasOptions.ts +41 -0
            • 📄 useGasPriceEstimateOption.test.ts +116 -0
            • 📄 useGasPriceEstimateOption.ts +155 -0
          • 📁 transactions/
            • 📄 useTransactionNativeTicker.test.ts +44 -0
            • 📄 useTransactionNativeTicker.ts +13 -0
        • 📁 types/
          • 📄 gas.ts +19 -0
        • 📁 utils/
          • 📄 time.test.ts +37 -0

@metamaskbot
Copy link
Collaborator

Builds ready [dbbcef8]
UI Startup Metrics (1260 ± 98 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1260103715749813111466
load104488512808310801231
domContentLoaded103888212638110751221
domInteractive22154672238
firstPaint51879127840310291144
backgroundConnect21520127711221240
firstReactRender463083145677
getState3816124204186
initialActions1013113
loadScripts829679106677864994
setupStore1272741421
numNetworkReqs86337629
BrowserifyPower User HomeuiStartup18941642233912919502154
load1097902149210311721254
domContentLoaded1084897148210211621237
domInteractive3017243282758
firstPaint55897148741910421222
backgroundConnect24319865675239477
firstReactRender44367154654
getState19914830131220257
initialActions103112
loadScripts87269412671029611030
setupStore161096101534
numNetworkReqs69582081867102
WebpackStandard HomeuiStartup830714105864859958
load64356185265676813
domContentLoaded63755584465669806
domInteractive22154882243
firstPaint25675813194220688
backgroundConnect15585131732
firstReactRender54331473058128
getState24135293143
initialActions105112
loadScripts63555384265667804
setupStore1073251218
numNetworkReqs86306629
WebpackPower User HomeuiStartup14711184186214915771733
load73459694686795895
domContentLoaded72658993585786886
domInteractive271783102747
firstPaint265102876202227768
backgroundConnect53662212028524
firstReactRender47396754959
getState19214331538225268
initialActions103011
loadScripts72358792684783883
setupStore1573871436
numNetworkReqs6757108106799
FirefoxBrowserifyStandard HomeuiStartup12591064166512313481526
load103590514048010611156
domContentLoaded103490414038010611156
domInteractive5431258326798
firstPaint------
backgroundConnect40201202445105
firstReactRender35297883657
getState126150181023
initialActions102112
loadScripts101288813807510371134
setupStore1055461017
numNetworkReqs86255725
BrowserifyPower User HomeuiStartup25121537396662630443250
load1576943237752720902302
domContentLoaded1576942237752720902302
domInteractive11031109721575980
firstPaint------
backgroundConnect142181060196173370
firstReactRender5533130165992
getState1467729844172251
initialActions209123
loadScripts1499914231249419922204
setupStore654106916336318
numNetworkReqs67481141469106
WebpackStandard HomeuiStartup15631331196913716301875
load1258108315518313211408
domContentLoaded1257108215518313211407
domInteractive62282333780133
firstPaint------
backgroundConnect50201743350131
firstReactRender43356264656
getState167161191556
initialActions103122
loadScripts1232106615228012891377
setupStore166205241246
numNetworkReqs86275726
WebpackPower User HomeuiStartup28781790446956532583602
load18691098361755023752588
domContentLoaded18681098361054923742587
domInteractive174301178306991044
firstPaint------
backgroundConnect1792412462462041005
firstReactRender5838137156387
getState153831158115180269
initialActions207123
loadScripts17541084266349222522466
setupStore804112021439334
numNetworkReqs71521231572112
📊 Page Load Benchmark Results

Current Commit: dbbcef8 | Date: 12/8/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±37ms) 🟡 | historical mean value: 1.03s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 733ms (±35ms) 🟢 | historical mean value: 718ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 79ms (±11ms) 🟢 | historical mean value: 76ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 37ms 1.02s 1.33s 1.07s 1.33s
domContentLoaded 733ms 35ms 709ms 996ms 752ms 996ms
firstPaint 79ms 11ms 60ms 176ms 88ms 176ms
firstContentfulPaint 79ms 11ms 60ms 176ms 88ms 176ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 874 Bytes (0.01%)
  • common: 20 Bytes (0%)

@OGPoyraz OGPoyraz marked this pull request as ready for review December 8, 2025 13:10
@OGPoyraz OGPoyraz requested a review from a team as a code owner December 8, 2025 13:10
@OGPoyraz OGPoyraz changed the title [WIP] feat: Gas modals refactor I feat: Gas modals refactor I Dec 8, 2025
@OGPoyraz OGPoyraz added the no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed label Dec 8, 2025
// Note: feePerGas and priorityFeePerGas are hex strings from txParams/gasFeeEstimates
let minimumFeePerGas = addHexes(
decGWEIToHexWEI(estimatedBaseFee) || HEX_ZERO,
feePerGas ? (priorityFeePerGas as Hex) : HEX_ZERO,
Copy link

Choose a reason for hiding this comment

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

Bug: Wrong variable checked in gas fee conditional logic

The calculateGasEstimateCallback function checks feePerGas to decide whether to add priorityFeePerGas to the calculation, but this is the wrong variable to check. The condition feePerGas ? (priorityFeePerGas as Hex) : HEX_ZERO should check priorityFeePerGas directly since that's the value being conditionally added. While current callers happen to set both values together (either both valid or both HEX_ZERO), this logic is confusing and could cause incorrect gas estimates if a caller ever passes a valid priorityFeePerGas with an empty/undefined feePerGas.

Fix in Cursor Fix in Web

priorityFeePerGas = dappSuggestedGasFees?.maxPriorityFeePerGas;
} else if (dappSuggestedGasFees?.gasPrice) {
gasPrice = dappSuggestedGasFees?.gasPrice;
gas = dappSuggestedGasFees?.gas || HEX_ZERO;
Copy link

Choose a reason for hiding this comment

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

Bug: Legacy gas option loses gas limit fallback

When a dapp suggests legacy gas pricing, the code sets gas = dappSuggestedGasFees?.gas || HEX_ZERO, falling back to HEX_ZERO if the dapp doesn't provide a gas value. This loses the transactionMeta.gasLimitNoBuffer fallback that was set on line 57. For EIP-1559 suggestions, gasLimitNoBuffer is used, but for legacy suggestions without a gas value, the estimate would incorrectly use zero gas, resulting in a zero fee estimate rather than using the transaction's actual gas limit.

Fix in Cursor Fix in Web

@metamaskbot
Copy link
Collaborator

Builds ready [e3526c8]
UI Startup Metrics (1210 ± 89 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1210102714418912751353
load99584412037310391104
domContentLoaded98883911897210331096
domInteractive21155582041
firstPaint61496123039910171114
backgroundConnect20217824812209225
firstReactRender462889145879
getState3315131183865
initialActions104112
loadScripts79365399669842898
setupStore1172831118
numNetworkReqs86307629
BrowserifyPower User HomeuiStartup18851626243913719592129
load107788513189811671235
domContentLoaded106688013109711581225
domInteractive271792112850
firstPaint5109312204019831198
backgroundConnect24319857576239524
firstReactRender443610174554
getState19114128431212255
initialActions102112
loadScripts8546781098979551014
setupStore17104181736
numNetworkReqs69591131267108
WebpackStandard HomeuiStartup83171098252853932
load63357175549672723
domContentLoaded62856675149666720
domInteractive22154162239
firstPaint19178703125196644
backgroundConnect1153171429
firstReactRender65342504588160
getState2614120133443
initialActions106112
loadScripts62556574949663718
setupStore1052731316
numNetworkReqs86306623
WebpackPower User HomeuiStartup14771176231717115931788
load72859799088773912
domContentLoaded72158596986764903
domInteractive271778102750
firstPaint24396761151238691
backgroundConnect62663211461223
firstReactRender47406554958
getState19314231142202305
initialActions104112
loadScripts71858395886756901
setupStore1564071436
numNetworkReqs69582031767100
FirefoxBrowserifyStandard HomeuiStartup13271131171911714001526
load107996114477711251196
domContentLoaded107996114477811251195
domInteractive60331563083129
firstPaint------
backgroundConnect4123118194975
firstReactRender38317363952
getState137100151026
initialActions103122
loadScripts105494214257310921161
setupStore1175871125
numNetworkReqs86275723
BrowserifyPower User HomeuiStartup25481594410154329643166
load1571987236647620032274
domContentLoaded1570987236547620032274
domInteractive12535971208100843
firstPaint------
backgroundConnect171251184244171975
firstReactRender523899115681
getState1317425042150220
initialActions3136628
loadScripts1467958231743318922126
setupStore1125109823359917
numNetworkReqs71571261768119
WebpackStandard HomeuiStartup15261328219514715811898
load1236107016519212751391
domContentLoaded1235107016519212741391
domInteractive62272003581134
firstPaint------
backgroundConnect46211542645110
firstReactRender433391104557
getState157161191527
initialActions103112
loadScripts1212105316189012481373
setupStore165159241263
numNetworkReqs86255824
WebpackPower User HomeuiStartup28431878402154932163550
load18751170261850823062518
domContentLoaded18741170261750823062518
domInteractive12030109023182965
firstPaint------
backgroundConnect156271155200181382
firstReactRender60381291862107
getState147761047103177259
initialActions204123
loadScripts17921152258847422222464
setupStore584107613432318
numNetworkReqs72591161472105
📊 Page Load Benchmark Results

Current Commit: e3526c8 | Date: 12/8/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.03s (±40ms) 🟡 | historical mean value: 1.03s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 715ms (±37ms) 🟢 | historical mean value: 718ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±13ms) 🟢 | historical mean value: 76ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.03s 40ms 1.01s 1.33s 1.06s 1.33s
domContentLoaded 715ms 37ms 697ms 1.00s 744ms 1.00s
firstPaint 76ms 13ms 60ms 200ms 84ms 200ms
firstContentfulPaint 76ms 13ms 60ms 200ms 84ms 200ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 814 Bytes (0.01%)
  • common: 67 Bytes (0%)

import { useSelector } from 'react-redux';
import { TransactionMeta } from '@metamask/transaction-controller';
import { useConfirmContext } from '../../context/confirm';
import { getNetworkConfigurationsByChainId } from '../../../../../shared/modules/selectors/networks';
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: sort imports

} else {
gasPropertiesToUpdate = {
maxFeePerGas: transactionGasFeeEstimates?.gasPrice,
maxPriorityFeePerGas: transactionGasFeeEstimates?.gasPrice,
Copy link
Contributor

Choose a reason for hiding this comment

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

gasPrice only is assigned above to maxFeePerGas and maxPriorityFeePerGas

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes because estimation is done via gasPrice meaning only maxFeePerGas and maxPriorityFeePerGas should be defined if EIP1559

isSelected: isGasPriceEstimateSelected,
key: 'gasPrice',
name: t('networkSuggested'),
onSelect: () => onGasPriceEstimateLevelClick(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Creating new fn here does not look like is needed () => onGasPriceEstimateLevelClick(), instead we can just use onGasPriceEstimateLevelClick.

</ModalContent>
</Modal>
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

This modal does not seems to have any content.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I will implement details in step II as mentioned here

@OGPoyraz OGPoyraz enabled auto-merge December 9, 2025 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog no-changelog Indicates no external facing user changes, therefore no changelog documentation needed size-XL team-confirmations Push issues to confirmations team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants