Skip to content

Commit c16d9c7

Browse files
authored
fix(perps): show long orders in Activity and dedupe perps deposits + clean up (#33235)
## **Description** Fixes to the redesigned Perps Activity 1. Long orders were missing from Perps → Orders (TMCU-1070). The activity adapter only modelled short order kinds and dropped every long-side order (it string-matched the display title and returned null for anything containing "long"/"buy"). A user who placed long limit/market/stop orders saw them under Trades but never under Orders (e.g. MetaMask showed 3 orders where HyperLiquid had 10+). 2. Perps deposits double-rendered (TMCU-1066). A HyperLiquid deposit is a real Arbitrum USDC transaction, so it appeared once under Perps → Deposits (as a perps deposit) and again under Transactions (as a generic "Interaction in progress"). The generic EVM copy of perpsDeposit/perpsDepositAndOrder/perpsWithdraw transactions is now suppressed from the generic stream when Perps is enabled (the perps source already surfaces them); the confirmed API copy is still collapsed by the existing hash-based dedup. 3. Order status now reads as an order lifecycle, not a blockchain status (TMCU-1072). Both surfaces updated to mirror the extension's Orders view: - List row: shows the lifecycle status (Filled / Canceled / Rejected / Open), muted, in the right column instead of a misleading notional amount (the notional duplicated the size subtitle and was wrong for canceled/rejected orders). Order titles also drop the "— Canceled"/"— Failed" suffix since the status now lives in the right column. - Details screen: the order Status row shows Filled / Canceled / Rejected / Open instead of the blockchain-oriented Confirmed / Failed. Scoped to orders only — sends/swaps/trades/funding/deposits keep "Confirmed". 4. Duplicate filter bar (TMCU-1063). Switching filters after scrolling briefly rendered two filter rows — the pinned bar wasn't unpinning because scrollY wasn't reset on the programmatic scroll-to-top. Now reset on filter change. ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: Fixed Perps long and limit orders not appearing in the Activity "Orders" filter, and stopped HyperLiquid deposits from also showing as generic entries under the "Transactions" filter. ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-1070 Fixes: https://consensyssoftware.atlassian.net/browse/TMCU-1066 Refs: https://consensyssoftware.atlassian.net/browse/TMCU-1072 Refs: https://consensyssoftware.atlassian.net/browse/TMCU-1063 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> ```gherkin Feature: Perps orders in the Activity feed Scenario: Long orders appear under the Orders filter with a lifecycle status Given I have placed long-side perps orders on HyperLiquid (market, limit, and/or a triggered stop) When I open Activity and select Perps → Order Then I see the long orders (e.g. "Market long", "Market — close long", "Limit long") And each row shows Filled / Canceled / Rejected on the right, muted, instead of a dollar amount And short orders continue to appear unchanged Scenario: Order details use order-lifecycle status, not blockchain status Given I open the details for a filled perps order Then the Status row reads "Filled" (not "Confirmed") And a non-order transaction's details still read "Confirmed" Scenario: A perps deposit is not duplicated into the Transactions filter Given I fund a perps position, creating a HyperLiquid (Arbitrum USDC) deposit When I open Activity Then the deposit appears once under Perps → Deposits And it does not also appear as a generic "Interaction in progress" under the Transactions filter Scenario: Filter bar does not duplicate Given I open Activity and scroll down When I switch the filter to Perps Then only one filter row is shown ``` ## **Screenshots/Recordings** - **Long orders were missing from Perps → Orders [TMCU-1070](https://consensyssoftware.atlassian.net/browse/TMCU-1070)** - **Order rows now show their lifecycle status [TMCU-1072](https://consensyssoftware.atlassian.net/browse/TMCU-1072)** | Before | After | | --- | --- | | <img alt="orders_before" src="https://github.com/user-attachments/assets/ad81a6d2-0aa4-4351-a0b5-6e66be4656b9" /> | <img alt="orders_after" src="https://github.com/user-attachments/assets/10522f2b-1a0c-46dc-a239-f14255679b20" /> | | Before | After | | --- | --- | | <img alt="oders_details_before" src="https://github.com/user-attachments/assets/12a7e4df-1310-4f45-8395-531629a792f5" /> | <img alt="oders_details_after" src="https://github.com/user-attachments/assets/4cc1c519-e072-4f7b-933c-53c81855ee5d" /> | - **Perps deposits double-rendered [TMCU-1066](https://consensyssoftware.atlassian.net/browse/TMCU-1066)** **Before** https://github.com/user-attachments/assets/af4be45c-d082-410a-a8a0-e41170717ed4 **After** https://github.com/user-attachments/assets/6c61c3b9-3bc0-45e0-aa43-3c41344e2af2 - **Fixed the double filter bar [TMCU-1063](https://consensyssoftware.atlassian.net/browse/TMCU-163)** **Before** https://github.com/user-attachments/assets/42d5b9bd-eac8-4826-8a4a-083bbbd15d21 **After** https://github.com/user-attachments/assets/47203864-22d2-4487-be7a-9b338923289b ### **Before** `~` ### **After** `~` ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [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 - [ ] 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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] 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] > **Medium Risk** > Touches activity list merging/dedup and perps order classification across list, adapter, and details; mistakes could hide wallet txs or mis-bucket orders, but behavior is heavily covered by new tests. > > **Overview** > Fixes **Perps Activity** so long orders appear under **Orders**, deposits aren’t duplicated, and order rows match extension-style lifecycle labels. > > **Long orders (TMCU-1070):** `mapPerpsTransaction` no longer drops long orders or infers kind from the display title. It classifies orders from structured `side`, `reduceOnly`, `isTrigger`, and limit/market/stop signals via shared `orderDirection` helpers (aligned with `formatOrderLabel`). New activity kinds (`marketLong`, `limitLong`, close/stop variants) are added to `PERPS_ORDER_KINDS` / `isPerpsOrderKind` and wired through titles, icons, filters, and details routing. `transformOrdersToTransactions` now passes those fields on the normalized `order` payload. > > **Deposit dedupe (TMCU-1066):** When Perps is enabled, local EVM rows for `perpsDeposit`, `perpsDepositAndOrder`, and `perpsWithdraw` (including batch-wrapped and speed-up/cancel groups) are filtered out so only the perps-domain row shows. > > **Order row UX:** List rows show **Filled / Canceled / Rejected / Open** (muted) in the amount column instead of notional USD; order titles drop the `— Canceled` suffix. Details use the same `resolvePerpsOrderStatusLabel` on the status pill (e.g. Filled vs Confirmed). > > **Other:** Resets `scrollY` when the activity type filter changes to avoid a double pinned filter bar; expands tests and i18n for long order titles. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a9525a5. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [TMCU-1070]: https://consensyssoftware.atlassian.net/browse/TMCU-1070?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 5baa9a2 commit c16d9c7

27 files changed

Lines changed: 771 additions & 245 deletions

app/components/UI/ActivityListItemRow/ActivityListItemRow.styles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ export const createStyles = (
9898
listItemAmountLoss: {
9999
color: colors.error.default,
100100
} as TextStyle,
101+
listItemAmountMuted: {
102+
color: colors.text.muted,
103+
} as TextStyle,
101104
listItemSecondaryAmount: {
102105
...typography.sBodySM,
103106
fontFamily: getFontFamily(TextVariant.BodySM),

app/components/UI/ActivityListItemRow/ActivityListItemRow.test.tsx

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -796,19 +796,15 @@ describe('ActivityListItemRow — row content', () => {
796796
/>,
797797
).getByTestId('activity-title-0xfail');
798798

799-
// Cancelled → same neutral color as an ordinary row; the spaced " — Canceled"
800-
// suffix still conveys the status. Failed → a distinct (error) color.
801799
expect(flattenColor(cancelled)).toBe(flattenColor(neutral));
802800
expect(flattenColor(failed)).not.toBe(flattenColor(neutral));
803801
expect(cancelled.props.children).toBe(
804-
`${strings('transactions.activity_limit_short')}${strings(
805-
'transaction.canceled',
806-
)}`,
802+
strings('transactions.activity_limit_short'),
807803
);
808804
});
809805

810-
it('renders limit orders with the market logo, no network badge, and a $ amount', () => {
811-
const { getByTestId } = render(
806+
it('renders limit orders with the market logo, no network badge, and a status label (not a notional amount)', () => {
807+
const { getByTestId, queryByTestId } = render(
812808
<ActivityListItemRow
813809
item={makeLimitOrder('cancelled', '0xlimit')}
814810
index={0}
@@ -820,14 +816,89 @@ describe('ActivityListItemRow — row content', () => {
820816
expect(logo.props.symbol).toBe('BTC');
821817
// Perps is single-network (Arbitrum) → no network badge is resolved.
822818
expect(getNetworkImageSource).not.toHaveBeenCalled();
823-
// Fiat-style amount ("$"), not the "<amount> USD" token suffix.
824-
const primary = getByTestId('activity-primary-amount-0xlimit').props
825-
.children as string;
826-
expect(primary).toContain('$');
827-
expect(primary).not.toContain('USD');
828-
expect(primary.startsWith('-')).toBe(true);
819+
820+
expect(queryByTestId('activity-secondary-amount-0xlimit')).toBeNull();
821+
expect(getByTestId('activity-primary-amount-0xlimit').props.children).toBe(
822+
strings('transactions.activity_order_status_canceled'),
823+
);
829824
});
830825

826+
it.each([
827+
['success', 'activity_order_status_filled'],
828+
['cancelled', 'activity_order_status_canceled'],
829+
['failed', 'activity_order_status_rejected'],
830+
['pending', 'activity_order_status_open'],
831+
] as const)(
832+
'shows the order status label "%s" in the right column instead of an amount',
833+
(status, i18nKey) => {
834+
const { getByTestId, queryByTestId } = render(
835+
<ActivityListItemRow
836+
item={makeLimitOrder(status, `0x${status}`)}
837+
index={0}
838+
/>,
839+
);
840+
841+
expect(queryByTestId(`activity-secondary-amount-0x${status}`)).toBeNull();
842+
expect(
843+
getByTestId(`activity-primary-amount-0x${status}`).props.children,
844+
).toBe(strings(`transactions.${i18nKey}`));
845+
},
846+
);
847+
848+
it('renders the order status muted (matches the extension TextMuted), distinct from a normal amount color', () => {
849+
const orderStatus = flattenColor(
850+
render(
851+
<ActivityListItemRow
852+
item={makeLimitOrder('success', '0xmuted')}
853+
index={0}
854+
/>,
855+
).getByTestId('activity-primary-amount-0xmuted'),
856+
);
857+
const normalAmount = flattenColor(
858+
render(
859+
<ActivityListItemRow
860+
item={makeItem({
861+
type: 'send',
862+
status: 'success',
863+
token: { amount: '1', symbol: 'ETH', direction: 'out' },
864+
})}
865+
index={1}
866+
/>,
867+
).getByTestId('activity-primary-amount-0xabc'),
868+
);
869+
870+
// Muted status color is applied and differs from the default amount color.
871+
expect(orderStatus).not.toBe(normalAmount);
872+
});
873+
874+
it.each([
875+
['cancelled', 'transaction.canceled'],
876+
['failed', 'transaction.failed'],
877+
] as const)(
878+
'keeps the "— status" suffix on non-order domain rows (%s deposit)',
879+
(status, i18nKey) => {
880+
const { getByTestId } = render(
881+
<ActivityListItemRow
882+
item={makeItem({
883+
type: 'perpsAddFunds',
884+
status,
885+
hash: `0xfunds-${status}`,
886+
})}
887+
index={0}
888+
/>,
889+
);
890+
891+
// Unlike orders, funds/predict rows still carry the status in the title.
892+
expect(
893+
getByTestId(`activity-title-0xfunds-${status}`).props.children,
894+
).toBe(
895+
`${strings('transactions.activity_perps_account_funded')}${strings(
896+
i18nKey,
897+
)}`,
898+
);
899+
},
900+
);
901+
831902
it('colors a perps trade loss with the error color and a gain with the incoming color', () => {
832903
const makeClose = (
833904
direction: 'in' | 'out',
@@ -851,16 +922,9 @@ describe('ActivityListItemRow — row content', () => {
851922
const gain = render(
852923
<ActivityListItemRow item={makeClose('in', '0xgain')} index={1} />,
853924
).getByTestId('activity-primary-amount-0xgain');
854-
// An order amount (notional, not PnL) stays neutral even though outgoing.
855-
const order = render(
856-
<ActivityListItemRow
857-
item={makeLimitOrder('cancelled', '0xorder')}
858-
index={2}
859-
/>,
860-
).getByTestId('activity-primary-amount-0xorder');
861925

862926
// Loss shares the error color used for a failed title; gain shares the green
863-
// used for an incoming receive amount; the order amount matches neither.
927+
// used for an incoming receive amount.
864928
const errorColor = flattenColor(
865929
render(
866930
<ActivityListItemRow
@@ -884,8 +948,6 @@ describe('ActivityListItemRow — row content', () => {
884948

885949
expect(flattenColor(loss)).toBe(errorColor);
886950
expect(flattenColor(gain)).toBe(incomingColor);
887-
expect(flattenColor(order)).not.toBe(errorColor);
888-
expect(flattenColor(order)).not.toBe(incomingColor);
889951
});
890952

891953
it('strips the HyperLiquid builder prefix from the trade subtitle', () => {
@@ -1763,6 +1825,11 @@ const ALL_KINDS: ActivityListItem['type'][] = [
17631825
'marketCloseShort',
17641826
'limitShort',
17651827
'limitCloseShort',
1828+
'marketLong',
1829+
'stopMarketCloseLong',
1830+
'marketCloseLong',
1831+
'limitLong',
1832+
'limitCloseLong',
17661833
];
17671834

17681835
const EXPECTED_TITLES = {
@@ -1838,6 +1905,11 @@ const EXPECTED_TITLES = {
18381905
marketCloseShort: strings('transactions.activity_market_close_short'),
18391906
limitShort: strings('transactions.activity_limit_short'),
18401907
limitCloseShort: strings('transactions.activity_limit_close_short'),
1908+
marketLong: strings('transactions.activity_market_long'),
1909+
stopMarketCloseLong: strings('transactions.activity_stop_market_close_long'),
1910+
marketCloseLong: strings('transactions.activity_market_close_long'),
1911+
limitLong: strings('transactions.activity_limit_long'),
1912+
limitCloseLong: strings('transactions.activity_limit_close_long'),
18411913
} satisfies Record<ActivityListItem['type'], string>;
18421914

18431915
describe('ActivityListItemRow — title display for all ActivityKind values', () => {

app/components/UI/ActivityListItemRow/ActivityListItemRow.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import { resolveTransactionIconName } from './resolveIconType';
1313
import { useActivityListItemRowContent } from './useActivityListItemRowContent';
1414
import { useNftActivityImage } from './useNftActivityImage';
1515
import type { ActivityListItemRowProps } from './ActivityListItemRow.types';
16-
import type { ActivityKind } from '../../../util/activity-adapters';
16+
import {
17+
isPerpsOrderKind,
18+
type ActivityKind,
19+
} from '../../../util/activity-adapters';
1720

1821
export { resolveActivityListItemTitle } from './useActivityListItemRowContent';
1922
export { resolveIconType } from './resolveIconType';
@@ -26,9 +29,7 @@ function isSingleNetworkDomainKind(type: ActivityKind): boolean {
2629
return (
2730
type.startsWith('perps') ||
2831
type.startsWith('prediction') ||
29-
type.startsWith('market') ||
30-
type.startsWith('limit') ||
31-
type.startsWith('stopMarket')
32+
isPerpsOrderKind(type)
3233
);
3334
}
3435

@@ -100,6 +101,7 @@ function ResolvedActivityListItemRow({
100101
primaryAmount={content.primaryAmount}
101102
primaryToken={content.primaryToken}
102103
amountIsPnl={content.isPnlAmount}
104+
amountIsMuted={content.isMutedAmount}
103105
secondaryAmount={content.secondaryAmount}
104106
styles={styles}
105107
subtitle={content.subtitle}

app/components/UI/ActivityListItemRow/ActivityListItemRow.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ export interface ActivityListItemRowContent {
4444
avatarIconUrl?: string;
4545
perpsMarketSymbol?: string;
4646
isPnlAmount?: boolean;
47+
isMutedAmount?: boolean;
4748
}

app/components/UI/ActivityListItemRow/ActivityListItemRowLayout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function ActivityListItemRowLayout({
1818
primaryAmount,
1919
primaryToken,
2020
amountIsPnl,
21+
amountIsMuted,
2122
secondaryAmount,
2223
styles,
2324
subtitle,
@@ -35,6 +36,7 @@ export function ActivityListItemRowLayout({
3536
primaryAmount?: string;
3637
primaryToken?: TokenAmount;
3738
amountIsPnl?: boolean;
39+
amountIsMuted?: boolean;
3840
secondaryAmount?: string;
3941
styles: ActivityListItemRowStyles;
4042
subtitle?: string;
@@ -98,6 +100,7 @@ export function ActivityListItemRowLayout({
98100
amountIsPnl &&
99101
primaryToken?.direction === 'out' &&
100102
styles.listItemAmountLoss,
103+
amountIsMuted && styles.listItemAmountMuted,
101104
]}
102105
testID={`activity-primary-amount-${testIdSuffix}`}
103106
>

app/components/UI/ActivityListItemRow/resolveIconType.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { IconName } from '@metamask/design-system-react-native';
2-
import type { ActivityKind } from '../../../util/activity-adapters';
2+
import {
3+
isPerpsOrderKind,
4+
type ActivityKind,
5+
} from '../../../util/activity-adapters';
36

47
/**
58
* Maps an activity kind to the transaction icon family used by
69
* `getTransactionIcon`. Shared by the resolved and pending row variants.
710
*/
811
export function resolveIconType(type: ActivityKind): string {
12+
if (isPerpsOrderKind(type)) {
13+
return 'interaction';
14+
}
15+
916
switch (type) {
1017
case 'send':
1118
case 'sell':
@@ -55,11 +62,6 @@ export function resolveIconType(type: ActivityKind): string {
5562
case 'perpsPaidFundingFees':
5663
case 'perpsCloseShortTakeProfit':
5764
case 'perpsCloseLongTakeProfit':
58-
case 'marketShort':
59-
case 'stopMarketCloseShort':
60-
case 'marketCloseShort':
61-
case 'limitShort':
62-
case 'limitCloseShort':
6365
return 'interaction';
6466
}
6567
}

app/components/UI/ActivityListItemRow/titleLabels.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
import { strings } from '../../../../locales/i18n';
2-
import type { ActivityKind } from '../../../util/activity-adapters';
2+
import type { ActivityKind, Status } from '../../../util/activity-adapters';
3+
4+
/**
5+
* Lifecycle label for perps order rows (Filled / Canceled / Rejected / Open).
6+
* Shared by the Activity list row (amount slot) and the details status pill so
7+
* the two surfaces can't drift.
8+
*/
9+
export function resolvePerpsOrderStatusLabel(status: Status): string {
10+
switch (status) {
11+
case 'cancelled':
12+
return strings('transactions.activity_order_status_canceled');
13+
case 'failed':
14+
return strings('transactions.activity_order_status_rejected');
15+
case 'pending':
16+
return strings('transactions.activity_order_status_open');
17+
default:
18+
return strings('transactions.activity_order_status_filled');
19+
}
20+
}
321

422
/**
523
* Fallback title resolvers for activity kinds whose title comes straight from an
@@ -45,6 +63,12 @@ export const ACTIVITY_FALLBACK_TITLE_RESOLVERS: Partial<
4563
marketCloseShort: () => strings('transactions.activity_market_close_short'),
4664
limitShort: () => strings('transactions.activity_limit_short'),
4765
limitCloseShort: () => strings('transactions.activity_limit_close_short'),
66+
marketLong: () => strings('transactions.activity_market_long'),
67+
stopMarketCloseLong: () =>
68+
strings('transactions.activity_stop_market_close_long'),
69+
marketCloseLong: () => strings('transactions.activity_market_close_long'),
70+
limitLong: () => strings('transactions.activity_limit_long'),
71+
limitCloseLong: () => strings('transactions.activity_limit_close_long'),
4872
};
4973

5074
/**

app/components/UI/ActivityListItemRow/useActivityListItemRowContent.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ import {
2929
getDisplaySignPrefix,
3030
getHumanReadableTokenAmount,
3131
isFailedOrCancelledTransfer,
32+
isPerpsOrderKind,
3233
isUnlimitedApprovalAmount,
3334
shouldShowPlusSign,
35+
type Status,
3436
type TokenAmount,
3537
toMarketRateLookupToken,
3638
} from '../../../util/activity-adapters';
@@ -45,6 +47,7 @@ import { getPerpsDisplaySymbol } from '@metamask/perps-controller';
4547
import type { ActivityListItemRowContent } from './ActivityListItemRow.types';
4648
import {
4749
ACTIVITY_FALLBACK_TITLE_RESOLVERS,
50+
resolvePerpsOrderStatusLabel,
4851
TOKEN_ACTION_LABELS,
4952
} from './titleLabels';
5053

@@ -69,12 +72,7 @@ function isPerpsPnlKind(type: ActivityKind): boolean {
6972
}
7073

7174
function isPerpsTradeKind(type: ActivityKind): boolean {
72-
return (
73-
isPerpsPnlKind(type) ||
74-
type.startsWith('market') ||
75-
type.startsWith('limit') ||
76-
type.startsWith('stopMarket')
77-
);
75+
return isPerpsPnlKind(type) || isPerpsOrderKind(type);
7876
}
7977

8078
function isPerpsMarketAvatarKind(type: ActivityKind): boolean {
@@ -301,6 +299,10 @@ function resolveFallbackTitle(item: ActivityListItem): string {
301299
const base =
302300
ACTIVITY_FALLBACK_TITLE_RESOLVERS[item.type]?.() ??
303301
strings('transactions.interaction');
302+
303+
if (isPerpsOrderKind(item.type)) {
304+
return base;
305+
}
304306
return withDomainStatusSuffix(base, item.status);
305307
}
306308

@@ -1221,6 +1223,8 @@ export function useActivityListItemRowContent(
12211223
})
12221224
: undefined;
12231225

1226+
const isOrderRow = isPerpsOrderKind(item.type);
1227+
12241228
const rawPrimaryAmount =
12251229
domainFiatAmount ?? resolveAmount(primaryToken, item.type);
12261230

@@ -1240,10 +1244,22 @@ export function useActivityListItemRowContent(
12401244
// so every consumer of this resolver (the list row and the details amount
12411245
// header) stays consistent.
12421246
const suppressTransferAmount = isFailedOrCancelledTransfer(item);
1243-
const primaryAmount = suppressTransferAmount ? undefined : rawPrimaryAmount;
1244-
const secondaryAmount = suppressTransferAmount
1245-
? undefined
1246-
: resolveRawSecondaryAmount();
1247+
1248+
// Order rows show their lifecycle status (muted, in the primary slot) instead
1249+
// of a notional amount — see isPerpsOrderKind. Orders aren't transfers, so the
1250+
// failed/cancelled-transfer suppression never applies to them.
1251+
let primaryAmount: string | undefined;
1252+
let secondaryAmount: string | undefined;
1253+
if (isOrderRow) {
1254+
primaryAmount = resolvePerpsOrderStatusLabel(item.status);
1255+
secondaryAmount = undefined;
1256+
} else if (suppressTransferAmount) {
1257+
primaryAmount = undefined;
1258+
secondaryAmount = undefined;
1259+
} else {
1260+
primaryAmount = rawPrimaryAmount;
1261+
secondaryAmount = resolveRawSecondaryAmount();
1262+
}
12471263

12481264
const perpsMarketSymbol = isPerpsMarketAvatarKind(item.type)
12491265
? 'sourceToken' in item.data
@@ -1273,5 +1289,6 @@ export function useActivityListItemRowContent(
12731289
primaryAmount,
12741290
secondaryAmount,
12751291
isPnlAmount: isPerpsPnlKind(item.type),
1292+
isMutedAmount: isOrderRow,
12761293
};
12771294
}

0 commit comments

Comments
 (0)