Commit c1497aa
authored
fix(perps): positionTPSL shouldn't appear in order section (#42661)
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->
## **Description**
Placing a market order with TP/SL via the perps order-entry UI sent both
the market order and the TP/SL inputs in a single `perpsPlaceOrder`
call. The `@metamask/perps-controller` default for that path is
`grouping: 'normalTpsl'` (`orderCalculations.cjs:282`), so Hyperliquid
returned the resulting trigger orders with `isPositionTpsl: false`. That
left `position.takeProfitPrice/stopLossPrice` unpopulated (the
controller's `extractTPSLFromOrders` skips orders without the flag) and
the predicate behind the market-detail Orders section let the trigger
orders through — TP/SL ended up in the Orders list instead of the
Auto-close row.
Mobile already routes the same scenario through a separate
`updatePositionTPSL` call (`PerpsOrderView.tsx:1126`) which the
controller submits under `grouping: 'positionTpsl'`
(`HyperLiquidProvider.cjs:1098`). The fix mirrors that split on the
extension: when a market order with TP/SL targets a new (or flipping)
position, we strip the TP/SL from `perpsPlaceOrder` and follow up with
`perpsUpdatePositionTPSL`. The resulting trigger orders come back tagged
`isPositionTpsl: true`, so the existing partition logic works as
designed.
Defense in depth: `isOrderAssociatedWithFullPosition` now tolerates the
rare `isPositionTpsl: false` flag on TP/SL trigger orders (size match
wins), and the market-detail page derives Auto-close prices from the
matching positionTPSL orders when the controller has not surfaced them.
These guard against legacy on-chain orders and any future WS update path
that drops the flag.
## **Changelog**
CHANGELOG entry: Fixed a perps bug where market orders submitted with
TP/SL left the Auto-close section empty and surfaced the TP/SL orders in
the Orders section of the market detail page.
## **Related issues**
Fixes:
[TAT-3065](https://consensyssoftware.atlassian.net/browse/TAT-3065)
## **Manual testing steps**
1. Unlock the wallet and open the Perps tab.
2. Pick a market where you do not currently hold a position (e.g. AVAX).
3. From the market detail page, tap **Long** to open the order entry
screen.
4. Enter a notional (e.g. `10`), enable Auto-close, set a TP price above
the entry, a SL price below the entry but above liquidation, and submit.
5. After the toast confirms the order filled, navigate back to the same
market detail page.
6. **Expected**: the Auto-close row shows the TP/SL values you just
entered, and the Orders section does not list any TP/SL trigger orders.
7. (Optional) Repeat with a limit order plus TP/SL. The TP/SL orders
should appear in the Orders section while the limit is open and migrate
to Auto-close once the limit fills.
## **Screenshots/Recordings**
<table>
<tr><td align="center" width="50%"><strong>Screenshots/evidence Ac1 Real
Avax Market Tpsl.png 1778835137152</strong><br/><img
src="https://raw.githubusercontent.com/abretonc7s/mm-extension-farm-artifacts/main/fixes/42661/screenshots/evidence-ac1-real-avax-market-tpsl.png-1778835137152.png"
alt="Screenshots/evidence Ac1 Real Avax Market Tpsl.png 1778835137152"
width="400" /></td><td align="center"
width="50%"><strong>Screenshots/perps Tab
1778835132382</strong><br/><img
src="https://raw.githubusercontent.com/abretonc7s/mm-extension-farm-artifacts/main/fixes/42661/screenshots/perps-tab-1778835132382.png"
alt="Screenshots/perps Tab 1778835132382" width="400"
/><br/><sub>caption confidence: LOW — generic filename — no
state-specific suffix</sub></td></tr>
</table>
## **Validation Recipe**
`temp/tasks/fix/tat-3065-0513-153038/artifacts/recipe.json` (verify) and
`recipe-baseline.json` (buggy main capture). Both drive the same UI flow
against a live Hyperliquid mainnet account — no injected channel state.
The verify recipe passes 22/22 on the fix branch; the baseline recipe
passes 22/22 against vanilla main and asserts the buggy values
(`isPositionTpsl: false`, `TP -, SL -`, `leakCount > 0`).
<details>
<summary>recipe.json (verify)</summary>
```json
{
"title": "TAT-3065 real $10 AVAX market+TPSL via UI",
"schema_version": 1,
"validate": {
"workflow": {
"pre_conditions": ["wallet.unlocked", "perps.feature_enabled"],
"entry": "gate-nav-perps",
"teardown": [
{
"id": "teardown-close-avax",
"action": "eval_async",
"expression": "perpsClosePosition({symbol:'AVAX',orderType:'market'})"
}
],
"nodes": {
"gate-nav-perps": { "action": "call", "ref": "perps/navigate-perps-tab" },
"gate-cleanup-pre-existing-avax": { "action": "eval_async", "expression": "perpsClosePosition AVAX if present" },
"setup-nav-market": { "action": "call", "ref": "perps/navigate-to-market-detail", "params": { "symbol": "AVAX" } },
"setup-press-long": { "action": "press", "test_id": "perps-long-cta-button" },
"setup-set-amount": { "action": "set_input", "test_id": "amount-input-field", "value": "10" },
"setup-toggle-autoclose": { "action": "eval_sync", "expression": "click .toggle-button label" },
"setup-set-tp": { "action": "set_input", "test_id": "tp-price-input", "value": "11" },
"setup-set-sl": { "action": "set_input", "test_id": "sl-price-input", "value": "9" },
"setup-submit": { "action": "press", "test_id": "submit-order-button" },
"setup-wait-fill": { "action": "eval_async", "expression": "poll perpsGetPositions until AVAX appears" },
"setup-wait-tpsl-orders": {
"action": "eval_async",
"assert": { "all": [{ "operator": "eq", "field": "allPositionTpsl", "value": true }] }
},
"ac1-assert-autoclose-shows-positiontpsl": {
"action": "eval_sync",
"assert": { "all": [
{ "operator": "eq", "field": "hasTpPrice", "value": true },
{ "operator": "eq", "field": "hasSlPrice", "value": true },
{ "operator": "eq", "field": "hasPlaceholderTp", "value": false },
{ "operator": "eq", "field": "hasPlaceholderSl", "value": false }
]}
},
"ac2-assert-positiontpsl-not-in-orders": {
"action": "eval_async",
"assert": { "all": [{ "operator": "eq", "field": "leakCount", "value": 0 }] }
},
"ac-screenshot-final": { "action": "screenshot", "filename": "evidence-ac1-real-avax-market-tpsl.png" }
}
}
}
}
```
Full recipe + baseline are checked in alongside this PR under
`temp/tasks/fix/tat-3065-0513-153038/artifacts/`.
</details>
## **Recipe Workflow**
<details>
<summary>workflow.mmd</summary>
```mermaid
flowchart TD
ENTRY([ENTRY]) --> gate_nav_perps
gate_nav_perps[[gate-nav-perps perps/navigate-perps-tab]] --> gate_cleanup_pre_existing_avax
gate_cleanup_pre_existing_avax[gate-cleanup-pre-existing-avax eval_async] --> setup_nav_market
setup_nav_market[[setup-nav-market perps/navigate-to-market-detail]] --> setup_press_long
setup_press_long[setup-press-long press] --> setup_set_amount
setup_set_amount[setup-set-amount set_input 10] --> setup_toggle_autoclose
setup_toggle_autoclose[setup-toggle-autoclose eval_sync] --> setup_set_tp
setup_set_tp[setup-set-tp set_input 11] --> setup_set_sl
setup_set_sl[setup-set-sl set_input 9] --> setup_submit
setup_submit[setup-submit press submit-order-button] --> setup_wait_fill
setup_wait_fill[setup-wait-fill eval_async poll position] --> setup_wait_tpsl_orders
setup_wait_tpsl_orders[setup-wait-tpsl-orders allPositionTpsl=true] --> setup_nav_avax_detail
setup_nav_avax_detail[[setup-nav-avax-detail perps/navigate-to-market-detail]] --> ac1
ac1[ac1-assert-autoclose-shows-positiontpsl] --> ac2
ac2[ac2-assert-positiontpsl-not-in-orders leakCount=0] --> screenshot
screenshot[ac-screenshot-final] --> done([end])
```
</details>
## **Pre-merge author checklist**
- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/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-extension/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.
[TAT-3065]:
https://consensyssoftware.atlassian.net/browse/TAT-3065?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes perps order-submission and order/position association logic,
including a new two-step background call flow (`perpsPlaceOrder` then
`perpsUpdatePositionTPSL`) that could affect trade submission and TP/SL
attachment behavior.
>
> **Overview**
> Fixes market orders submitted with TP/SL so they create
*position-level* TP/SL triggers: when a market order includes TP/SL and
would open a new position (or flip an existing one), the UI now submits
`perpsPlaceOrder` **without** TP/SL and follows up with
`perpsUpdatePositionTPSL` (with failure handled as “order filled but
TP/SL attach failed”).
>
> Hardens market-detail display when Hyperliquid/controller data is
incomplete: `isOrderAssociatedWithFullPosition` now treats explicit
`isPositionTpsl: false` as authoritative (avoids misclassifying
limit-order TP/SL children) while also handling zero-size reduce-only
triggers as position-bound; the market detail page derives effective
TP/SL prices from matching trigger orders when
`position.takeProfitPrice`/`stopLossPrice` are missing, and passes these
through to chart lines and the `UpdateTPSLModal`.
>
> Exports are updated so `willFlipPosition` is available consistently
via `utils.ts`/`utils/index.ts`, and tests add coverage for the new flip
logic, TP/SL derivation, and the two-step market+TP/SL submission flow.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5941831. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent a4bbe61 commit c1497aa
7 files changed
Lines changed: 523 additions & 23 deletions
File tree
- ui
- components/app/perps
- utils
- pages/perps
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
15 | 20 | | |
16 | 21 | | |
17 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
7 | 9 | | |
8 | 10 | | |
9 | 11 | | |
| |||
66 | 68 | | |
67 | 69 | | |
68 | 70 | | |
69 | | - | |
| 71 | + | |
70 | 72 | | |
71 | 73 | | |
72 | 74 | | |
| |||
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
81 | 104 | | |
82 | 105 | | |
83 | 106 | | |
| |||
165 | 188 | | |
166 | 189 | | |
167 | 190 | | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
168 | 382 | | |
169 | 383 | | |
170 | 384 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
156 | 161 | | |
157 | 162 | | |
158 | 163 | | |
159 | 164 | | |
160 | 165 | | |
161 | 166 | | |
162 | | - | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
163 | 177 | | |
164 | 178 | | |
165 | 179 | | |
166 | 180 | | |
167 | 181 | | |
168 | 182 | | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
169 | 269 | | |
170 | 270 | | |
171 | 271 | | |
| |||
0 commit comments