From f41b05180503ff31d954793a296a164d166a4377 Mon Sep 17 00:00:00 2001 From: Michal Szorad Date: Thu, 13 Aug 2026 11:22:21 +0200 Subject: [PATCH 1/2] feat(perps): persist Pro orders sort prefs Store ordersSortField and ordersSortDirection on proLayoutPreferences so the Orders panel sort survives market navigation and app restarts, matching the positions prefs pattern. --- packages/perps-controller/CHANGELOG.md | 3 ++ .../perps-controller/src/PerpsController.ts | 2 + .../src/constants/perpsConfig.ts | 22 +++++++++-- packages/perps-controller/src/index.ts | 2 + .../src/PerpsController.configuration.test.ts | 38 ++++++++++++++++++- .../tests/src/selectors.test.ts | 4 ++ 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/packages/perps-controller/CHANGELOG.md b/packages/perps-controller/CHANGELOG.md index 58e15499ef..421a3ccaea 100644 --- a/packages/perps-controller/CHANGELOG.md +++ b/packages/perps-controller/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **BREAKING:** Add `ordersSortField` and `ordersSortDirection` to the flat `ProLayoutPreferences` object (defaults `'time'`, `'desc'`) so Pro Orders panel sort preferences persist across markets and app restarts via the existing `getProLayoutPreferences()` / `setProLayoutPreferences(patch)` API; export `ProOrdersSortField` and `ProOrdersSortDirection` + - Consumers that construct a full `ProLayoutPreferences` object (instead of using `DEFAULT_PRO_LAYOUT_PREFERENCES`, the getter, or the patch setter) must include the new fields. Persisted state that predates them remains valid at runtime because the getter/selector merge over defaults. + - The Positions/Orders side filter remains the shared `positionsSideFilter` field. Orders sort is independent of positions sort (`orderValue` | `size` | `price` | `time`). - **BREAKING:** Add `positionsSideFilter`, `positionsSortField`, and `positionsSortDirection` to the flat `ProLayoutPreferences` object (defaults `'all'`, `'positionValue'`, `'desc'`) so Pro Positions/Orders panel sort and side-filter preferences persist across markets and app restarts via the existing `getProLayoutPreferences()` / `setProLayoutPreferences(patch)` API; export `ProPositionsSideFilter`, `ProPositionsSortField`, and `ProPositionsSortDirection` ([#9838](https://github.com/MetaMask/core/pull/9838)) - Consumers that construct a full `ProLayoutPreferences` object (instead of using `DEFAULT_PRO_LAYOUT_PREFERENCES`, the getter, or the patch setter) must include the new fields. Persisted state that predates them remains valid at runtime because the getter/selector merge over defaults. - **BREAKING:** Add strategy placement order types to `OrderType`: `twap`, `scale`, and `chase`, placeable through `placeOrder` alongside the existing `market`, `limit`, and trigger types ([#9832](https://github.com/MetaMask/core/pull/9832)) diff --git a/packages/perps-controller/src/PerpsController.ts b/packages/perps-controller/src/PerpsController.ts index 84d0d20ca7..acd12da354 100644 --- a/packages/perps-controller/src/PerpsController.ts +++ b/packages/perps-controller/src/PerpsController.ts @@ -297,6 +297,8 @@ export { } from './constants/perpsConfig.js'; export type { ProLayoutPreferences, + ProOrdersSortDirection, + ProOrdersSortField, ProPositionsSideFilter, ProPositionsSortDirection, ProPositionsSortField, diff --git a/packages/perps-controller/src/constants/perpsConfig.ts b/packages/perps-controller/src/constants/perpsConfig.ts index 05807168c0..e368ac5af0 100644 --- a/packages/perps-controller/src/constants/perpsConfig.ts +++ b/packages/perps-controller/src/constants/perpsConfig.ts @@ -506,15 +506,25 @@ export type ProPositionsSortField = */ export type ProPositionsSortDirection = 'asc' | 'desc'; +/** + * Sort fields available on the Pro Orders list. + */ +export type ProOrdersSortField = 'orderValue' | 'size' | 'price' | 'time'; + +/** + * Sort direction for the Pro Orders list. + */ +export type ProOrdersSortDirection = 'asc' | 'desc'; + /** * Pro-mode layout preferences (network-independent). * * Flat object that persists across markets (unlike the per-market * `tradeConfigurations`). `chartExpanded` and the `*Position` fields are - * reserved for future container-position UI. `positionsSideFilter` / - * `positionsSortField` / `positionsSortDirection` back the Positions/Orders - * panel sort and side filter so they survive market navigation and app - * restarts. + * reserved for future container-position UI. `positionsSideFilter` is the + * shared Positions/Orders side filter. `positionsSortField` / + * `positionsSortDirection` and `ordersSortField` / `ordersSortDirection` + * back each list's sort so they survive market navigation and app restarts. */ export type ProLayoutPreferences = { orderBookExpanded: boolean; @@ -524,6 +534,8 @@ export type ProLayoutPreferences = { positionsSideFilter: ProPositionsSideFilter; positionsSortField: ProPositionsSortField; positionsSortDirection: ProPositionsSortDirection; + ordersSortField: ProOrdersSortField; + ordersSortDirection: ProOrdersSortDirection; }; /** @@ -541,6 +553,8 @@ export const DEFAULT_PRO_LAYOUT_PREFERENCES: ProLayoutPreferences = { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSortField: 'time', + ordersSortDirection: 'desc', }; /** diff --git a/packages/perps-controller/src/index.ts b/packages/perps-controller/src/index.ts index 635231ec6b..62e09b7cb4 100644 --- a/packages/perps-controller/src/index.ts +++ b/packages/perps-controller/src/index.ts @@ -44,6 +44,8 @@ export type { PerpsControllerActions, PerpsControllerEvents, ProLayoutPreferences, + ProOrdersSortDirection, + ProOrdersSortField, ProPositionsSideFilter, ProPositionsSortDirection, ProPositionsSortField, diff --git a/packages/perps-controller/tests/src/PerpsController.configuration.test.ts b/packages/perps-controller/tests/src/PerpsController.configuration.test.ts index d5de543eb8..601dfb0481 100644 --- a/packages/perps-controller/tests/src/PerpsController.configuration.test.ts +++ b/packages/perps-controller/tests/src/PerpsController.configuration.test.ts @@ -694,7 +694,7 @@ describe('PerpsController', () => { }); describe('pro layout preferences', () => { - it('defaults to collapsed order book, collapsed chart, reserved positions, and positions sort/filter defaults', () => { + it('defaults to collapsed order book, collapsed chart, reserved positions, and positions/orders sort/filter defaults', () => { expect(controller.getProLayoutPreferences()).toEqual({ orderBookExpanded: false, chartExpanded: false, @@ -703,6 +703,8 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSortField: 'time', + ordersSortDirection: 'desc', }); }); @@ -717,6 +719,8 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSortField: 'time', + ordersSortDirection: 'desc', }); }); @@ -729,6 +733,10 @@ describe('PerpsController', () => { positionsSortField: 'unrealizedPnl', positionsSortDirection: 'asc', }); + controller.setProLayoutPreferences({ + ordersSortField: 'orderValue', + ordersSortDirection: 'asc', + }); expect(controller.getProLayoutPreferences()).toEqual({ orderBookExpanded: true, @@ -738,6 +746,8 @@ describe('PerpsController', () => { positionsSideFilter: 'long', positionsSortField: 'unrealizedPnl', positionsSortDirection: 'asc', + ordersSortField: 'orderValue', + ordersSortDirection: 'asc', }); }); @@ -758,6 +768,30 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'unrealizedPnl', positionsSortDirection: 'asc', + ordersSortField: 'time', + ordersSortDirection: 'desc', + }); + }); + + it('updates orders sort field without clobbering orders sort direction or positions sort', () => { + controller.setProLayoutPreferences({ + ordersSortField: 'size', + ordersSortDirection: 'asc', + }); + controller.setProLayoutPreferences({ + ordersSortField: 'price', + }); + + expect(controller.getProLayoutPreferences()).toEqual({ + orderBookExpanded: false, + chartExpanded: false, + orderBookPosition: 'left', + orderFormPosition: 'right', + positionsSideFilter: 'all', + positionsSortField: 'positionValue', + positionsSortDirection: 'desc', + ordersSortField: 'price', + ordersSortDirection: 'asc', }); }); @@ -783,6 +817,8 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSortField: 'time', + ordersSortDirection: 'desc', }); }); }); diff --git a/packages/perps-controller/tests/src/selectors.test.ts b/packages/perps-controller/tests/src/selectors.test.ts index 01983cd655..2e1a288c84 100644 --- a/packages/perps-controller/tests/src/selectors.test.ts +++ b/packages/perps-controller/tests/src/selectors.test.ts @@ -639,6 +639,8 @@ describe('PerpsController selectors', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSortField: 'time', + ordersSortDirection: 'desc', }; it('returns the pro-mode layout preferences', () => { @@ -650,6 +652,8 @@ describe('PerpsController selectors', () => { positionsSideFilter: 'long' as const, positionsSortField: 'unrealizedPnl' as const, positionsSortDirection: 'asc' as const, + ordersSortField: 'orderValue' as const, + ordersSortDirection: 'asc' as const, }; const state = { proLayoutPreferences, From f05c43524a99bac27c3cc9d009fab3f35d7eae34 Mon Sep 17 00:00:00 2001 From: Michal Szorad Date: Thu, 13 Aug 2026 11:30:43 +0200 Subject: [PATCH 2/2] feat(perps): persist independent Pro orders side filter Store ordersSideFilter separately from positionsSideFilter so Positions and Orders can keep different long/short/all prefs. --- packages/perps-controller/CHANGELOG.md | 4 +-- .../perps-controller/src/PerpsController.ts | 1 + .../src/constants/perpsConfig.ts | 22 +++++++++++----- packages/perps-controller/src/index.ts | 1 + .../src/PerpsController.configuration.test.ts | 25 +++++++++++++++++++ .../tests/src/selectors.test.ts | 2 ++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/perps-controller/CHANGELOG.md b/packages/perps-controller/CHANGELOG.md index 421a3ccaea..fe74d5cc0a 100644 --- a/packages/perps-controller/CHANGELOG.md +++ b/packages/perps-controller/CHANGELOG.md @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- **BREAKING:** Add `ordersSortField` and `ordersSortDirection` to the flat `ProLayoutPreferences` object (defaults `'time'`, `'desc'`) so Pro Orders panel sort preferences persist across markets and app restarts via the existing `getProLayoutPreferences()` / `setProLayoutPreferences(patch)` API; export `ProOrdersSortField` and `ProOrdersSortDirection` +- **BREAKING:** Add `ordersSideFilter`, `ordersSortField`, and `ordersSortDirection` to the flat `ProLayoutPreferences` object (defaults `'all'`, `'time'`, `'desc'`) so Pro Orders panel side-filter and sort preferences persist independently of Positions across markets and app restarts via the existing `getProLayoutPreferences()` / `setProLayoutPreferences(patch)` API; export `ProOrdersSideFilter`, `ProOrdersSortField`, and `ProOrdersSortDirection` ([#9862](https://github.com/MetaMask/core/pull/9862)) - Consumers that construct a full `ProLayoutPreferences` object (instead of using `DEFAULT_PRO_LAYOUT_PREFERENCES`, the getter, or the patch setter) must include the new fields. Persisted state that predates them remains valid at runtime because the getter/selector merge over defaults. - - The Positions/Orders side filter remains the shared `positionsSideFilter` field. Orders sort is independent of positions sort (`orderValue` | `size` | `price` | `time`). + - Orders side filter (`all` | `long` | `short`) is independent of `positionsSideFilter`. Orders sort fields are `orderValue` | `size` | `price` | `time`. - **BREAKING:** Add `positionsSideFilter`, `positionsSortField`, and `positionsSortDirection` to the flat `ProLayoutPreferences` object (defaults `'all'`, `'positionValue'`, `'desc'`) so Pro Positions/Orders panel sort and side-filter preferences persist across markets and app restarts via the existing `getProLayoutPreferences()` / `setProLayoutPreferences(patch)` API; export `ProPositionsSideFilter`, `ProPositionsSortField`, and `ProPositionsSortDirection` ([#9838](https://github.com/MetaMask/core/pull/9838)) - Consumers that construct a full `ProLayoutPreferences` object (instead of using `DEFAULT_PRO_LAYOUT_PREFERENCES`, the getter, or the patch setter) must include the new fields. Persisted state that predates them remains valid at runtime because the getter/selector merge over defaults. - **BREAKING:** Add strategy placement order types to `OrderType`: `twap`, `scale`, and `chase`, placeable through `placeOrder` alongside the existing `market`, `limit`, and trigger types ([#9832](https://github.com/MetaMask/core/pull/9832)) diff --git a/packages/perps-controller/src/PerpsController.ts b/packages/perps-controller/src/PerpsController.ts index acd12da354..b097cc91ba 100644 --- a/packages/perps-controller/src/PerpsController.ts +++ b/packages/perps-controller/src/PerpsController.ts @@ -297,6 +297,7 @@ export { } from './constants/perpsConfig.js'; export type { ProLayoutPreferences, + ProOrdersSideFilter, ProOrdersSortDirection, ProOrdersSortField, ProPositionsSideFilter, diff --git a/packages/perps-controller/src/constants/perpsConfig.ts b/packages/perps-controller/src/constants/perpsConfig.ts index e368ac5af0..34fafcbf82 100644 --- a/packages/perps-controller/src/constants/perpsConfig.ts +++ b/packages/perps-controller/src/constants/perpsConfig.ts @@ -487,9 +487,10 @@ export enum PerpsMode { } /** - * Side filter for the Pro Positions/Orders panel (long/short/all). + * Side filter for the Pro Positions list (long/short/all). * - * Shared across markets via `proLayoutPreferences.positionsSideFilter`. + * Independent of `ordersSideFilter`. Shared across markets via + * `proLayoutPreferences.positionsSideFilter`. */ export type ProPositionsSideFilter = 'all' | 'long' | 'short'; @@ -506,6 +507,14 @@ export type ProPositionsSortField = */ export type ProPositionsSortDirection = 'asc' | 'desc'; +/** + * Side filter for the Pro Orders list (long/short/all). + * + * Independent of `positionsSideFilter`. Shared across markets via + * `proLayoutPreferences.ordersSideFilter`. + */ +export type ProOrdersSideFilter = 'all' | 'long' | 'short'; + /** * Sort fields available on the Pro Orders list. */ @@ -521,10 +530,9 @@ export type ProOrdersSortDirection = 'asc' | 'desc'; * * Flat object that persists across markets (unlike the per-market * `tradeConfigurations`). `chartExpanded` and the `*Position` fields are - * reserved for future container-position UI. `positionsSideFilter` is the - * shared Positions/Orders side filter. `positionsSortField` / - * `positionsSortDirection` and `ordersSortField` / `ordersSortDirection` - * back each list's sort so they survive market navigation and app restarts. + * reserved for future container-position UI. Positions and Orders each have + * their own side filter and sort so they survive market navigation and app + * restarts independently. */ export type ProLayoutPreferences = { orderBookExpanded: boolean; @@ -534,6 +542,7 @@ export type ProLayoutPreferences = { positionsSideFilter: ProPositionsSideFilter; positionsSortField: ProPositionsSortField; positionsSortDirection: ProPositionsSortDirection; + ordersSideFilter: ProOrdersSideFilter; ordersSortField: ProOrdersSortField; ordersSortDirection: ProOrdersSortDirection; }; @@ -553,6 +562,7 @@ export const DEFAULT_PRO_LAYOUT_PREFERENCES: ProLayoutPreferences = { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }; diff --git a/packages/perps-controller/src/index.ts b/packages/perps-controller/src/index.ts index 62e09b7cb4..b9cb86d190 100644 --- a/packages/perps-controller/src/index.ts +++ b/packages/perps-controller/src/index.ts @@ -44,6 +44,7 @@ export type { PerpsControllerActions, PerpsControllerEvents, ProLayoutPreferences, + ProOrdersSideFilter, ProOrdersSortDirection, ProOrdersSortField, ProPositionsSideFilter, diff --git a/packages/perps-controller/tests/src/PerpsController.configuration.test.ts b/packages/perps-controller/tests/src/PerpsController.configuration.test.ts index 601dfb0481..2d603e754c 100644 --- a/packages/perps-controller/tests/src/PerpsController.configuration.test.ts +++ b/packages/perps-controller/tests/src/PerpsController.configuration.test.ts @@ -703,6 +703,7 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }); @@ -719,6 +720,7 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }); @@ -734,6 +736,7 @@ describe('PerpsController', () => { positionsSortDirection: 'asc', }); controller.setProLayoutPreferences({ + ordersSideFilter: 'short', ordersSortField: 'orderValue', ordersSortDirection: 'asc', }); @@ -746,6 +749,7 @@ describe('PerpsController', () => { positionsSideFilter: 'long', positionsSortField: 'unrealizedPnl', positionsSortDirection: 'asc', + ordersSideFilter: 'short', ordersSortField: 'orderValue', ordersSortDirection: 'asc', }); @@ -768,6 +772,7 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'unrealizedPnl', positionsSortDirection: 'asc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }); @@ -790,11 +795,30 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'price', ordersSortDirection: 'asc', }); }); + it('updates orders side filter without clobbering positions side filter', () => { + controller.setProLayoutPreferences({ positionsSideFilter: 'long' }); + controller.setProLayoutPreferences({ ordersSideFilter: 'short' }); + + expect(controller.getProLayoutPreferences()).toEqual({ + orderBookExpanded: false, + chartExpanded: false, + orderBookPosition: 'left', + orderFormPosition: 'right', + positionsSideFilter: 'long', + positionsSortField: 'positionValue', + positionsSortDirection: 'desc', + ordersSideFilter: 'short', + ordersSortField: 'time', + ordersSortDirection: 'desc', + }); + }); + it('persists the update to controller state', () => { controller.setProLayoutPreferences({ chartExpanded: true }); @@ -817,6 +841,7 @@ describe('PerpsController', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }); diff --git a/packages/perps-controller/tests/src/selectors.test.ts b/packages/perps-controller/tests/src/selectors.test.ts index 2e1a288c84..b1b309013c 100644 --- a/packages/perps-controller/tests/src/selectors.test.ts +++ b/packages/perps-controller/tests/src/selectors.test.ts @@ -639,6 +639,7 @@ describe('PerpsController selectors', () => { positionsSideFilter: 'all', positionsSortField: 'positionValue', positionsSortDirection: 'desc', + ordersSideFilter: 'all', ordersSortField: 'time', ordersSortDirection: 'desc', }; @@ -652,6 +653,7 @@ describe('PerpsController selectors', () => { positionsSideFilter: 'long' as const, positionsSortField: 'unrealizedPnl' as const, positionsSortDirection: 'asc' as const, + ordersSideFilter: 'short' as const, ordersSortField: 'orderValue' as const, ordersSortDirection: 'asc' as const, };