Skip to content

Commit 4f61bc9

Browse files
Merge branch 'main' into refactor/perps-home-closeallpositions
2 parents 991870b + 955fd0a commit 4f61bc9

119 files changed

Lines changed: 3437 additions & 3057 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.depcheckrc.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ ignores:
4646
- 'xml2js'
4747
# jest-junit is used as a Jest reporter in tests/jest.e2e.detox.config.js
4848
- 'jest-junit'
49+
# Required directly only by scripts/babel/named-capturing-groups-regex.test.ts
50+
# (a regression guard for the @react-native/babel-preset patch). It's provided
51+
# transitively via @expo/metro-config + Metro, so we don't declare it as a
52+
# direct dependency.
53+
- 'babel-preset-expo'
4954

5055
# tsconfig path alias pointing to app/controllers/perps (not an npm package)
5156
- '@metamask/perps-controller'
@@ -76,7 +81,6 @@ ignores:
7681
- 'chromedriver'
7782
- 'execa'
7883
- 'jetifier'
79-
- 'metro-react-native-babel-preset'
8084
- 'react-native-svg-asset-plugin'
8185
- 'regenerator-runtime'
8286
- 'prettier-2'

.github/workflows/ci.yml

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -307,22 +307,6 @@ jobs:
307307
command: yarn install --immutable
308308
- name: Clean state and following up dependencies installation
309309
run: yarn setup:github-ci --node
310-
- name: Restore ESLint cache
311-
if: ${{ matrix.scripts == 'lint' }}
312-
uses: actions/cache@v4
313-
with:
314-
path: .eslintcache
315-
key: eslintcache-${{ runner.os }}-${{ github.run_id }}
316-
restore-keys: |
317-
eslintcache-${{ runner.os }}-
318-
- name: Restore TypeScript incremental build cache
319-
if: ${{ matrix.scripts == 'lint:tsc' }}
320-
uses: actions/cache@v4
321-
with:
322-
path: tsconfig.tsbuildinfo
323-
key: tsbuildinfo-${{ runner.os }}-${{ github.run_id }}
324-
restore-keys: |
325-
tsbuildinfo-${{ runner.os }}-
326310
- run: yarn ${{ matrix['scripts'] }}
327311
- name: Require clean working directory
328312
shell: bash
@@ -1354,13 +1338,7 @@ jobs:
13541338
permissions:
13551339
contents: read
13561340
id-token: write
1357-
needs:
1358-
[
1359-
get_requirements,
1360-
build-android-apks,
1361-
smart-e2e-selection,
1362-
prepare-e2e-timings,
1363-
]
1341+
needs: [get_requirements, build-android-apks, smart-e2e-selection, prepare-e2e-timings]
13641342
uses: ./.github/workflows/run-e2e-smoke-tests-android.yml
13651343
with:
13661344
changed_files: ${{ needs.get_requirements.outputs.changed_files }}
@@ -1414,13 +1392,7 @@ jobs:
14141392
permissions:
14151393
contents: read
14161394
id-token: write
1417-
needs:
1418-
[
1419-
get_requirements,
1420-
ios-tests-ready,
1421-
smart-e2e-selection,
1422-
prepare-e2e-timings,
1423-
]
1395+
needs: [get_requirements, ios-tests-ready, smart-e2e-selection, prepare-e2e-timings]
14241396
uses: ./.github/workflows/run-e2e-smoke-tests-ios.yml
14251397
with:
14261398
changed_files: ${{ needs.get_requirements.outputs.changed_files }}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
diff --git a/src/configs/main.js b/src/configs/main.js
2+
index a2f5e1e53a663c07ce8fa3def3a1d6d7828a4557..5179e8cf5bb6f085275daa183a05d21553cf695a 100644
3+
--- a/src/configs/main.js
4+
+++ b/src/configs/main.js
5+
@@ -120,9 +120,14 @@ const getPreset = (src, options) => {
6+
extraPlugins.push([require('@babel/plugin-transform-numeric-separator')]);
7+
extraPlugins.push([require('@babel/plugin-transform-sticky-regex')]);
8+
} else {
9+
- extraPlugins.push([
10+
- require('@babel/plugin-transform-named-capturing-groups-regex'),
11+
- ]);
12+
+ // NOTE (MetaMask): Do NOT down-level named-capture-group regexes on Hermes.
13+
+ // Current Hermes supports them natively, and the `@babel/runtime/helpers/wrapRegExp`
14+
+ // helper that this transform introduces breaks under SES lockdown (frozen
15+
+ // intrinsics), causing `RegExp.exec` to return null for matching strings
16+
+ // (e.g. NetworkController's INFURA_URL_REGEX -> "Could not derive Infura network").
17+
+ // extraPlugins.push([
18+
+ // require('@babel/plugin-transform-named-capturing-groups-regex'),
19+
+ // ]);
20+
// Needed for regenerator
21+
if (isHermes && enableRegenerator) {
22+
extraPlugins.push([

.yarn/patches/@tommasini-react-native-scrollable-tab-view-npm-1.1.1-153a9df479.patch

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ index 81a8a70576a2336fbfce44f429b80acb6bacb2c1..f8916b181efe7ba4ab598d3e6fb11477
5454
borderTopWidth: 0,
5555
borderLeftWidth: 0,
5656
diff --git a/index.js b/index.js
57-
index dc9d4f4e0bb7b5a165e0bcc89133e4bf6cdfff91..496530970bce3754e76362737be4c3491d159f05 100644
57+
index dc9d4f4e0bb7b5a165e0bcc89133e4bf6cdfff91..94f7ac60e15478b197aa3f537224745d0d5fecb6 100644
5858
--- a/index.js
5959
+++ b/index.js
6060
@@ -13,17 +13,14 @@ const {
@@ -98,3 +98,12 @@ index dc9d4f4e0bb7b5a165e0bcc89133e4bf6cdfff91..496530970bce3754e76362737be4c349
9898
)}
9999
ref={(scrollView) => { this.scrollView = scrollView; }}
100100
{...this.props.contentProps}
101+
@@ -348,7 +342,7 @@ const ScrollableTabView = createReactClass({
102+
// Need to call __makeNative manually to avoid a native animated bug. See
103+
// https://github.com/facebook/react-native/pull/14435
104+
containerWidthAnimatedValue.__makeNative();
105+
- scrollValue = Animated.divide(this.state.scrollXIOS, containerWidthAnimatedValue);
106+
+ const scrollValue = Animated.divide(this.state.scrollXIOS, containerWidthAnimatedValue);
107+
this.setState({ containerWidth: width, scrollValue, });
108+
} else {
109+
this.setState({ containerWidth: width, });

app/actions/settings/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,10 @@ export function setPerpsChartPreferredCandlePeriod(preferredCandlePeriod) {
107107
preferredCandlePeriod,
108108
};
109109
}
110+
111+
export function setShowAccountOnLeaderboard(showAccountOnLeaderboard) {
112+
return {
113+
type: 'SET_SHOW_ACCOUNT_ON_LEADERBOARD',
114+
showAccountOnLeaderboard,
115+
};
116+
}

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
>

0 commit comments

Comments
 (0)