[pull] main from MetaMask:main#638
Merged
pull[bot] merged 1 commit intoReality2byte:mainfrom Mar 28, 2026
Merged
Conversation
## **Description** The flip position confirmation sheet showed a fee estimate ~2x lower than the actual fee charged. A flip order is 2x position size (close current + open opposite), but the fee estimate was calculated on 1x notional. A secondary bug in `TradingService` applied the combined fee rate to 2x notional for the balance check, incorrectly blocking users with sufficient balance. ## **Changelog** CHANGELOG entry: Fixed flip position fee estimate being ~2x lower than actual fee charged ## **Related issues** Fixes: [TAT-2418](https://consensyssoftware.atlassian.net/browse/TAT-2418) ## **Manual testing steps** ```gherkin Feature: Flip position fee estimate accuracy Scenario: Fee estimate matches actual fee charged Given a wallet with an open BTC long position of ~$11 When user taps Modify then selects Flip Position Then the fee shown on the confirmation sheet is approximately 0.09% of the full position value (2x notional) not 0.045% (1x notional) Scenario: User with sufficient balance is not blocked Given a wallet with an open BTC position And available balance is $30 (above 1x fee ~$22.50, below 2x fee ~$45) When user attempts to flip the position Then the flip proceeds successfully ``` ## **Screenshots/Recordings** _Evidence available in task artifacts — will be added by reviewer if needed._ ### **Before** <!-- [screenshots/recordings] --> <img width="1206" height="2622" alt="before-flip-fee-estimate" src="https://github.com/user-attachments/assets/80b989b3-8ae2-4c87-ae03-610371a592e7" /> ### **After** <!-- [screenshots/recordings] --> <img width="1206" height="2622" alt="after-flip-fee-estimate" src="https://github.com/user-attachments/assets/5b9701b8-ab9d-4f82-88d5-08be1bf2c4b7" /> ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **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** <details> <summary>recipe.json — automated validation (14 steps)</summary> ```json { "pr": "28013", "title": "Flip position fee estimate is ~2x lower than actual fee charged", "jira": "TAT-2418", "acceptance_criteria": [ "Fee estimate shown on flip confirmation sheet matches actual fee charged (within normal rounding tolerance)", "Users with sufficient margin for the actual fee are not blocked from submitting a flip order", "No new TypeScript errors" ], "validate": { "static": ["yarn lint:tsc"], "runtime": { "pre_conditions": ["wallet.unlocked", "perps.feature_enabled"], "steps": [ { "id": "open_pos", "description": "Open a BTC long position to flip", "action": "flow_ref", "ref": "trade-open-market", "params": { "symbol": "BTC", "side": "long", "usdAmount": "11" } }, { "id": "wait_position", "description": "Wait for BTC position to appear after opening", "action": "wait_for", "expression": "Engine.context.PerpsController.getPositions().then(function(positions){ var btc = positions.filter(function(p){ return p.symbol === 'BTC'; }); return JSON.stringify({positions: btc}); })", "timeout_ms": 20000, "assert": { "operator": "length_gt", "field": "positions", "value": 0 } }, { "id": "nav_market_details", "description": "Navigate to BTC market details to access flip confirmation sheet", "action": "navigate", "target": "PerpsMarketDetails", "params": { "symbol": "BTC" } }, { "id": "wait_market_details", "description": "Wait for market details to load", "action": "wait_for", "route": "PerpsMarketDetails" }, { "id": "wait_modify_button", "description": "Wait for the Modify button to appear (position must be loaded)", "action": "wait_for", "test_id": "perps-market-details-modify-button" }, { "id": "press_modify", "description": "Open the modify action sheet", "action": "press", "test_id": "perps-market-details-modify-button" }, { "id": "wait_flip_option", "description": "Wait for the flip position option in the modify action sheet", "action": "wait_for", "test_id": "undefined-flip_position" }, { "id": "press_flip_option", "description": "Select flip position to open flip confirmation sheet", "action": "press", "test_id": "undefined-flip_position" }, { "id": "wait_flip_sheet", "description": "Wait for flip confirmation bottom sheet to appear", "action": "wait_for", "test_id": "perps-flip-position-confirm-sheet" }, { "id": "check_fee_calculation", "description": "Assert the fee is calculated on 2x notional.", "action": "eval_async", "expression": "Engine.context.PerpsController.getPositions().then(function(positions) { var pos = null; for (var i = 0; i < positions.length; i++) { if (positions[i].symbol === 'BTC') { pos = positions[i]; break; } } if (!pos) return JSON.stringify({error: 'no BTC position'}); var size = Math.abs(parseFloat(pos.size)); var price = parseFloat(pos.entryPrice); var usdAmount1x = size * price; var usdAmount2x = size * 2 * price; return JSON.stringify({positionSize: size, entryPrice: price, fee_base_1x: usdAmount1x, fee_base_2x_correct: usdAmount2x}); })", "assert": { "operator": "not_null" } }, { "id": "evidence_fee_sheet", "description": "Capture fee estimate shown on confirmation sheet", "action": "screenshot", "filename": "evidence-flip-fee-estimate.png" }, { "id": "check_no_errors", "description": "Verify no errors in Metro logs during flip flow", "action": "log_watch", "window_seconds": 5, "must_not_appear": ["TypeError", "undefined is not an object"] }, { "id": "cancel_flip", "description": "Cancel the flip sheet without submitting", "action": "press", "test_id": "perps-flip-position-cancel-button" }, { "id": "cleanup_position", "description": "Close BTC position to leave clean state", "action": "flow_ref", "ref": "trade-close-position", "params": { "symbol": "BTC" } } ] } } } ``` </details> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes fee/notional math used in the flip-position UI and the `TradingService.flipPosition` balance gate; incorrect values could affect whether users can submit flips and what fees they expect. > > **Overview** > Fixes flip-position fee math to align with how fees are actually charged. > > The flip confirmation sheet now estimates fees using **2x position notional** (close + open) and adds stable `testID`s for the sheet and its action buttons, with a unit test asserting the `usePerpsOrderFees` amount. > > `TradingService.flipPosition` updates its **available-balance fee check** to apply the combined fee rate to **1x notional** (since the rate already includes both legs), and adds a regression test ensuring a flip is allowed when balance covers that corrected estimate. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit dcf9361. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )