Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/build-android-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,6 @@ jobs:
# - "Cache Gradle dependencies"
# - "Cache build artifacts"
key: android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-${{ steps.generate-fingerprint.outputs.fingerprint }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
android-apk-${{ inputs.build_type }}-${{ env.CACHE_GENERATION }}-
android-apk-

- name: Cache Gradle dependencies
uses: cirruslabs/cache@v4
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/build-ios-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,6 @@ jobs:
path: |
ios/build/Build/Products/Release-iphonesimulator/MetaMask.app
key: ios-app-${{ steps.generate-fingerprint.outputs.fingerprint }}
restore-keys: |
ios-app-${{ steps.generate-fingerprint.outputs.fingerprint }}
ios-app-

# Build the iOS E2E app for simulator
- name: Build iOS E2E App
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/cursor-issue-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ permissions:
jobs:
analyze-issue:
runs-on: ubuntu-latest
# TEMPORARILY DISABLED - Re-enable after holidays by removing the 'false &&' below
# Check if issue has team-confirmations AND (Sev1-high OR Sev2-normal) AND NOT external-contributor
# Note: Only maintainers can add labels, providing first line of defense
if: |
false &&
contains(github.event.issue.labels.*.name, 'team-confirmations') &&
(contains(github.event.issue.labels.*.name, 'Sev1-high') || contains(github.event.issue.labels.*.name, 'Sev2-normal')) &&
!contains(github.event.issue.labels.*.name, 'external-contributor')
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/run-e2e-api-specs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ jobs:
with:
ref: ${{ github.head_ref || github.ref }}
clean: true
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/run-e2e-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ jobs:
with:
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || github.event.pull_request.head.sha || github.sha }}
clean: true
fetch-depth: 0

- name: Install Android System Images
if: ${{ inputs.platform == 'android' }}
Expand Down
1 change: 1 addition & 0 deletions .js.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export MM_PERPS_ENABLED="true"
export MM_PERPS_SERVICE_INTERRUPTION_BANNER_ENABLED="false"
export MM_PERPS_BLOCKED_REGIONS="US,CA-ON,GB,BE"
export MM_PERPS_GTM_MODAL_ENABLED="true"
export MM_PERPS_ORDER_BOOK_ENABLED="true"
# HIP-3 Feature Flags (remote override with local fallback)
export MM_PERPS_HIP3_ENABLED="true"
export MM_PERPS_HIP3_ALLOWLIST_MARKETS="" # Allowlist: Empty = enable all markets. Examples: "xyz:XYZ100,xyz:TSLA" or "xyz:*,abc:TSLA"
Expand Down
2 changes: 0 additions & 2 deletions app/components/Nav/Main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
import ProtectYourWalletModal from '../../UI/ProtectYourWalletModal';
import MainNavigator from './MainNavigator';
import { query } from '@metamask/controller-utils';
import SwapsLiveness from '../../UI/Swaps/SwapsLiveness';
import EarnTransactionMonitor from '../../UI/Earn/components/EarnTransactionMonitor';

import {
Expand Down Expand Up @@ -418,7 +417,6 @@ const Main = (props) => {
<FadeOutOverlay />
<Notification navigation={props.navigation} />
<RampOrders />
<SwapsLiveness />
<CardVerification />
<EarnTransactionMonitor />
{renderDeprecatedNetworkAlert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ describe('MarketDetailsList', () => {
);

expect(getByText('Market details')).toBeDefined();
expect(getByText('Market Cap')).toBeDefined();
expect(getByText('Market cap')).toBeDefined();
expect(getByText('5.24B')).toBeDefined();
expect(getByText('Total Volume (24h)')).toBeDefined();
expect(getByText('Total volume (24h)')).toBeDefined();
expect(getByText('153.14M')).toBeDefined();
expect(getByText('Volume / Market Cap')).toBeDefined();
expect(getByText('Volume / market cap')).toBeDefined();
expect(getByText('3.22%')).toBeDefined();
expect(getByText('Circulating supply')).toBeDefined();
expect(getByText('5.24B')).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ exports[`MarketDetailsList should render correctly 1`] = `
}
}
>
Market Cap
Market cap
</Text>
<Text
accessibilityRole="text"
Expand Down Expand Up @@ -89,7 +89,7 @@ exports[`MarketDetailsList should render correctly 1`] = `
}
}
>
Total Volume (24h)
Total volume (24h)
</Text>
<Text
accessibilityRole="text"
Expand Down Expand Up @@ -127,7 +127,7 @@ exports[`MarketDetailsList should render correctly 1`] = `
}
}
>
Volume / Market Cap
Volume / market cap
</Text>
<Text
accessibilityRole="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,10 @@ describe('TokenDetails', () => {
expect(getByText('Token list')).toBeDefined();
expect(getByText('Metamask, Coinmarketcap')).toBeDefined();
expect(getByText('Market details')).toBeDefined();
expect(getByText('Market Cap')).toBeDefined();
expect(getByText('Total Volume (24h)')).toBeDefined();
expect(getByText('Market cap')).toBeDefined();
expect(getByText('Total volume (24h)')).toBeDefined();
expect(getByText('$147.65M')).toBeDefined();
expect(getByText('Volume / Market Cap')).toBeDefined();
expect(getByText('Volume / market cap')).toBeDefined();
expect(getByText('2.83%')).toBeDefined();
expect(getByText('Circulating supply')).toBeDefined();
expect(getByText('5.21B')).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ exports[`TokenDetails should render correctly 1`] = `
}
}
>
Market Cap
Market cap
</Text>
<Text
accessibilityRole="text"
Expand Down Expand Up @@ -273,7 +273,7 @@ exports[`TokenDetails should render correctly 1`] = `
}
}
>
Total Volume (24h)
Total volume (24h)
</Text>
<Text
accessibilityRole="text"
Expand Down Expand Up @@ -311,7 +311,7 @@ exports[`TokenDetails should render correctly 1`] = `
}
}
>
Volume / Market Cap
Volume / market cap
</Text>
<Text
accessibilityRole="text"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ exports[`AssetSearch renders correctly with allTokens 1`] = `
onBlur={[Function]}
onChangeText={[Function]}
onFocus={[Function]}
placeholder="Search Tokens"
placeholder="Search tokens"
placeholderTextColor="#b7bbc8"
style={
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ import {
} from '../../hooks/stream';
import { usePerpsABTest } from '../../utils/abTesting/usePerpsABTest';
import { BUTTON_COLOR_TEST } from '../../utils/abTesting/tests';
import { selectPerpsButtonColorTestVariant } from '../../selectors/featureFlags';
import {
selectPerpsButtonColorTestVariant,
selectPerpsOrderBookEnabledFlag,
} from '../../selectors/featureFlags';
import PerpsPositionCard from '../../components/PerpsPositionCard';
import PerpsMarketStatisticsCard from '../../components/PerpsMarketStatisticsCard';
import type { PerpsTooltipContentKey } from '../../components/PerpsBottomSheetTooltip/PerpsBottomSheetTooltip.types';
Expand Down Expand Up @@ -183,6 +186,9 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {

const isEligible = useSelector(selectPerpsEligibility);

// Feature flag for Order Book visibility
const isOrderBookEnabled = useSelector(selectPerpsOrderBookEnabledFlag);

// Check if current market is in watchlist
const selectIsWatchlist = useMemo(
() => createSelectIsWatchlistMarket(market?.symbol || ''),
Expand Down Expand Up @@ -709,21 +715,20 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
}, []);

// Order book handler - navigates to order book view
// Temporarily disabled - uncomment to re-enable order book entry point
// const handleOrderBookPress = useCallback(() => {
// if (!market?.symbol) return;

// track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
// [PerpsEventProperties.INTERACTION_TYPE]:
// PerpsEventValues.INTERACTION_TYPE.TAP,
// [PerpsEventProperties.ASSET]: market.symbol,
// });

// navigation.navigate(Routes.PERPS.ORDER_BOOK, {
// symbol: market.symbol,
// marketData: market,
// });
// }, [market, navigation, track]);
const handleOrderBookPress = useCallback(() => {
if (!market?.symbol) return;

track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PerpsEventProperties.INTERACTION_TYPE]:
PerpsEventValues.INTERACTION_TYPE.TAP,
[PerpsEventProperties.ASSET]: market.symbol,
});

navigation.navigate(Routes.PERPS.ORDER_BOOK, {
symbol: market.symbol,
marketData: market,
});
}, [market, navigation, track]);

// Close position handler
const handleClosePosition = useCallback(() => {
Expand Down Expand Up @@ -1044,8 +1049,9 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
nextFundingTime={market?.nextFundingTime}
fundingIntervalHours={market?.fundingIntervalHours}
dexName={market?.marketSource || undefined}
// Temporarily disabled - uncomment to re-enable order book entry point
// onOrderBookPress={handleOrderBookPress}
onOrderBookPress={
isOrderBookEnabled ? handleOrderBookPress : undefined
}
/>
</View>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import styleSheet from './PerpsMarketStatisticsCard.styles';
import type { PerpsMarketStatisticsCardProps } from './PerpsMarketStatisticsCard.types';
import {
PerpsMarketDetailsViewSelectorsIDs,
// PerpsOrderBookViewSelectorsIDs,
PerpsOrderBookViewSelectorsIDs,
} from '../../../../../../e2e/selectors/Perps/Perps.selectors';
import FundingCountdown from '../FundingCountdown';
import { usePerpsLivePrices } from '../../hooks/stream';
Expand All @@ -35,7 +35,7 @@ const PerpsMarketStatisticsCard: React.FC<PerpsMarketStatisticsCardProps> = ({
nextFundingTime,
fundingIntervalHours,
dexName,
// onOrderBookPress,
onOrderBookPress,
}) => {
const { styles } = useStyles(styleSheet, {});

Expand Down Expand Up @@ -126,8 +126,7 @@ const PerpsMarketStatisticsCard: React.FC<PerpsMarketStatisticsCardProps> = ({
{/* Stats rows with card background */}
<View style={styles.statsRowsContainer}>
{/* Order Book - Clickable row */}
{/* TODO: Re-enable order book row, when feature has been more fully tested */}
{/* {onOrderBookPress && (
{onOrderBookPress && (
<TouchableOpacity
style={[styles.orderBookRow, styles.statsRowFirst]}
onPress={onOrderBookPress}
Expand All @@ -144,7 +143,7 @@ const PerpsMarketStatisticsCard: React.FC<PerpsMarketStatisticsCardProps> = ({
color={IconColor.Alternative}
/>
</TouchableOpacity>
)} */}
)}

{/* 24h volume */}
<KeyValueRow
Expand All @@ -162,11 +161,7 @@ const PerpsMarketStatisticsCard: React.FC<PerpsMarketStatisticsCardProps> = ({
color: TextColor.Default,
},
}}
style={[
styles.statsRow,
// !onOrderBookPress &&
styles.statsRowFirst,
]}
style={[styles.statsRow, !onOrderBookPress && styles.statsRowFirst]}
/>

{/* Open interest with tooltip */}
Expand Down
21 changes: 14 additions & 7 deletions app/components/UI/Perps/hooks/usePerpsTransactionHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,14 @@ export const usePerpsTransactionHistory = ({

// Merge live WebSocket fills with REST transactions for instant updates
// Live fills take precedence for recent trades
// IMPORTANT: Deduplicate trades using asset+timestamp, not tx.id, because
// the ID includes array index which differs between REST and WebSocket arrays
// IMPORTANT: Deduplicate trades using asset+timestamp (truncated to seconds), not tx.id,
// because:
// 1. The ID includes array index which differs between REST and WebSocket arrays
// 2. Aggregated fills (from split stop loss/TP) may have slightly different first-fill
// timestamps between REST and WebSocket if fills arrive in different order
const mergedTransactions = useMemo(() => {
// Transform live fills to PerpsTransaction format
// Note: transformFillsToTransactions now aggregates split stop loss/TP fills
const liveTransactions = transformFillsToTransactions(liveFills);

// If no REST transactions yet, return only live fills
Expand All @@ -194,20 +198,23 @@ export const usePerpsTransactionHistory = ({
(tx) => tx.type !== 'trade',
);

// Merge trades using asset+timestamp as dedup key (avoids index mismatch in IDs)
// This ensures the same fill from REST and WebSocket is deduplicated correctly
// Merge trades using asset+timestamp(seconds) as dedup key
// Use seconds-truncated timestamp to handle cases where REST and WebSocket
// aggregated fills have slightly different first-fill timestamps
const tradeMap = new Map<string, PerpsTransaction>();

// Add REST trade transactions first
for (const tx of restTradeTransactions) {
// Use asset + timestamp as key (unique per fill, index-independent)
const dedupKey = `${tx.asset}-${tx.timestamp}`;
// Use asset + timestamp (truncated to seconds) as key
const timestampSeconds = Math.floor(tx.timestamp / 1000);
const dedupKey = `${tx.asset}-${timestampSeconds}`;
tradeMap.set(dedupKey, tx);
}

// Add live fills (overwrites REST duplicates - live data is fresher)
for (const tx of liveTransactions) {
const dedupKey = `${tx.asset}-${tx.timestamp}`;
const timestampSeconds = Math.floor(tx.timestamp / 1000);
const dedupKey = `${tx.asset}-${timestampSeconds}`;
tradeMap.set(dedupKey, tx);
}

Expand Down
1 change: 1 addition & 0 deletions app/components/UI/Perps/mocks/remoteFeatureFlagMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export const mockedPerpsFeatureFlagsEnabledState: Record<
> = {
perpsPerpTradingEnabled: mockEnabledPerpsLDFlag,
perpsPerpTradingServiceInterruptionBannerEnabled: mockEnabledPerpsLDFlag,
perpsOrderBookEnabled: mockEnabledPerpsLDFlag,
};
Loading
Loading