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
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,18 @@ const CellBase = ({
{...avatarProps}
/>
<View style={styles.cellBaseInfo}>
<Text
numberOfLines={1}
variant={DEFAULT_CELLBASE_AVATAR_TITLE_TEXTVARIANT}
testID={CellComponentSelectorsIDs.BASE_TITLE}
{...titleProps}
>
{title}
</Text>
{title === undefined || typeof title === 'string' ? (
<Text
numberOfLines={1}
variant={DEFAULT_CELLBASE_AVATAR_TITLE_TEXTVARIANT}
testID={CellComponentSelectorsIDs.BASE_TITLE}
{...titleProps}
>
{title}
</Text>
) : (
title
)}
{!!secondaryText && (
<Text
numberOfLines={1}
Expand Down
24 changes: 22 additions & 2 deletions app/components/UI/Bridge/Views/BridgeView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState, useRef } from 'react';
import React, { useEffect, useState, useRef, useMemo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import ScreenView from '../../../../Base/ScreenView';
import {
Expand Down Expand Up @@ -78,10 +78,11 @@ import { useRecipientInitialization } from '../../hooks/useRecipientInitializati
import ApprovalTooltip from '../../components/ApprovalText';
import { RootState } from '../../../../../reducers/index.ts';
import { BRIDGE_MM_FEE_RATE } from '@metamask/bridge-controller';
import { isNullOrUndefined } from '@metamask/utils';
import { isNullOrUndefined, Hex } from '@metamask/utils';
import { useBridgeQuoteEvents } from '../../hooks/useBridgeQuoteEvents/index.ts';
import { SwapsKeypad } from '../../components/SwapsKeypad/index.tsx';
import { useGasIncluded } from '../../hooks/useGasIncluded';
import { getGasFeesSponsoredNetworkEnabled } from '../../../../../selectors/featureFlagController/gasFeesSponsored';
import { FLipQuoteButton } from '../../components/FlipQuoteButton/index.tsx';

export interface BridgeRouteParams {
Expand Down Expand Up @@ -205,6 +206,23 @@ const BridgeView = () => {
latestAtomicBalance: latestSourceBalance?.atomicBalance,
});

const isGasFeesSponsoredNetworkEnabled = useSelector(
getGasFeesSponsoredNetworkEnabled,
);

// Check if quote is sponsored: both tokens must be on the same chain and that chain must be sponsored
const isQuoteSponsored = useMemo(() => {
if (!sourceToken?.chainId || !destToken?.chainId) return false;
// Both tokens must be on the same chain
if (sourceToken.chainId !== destToken.chainId) return false;
// Check if the chain is sponsored
return isGasFeesSponsoredNetworkEnabled(sourceToken.chainId as Hex);
}, [
sourceToken?.chainId,
destToken?.chainId,
isGasFeesSponsoredNetworkEnabled,
]);

const isSubmitDisabled =
isLoading ||
hasInsufficientBalance ||
Expand Down Expand Up @@ -505,6 +523,7 @@ const BridgeView = () => {
onMaxPress={handleSourceMaxPress}
latestAtomicBalance={latestSourceBalance?.atomicBalance}
isSourceToken
isQuoteSponsored={isQuoteSponsored}
/>
<FLipQuoteButton
onPress={handleSwitchTokens(destTokenAmount)}
Expand All @@ -523,6 +542,7 @@ const BridgeView = () => {
onTokenPress={handleDestTokenPress}
isLoading={!destTokenAmount && isLoading}
style={styles.destTokenArea}
isQuoteSponsored={isQuoteSponsored}
/>
</Box>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -625,20 +625,29 @@ exports[`BridgeDestNetworkSelector renders with initial state and displays netwo
testID="network-avatar-image"
/>
</View>
<Text
accessibilityRole="text"
<View
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
[
{},
undefined,
]
}
>
Bitcoin
</Text>
<Text
accessibilityRole="text"
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
}
>
Bitcoin
</Text>
</View>
</View>
</View>
</View>
Expand Down Expand Up @@ -722,20 +731,29 @@ exports[`BridgeDestNetworkSelector renders with initial state and displays netwo
testID="network-avatar-image"
/>
</View>
<Text
accessibilityRole="text"
<View
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
[
{},
undefined,
]
}
>
Solana
</Text>
<Text
accessibilityRole="text"
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
}
>
Solana
</Text>
</View>
</View>
</View>
</View>
Expand Down Expand Up @@ -819,20 +837,29 @@ exports[`BridgeDestNetworkSelector renders with initial state and displays netwo
testID="network-avatar-image"
/>
</View>
<Text
accessibilityRole="text"
<View
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
[
{},
undefined,
]
}
>
Optimism
</Text>
<Text
accessibilityRole="text"
style={
{
"color": "#121314",
"fontFamily": "Geist Medium",
"fontSize": 18,
"letterSpacing": 0,
"lineHeight": 24,
}
}
>
Optimism
</Text>
</View>
</View>
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fireEvent, waitFor } from '@testing-library/react-native';
import { renderScreen } from '../../../../../util/test/renderWithProvider';
import { BridgeSourceNetworkSelector } from '.';
import Routes from '../../../../../constants/navigation/Routes';
import { strings } from '../../../../../../locales/i18n';
import { Hex } from '@metamask/utils';
import { setSelectedSourceChainIds } from '../../../../../core/redux/slices/bridge';
import { BridgeSourceNetworkSelectorSelectorsIDs } from '../../../../../../e2e/selectors/Bridge/BridgeSourceNetworkSelector.selectors';
Expand Down Expand Up @@ -277,4 +278,65 @@ describe('BridgeSourceNetworkSelector', () => {
expect(getByText('Optimism')).toBeTruthy();
});
});

describe('gas fees sponsored label in source networks', () => {
it('shows label for sponsored networks only', async () => {
const state = cloneDeep(initialState);

type RemoteFlagsWithSponsored =
typeof state.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags & {
gasFeesSponsoredNetwork: Record<string, boolean>;
};
const remoteFlags = state.engine.backgroundState
.RemoteFeatureFlagController
.remoteFeatureFlags as RemoteFlagsWithSponsored;
remoteFlags.gasFeesSponsoredNetwork = {
[CHAIN_IDS.OPTIMISM]: true,
[CHAIN_IDS.MAINNET]: false,
};

const { getByText, queryAllByText } = renderScreen(
BridgeSourceNetworkSelector,
{
name: Routes.BRIDGE.MODALS.SOURCE_NETWORK_SELECTOR,
},
{ state },
);

await waitFor(() => {
expect(getByText('Ethereum Mainnet')).toBeTruthy();
expect(getByText('Optimism')).toBeTruthy();

const labels = queryAllByText(strings('networks.no_network_fee'));
expect(labels.length).toBe(1);
});
});

it('omits label when sponsorship map is empty', async () => {
const state = cloneDeep(initialState);

type RemoteFlagsWithSponsoredEmpty =
typeof state.engine.backgroundState.RemoteFeatureFlagController.remoteFeatureFlags & {
gasFeesSponsoredNetwork: Record<string, boolean>;
};
const remoteFlagsEmpty = state.engine.backgroundState
.RemoteFeatureFlagController
.remoteFeatureFlags as RemoteFlagsWithSponsoredEmpty;
remoteFlagsEmpty.gasFeesSponsoredNetwork = {};

const { getByText, queryByText } = renderScreen(
BridgeSourceNetworkSelector,
{
name: Routes.BRIDGE.MODALS.SOURCE_NETWORK_SELECTOR,
},
{ state },
);

await waitFor(() => {
expect(getByText('Ethereum Mainnet')).toBeTruthy();
expect(getByText('Optimism')).toBeTruthy();
expect(queryByText(strings('networks.no_network_fee'))).toBeNull();
});
});
});
});
Loading
Loading