Skip to content

Commit

Permalink
feat(ramp): add ramp intent (#9390)
Browse files Browse the repository at this point in the history
Co-authored-by: Nico MASSART <NicolasMassart@users.noreply.github.com>
  • Loading branch information
wachunei and NicolasMassart authored Jul 22, 2024
1 parent 1bb1768 commit 34dfd45
Show file tree
Hide file tree
Showing 38 changed files with 1,185 additions and 72 deletions.
2 changes: 2 additions & 0 deletions app/components/UI/Ramp/Views/BuildQuote/BuildQuote.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ jest.mock('../../hooks/useGasPriceEstimation', () =>
jest.fn(() => mockUseGasPriceEstimationValue),
);

jest.mock('../../hooks/useIntentAmount');

jest.mock('../../../../../util/navigation/navUtils', () => ({
...jest.requireActual('../../../../../util/navigation/navUtils'),
useParams: jest.fn(() => mockUseParamsValues),
Expand Down
8 changes: 8 additions & 0 deletions app/components/UI/Ramp/Views/BuildQuote/BuildQuote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
fromTokenMinimalUnitString,
} from '../../../../../util/number';
import useGasPriceEstimation from '../../hooks/useGasPriceEstimation';
import useIntentAmount from '../../hooks/useIntentAmount';

// TODO: Convert into typescript and correctly type
// TODO: Replace "any" with type
Expand Down Expand Up @@ -184,6 +185,13 @@ const BuildQuote = () => {
const { limits, isAmountBelowMinimum, isAmountAboveMaximum, isAmountValid } =
useLimits();

useIntentAmount(
setAmount,
setAmountNumber,
setAmountBNMinimalUnit,
currentFiatCurrency,
);

const gasPriceEstimation = useGasPriceEstimation({
// 0 is set when buying since there's no transaction involved
gasLimit: isBuy ? 0 : TRANSFER_GAS_LIMIT,
Expand Down
2 changes: 2 additions & 0 deletions app/components/UI/Ramp/Views/GetStarted/GetStarted.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const mockuseRampSDKInitialValues: Partial<RampSDK> = {
rampType: RampType.BUY,
isBuy: true,
isSell: false,
intent: undefined,
setIntent: jest.fn(),
};

let mockUseRampSDKValues: Partial<RampSDK> = {
Expand Down
37 changes: 33 additions & 4 deletions app/components/UI/Ramp/Views/GetStarted/GetStarted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@ import useAnalytics from '../../hooks/useAnalytics';
import useRampNetwork from '../../hooks/useRampNetwork';
import styles from './GetStarted.styles';
import useRegions from '../../hooks/useRegions';
import { useParams } from '../../../../../util/navigation/navUtils';
import { RampIntent } from '../../types';

/* eslint-disable import/no-commonjs, @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports */
const getStartedIcon = require('../../components/images/WalletInfo.png');

const GetStarted: React.FC = () => {
const navigation = useNavigation();
const { getStarted, setGetStarted, sdkError, selectedChainId, isBuy } =
useRampSDK();
const {
getStarted,
setGetStarted,
sdkError,
selectedChainId,
isBuy,
setIntent,
} = useRampSDK();
const { selectedRegion } = useRegions();
const [isNetworkRampSupported] = useRampNetwork();
const trackEvent = useAnalytics();
const params = useParams<RampIntent>();

const { colors } = useTheme();

Expand All @@ -42,6 +51,12 @@ const GetStarted: React.FC = () => {
}
}, [isBuy, selectedChainId, trackEvent]);

useEffect(() => {
if (params) {
setIntent(params);
}
}, [params, setIntent]);

useEffect(() => {
navigation.setOptions(
getFiatOnRampAggNavbar(
Expand Down Expand Up @@ -69,7 +84,14 @@ const GetStarted: React.FC = () => {

useEffect(() => {
if (getStarted) {
if (!isNetworkRampSupported) {
// Redirects to Network Switcher view if the current network is not supported by Ramp
// or if the chainId from the URL params doesn't match the selected chainId.
// The Network Switcher handles adding or switching to the network specified in the URL params
// and continues the intent with any additional params (like token and amount).
if (
!isNetworkRampSupported ||
(params?.chainId && params.chainId !== selectedChainId)
) {
navigation.reset({
index: 0,
routes: [{ name: Routes.RAMP.NETWORK_SWITCHER }],
Expand All @@ -93,7 +115,14 @@ const GetStarted: React.FC = () => {
});
}
}
}, [getStarted, isNetworkRampSupported, navigation, selectedRegion]);
}, [
getStarted,
isNetworkRampSupported,
navigation,
selectedChainId,
selectedRegion,
params,
]);

if (sdkError) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ describe('NetworkSwitcher View', () => {
[
[
"GetStarted",
{
"chainId": undefined,
},
],
]
`);
Expand Down
118 changes: 98 additions & 20 deletions app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigation } from '@react-navigation/native';
import { RefreshControl, TouchableOpacity, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { ChainId, NetworkType, toHex } from '@metamask/controller-utils';
import { ChainId, toHex } from '@metamask/controller-utils';
import { useSelector } from 'react-redux';

import LoadingNetworksSkeleton from './LoadingNetworksSkeleton';
Expand Down Expand Up @@ -36,6 +36,7 @@ import { strings } from '../../../../../../locales/i18n';
import Routes from '../../../../../constants/navigation/Routes';

import { PopularList } from '../../../../../util/networks/customNetworks';
import { getDecimalChainId } from '../../../../../util/networks';

function NetworkSwitcher() {
const navigation = useNavigation();
Expand All @@ -53,14 +54,14 @@ function NetworkSwitcher() {
} = useRampNetworksDetail();
const supportedNetworks = useSelector(getRampNetworks);
const [isCurrentNetworkRampSupported] = useRampNetwork();
const { selectedChainId, isBuy } = useRampSDK();
const { selectedChainId, isBuy, intent } = useRampSDK();

const networkConfigurations = useSelector(selectNetworkConfigurations);
const [networkToBeAdded, setNetworkToBeAdded] = useState<Network>();

const isLoading = isLoadingNetworks || isLoadingNetworksDetail;
const error = errorFetchingNetworks || errorFetchingNetworksDetail;
const rampNetworks = useMemo(() => {
const rampNetworksDetails = useMemo(() => {
const activeNetworkDetails: Network[] = [];
// TODO(ramp, chainId-string): filter supportedNetworks by EVM compatible chains (chainId are strings of decimal numbers)
supportedNetworks.forEach(({ chainId: supportedChainId, active }) => {
Expand All @@ -78,12 +79,12 @@ function NetworkSwitcher() {
return;
}

const popularNetwork = PopularList.find(
const popularNetworkDetail = PopularList.find(
({ chainId }) => chainId === rampSupportedNetworkChainIdAsHex,
);

if (popularNetwork) {
activeNetworkDetails.push(popularNetwork);
if (popularNetworkDetail) {
activeNetworkDetails.push(popularNetworkDetail);
return;
}

Expand Down Expand Up @@ -132,16 +133,18 @@ function NetworkSwitcher() {
);
}, [isBuy, navigation, colors, handleCancelPress]);

useEffect(() => {
if (isCurrentNetworkRampSupported) {
navigation.navigate(Routes.RAMP.GET_STARTED);
}
}, [isCurrentNetworkRampSupported, navigation]);
const navigateToGetStarted = useCallback(() => {
navigation.navigate(Routes.RAMP.GET_STARTED, { chainId: undefined });
}, [navigation]);

const switchToMainnet = useCallback((type: 'mainnet' | 'linea-mainnet') => {
const { NetworkController } = Engine.context;
NetworkController.setProviderType(type as NetworkType);
}, []);
const switchToMainnet = useCallback(
(type: 'mainnet' | 'linea-mainnet') => {
const { NetworkController } = Engine.context;
NetworkController.setProviderType(type);
navigateToGetStarted();
},
[navigateToGetStarted],
);

const switchNetwork = useCallback(
(networkConfiguration) => {
Expand All @@ -156,9 +159,10 @@ function NetworkSwitcher() {

CurrencyRateController.updateExchangeRate(ticker);
NetworkController.setActiveNetwork(networkConfigurationId);
navigateToGetStarted();
}
},
[networkConfigurations],
[navigateToGetStarted, networkConfigurations],
);

const handleNetworkPress = useCallback(
Expand All @@ -172,13 +176,75 @@ function NetworkSwitcher() {
[switchNetwork],
);

const handleIntentChainId = useCallback(
(chainId: string) => {
if (!isNetworkRampSupported(chainId, supportedNetworks)) {
return;
}
if (getDecimalChainId(ChainId.mainnet) === chainId) {
return switchToMainnet('mainnet');
}

if (getDecimalChainId(ChainId['linea-mainnet']) === chainId) {
return switchToMainnet('linea-mainnet');
}

const supportedNetworkConfigurations = rampNetworksDetails.map(
(networkConfiguration) => {
const isAdded = Object.values(networkConfigurations).some(
(savedNetwork) =>
savedNetwork.chainId === networkConfiguration.chainId,
);
return {
...networkConfiguration,
isAdded,
};
},
);

const networkConfiguration = supportedNetworkConfigurations.find(
({ chainId: configurationChainId }) =>
toHex(configurationChainId) === toHex(chainId),
);

if (networkConfiguration) {
handleNetworkPress(networkConfiguration);
}
},
[
supportedNetworks,
switchToMainnet,
rampNetworksDetails,
networkConfigurations,
handleNetworkPress,
],
);

useEffect(() => {
if (
isCurrentNetworkRampSupported &&
(!intent?.chainId || selectedChainId === intent.chainId)
) {
navigateToGetStarted();
} else if (intent?.chainId) {
handleIntentChainId(intent.chainId);
}
}, [
handleIntentChainId,
intent?.chainId,
isCurrentNetworkRampSupported,
navigateToGetStarted,
navigation,
selectedChainId,
]);

const handleNetworkModalClose = useCallback(() => {
if (networkToBeAdded) {
setNetworkToBeAdded(undefined);
}
}, [networkToBeAdded]);

if (!isLoading && (error || rampNetworks.length === 0)) {
if (!isLoading && (error || rampNetworksDetails.length === 0)) {
return (
<ScreenLayout>
<ScreenLayout.Body>
Expand Down Expand Up @@ -251,7 +317,12 @@ function NetworkSwitcher() {
<Text bold>Ethereum Main Network</Text>
</View>
<View style={customNetworkStyle.popularWrapper}>
<Text link>{strings('networks.switch')}</Text>
{selectedChainId ===
getDecimalChainId(ChainId.mainnet) ? (
<Text link>{strings('networks.continue')}</Text>
) : (
<Text link>{strings('networks.switch')}</Text>
)}
</View>
</TouchableOpacity>
) : null}
Expand All @@ -275,7 +346,12 @@ function NetworkSwitcher() {
<Text bold>Linea Main Network</Text>
</View>
<View style={customNetworkStyle.popularWrapper}>
<Text link>{strings('networks.switch')}</Text>
{selectedChainId ===
getDecimalChainId(ChainId['linea-mainnet']) ? (
<Text link>{strings('networks.continue')}</Text>
) : (
<Text link>{strings('networks.switch')}</Text>
)}
</View>
</TouchableOpacity>
) : null}
Expand All @@ -288,7 +364,9 @@ function NetworkSwitcher() {
showNetworkModal={handleNetworkPress}
onNetworkSwitch={() => undefined}
shouldNetworkSwitchPopToWallet={false}
customNetworksList={rampNetworks}
customNetworksList={rampNetworksDetails}
showCompletionMessage={false}
displayContinue
/>
</>
)}
Expand Down
14 changes: 9 additions & 5 deletions app/components/UI/Ramp/Views/OrderDetails/OrderDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ import { FIAT_ORDER_STATES } from '../../../../../constants/on-ramp';
import ErrorView from '../../components/ErrorView';
import useInterval from '../../../../hooks/useInterval';
import AppConstants from '../../../../../core/AppConstants';
import {
createBuyNavigationDetails,
createSellNavigationDetails,
} from '../../routes/utils';

interface OrderDetailsParams {
orderId?: string;
Expand Down Expand Up @@ -174,11 +178,11 @@ const OrderDetails = () => {

const handleMakeAnotherPurchase = useCallback(() => {
navigation.goBack();
navigation.navigate(
order?.orderType === OrderOrderTypeEnum.Buy
? Routes.RAMP.BUY
: Routes.RAMP.SELL,
);
if (order?.orderType === OrderOrderTypeEnum.Buy) {
navigation.navigate(...createBuyNavigationDetails());
} else {
navigation.navigate(...createSellNavigationDetails());
}
}, [navigation, order?.orderType]);

useInterval(
Expand Down
Loading

0 comments on commit 34dfd45

Please sign in to comment.