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
18 changes: 0 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,6 @@ jobs:
uses: ./.github/workflows/build-android-e2e.yml
secrets: inherit

build-ios-apps:
name: "Build iOS Apps"
if: ${{ github.event_name != 'merge_group' }}
permissions:
contents: read
id-token: write
uses: ./.github/workflows/build-ios-e2e.yml
secrets: inherit

e2e-smoke-tests-android:
name: "Android E2E Smoke Tests"
permissions:
Expand All @@ -193,15 +184,6 @@ jobs:
uses: ./.github/workflows/run-e2e-smoke-tests-android.yml
secrets: inherit

e2e-smoke-tests-ios:
name: "iOS E2E Smoke Tests"
permissions:
contents: read
id-token: write
needs: [build-ios-apps]
uses: ./.github/workflows/run-e2e-smoke-tests-ios.yml
secrets: inherit

js-bundle-size-check:
runs-on: ubuntu-latest
steps:
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/nightly-temp-branch-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Nightly Temp Branch Sync

# Required permissions for the action to work
permissions:
contents: write

on:
schedule:
# Run at 11 PM UTC daily (adjust timezone as needed)
# NOTE: Scheduled workflows ALWAYS run from the default branch (main)
- cron: '0 23 * * *'

# Allow manual trigger for testing from ANY branch
workflow_dispatch:

jobs:
sync-temp-nightly-branch:
name: Sync chore/temp-nightly with main
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Fetch all history so we can work with branches
fetch-depth: 0
# Use the default GITHUB_TOKEN which has write permissions
token: ${{ secrets.GITHUB_TOKEN }}

- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Make sync script executable
run: chmod +x scripts/create-temp-nightly-branch.sh

- name: Run temp-nightly branch sync
run: ./scripts/create-temp-nightly-branch.sh

- name: Report sync status
run: |
echo "✅ Successfully synced chore/temp-nightly branch with main"
echo "🌿 Workflow triggered from branch: ${{ github.ref_name }}"
echo "📍 Current branch after sync: $(git branch --show-current)"
echo "📄 Latest commit: $(git log --oneline -1)"
echo "🔄 Trigger event: ${{ github.event_name }}"
34 changes: 34 additions & 0 deletions .github/workflows/temp-ios-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Temporary workflow to monitor iOS builds and E2E tests

name: TEMPORARY iOS Workflow

on:
push:
branches: [main]
pull_request:

schedule:
- cron: '0 2-6 * * *'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !(contains(github.ref, 'refs/heads/main') || contains(github.ref, 'refs/heads/stable')) }}

jobs:
build-ios-apps:
name: "Build iOS Apps"
if: ${{ github.event_name != 'merge_group' }}
permissions:
contents: read
id-token: write
uses: ./.github/workflows/build-ios-e2e.yml
secrets: inherit

e2e-smoke-tests-ios:
name: "iOS E2E Smoke Tests"
permissions:
contents: read
id-token: write
needs: [build-ios-apps]
uses: ./.github/workflows/run-e2e-smoke-tests-ios.yml
secrets: inherit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useCallback, useState } from 'react';
import React, { memo, useCallback, useState, useEffect } from 'react';
import { View, TouchableOpacity, InteractionManager } from 'react-native';
import { useSelector } from 'react-redux';

Expand All @@ -21,6 +21,12 @@ import { useWalletInfo } from '../../../../../components/Views/MultichainAccount
import { AccountWalletId } from '@metamask/account-api';
import createStyles from './AccountListFooter.styles';
import Engine from '../../../../../core/Engine';
import {
TraceName,
TraceOperation,
endTrace,
trace,
} from '../../../../../util/trace';

interface AccountListFooterProps {
walletId: AccountWalletId;
Expand All @@ -37,14 +43,20 @@ const AccountListFooter = memo(
const wallet = walletsMap?.[walletId];
const walletInfo = useWalletInfo(wallet);

// End trace when the loading finishes
useEffect(() => {
if (!isLoading) {
endTrace({ name: TraceName.CreateMultichainAccount });
}
}, [isLoading]);

const handleCreateAccount = useCallback(async () => {
if (!walletInfo?.keyringId) {
Logger.error(
new Error('No keyring ID found for wallet'),
'Cannot create account without keyring ID',
);
setIsLoading(false);

return;
}

Expand Down Expand Up @@ -73,6 +85,12 @@ const AccountListFooter = memo(
}, [walletInfo?.keyringId, onAccountCreated]);

const handlePress = useCallback(() => {
// Start the trace before setting the loading state
trace({
name: TraceName.CreateMultichainAccount,
op: TraceOperation.AccountCreate,
});

// Force immediate state update
setIsLoading(true);

Expand Down
1 change: 1 addition & 0 deletions app/components/Nav/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ const RootModalFlow = (props: RootModalFlowProps) => (
<Stack.Screen
name={Routes.SHEET.RETURN_TO_DAPP_MODAL}
component={ReturnToAppModal}
initialParams={{ ...props.route.params }}
/>
<Stack.Screen
name={Routes.SHEET.AMBIGUOUS_ADDRESS}
Expand Down
5 changes: 0 additions & 5 deletions app/components/UI/Navbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1205,11 +1205,6 @@ export function getWalletNavbarOptions(
ref={accountActionsRef}
accountName={accountName}
onPress={() => {
trace({
name: TraceName.AccountList,
tags: getTraceTags(store.getState()),
op: TraceOperation.AccountList,
});
navigation.navigate(...createAccountSelectorNavDetails({}));
}}
testID={WalletViewSelectorsIDs.ACCOUNT_ICON}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,45 @@ describe('PerpsMarketDetailsView', () => {
expect(mockNavigate).not.toHaveBeenCalled();
});

it('shows geo block modal when add funds button is pressed and user is not eligible', () => {
// Set user as not eligible
const { useSelector } = jest.requireMock('react-redux');
const mockSelectPerpsEligibility = jest.requireMock(
'../../selectors/perpsController',
).selectPerpsEligibility;
useSelector.mockImplementation((selector: unknown) => {
if (selector === mockSelectPerpsEligibility) {
return false;
}
return undefined;
});

// Set zero balance to show add funds button
mockUsePerpsAccount.mockReturnValue({
availableBalance: '0.00',
totalBalance: '0.00',
marginUsed: '0.00',
unrealizedPnl: '0.00',
});

const { getByTestId, getByText } = renderWithProvider(
<PerpsConnectionProvider>
<PerpsMarketDetailsView />
</PerpsConnectionProvider>,
{
state: initialState,
},
);

const addFundsButton = getByTestId(
PerpsMarketDetailsViewSelectorsIDs.ADD_FUNDS_BUTTON,
);
fireEvent.press(addFundsButton);

expect(getByText('Geo Block Tooltip')).toBeTruthy();
expect(mockNavigate).not.toHaveBeenCalled();
});

it('closes geo block modal when onClose is called', () => {
const { useSelector } = jest.requireMock('react-redux');
const mockSelectPerpsEligibility = jest.requireMock(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {

const handleAddFundsPress = async () => {
try {
if (!isEligible) {
setIsEligibilityModalVisible(true);
return;
}

// Ensure the network exists before proceeding
await ensureArbitrumNetworkExists();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,60 @@ describe('PerpsOrderView', () => {
});
});

it('calculates liquidation price using market price for market orders', async () => {
// Set route params for market order
(useRoute as jest.Mock).mockReturnValue({
params: {
asset: 'BTC',
direction: 'long',
amount: '100',
leverage: 10,
},
});

render(<PerpsOrderView />, { wrapper: TestWrapper });

// Wait for component to render and liquidation price to be calculated
await waitFor(() => {
expect(screen.getByText('Liquidation price')).toBeDefined();
});

// Since the default order type is 'market' and no limit price is set,
// the hook should be called with the current market price (0 from mock data)
expect(usePerpsLiquidationPrice).toHaveBeenCalledWith(
expect.objectContaining({
entryPrice: 0, // Current mock price from assetData
}),
);
});

it('calculates liquidation price using limit price for limit orders', async () => {
// We need to test the logic by examining what happens when the order context
// provides limit order data. Since the actual context logic is complex,
// we'll verify the memoized calculation logic instead.
// Set route params that would lead to a limit order
(useRoute as jest.Mock).mockReturnValue({
params: {
asset: 'BTC',
direction: 'long',
amount: '100',
leverage: 10,
},
});

render(<PerpsOrderView />, { wrapper: TestWrapper });

// Wait for initial render
await waitFor(() => {
expect(screen.getByText('Liquidation price')).toBeDefined();
});

// The liquidation price hook should be called - the exact parameters
// depend on the order form state. We verify it's being called which
// confirms our logic is reached.
expect(usePerpsLiquidationPrice).toHaveBeenCalled();
});

it('shows margin required', async () => {
render(<PerpsOrderView />, { wrapper: TestWrapper });

Expand Down
26 changes: 20 additions & 6 deletions app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,15 +437,27 @@ const PerpsOrderViewContentBase: React.FC = () => {
const marginRequired = calculations.marginRequired;

// Memoize liquidation price params to prevent infinite recalculation
const liquidationPriceParams = useMemo(
() => ({
entryPrice: assetData.price,
const liquidationPriceParams = useMemo(() => {
// Use limit price for limit orders, market price for market orders
const entryPrice =
orderForm.type === 'limit' && orderForm.limitPrice
? parseFloat(orderForm.limitPrice)
: assetData.price;

return {
entryPrice,
leverage: orderForm.leverage,
direction: orderForm.direction,
asset: orderForm.asset,
}),
[assetData.price, orderForm.leverage, orderForm.direction, orderForm.asset],
);
};
}, [
assetData.price,
orderForm.leverage,
orderForm.direction,
orderForm.asset,
orderForm.type,
orderForm.limitPrice,
]);

// Real-time liquidation price calculation
const { liquidationPrice } = usePerpsLiquidationPrice(liquidationPriceParams);
Expand Down Expand Up @@ -1111,6 +1123,8 @@ const PerpsOrderViewContentBase: React.FC = () => {
currentPrice={assetData.price}
direction={orderForm.direction}
asset={orderForm.asset}
limitPrice={orderForm.limitPrice}
orderType={orderForm.type}
/>
{/* Limit Price Bottom Sheet */}
<PerpsLimitPriceBottomSheet
Expand Down
Loading
Loading