Commit 84da92c
fix(perps): TAT-1966 close all positions not including discount (MetaMask#22112)
## **Description**
**Problem:**
Fee discount was not being displayed in the "Close all positions" view,
despite working correctly in the single position close view. This caused
users to not see their rewards discount (e.g., -65%) when closing all
positions.
**Solution:**
Added account-level fee discount fetching to
`usePerpsCloseAllCalculations` hook, matching the implementation in the
single position close flow. The fix includes:
1. **Fee Discount Fetching**: Added
`RewardsController.getPerpsDiscountForAccount()` call to fetch user's
discount
2. **Discount Application**: Applied discount to MetaMask fees using
formula: `adjusted_rate = original_rate * (1 - discount_bips/10000)`
3. **Batch API Migration**: Migrated to batch points estimation API for
better performance (N+1 API calls � 2 total calls)
4. **Error Handling**: Preserved `undefined` state for unavailable fees
instead of defaulting to 0
**Technical Details:**
- Account-level discount applies uniformly to all positions
- Per-position fee calculations for accuracy (coin-specific parameters)
- Freeze mechanism prevents repeated calculations on WebSocket position
updates
- Removed `feeDiscountBips` from effect dependencies to prevent
recalculation on every update
## **Changelog**
CHANGELOG entry: Fixed fee discount not displaying in close all
positions view
## **Related issues**
Fixes: https://consensyssoftware.atlassian.net/browse/TAT-1966
## **Manual testing steps**
```gherkin
Feature: Fee discount display in close all positions
Scenario: user views fee discount when closing all positions
Given user has an account with fee discount (e.g., 65%)
And user has multiple open positions
When user navigates to "Close all positions" view
Then fee discount badge should display (e.g., "-65%")
And fees breakdown should show discounted MetaMask fee rate
And estimated points should be calculated correctly
```
## **Screenshots/Recordings**
### **Before**
Fee discount was hardcoded to 0%, not showing user's actual rewards
discount.
### **After**
Fee discount now displays correctly (e.g., -65%) in the close all
positions view, matching the single position close behavior.
https://github.com/user-attachments/assets/76fa2bbc-bc1f-4b15-8655-82df18df8f3f
## **Implementation Details**
### Files Changed
- `app/components/UI/Perps/hooks/usePerpsCloseAllCalculations.ts`
### Key Changes
1. **Added Fee Discount State** (lines 108-109):
```typescript
const [feeDiscountBips, setFeeDiscountBips] = useState<number>(0);
```
2. **Fetch Account-Level Discount** (lines 137-166):
```typescript
useEffect(() => {
async function fetchFeeDiscount() {
const discountBips = await Engine.context.RewardsController.getPerpsDiscountForAccount(caipAccountId);
setFeeDiscountBips(discountBips);
}
fetchFeeDiscount();
}, [selectedAddress, currentChainId]);
```
3. **Apply Discount to Fees** (lines 226-252):
```typescript
const discountMultiplier = feeDiscountBips > 0 ? 1 - feeDiscountBips / 10000 : 1;
const adjustedMetamaskFeeRate = baseFees.metamaskFeeRate * discountMultiplier;
const adjustedMetamaskFeeAmount = baseFees.metamaskFeeAmount !== undefined
? baseFees.metamaskFeeAmount * discountMultiplier
: undefined;
```
4. **Batch Points Estimation** (lines 274-295):
```typescript
const batchEstimateBody: EstimatePointsDto = {
activityType: 'PERPS',
account: caipAccountId,
activityContext: {
perpsContext: perpsContextArray, // Array of all positions
},
};
batchPoints = await Engine.context.RewardsController.estimatePoints(batchEstimateBody);
```
5. **Calculate Discount Percentage** (lines 387-395):
```typescript
const avgOriginalMetamaskFeeRate = feeDiscountBips > 0 && avgMetamaskFeeRate > 0
? avgMetamaskFeeRate / (1 - feeDiscountBips / 10000)
: avgMetamaskFeeRate;
const avgFeeDiscountPercentage = feeDiscountBips > 0 ? feeDiscountBips / 100 : 0;
```
6. **Optimized Dependencies** (line 333):
- Removed `feeDiscountBips` from effect dependencies to prevent
recalculation on every WebSocket position update
- Uses ref-based freeze mechanism (`hasValidResultsRef`) to prevent
repeated calculations
### Performance Improvements
- **Batch API**: Reduced API calls from N+1 (one per position + one for
discount) to 2 total calls
- **Freeze Mechanism**: Prevents slow points computation from
retriggering on WebSocket position updates
## **Known Limitations**
There is a separate UI issue with tooltip z-index in the close position
views where the "Close position" button appears on top of tooltip
modals. This will be addressed in a follow-up PR.
## **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.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds account-level fee discount to close-all flow, switches rewards to
batch estimation, and makes fee rates/price-driven calculations
resilient with UI fallbacks.
>
> - **Perps Close All (hook `usePerpsCloseAllCalculations`)**:
> - Fetches and applies account-level discount
(`RewardsController.getPerpsDiscountForAccount`) to MetaMask fees;
computes original vs discounted rates and avg discount.
> - Migrates rewards to batch points estimation; aggregates
totals/averages and guards errors; introduces `isFetchingInBackground`.
> - Freezes results to avoid WS-induced recomputes; resets on
positions/account/discount changes; robust cleanup/race guards.
> - Treats fee rates/amounts as optional (`undefined`) and propagates
through aggregates; `receiveAmount` falls back when fees unavailable.
> - **Perps Close Single (`PerpsClosePositionView`)**:
> - Stabilizes calculations using price refs; uses limit price when
valid; updates P&L/position value derivations; expands tests for limit
validation and price parsing.
> - **UI Summary (`PerpsCloseSummary`)**:
> - Accepts optional fees/rates; shows `"--"` when unavailable; adds
tooltip interactions; updated tests.
> - **Types & Fees Hook**:
> - Marks `FeeCalculationResult` fee fields as optional;
`usePerpsOrderFees` handles undefined rates (no misleading $0), updates
caching checks, and tests edge cases.
> - **Styling**:
> - Ensures close-all footer stays behind overlays
(`zIndex`/`elevation`).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
bf388c8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Ramon AC <36987446+racitores@users.noreply.github.com>
Co-authored-by: Christopher Ferreira <104831203+christopherferreira9@users.noreply.github.com>
Co-authored-by: javiergarciavera <76975121+javiergarciavera@users.noreply.github.com>
Co-authored-by: Nick Gambino <35090461+gambinish@users.noreply.github.com>
Co-authored-by: George Weiler <georgejweiler@gmail.com>
Co-authored-by: Luis Taniça <matallui@gmail.com>
Co-authored-by: AxelGes <34173844+AxelGes@users.noreply.github.com>
Co-authored-by: Pedro Pablo Aste Kompen <wachunei@gmail.com>
Co-authored-by: Kylan Hurt <6249205+smilingkylan@users.noreply.github.com>
Co-authored-by: SteP-n-s <stylianos.panagakos@consensys.net>
Co-authored-by: Brian August Nguyen <brianacnguyen@gmail.com>
Co-authored-by: tommasini <46944231+tommasini@users.noreply.github.com>
Co-authored-by: Kevin Bluer <kevin@bluer.com>
Co-authored-by: Vince Howard <vincenguyenhoward@gmail.com>
Co-authored-by: Curtis David <Curtis.David7@gmail.com>
Co-authored-by: Caainã Jeronimo <caainaje@gmail.com>1 parent bde8943 commit 84da92c
11 files changed
Lines changed: 855 additions & 208 deletions
File tree
- app/components/UI/Perps
- Views
- PerpsCloseAllPositionsView
- PerpsClosePositionView
- components/PerpsCloseSummary
- controllers/types
- hooks
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
151 | 151 | | |
152 | 152 | | |
153 | 153 | | |
| 154 | + | |
154 | 155 | | |
155 | 156 | | |
156 | 157 | | |
| |||
Lines changed: 93 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1377 | 1377 | | |
1378 | 1378 | | |
1379 | 1379 | | |
| 1380 | + | |
| 1381 | + | |
| 1382 | + | |
| 1383 | + | |
| 1384 | + | |
| 1385 | + | |
| 1386 | + | |
| 1387 | + | |
| 1388 | + | |
| 1389 | + | |
| 1390 | + | |
| 1391 | + | |
| 1392 | + | |
| 1393 | + | |
| 1394 | + | |
| 1395 | + | |
| 1396 | + | |
| 1397 | + | |
| 1398 | + | |
| 1399 | + | |
| 1400 | + | |
| 1401 | + | |
| 1402 | + | |
| 1403 | + | |
| 1404 | + | |
| 1405 | + | |
| 1406 | + | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + | |
| 1410 | + | |
| 1411 | + | |
| 1412 | + | |
| 1413 | + | |
| 1414 | + | |
| 1415 | + | |
| 1416 | + | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
| 1422 | + | |
| 1423 | + | |
| 1424 | + | |
| 1425 | + | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
| 1447 | + | |
| 1448 | + | |
| 1449 | + | |
| 1450 | + | |
| 1451 | + | |
| 1452 | + | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
1380 | 1473 | | |
1381 | 1474 | | |
1382 | 1475 | | |
| |||
Lines changed: 21 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
118 | 123 | | |
119 | 124 | | |
120 | 125 | | |
| |||
157 | 162 | | |
158 | 163 | | |
159 | 164 | | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
164 | 173 | | |
165 | 174 | | |
| 175 | + | |
166 | 176 | | |
167 | 177 | | |
168 | 178 | | |
169 | 179 | | |
170 | 180 | | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
171 | 185 | | |
172 | | - | |
173 | | - | |
| 186 | + | |
| 187 | + | |
174 | 188 | | |
175 | | - | |
| 189 | + | |
176 | 190 | | |
177 | 191 | | |
178 | 192 | | |
| |||
Lines changed: 57 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
205 | 205 | | |
206 | 206 | | |
207 | 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 | + | |
208 | 264 | | |
Lines changed: 11 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
38 | | - | |
39 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
40 | 40 | | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
216 | | - | |
| 216 | + | |
217 | 217 | | |
218 | 218 | | |
219 | 219 | | |
220 | 220 | | |
221 | 221 | | |
222 | 222 | | |
223 | 223 | | |
| 224 | + | |
| 225 | + | |
224 | 226 | | |
225 | 227 | | |
226 | 228 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
622 | 622 | | |
623 | 623 | | |
624 | 624 | | |
625 | | - | |
| 625 | + | |
626 | 626 | | |
627 | 627 | | |
628 | 628 | | |
629 | | - | |
| 629 | + | |
630 | 630 | | |
631 | 631 | | |
632 | 632 | | |
633 | | - | |
| 633 | + | |
634 | 634 | | |
635 | 635 | | |
636 | 636 | | |
| |||
0 commit comments