diff --git a/.js.env.example b/.js.env.example index a64ab593fdc..98c48ef4e1d 100644 --- a/.js.env.example +++ b/.js.env.example @@ -112,6 +112,12 @@ export MM_STABLECOIN_LENDING_UI_ENABLED_REDESIGNED="true" ## Pooled-Staking export MM_POOLED_STAKING_ENABLED="true" export MM_POOLED_STAKING_SERVICE_INTERRUPTION_BANNER_ENABLED="true" +# mUSD +export MM_MUSD_CONVERSION_FLOW_ENABLED="false" +# Allowlist of convertible tokens by chain +# IMPORTANT: Must use SINGLE QUOTES to preserve JSON format +# Example: MM_MUSD_CONVERTIBLE_TOKENS_ALLOWLIST='{"0x1":["USDC","USDT"],"0xa4b1":["USDC","DAI"]}' +export MM_MUSD_CONVERTIBLE_TOKENS_ALLOWLIST='' # Activates remote feature flag override mode. # Remote feature flag values won't be updated, diff --git a/app/component-library/components/Navigation/TabBar/TabBar.constants.ts b/app/component-library/components/Navigation/TabBar/TabBar.constants.ts index 60df364129e..2c9e1731917 100644 --- a/app/component-library/components/Navigation/TabBar/TabBar.constants.ts +++ b/app/component-library/components/Navigation/TabBar/TabBar.constants.ts @@ -13,7 +13,7 @@ export const ICON_BY_TAB_BAR_ICON_KEY: IconByTabBarIconKey = { [TabBarIconKey.Activity]: IconName.Activity, [TabBarIconKey.Setting]: IconName.Setting, [TabBarIconKey.Rewards]: IconName.MetamaskFoxOutline, - [TabBarIconKey.Trending]: IconName.TrendUp, + [TabBarIconKey.Trending]: IconName.Search, }; export const LABEL_BY_TAB_BAR_ICON_KEY = { diff --git a/app/components/Nav/Main/MainNavigator.js b/app/components/Nav/Main/MainNavigator.js index c4b65c57dff..47ee95f3fac 100644 --- a/app/components/Nav/Main/MainNavigator.js +++ b/app/components/Nav/Main/MainNavigator.js @@ -1103,7 +1103,19 @@ const MainNavigator = () => { name={Routes.PERPS.ROOT} component={PerpsScreenStack} options={{ - animationEnabled: false, + animationEnabled: true, + cardStyleInterpolator: ({ current, layouts }) => ({ + cardStyle: { + transform: [ + { + translateX: current.progress.interpolate({ + inputRange: [0, 1], + outputRange: [layouts.screen.width, 0], + }), + }, + ], + }, + }), }} /> { + {renderDeprecatedNetworkAlert( props.chainId, props.backUpSeedphraseVisible, diff --git a/app/components/UI/Bridge/hooks/useTokens.test.ts b/app/components/UI/Bridge/hooks/useTokens.test.ts index b98125ffbae..383a1128ab7 100644 --- a/app/components/UI/Bridge/hooks/useTokens.test.ts +++ b/app/components/UI/Bridge/hooks/useTokens.test.ts @@ -752,7 +752,7 @@ describe('useTokens', () => { it('filters out non-tradable Tron tokens from remainingTokens', async () => { const tronMaxBandwidthToken = { address: '0x789', - symbol: 'Max Bandwidth', + symbol: 'Max-Bandwidth', name: 'Max Bandwidth', decimals: 6, chainId: TrxScope.Mainnet, diff --git a/app/components/UI/Bridge/utils/isTradableToken/index.test.ts b/app/components/UI/Bridge/utils/isTradableToken/index.test.ts index a5dfa33cbed..0efd75c3f7b 100644 --- a/app/components/UI/Bridge/utils/isTradableToken/index.test.ts +++ b/app/components/UI/Bridge/utils/isTradableToken/index.test.ts @@ -176,7 +176,7 @@ describe('isTradableToken', () => { it('returns false for Tron Energy token', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'Energy', + symbol: 'energy', }); const result = isTradableToken(token); @@ -187,7 +187,7 @@ describe('isTradableToken', () => { it('returns false for Tron Bandwidth token', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'Bandwidth', + symbol: 'bandwidth', }); const result = isTradableToken(token); @@ -198,7 +198,7 @@ describe('isTradableToken', () => { it('returns false for Tron Max Bandwidth token', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'Max Bandwidth', + symbol: 'max-bandwidth', }); const result = isTradableToken(token); @@ -209,7 +209,7 @@ describe('isTradableToken', () => { it('returns false for Tron energy token with lowercase', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'energy', + symbol: 'energy', }); const result = isTradableToken(token); @@ -220,7 +220,7 @@ describe('isTradableToken', () => { it('returns false for Tron bandwidth token with uppercase', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'BANDWIDTH', + symbol: 'BANDWIDTH', }); const result = isTradableToken(token); @@ -231,7 +231,7 @@ describe('isTradableToken', () => { it('returns false for Tron max bandwidth token with mixed case', () => { const token = createTestToken({ chainId: TrxScope.Mainnet, - name: 'mAx BaNdWiDtH', + symbol: 'MaX-BaNdWiDtH', }); const result = isTradableToken(token); diff --git a/app/components/UI/Bridge/utils/isTradableToken/index.ts b/app/components/UI/Bridge/utils/isTradableToken/index.ts index 8b8f3885093..4527bc7bae2 100644 --- a/app/components/UI/Bridge/utils/isTradableToken/index.ts +++ b/app/components/UI/Bridge/utils/isTradableToken/index.ts @@ -1,15 +1,15 @@ import { TrxScope } from '@metamask/keyring-api'; import { BridgeToken } from '../../types'; +import { + TRON_RESOURCE_SYMBOLS, + TronResourceSymbol, +} from '../../../../../core/Multichain/constants'; export const isTradableToken = (token: BridgeToken) => { - if ( - token.chainId === TrxScope.Mainnet && - (token.name?.toLowerCase() === 'energy' || - token.name?.toLowerCase() === 'bandwidth' || - token.name?.toLowerCase() === 'max bandwidth') - ) { - return false; + if (token.chainId === TrxScope.Mainnet) { + return !TRON_RESOURCE_SYMBOLS.includes( + token.symbol?.toLowerCase() as TronResourceSymbol, + ); } - return true; }; diff --git a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.styles.ts b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.styles.ts index 3d37fa59e23..ea480d29cca 100644 --- a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.styles.ts +++ b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.styles.ts @@ -12,13 +12,11 @@ const createStyles = (theme: Theme) => container: { flex: 1, backgroundColor: theme.colors.background.default, - paddingHorizontal: 16, }, containerSpaceAround: { flex: 1, backgroundColor: theme.colors.background.default, justifyContent: 'space-around', - paddingHorizontal: 16, }, title: { marginTop: 24, diff --git a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx index 5c141c82668..5311fb82542 100644 --- a/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx +++ b/app/components/UI/Card/Views/CardAuthentication/CardAuthentication.tsx @@ -2,9 +2,7 @@ import { useNavigation } from '@react-navigation/native'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Image, - KeyboardAvoidingView, Platform, - ScrollView, TouchableOpacity, View, TextInput, @@ -55,6 +53,8 @@ import { MetaMetricsEvents, useMetrics } from '../../../../hooks/useMetrics'; import { useDispatch } from 'react-redux'; import { setOnboardingId } from '../../../../../core/redux/slices/card'; import { CardActions, CardScreens } from '../../util/metrics'; +import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { useTailwind } from '@metamask/design-system-twrnc-preset'; const CELL_COUNT = 6; const autoComplete = Platform.select({ @@ -118,9 +118,9 @@ const CardAuthentication = () => { clearOtpError, otpLoading, } = useCardProviderAuthentication(); - const styles = createStyles(theme); const { styles: otpStyles } = useStyles(createOtpStyles, {}); + const tw = useTailwind(); const handleEmailChange = (newEmail: string) => { setEmail(newEmail); @@ -240,13 +240,17 @@ const CardAuthentication = () => { return; } - if ( - loginResponse?.verificationState === 'PENDING' || - loginResponse?.phase - ) { - // Switch to OTP step instead of navigating + if (loginResponse?.phase) { dispatch(setOnboardingId(loginResponse.userId)); - navigation.navigate(Routes.CARD.ONBOARDING.ROOT); + navigation.reset({ + index: 0, + routes: [ + { + name: Routes.CARD.ONBOARDING.ROOT, + params: { cardUserPhase: loginResponse.phase }, + }, + ], + }); return; } @@ -326,154 +330,136 @@ const CardAuthentication = () => { clearOtpError(); }, [clearOtpError]); - // Render OTP step - if (step === 'otp') { - return ( - + - - - - - - + + + + + + {strings('card.card_otp_authentication.title')} + + + {otpData?.maskedPhoneNumber + ? strings( + 'card.card_otp_authentication.description_with_phone_number', + { maskedPhoneNumber: otpData.maskedPhoneNumber }, + ) + : strings( + 'card.card_otp_authentication.description_without_phone_number', + )} + + + + + } + {...props} + value={confirmCode} + onChangeText={handleOtpValueChange} + cellCount={CELL_COUNT} + rootStyle={otpStyles.codeFieldRoot} + keyboardType="number-pad" + textContentType="oneTimeCode" + autoComplete={autoComplete} + renderCell={({ index, symbol, isFocused }) => ( + + + {symbol || (isFocused ? : null)} + + + )} /> - - {strings('card.card_otp_authentication.title')} - - - {otpData?.maskedPhoneNumber - ? strings( - 'card.card_otp_authentication.description_with_phone_number', - { maskedPhoneNumber: otpData.maskedPhoneNumber }, - ) - : strings( - 'card.card_otp_authentication.description_without_phone_number', - )} - - - - - } - {...props} - value={confirmCode} - onChangeText={handleOtpValueChange} - cellCount={CELL_COUNT} - rootStyle={otpStyles.codeFieldRoot} - keyboardType="number-pad" - textContentType="oneTimeCode" - autoComplete={autoComplete} - renderCell={({ index, symbol, isFocused }) => ( - - - {symbol || (isFocused ? : null)} - - - )} - /> + {otpError && ( + + + {otpError} + - {otpError && ( - - - {otpError} - - - )} - - {resendCountdown > 0 ? ( + )} + + {resendCountdown > 0 ? ( + + {strings('card.card_otp_authentication.resend_timer', { + seconds: resendCountdown, + })} + + ) : ( + - {strings('card.card_otp_authentication.resend_timer', { - seconds: resendCountdown, - })} + {strings('card.card_otp_authentication.resend_code')} - ) : ( - - - {strings('card.card_otp_authentication.resend_code')} - - - )} - - - - -