Skip to content

fix(perps): [Bug]: Perps - Available to trade percentage shows truncated decimal on initial load (e.g. "22...") - #45561

Open
abretonc7s wants to merge 1 commit into
mainfrom
TAT-3763-fix-fix-perps-percentage-truncatio
Open

fix(perps): [Bug]: Perps - Available to trade percentage shows truncated decimal on initial load (e.g. "22...")#45561
abretonc7s wants to merge 1 commit into
mainfrom
TAT-3763-fix-fix-perps-percentage-truncatio

Conversation

@abretonc7s

@abretonc7s abretonc7s commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Description

The Perps order form seeded its initial balancePercent rounded to two decimals, so on initial load the percentage pill next to the size slider rendered a value like 0.44. That text overflows the pill's fixed 4.5rem field (measured live: scrollWidth 50px vs clientWidth 45px), so the browser clipped it — the reported 22.... Every other writer of balancePercent (slider step={1}, size input, percent field, leverage) already rounds to a whole number, which is why the value corrected itself as soon as the user touched the slider.

Rounding the seed to a whole percent at ui/hooks/perps/usePerpsOrderForm.ts makes initial load agree with the slider's step grid and with every user-driven write path, and keeps the single source of truth consistent for all three consumers: the pill, the slider thumb, and the ORDER_SIZE_PERCENT analytics property.

Mobile carries the identical expression but never renders balancePercent, so the visible pill is Extension-only — this fixes the Extension renderer contract without diverging from mobile's internal state. It is a percentage rather than a fiat value, so no toFixed or currency formatter was introduced.

Changelog

CHANGELOG entry: Fixed the Perps available-to-trade percentage showing a truncated decimal on initial load

Related issues

Fixes: TAT-3763

Manual testing steps

  1. Open the extension with an account that has a non-zero Perps tradeable balance.
  2. Go to Perps and open the order form for a market (#/perps/trade/ETH).
  3. On initial load, check the percentage pill to the right of the size slider — it shows a whole number (e.g. 0 %), fully visible and not clipped.
  4. Click the slider and press ArrowRight a few times — the pill still shows a whole number matching the slider position, and the size/margin rows update accordingly.

Screenshots/Recordings

Post-rebase re-validation: the Perps available-to-trade percentage pill renders a clean whole number on initial load.

Perps order form, initial load — available-to-trade percentage pill (post-rebase re-validation)
Perps order form, initial load — available-to-trade percentage pill (post-rebase re-validation)

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.

Validation Recipe

Runs against the live extension over CDP. Both AC assertion groups fail if the fix is reverted; a sibling baseline recipe asserting the buggy values passes on the unfixed tree, which is what makes the before-evidence captures honest.

recipe.json
{
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
  "title": "TAT-3763 — Perps available-to-trade percentage is a clean rounded number on initial load",
  "description": "Opens the live Perps order-entry form and proves the balance-percent pill renders a whole, unclipped percentage on initial load (AC1), then drives the slider through trusted UI input to prove the already-working interaction path still renders a whole, unclipped percentage (AC2). Data prerequisites, enforced by the gate-cdp and setup-unlock nodes: the Extension runtime must be reachable over CDP on the configured port, and the fixture wallet's active account must have a non-zero Perps tradeable balance — a zero balance makes the default balancePercent 0 and leaves AC1 unobservable.",
  "workflow": {
    "entry": "setup-status",
    "nodes": {
      "setup-status": {
        "action": "app.status",
        "next": "gate-cdp",
        "intent": "Resolve the Extension checkout and adapter status before proving the fix"
      },
      "gate-cdp": {
        "action": "cdp.target",
        "required": true,
        "timeout_ms": 15000,
        "next": "setup-unlock",
        "intent": "Fail fast unless the Extension CDP runtime is reachable"
      },
      "setup-unlock": {
        "action": "metamask.wallet.ensure_unlocked",
        "timeout_ms": 45000,
        "next": "setup-open-home",
        "intent": "Ensure the wallet is unlocked before opening the Perps order form"
      },
      "setup-open-home": {
        "action": "ui.navigate",
        "page": "home",
        "timeout_ms": 30000,
        "next": "setup-open-order-entry",
        "intent": "Leave any previous Perps route so the order form mounts fresh and the initial-load state is genuine"
      },
      "setup-open-order-entry": {
        "action": "ui.navigate",
        "hash": "#/perps/trade/ETH",
        "timeout_ms": 30000,
        "next": "ac1-wait-percent-pill",
        "intent": "Open the ETH Perps order-entry form where the available-to-trade percentage pill renders"
      },
      "ac1-wait-percent-pill": {
        "action": "ui.wait_for",
        "selector": "[data-testid=\"balance-percent-input\"] input",
        "visible": true,
        "timeout_ms": 30000,
        "next": "ac1-probe-initial-load",
        "intent": "AC1: confirm the balance-percent pill is visible on initial load before asserting and capturing it"
      },
      "ac1-probe-initial-load": {
        "action": "command",

        "timeout_ms": 30000,
        "next": "ac1-assert-percent-is-whole-number",
        "intent": "AC1: read the initial-load percentage value and its clipping geometry without mutating any state"
      },
      "ac1-assert-percent-is-whole-number": {
        "action": "assert_json",

        "assert": {
          "path": "$.percentIsInteger",
          "operator": "eq",
          "value": true
        },
        "next": "ac1-assert-percent-not-clipped",
        "intent": "AC1: the initial-load percentage is a clean rounded whole number, not a long decimal"
      },
      "ac1-assert-percent-not-clipped": {
        "action": "assert_json",

        "assert": {
          "path": "$.percentIsClipped",
          "operator": "eq",
          "value": false
        },
        "next": "ac1-assert-slider-on-step-grid",
        "intent": "AC1: the pill's text fits its field, so the user never sees the reported truncated value"
      },
      "ac1-assert-slider-on-step-grid": {
        "action": "assert_json",

        "assert": {
          "path": "$.sliderIsOnStepGrid",
          "operator": "eq",
          "value": true
        },
        "next": "ac1-clear-capture-orphans",
        "intent": "AC1: the slider's aria-valuenow sits on its declared step=1 grid instead of an off-step decimal"
      },
      "ac1-clear-capture-orphans": {
        "action": "command",
        "cmd": "pkill -9 -f capture-helper || true",
        "allow_failure": true,
        "next": "ac1-screenshot-initial-load-percent",
        "intent": "AC1: clear wedged capture-helper sessions so the screenshot comes from capture-helper, not the raster fallback"
      },
      "ac1-screenshot-initial-load-percent": {
        "action": "ui.screenshot",

        "label": "AC1: initial load shows a clean rounded percentage in the pill, not a truncated decimal",
        "timeout_ms": 60000,
        "next": "ac2-press-slider",
        "intent": "AC1: capture the visible pill on initial load, the exact surface the ticket reports as truncated"
      },
      "ac2-press-slider": {
        "action": "ui.press",
        "selector": "[data-testid=\"amount-slider\"] input[type=\"range\"]",
        "timeout_ms": 20000,
        "next": "ac2-key-increment-1",
        "intent": "AC2: move the slider through a real click, as the ticket's reproduction step 5 describes"
      },
      "ac2-key-increment-1": {
        "action": "ui.key_press",
        "key": "ArrowRight",
        "timeout_ms": 10000,
        "next": "ac2-key-increment-2",
        "intent": "AC2: nudge the focused slider with trusted keyboard input to a non-zero percentage"
      },
      "ac2-key-increment-2": {
        "action": "ui.key_press",
        "key": "ArrowRight",
        "timeout_ms": 10000,
        "next": "ac2-key-increment-3",
        "intent": "AC2: continue moving the slider through trusted keyboard input"
      },
      "ac2-key-increment-3": {
        "action": "ui.key_press",
        "key": "ArrowRight",
        "timeout_ms": 10000,
        "next": "ac2-wait-percent-pill",
        "intent": "AC2: settle the slider on a non-zero percentage so the assertion is not trivially satisfied by 0"
      },
      "ac2-wait-percent-pill": {
        "action": "ui.wait_for",
        "selector": "[data-testid=\"amount-slider\"] input[type=\"range\"][aria-valuenow=\"3\"]",
        "visible": true,
        "timeout_ms": 20000,
        "next": "ac2-probe-after-interaction",
        "intent": "AC2: confirm the three trusted key presses actually moved the slider to 3 before asserting"
      },
      "ac2-probe-after-interaction": {
        "action": "command",

        "timeout_ms": 30000,
        "next": "ac2-assert-percent-value",
        "intent": "AC2: read the post-interaction percentage value and its clipping geometry"
      },
      "ac2-assert-percent-value": {
        "action": "assert_json",

        "assert": {
          "path": "$.percentValue",
          "operator": "eq",
          "value": "3"
        },
        "next": "ac2-assert-percent-is-whole-number",
        "intent": "AC2: the pill reflects the exact percentage the user's slider interaction produced"
      },
      "ac2-assert-percent-is-whole-number": {
        "action": "assert_json",

        "assert": {
          "path": "$.percentIsInteger",
          "operator": "eq",
          "value": true
        },
        "next": "ac2-assert-percent-not-clipped",
        "intent": "AC2: the already-working interaction path still renders a whole number after the fix"
      },
      "ac2-assert-percent-not-clipped": {
        "action": "assert_json",

        "assert": {
          "path": "$.percentIsClipped",
          "operator": "eq",
          "value": false
        },
        "next": "ac2-clear-capture-orphans",
        "intent": "AC2: the post-interaction pill still fits its field, confirming no regression"
      },
      "ac2-clear-capture-orphans": {
        "action": "command",
        "cmd": "pkill -9 -f capture-helper || true",
        "allow_failure": true,
        "next": "ac2-screenshot-after-interaction",
        "intent": "AC2: clear wedged capture-helper sessions before the second capture"
      },
      "ac2-screenshot-after-interaction": {
        "action": "ui.screenshot",

        "label": "AC2: after moving the slider the pill shows 3, a whole unclipped percentage",
        "timeout_ms": 60000,
        "next": "done",
        "intent": "AC2: capture the moved-slider state so reviewers can see the interaction path is unchanged"
      },
      "done": {
        "action": "end",
        "status": "pass"
      }
    }
  }
}

Recipe Workflow

workflow.mmd
flowchart TD
    setup-status["setup-status<br/>app.status"] --> gate-cdp
    gate-cdp["gate-cdp<br/>cdp.target"] --> setup-unlock
    setup-unlock["setup-unlock<br/>metamask.wallet.ensure_unlocked"] --> setup-open-home
    setup-open-home["setup-open-home<br/>ui.navigate page=home"] --> setup-open-order-entry
    setup-open-order-entry["setup-open-order-entry<br/>ui.navigate #/perps/trade/ETH"] --> ac1-wait-percent-pill

    subgraph AC1["AC1 — initial load shows a clean rounded percentage (mixed)"]
        ac1-wait-percent-pill["ac1-wait-percent-pill<br/>ui.wait_for balance-percent-input"] --> ac1-probe-initial-load
        ac1-probe-initial-load["ac1-probe-initial-load<br/>command: read-only DOM probe"] --> ac1-assert-percent-is-whole-number
        ac1-assert-percent-is-whole-number["ac1-assert-percent-is-whole-number<br/>assert_json percentIsInteger eq true"] --> ac1-assert-percent-not-clipped
        ac1-assert-percent-not-clipped["ac1-assert-percent-not-clipped<br/>assert_json percentIsClipped eq false"] --> ac1-assert-slider-on-step-grid
        ac1-assert-slider-on-step-grid["ac1-assert-slider-on-step-grid<br/>assert_json sliderIsOnStepGrid eq true"] --> ac1-clear-capture-orphans
        ac1-clear-capture-orphans["ac1-clear-capture-orphans<br/>command: pkill -9 capture-helper"] --> ac1-screenshot-initial-load-percent
        ac1-screenshot-initial-load-percent["ac1-screenshot-initial-load-percent<br/>ui.screenshot evidence-ac1"]
    end

    ac1-screenshot-initial-load-percent --> ac2-press-slider

    subgraph AC2["AC2 — moving the slider still shows the correct value (state)"]
        ac2-press-slider["ac2-press-slider<br/>ui.press slider range input"] --> ac2-key-increment-1
        ac2-key-increment-1["ac2-key-increment-1<br/>ui.key_press ArrowRight"] --> ac2-key-increment-2
        ac2-key-increment-2["ac2-key-increment-2<br/>ui.key_press ArrowRight"] --> ac2-key-increment-3
        ac2-key-increment-3["ac2-key-increment-3<br/>ui.key_press ArrowRight"] --> ac2-wait-percent-pill
        ac2-wait-percent-pill["ac2-wait-percent-pill<br/>ui.wait_for aria-valuenow=3"] --> ac2-probe-after-interaction
        ac2-probe-after-interaction["ac2-probe-after-interaction<br/>command: read-only DOM probe"] --> ac2-assert-percent-value
        ac2-assert-percent-value["ac2-assert-percent-value<br/>assert_json percentValue eq 3"] --> ac2-assert-percent-is-whole-number
        ac2-assert-percent-is-whole-number["ac2-assert-percent-is-whole-number<br/>assert_json percentIsInteger eq true"] --> ac2-assert-percent-not-clipped
        ac2-assert-percent-not-clipped["ac2-assert-percent-not-clipped<br/>assert_json percentIsClipped eq false"] --> ac2-clear-capture-orphans
        ac2-clear-capture-orphans["ac2-clear-capture-orphans<br/>command: pkill -9 capture-helper"] --> ac2-screenshot-after-interaction
        ac2-screenshot-after-interaction["ac2-screenshot-after-interaction<br/>ui.screenshot evidence-ac2"]
    end

    ac2-screenshot-after-interaction --> done
    done(["done — end status=pass"])
Loading

Note

Low Risk
Small display/state seeding change in the order form hook with targeted unit tests; no auth, payments, or trading execution paths.

Overview
Fixes TAT-3763 by changing how the Perps order form seeds balancePercent on first load in usePerpsOrderForm: it now uses Math.round(initialBalancePercent) instead of rounding to two decimals.

That aligns the initial value with the size slider’s step={1} and with slider, leverage, and percent-field updates, so the narrow available-to-trade % pill no longer shows clipped decimals like 0.44 or 22... before the user interacts.

Adds regression tests for whole-number initial percents (including zero and non-zero cases) and adjusts an existing leverage/balance recap test to expect whole-percent math.

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

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

@metamask-ci

metamask-ci Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

✨ Files requiring CODEOWNER review ✨

👨‍🔧 @MetaMask/perps (2 files, +53 -6)
  • 📁 ui/
    • 📁 hooks/
      • 📁 perps/
        • 📄 usePerpsOrderForm.test.ts +46 -5
        • 📄 usePerpsOrderForm.ts +7 -1

@metamask-ci metamask-ci Bot added the team-perps Perps team label Aug 15, 2026
@abretonc7s abretonc7s changed the title chore: prepare farmslot publication pkg-ec24b83a-mstma5g7 fix(perps): [Bug]: Perps - Available to trade percentage shows truncated decimal on initial load (e.g. "22...") Aug 15, 2026
@abretonc7s
abretonc7s marked this pull request as ready for review August 15, 2026 01:19
@abretonc7s
abretonc7s requested a review from a team as a code owner August 15, 2026 01:19
…mber

The order form seeded balancePercent rounded to two decimals, so on initial
load the percentage pill next to the size slider rendered a value like 0.44.
That text overflows the pill's 4.5rem field and the browser clipped it, which
is the reported "22...". Every other writer of balancePercent already yields
a whole number, so the value corrected itself as soon as the user touched the
slider.

Round the seed to a whole percent so initial load matches the slider's step=1
grid and every user-driven write path.
@metamask-ci

metamask-ci Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor
Builds ready [91f094d]
⚡ Performance Benchmarks (Total: 🟢 10 pass · 🟡 11 warn · 🔴 3 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 31856167865 | Baseline logs

Metricschrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 load_new_account(p95) [CI log]🔴 load_new_account(p95) [CI log]
onboardingImportWallet
[Sentry log · main/release]
🔴 srpButtonToSrpForm(p95) [CI log]🟢 [CI log]

Regressions (🔴 3 failures)

Interaction Benchmarks · Samples: 5 🔴 2
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 [CI log]
🔴 load_new_account
🔴 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +190%
  • loadNewAccount/total: +190%
  • loadNewAccount/inp: -17%
  • confirmTx/longTaskMaxDuration: +43%
  • confirmTx/lcp: +15%
  • bridgeUserActions/bridge_load_asset_picker: +62%
  • bridgeUserActions/longTaskCount: +11%
  • bridgeUserActions/longTaskTotalDuration: +21%
  • bridgeUserActions/longTaskMaxDuration: +16%
  • bridgeUserActions/tbt: +45%
  • bridgeUserActions/inp: +15%
  • loadNewAccount/load_new_account: +190%
  • loadNewAccount/total: +190%
  • loadNewAccount/fcp: -44%
  • loadNewAccount/lcp: +1394%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/inp: -32%
  • confirmTx/fcp: -50%
  • confirmTx/lcp: +1168%
  • bridgeUserActions/bridge_load_page: +159%
  • bridgeUserActions/bridge_load_asset_picker: +71%
  • bridgeUserActions/bridge_search_token: -11%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +198%
  • bridgeUserActions/fcp: -47%
  • bridgeUserActions/lcp: +1159%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 confirmTx/FCP: p75 1.8s
  • 🟡 bridgeUserActions/FCP: p75 1.8s
  • 🟡 loadNewAccount/LCP: p75 2.6s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/firstReactRender: -99%
  • startupStandardHome/numNetworkReqs: -14%
  • startupStandardHome/longTaskCount: -33%
  • startupStandardHome/longTaskTotalDuration: -17%
  • startupStandardHome/longTaskMaxDuration: -11%
  • startupStandardHome/tbt: -14%
  • startupStandardHome/domInteractive: -19%
  • startupStandardHome/firstReactRender: -98%
  • startupStandardHome/setupStore: +13%
  • startupStandardHome/numNetworkReqs: -20%
  • startupStandardHome/fcp: -17%
  • startupPowerUserHome/load: +13%
  • startupPowerUserHome/domContentLoaded: +13%
  • startupPowerUserHome/domInteractive: +30%
  • startupPowerUserHome/backgroundConnect: +62%
  • startupPowerUserHome/firstReactRender: -99%
  • startupPowerUserHome/initialActions: +11%
  • startupPowerUserHome/loadScripts: +12%
  • startupPowerUserHome/setupStore: -58%
  • startupPowerUserHome/inp: -11%
  • startupPowerUserHome/fcp: +25%
  • startupPowerUserHome/lcp: +23%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 3.5s
User Journey Benchmarks · Samples: 5 · mock API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🔴 [CI log]
🔴 total
🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]
🔴 total
🟢 [CI log]
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +26%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -44%
  • onboardingImportWallet/longTaskCount: -86%
  • onboardingImportWallet/longTaskTotalDuration: -95%
  • onboardingImportWallet/longTaskMaxDuration: -91%
  • onboardingImportWallet/tbt: -99%
  • onboardingImportWallet/total: -79%
  • onboardingNewWallet/skipBackupToMetricsScreen: -13%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -11%
  • onboardingNewWallet/longTaskCount: -38%
  • onboardingNewWallet/longTaskTotalDuration: -54%
  • onboardingNewWallet/longTaskMaxDuration: -46%
  • onboardingNewWallet/tbt: -93%
  • solanaAssetDetails/assetClickToPriceChart: +315%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: +315%
  • solanaAssetDetails/inp: +11%
  • solanaAssetDetails/fcp: +16%
  • solanaAssetDetails/lcp: +20%
  • importSrpHome/loginToHomeScreen: -13%
  • importSrpHome/homeAfterImportWithNewWallet: -13%
  • importSrpHome/longTaskCount: -15%
  • importSrpHome/longTaskTotalDuration: -31%
  • importSrpHome/longTaskMaxDuration: -10%
  • importSrpHome/tbt: -40%
  • importSrpHome/total: -13%
  • importSrpHome/inp: -33%
  • importSrpHome/cls: +499%
  • sendTransactions/selectTokenToSendFormLoaded: +113%
  • sendTransactions/reviewTransactionToConfirmationPage: -98%
  • sendTransactions/longTaskCount: -100%
  • sendTransactions/longTaskTotalDuration: -100%
  • sendTransactions/longTaskMaxDuration: -100%
  • sendTransactions/tbt: -100%
  • sendTransactions/total: -91%
  • sendTransactions/inp: -44%
  • sendTransactions/fcp: +12%
  • sendTransactions/lcp: -64%
  • sendTransactions/cls: +171%
  • swap/openSwapPageFromHome: +812%
  • swap/fetchAndDisplaySwapQuotes: +80%
  • swap/longTaskCount: +33%
  • swap/longTaskTotalDuration: +11%
  • swap/longTaskMaxDuration: +17%
  • swap/tbt: -16%
  • swap/total: +97%
  • swap/lcp: -71%
  • swap/cls: -92%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 solanaAssetDetails/FCP: p75 1.8s
  • 🟡 importSrpHome/INP: p75 208ms
  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 1.8s
  • 🟡 swap/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.8s
  • 🟡 importSrpHome/FCP: p75 2.0s
  • 🟡 sendTransactions/FCP: p75 2.0s
  • 🟡 swap/FCP: p75 1.9s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: -57%
  • dappPageLoad/firstPaint: -41%
  • dappPageLoad/firstContentfulPaint: -41%
Bundle size diffs
  • background: 105 Bytes (0%)
  • ui: 40 Bytes (0%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 0 Bytes (0%)
  • zip: 33 Bytes (0%)

@abretonc7s
abretonc7s force-pushed the TAT-3763-fix-fix-perps-percentage-truncatio branch from 91f094d to edba250 Compare August 15, 2026 01:51
@abretonc7s

Copy link
Copy Markdown
Contributor Author
Run Duration Model Nudges Grade Cost
655ae939 opus 0 ungraded $unknown
Worker report

PR #45561 — Comments report

PR: fix(perps): [Bug]: Perps - Available to trade percentage shows truncated decimal on initial load (e.g. "22...")
Branch: TAT-3763-fix-fix-perps-percentage-truncatio · Ticket: TAT-3763

Context gathered

  • PR purpose — seeds the Perps order form's initial balancePercent with Math.round(x) instead of Math.round(x * 100) / 100, so the percentage pill beside the size slider renders a whole number on initial load rather than a decimal that overflows its 4.5rem field and is clipped (the reported 22...).
  • PR diff — 2 files: ui/hooks/perps/usePerpsOrderForm.ts (+7 -1) and ui/hooks/perps/usePerpsOrderForm.test.ts (+46 -5).
  • PR state — open, not draft, mergeable: true, mergeable_state: blocked (required checks not yet all green).

Comment triage

Author Source Triage Action
github-actions[bot] issue_comment routine automation CLA signature status (skipped)
metamask-ci[bot] issue_comment routine automation CODEOWNERS listing (skipped)
  • Inline review comments: 0
  • CHANGES_REQUESTED reviews: 0
  • Actionable comments: 0

No review-fix commit was created.

CI status

mergeable_state is blocked due to run 31856167865: 3 fail, 23 pending, 5 skipping, 98 pass.

All three red checks stem from a single pre-existing flake in ui/hooks/useEventFragment.test.js › identifying appropriate fragment › should create a new fragment when a matching fragment does not exist (unrelated to this PR, already triaged as OUT OF SCOPE). The test passes at the rebased HEAD.

Resolution: force-push triggers a fresh CI run that re-runs the flaked unit-test shards.

Integration status

Rebased cleanly onto origin/main (9e713efc26, "release: Bump main version to 13.46.0"). Linear history. Only package.json changed on main side; yarn.lock untouched.

Validation

  • Recipe re-validation: PASS — 24/24 nodes ok: true. Initial load: percentValue "0", percentIsInteger true, percentIsClipped false, sliderIsOnStepGrid true. Post-interaction: "3", integer, unclipped.
  • Local validation: PASScheck diff --profile fast (policy-suppressions, eslint, oxfmt, jest), coverage-analyze VERDICT PASS (98%), parity gate (lint:changed, verify-locales, circular-deps:check).

Summary

  • Total comments: 2 — 0 REAL, 0 FALSE POSITIVE, 2 OUT OF SCOPE (routine automation, skipped).
  • Published SHA: edba2504fc (rebased twin of original fix commit).
  • Files changed: ui/hooks/perps/usePerpsOrderForm.ts, ui/hooks/perps/usePerpsOrderForm.test.ts.

@sonarqubecloud

Copy link
Copy Markdown

@metamask-ci

metamask-ci Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor
Builds ready [edba250] [reused from 91f094d]
⚡ Performance Benchmarks (Total: 🟢 10 pass · 🟡 11 warn · 🔴 3 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 31857625648 | Baseline logs

Metricschrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 load_new_account(p95) [CI log]🔴 load_new_account(p95) [CI log]
onboardingImportWallet
[Sentry log · main/release]
🔴 srpButtonToSrpForm(p95) [CI log]🟢 [CI log]

Regressions (🔴 3 failures)

Interaction Benchmarks · Samples: 5 🔴 2
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🔴 [CI log]
🔴 load_new_account
🔴 [CI log]
🔴 load_new_account
confirmTx
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +190%
  • loadNewAccount/total: +190%
  • loadNewAccount/inp: -17%
  • confirmTx/longTaskMaxDuration: +43%
  • confirmTx/lcp: +15%
  • bridgeUserActions/bridge_load_asset_picker: +62%
  • bridgeUserActions/longTaskCount: +11%
  • bridgeUserActions/longTaskTotalDuration: +21%
  • bridgeUserActions/longTaskMaxDuration: +16%
  • bridgeUserActions/tbt: +45%
  • bridgeUserActions/inp: +15%
  • loadNewAccount/load_new_account: +190%
  • loadNewAccount/total: +190%
  • loadNewAccount/fcp: -44%
  • loadNewAccount/lcp: +1394%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/inp: -32%
  • confirmTx/fcp: -50%
  • confirmTx/lcp: +1168%
  • bridgeUserActions/bridge_load_page: +159%
  • bridgeUserActions/bridge_load_asset_picker: +71%
  • bridgeUserActions/bridge_search_token: -11%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +198%
  • bridgeUserActions/fcp: -47%
  • bridgeUserActions/lcp: +1159%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 confirmTx/FCP: p75 1.8s
  • 🟡 bridgeUserActions/FCP: p75 1.8s
  • 🟡 loadNewAccount/LCP: p75 2.6s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/firstReactRender: -99%
  • startupStandardHome/numNetworkReqs: -14%
  • startupStandardHome/longTaskCount: -33%
  • startupStandardHome/longTaskTotalDuration: -17%
  • startupStandardHome/longTaskMaxDuration: -11%
  • startupStandardHome/tbt: -14%
  • startupStandardHome/domInteractive: -19%
  • startupStandardHome/firstReactRender: -98%
  • startupStandardHome/setupStore: +13%
  • startupStandardHome/numNetworkReqs: -20%
  • startupStandardHome/fcp: -17%
  • startupPowerUserHome/load: +13%
  • startupPowerUserHome/domContentLoaded: +13%
  • startupPowerUserHome/domInteractive: +30%
  • startupPowerUserHome/backgroundConnect: +62%
  • startupPowerUserHome/firstReactRender: -99%
  • startupPowerUserHome/initialActions: +11%
  • startupPowerUserHome/loadScripts: +12%
  • startupPowerUserHome/setupStore: -58%
  • startupPowerUserHome/inp: -11%
  • startupPowerUserHome/fcp: +25%
  • startupPowerUserHome/lcp: +23%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 3.5s
User Journey Benchmarks · Samples: 5 · mock API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🔴 [CI log]
🔴 total
🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]
🔴 total
🟢 [CI log]
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +26%
  • onboardingImportWallet/doneButtonToHomeScreen: -76%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -44%
  • onboardingImportWallet/longTaskCount: -86%
  • onboardingImportWallet/longTaskTotalDuration: -95%
  • onboardingImportWallet/longTaskMaxDuration: -91%
  • onboardingImportWallet/tbt: -99%
  • onboardingImportWallet/total: -79%
  • onboardingNewWallet/skipBackupToMetricsScreen: -13%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -11%
  • onboardingNewWallet/longTaskCount: -38%
  • onboardingNewWallet/longTaskTotalDuration: -54%
  • onboardingNewWallet/longTaskMaxDuration: -46%
  • onboardingNewWallet/tbt: -93%
  • solanaAssetDetails/assetClickToPriceChart: +315%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: +315%
  • solanaAssetDetails/inp: +11%
  • solanaAssetDetails/fcp: +16%
  • solanaAssetDetails/lcp: +20%
  • importSrpHome/loginToHomeScreen: -13%
  • importSrpHome/homeAfterImportWithNewWallet: -13%
  • importSrpHome/longTaskCount: -15%
  • importSrpHome/longTaskTotalDuration: -31%
  • importSrpHome/longTaskMaxDuration: -10%
  • importSrpHome/tbt: -40%
  • importSrpHome/total: -13%
  • importSrpHome/inp: -33%
  • importSrpHome/cls: +499%
  • sendTransactions/selectTokenToSendFormLoaded: +113%
  • sendTransactions/reviewTransactionToConfirmationPage: -98%
  • sendTransactions/longTaskCount: -100%
  • sendTransactions/longTaskTotalDuration: -100%
  • sendTransactions/longTaskMaxDuration: -100%
  • sendTransactions/tbt: -100%
  • sendTransactions/total: -91%
  • sendTransactions/inp: -44%
  • sendTransactions/fcp: +12%
  • sendTransactions/lcp: -64%
  • sendTransactions/cls: +171%
  • swap/openSwapPageFromHome: +812%
  • swap/fetchAndDisplaySwapQuotes: +80%
  • swap/longTaskCount: +33%
  • swap/longTaskTotalDuration: +11%
  • swap/longTaskMaxDuration: +17%
  • swap/tbt: -16%
  • swap/total: +97%
  • swap/lcp: -71%
  • swap/cls: -92%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 solanaAssetDetails/FCP: p75 1.8s
  • 🟡 importSrpHome/INP: p75 208ms
  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 1.8s
  • 🟡 swap/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.8s
  • 🟡 importSrpHome/FCP: p75 2.0s
  • 🟡 sendTransactions/FCP: p75 2.0s
  • 🟡 swap/FCP: p75 1.9s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: -57%
  • dappPageLoad/firstPaint: -41%
  • dappPageLoad/firstContentfulPaint: -41%
Bundle size diffs
  • background: 105 Bytes (0%)
  • ui: 40 Bytes (0%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 0 Bytes (0%)
  • zip: 33 Bytes (0%)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant