diff --git a/locales/base/translation.json b/locales/base/translation.json index 919e22375e4..76eb1158bb7 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -2828,5 +2828,23 @@ "fiatPriceUnavailable": "Price unavailable", "tokenDescription": "{{tokenName}} on {{tokenNetwork}}" }, + "gasFeeWarning": { + "title": "You need more {{tokenSymbol}} for gas fees", + "descriptionMaxAmount_Send": "Add {{tokenSymbol}} for gas fees or lower the amount you're sending", + "descriptionMaxAmount_Swap": "Add {{tokenSymbol}} for gas fees or lower the amount you're swapping", + "descriptionMaxAmount_Deposit": "Add {{tokenSymbol}} for gas fees or lower the amount you're depositing", + "descriptionMaxAmount_Withdraw": "Add {{tokenSymbol}} for gas fees or lower the amount you're withdrawing", + "descriptionNotEnoughGas_Send": "Adjust the amount you're sending or add {{tokenSymbol}} to continue", + "descriptionNotEnoughGas_Swap": "Adjust the amount you're swapping or add {{tokenSymbol}} to continue", + "descriptionNotEnoughGas_Deposit": "Adjust the amount you're depositing or add {{tokenSymbol}} to continue", + "descriptionNotEnoughGas_Withdraw": "Adjust the amount you're withdrawing or add {{tokenSymbol}} to continue", + "titleDapp": "You have an insufficient gas token balance", + "descriptionDapp": "Add {{tokenSymbol}} to complete this transaction", + "ctaBuy": "Buy {{tokenSymbol}}", + "ctaAction_Send": "Send smaller amount", + "ctaAction_Swap": "Swap smaller amount", + "ctaAction_Deposit": "Deposit smaller amount", + "ctaAction_Withdraw": "Withdraw smaller amount" + }, "on": "on" } diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index 651372b61e9..6e7213d98bb 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -26,6 +26,9 @@ export enum AppEvents { in_app_review_error = 'in_app_review_error', handle_deeplink = 'handle_deeplink', + + gas_fee_warning_impression = 'gas_fee_warning_impression', + gas_fee_warning_cta_press = 'gas_fee_warning_cta_press', } export enum HomeEvents { @@ -660,7 +663,6 @@ export enum EarnEvents { earn_deposit_submit_error = 'earn_deposit_submit_error', earn_deposit_submit_cancel = 'earn_deposit_submit_cancel', earn_enter_amount_continue_press = 'earn_enter_amount_continue_press', - earn_deposit_add_gas_press = 'earn_deposit_add_gas_press', earn_feed_item_select = 'earn_feed_item_select', earn_collect_earnings_press = 'earn_collect_earnings_press', earn_withdraw_submit_start = 'earn_withdraw_submit_start', diff --git a/src/analytics/Properties.tsx b/src/analytics/Properties.tsx index 9c4cefc0230..0c566fc20ed 100644 --- a/src/analytics/Properties.tsx +++ b/src/analytics/Properties.tsx @@ -52,6 +52,7 @@ import { } from 'src/analytics/types' import { ErrorMessages } from 'src/app/ErrorMessages' import { AddAssetsActionType } from 'src/components/AddAssetsBottomSheet' +import { GasFeeWarningFlow } from 'src/components/GasFeeWarning' import { TokenPickerOrigin } from 'src/components/TokenBottomSheet' import { DappSection } from 'src/dapps/types' import { BeforeDepositActionName, EarnActiveMode, SerializableRewardsInfo } from 'src/earn/types' @@ -152,6 +153,16 @@ interface AppEventsProperties { fullPath: string | null query: string | null } + [AppEvents.gas_fee_warning_impression]: { + flow: GasFeeWarningFlow + errorType: 'need-decrease-spend-amount-for-gas' | 'not-enough-balance-for-gas' + tokenId: string + } + [AppEvents.gas_fee_warning_cta_press]: { + flow: GasFeeWarningFlow + errorType: 'need-decrease-spend-amount-for-gas' | 'not-enough-balance-for-gas' + tokenId: string + } } interface HomeEventsProperties { @@ -1616,7 +1627,6 @@ interface EarnEventsProperties { depositTokenAmount?: string swapType?: SwapType // only for swap-deposit } & EarnCommonProperties - [EarnEvents.earn_deposit_add_gas_press]: EarnCommonProperties & { gasTokenId: string } [EarnEvents.earn_feed_item_select]: { origin: | TokenTransactionTypeV2.EarnDeposit diff --git a/src/analytics/docs.ts b/src/analytics/docs.ts index 779b7e7f3e0..4e69bb5bf28 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -67,6 +67,8 @@ export const eventDocs: Record = { [AppEvents.in_app_review_impression]: `User sees an in-app review request`, [AppEvents.in_app_review_error]: `Error while attempting to display in-app review`, [AppEvents.handle_deeplink]: `When a deeplink that leads into the app is detected and handled`, + [AppEvents.gas_fee_warning_impression]: `When the gas fee warning is shown to the user`, + [AppEvents.gas_fee_warning_cta_press]: `When the user presses the CTA on the gas fee warning`, [HomeEvents.account_circle_tapped]: `When the account circle used in the tab navigation is tapped`, [HomeEvents.profile_address_copy]: `When a user copies their wallet address from the profile screen`, [HomeEvents.notification_scroll]: ``, @@ -588,7 +590,6 @@ export const eventDocs: Record = { [EarnEvents.earn_deposit_submit_error]: `When the deposit transaction fails`, [EarnEvents.earn_deposit_submit_cancel]: `When the user cancels the deposit after submitting by cancelling PIN input`, [EarnEvents.earn_enter_amount_continue_press]: `When a user taps continue on the earn enter amount`, - [EarnEvents.earn_deposit_add_gas_press]: `When the user doesn't have enough for gas when trying to deposit and clicks on the button to add gas token`, [EarnEvents.earn_feed_item_select]: `When the users taps on an earn transaction feed item`, [EarnEvents.earn_collect_earnings_press]: `When the user taps on the collect earnings button in the collect screen`, [EarnEvents.earn_withdraw_submit_start]: `When the wallet is about to submit the withdraw and/or claim transactions to the network`, @@ -658,6 +659,7 @@ export const eventDocs: Record = { // [EarnEvents.earn_exit_pool_press]: `When the user taps on the exit pool button from the earn card in discover tab`, // [EarnEvents.earn_deposit_more_press]: `When the user taps deposit more button from the earn card in discover tab`, // [EarnEvents.earn_active_pools_card_press]: `When the user taps on the active pool card in discover tab.`, + // [EarnEvents.earn_deposit_add_gas_press]: `When the user doesn't have enough for gas when trying to deposit and clicks on the button to add gas token`, // [AppEvents.multichain_beta_opt_in]: `When the user taps the Try it Now button on the multichain beta screen`, // [AppEvents.multichain_beta_opt_out]: `When the user taps the No Thanks button on the multichain beta screen`, // [AppEvents.multichain_beta_contact_support]: `When the user taps the Contact Support button on the multichain beta screen`, diff --git a/src/components/GasFeeWarning.test.tsx b/src/components/GasFeeWarning.test.tsx new file mode 100644 index 00000000000..bdfced036a6 --- /dev/null +++ b/src/components/GasFeeWarning.test.tsx @@ -0,0 +1,121 @@ +import { fireEvent, render } from '@testing-library/react-native' +import BigNumber from 'bignumber.js' +import React from 'react' +import { Provider } from 'react-redux' +import AppAnalytics from 'src/analytics/AppAnalytics' +import { AppEvents } from 'src/analytics/Events' +import GasFeeWarning from 'src/components/GasFeeWarning' +import { + PreparedTransactionsNeedDecreaseSpendAmountForGas, + PreparedTransactionsNotEnoughBalanceForGas, + PreparedTransactionsPossible, +} from 'src/viem/prepareTransactions' +import { createMockStore } from 'test/utils' +import { mockArbEthTokenId, mockCeloTokenId, mockTokenBalances } from 'test/values' + +const mockPreparedTransactionPossible: PreparedTransactionsPossible = { + type: 'possible' as const, + transactions: [], + feeCurrency: { + ...mockTokenBalances[mockArbEthTokenId], + isNative: true, + balance: new BigNumber(10), + priceUsd: new BigNumber(1), + lastKnownPriceUsd: new BigNumber(1), + }, +} + +const mockPreparedTransactionNotEnoughCelo: PreparedTransactionsNotEnoughBalanceForGas = { + type: 'not-enough-balance-for-gas' as const, + feeCurrencies: [ + { + ...mockTokenBalances[mockCeloTokenId], + isNative: true, + balance: new BigNumber(0), + priceUsd: new BigNumber(1500), + lastKnownPriceUsd: new BigNumber(1500), + }, + ], +} + +const mockPreparedTransactionNeedDecreaseEth: PreparedTransactionsNeedDecreaseSpendAmountForGas = { + type: 'need-decrease-spend-amount-for-gas' as const, + feeCurrency: { + ...mockTokenBalances[mockArbEthTokenId], + isNative: true, + balance: new BigNumber(0), + priceUsd: new BigNumber(1500), + lastKnownPriceUsd: new BigNumber(1500), + }, + maxGasFeeInDecimal: new BigNumber(1), + estimatedGasFeeInDecimal: new BigNumber(1), + decreasedSpendAmount: new BigNumber(1), +} + +describe('GasFeeWarning', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + it('should return null if prepareTransactionsResult is undefined', () => { + const store = createMockStore() + const { queryByTestId } = render( + + + + ) + expect(queryByTestId('GasFeeWarning')).toBeFalsy() + }) + it('should return null if prepareTransactionsResult.type is possible', () => { + const store = createMockStore() + const { queryByTestId } = render( + + + + ) + expect(queryByTestId('GasFeeWarning')).toBeFalsy() + }) + it.each` + scenario | flow | prepareTransactionsResult | feeCurrencyTokenId | title | description | ctaLabel + ${'sending max amount of ETH'} | ${'Send'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Send","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Send"}'} + ${'sending with insufficient CELO'} | ${'Send'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"CELO"}'} | ${'gasFeeWarning.descriptionNotEnoughGas, {"context":"Send","tokenSymbol":"CELO"}'} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'swapping max amount of ETH'} | ${'Swap'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Swap","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Swap"}'} + ${'swapping with insufficient CELO'} | ${'Swap'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"CELO"}'} | ${'gasFeeWarning.descriptionNotEnoughGas, {"context":"Swap","tokenSymbol":"CELO"}'} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'withdrawing max amount of ETH'} | ${'Withdraw'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Withdraw","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Withdraw"}'} + ${'withdrawing with insufficient CELO'} | ${'Withdraw'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"CELO"}'} | ${'gasFeeWarning.descriptionNotEnoughGas, {"context":"Withdraw","tokenSymbol":"CELO"}'} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'depositing max amount of ETH'} | ${'Deposit'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Deposit","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Deposit"}'} + ${'depositing with insufficient CELO'} | ${'Deposit'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"tokenSymbol":"CELO"}'} | ${'gasFeeWarning.descriptionNotEnoughGas, {"context":"Deposit","tokenSymbol":"CELO"}'} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'dapp transaction with max amount of ETH'} | ${'Dapp'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.titleDapp'} | ${'gasFeeWarning.descriptionDapp, {"tokenSymbol":"ETH"}'} | ${undefined} + ${'dapp transaction with insufficient CELO'} | ${'Dapp'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.titleDapp'} | ${'gasFeeWarning.descriptionDapp, {"tokenSymbol":"CELO"}'} | ${undefined} + `( + 'renders error correctly when $scenario', + ({ flow, prepareTransactionsResult, feeCurrencyTokenId, title, description, ctaLabel }) => { + const store = createMockStore() + const changeInputValueFn = jest.fn() + const { getByTestId, getByText } = render( + + + + ) + expect(getByTestId('GasFeeWarning')).toBeTruthy() + expect(AppAnalytics.track).toHaveBeenCalledTimes(1) + expect(AppAnalytics.track).toHaveBeenCalledWith(AppEvents.gas_fee_warning_impression, { + flow, + errorType: prepareTransactionsResult.type, + tokenId: feeCurrencyTokenId, + }) + expect(getByText(title)).toBeTruthy() + expect(getByText(description)).toBeTruthy() + expect(ctaLabel ? getByText(ctaLabel) : true).toBeTruthy() + if (ctaLabel) { + fireEvent.press(getByText(ctaLabel)) + } + expect(changeInputValueFn).toHaveBeenCalledTimes( + ctaLabel && ctaLabel.includes('ctaAction') ? 1 : 0 + ) + } + ) +}) diff --git a/src/components/GasFeeWarning.tsx b/src/components/GasFeeWarning.tsx new file mode 100644 index 00000000000..73990e6b0dd --- /dev/null +++ b/src/components/GasFeeWarning.tsx @@ -0,0 +1,105 @@ +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet } from 'react-native' +import AppAnalytics from 'src/analytics/AppAnalytics' +import { AppEvents } from 'src/analytics/Events' +import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification' +import { CICOFlow } from 'src/fiatExchanges/types' +import { navigate } from 'src/navigator/NavigationService' +import { Screens } from 'src/navigator/Screens' +import { Spacing } from 'src/styles/styles' +import { PreparedTransactionsResult } from 'src/viem/prepareTransactions' + +export type GasFeeWarningFlow = 'Send' | 'Swap' | 'Withdraw' | 'Deposit' | 'Dapp' + +function GasFeeWarning({ + prepareTransactionsResult, + flow, + changeInputValueFn, +}: { + prepareTransactionsResult?: PreparedTransactionsResult + flow: GasFeeWarningFlow + changeInputValueFn?: (amount: string) => void +}) { + const { t } = useTranslation() + + useEffect(() => { + if (prepareTransactionsResult && prepareTransactionsResult.type !== 'possible') { + AppAnalytics.track(AppEvents.gas_fee_warning_impression, { + flow, + errorType: prepareTransactionsResult.type, + tokenId: feeCurrency.tokenId, + }) + } + }, [prepareTransactionsResult]) + + if (!prepareTransactionsResult || prepareTransactionsResult.type === 'possible') { + return false + } + + const feeCurrency = + prepareTransactionsResult.type === 'not-enough-balance-for-gas' + ? prepareTransactionsResult.feeCurrencies[0] + : prepareTransactionsResult.feeCurrency + + const title = + flow === 'Dapp' + ? t('gasFeeWarning.titleDapp') + : t('gasFeeWarning.title', { tokenSymbol: feeCurrency.symbol }) + const description = + flow === 'Dapp' + ? t('gasFeeWarning.descriptionDapp', { tokenSymbol: feeCurrency.symbol }) + : prepareTransactionsResult.type === 'not-enough-balance-for-gas' + ? t('gasFeeWarning.descriptionNotEnoughGas', { + context: flow, + tokenSymbol: feeCurrency.symbol, + }) + : t('gasFeeWarning.descriptionMaxAmount', { + context: flow, + tokenSymbol: feeCurrency.symbol, + }) + const ctaLabel = + flow === 'Dapp' + ? undefined + : prepareTransactionsResult.type === 'not-enough-balance-for-gas' + ? t('gasFeeWarning.ctaBuy', { tokenSymbol: feeCurrency.symbol }) + : t('gasFeeWarning.ctaAction', { context: flow }) + + const onPressCta = () => { + AppAnalytics.track(AppEvents.gas_fee_warning_cta_press, { + flow, + tokenId: feeCurrency.tokenId, + errorType: prepareTransactionsResult.type, + }) + prepareTransactionsResult.type === 'not-enough-balance-for-gas' + ? navigate(Screens.FiatExchangeAmount, { + tokenId: feeCurrency.tokenId, + flow: CICOFlow.CashIn, + tokenSymbol: feeCurrency.symbol, + }) + : changeInputValueFn + ? changeInputValueFn(prepareTransactionsResult.decreasedSpendAmount.toString()) + : null + } + return ( + + ) +} + +const styles = StyleSheet.create({ + warning: { + marginTop: Spacing.Regular16, + paddingHorizontal: Spacing.Regular16, + borderRadius: 16, + }, +}) + +export default GasFeeWarning diff --git a/src/earn/EarnEnterAmount.test.tsx b/src/earn/EarnEnterAmount.test.tsx index 74b25b2fe6e..c87471ba236 100644 --- a/src/earn/EarnEnterAmount.test.tsx +++ b/src/earn/EarnEnterAmount.test.tsx @@ -5,7 +5,7 @@ import { DeviceEventEmitter } from 'react-native' import { getNumberFormatSettings } from 'react-native-localize' import { Provider } from 'react-redux' import AppAnalytics from 'src/analytics/AppAnalytics' -import { EarnEvents } from 'src/analytics/Events' +import { AppEvents, EarnEvents } from 'src/analytics/Events' import EarnEnterAmount from 'src/earn/EarnEnterAmount' import { usePrepareEnterAmountTransactionsCallback } from 'src/earn/hooks' import { Status as EarnStatus } from 'src/earn/slice' @@ -922,7 +922,7 @@ describe('EarnEnterAmount', () => { }) }) - it('should track analytics and navigate correctly when tapping cta to add gas', async () => { + it('should show gas warning error when prepareTransactionsResult is type not-enough-balance-for-gas, and tapping cta behaves as expected', async () => { jest.mocked(usePrepareEnterAmountTransactionsCallback).mockReturnValue({ prepareTransactionsResult: { prepareTransactionsResult: mockPreparedTransactionNotEnough, @@ -939,18 +939,18 @@ describe('EarnEnterAmount', () => { ) - await waitFor(() => expect(getByTestId('EarnEnterAmount/NotEnoughForGasWarning')).toBeTruthy()) - fireEvent.press( - getByText( - 'earnFlow.enterAmount.notEnoughBalanceForGasWarning.noGasCta, {"feeTokenSymbol":"ETH","network":"Arbitrum Sepolia"}' - ) - ) - expect(AppAnalytics.track).toHaveBeenCalledWith(EarnEvents.earn_deposit_add_gas_press, { - gasTokenId: mockArbEthTokenId, - networkId: NetworkId['arbitrum-sepolia'], - poolId: mockEarnPositions[0].positionId, - providerId: mockEarnPositions[0].appId, - depositTokenId: mockArbUsdcTokenId, + await waitFor(() => expect(getByTestId('GasFeeWarning')).toBeTruthy()) + fireEvent.press(getByText('gasFeeWarning.ctaBuy, {"tokenSymbol":"ETH"}')) + expect(AppAnalytics.track).toHaveBeenCalledTimes(2) + expect(AppAnalytics.track).toHaveBeenCalledWith(AppEvents.gas_fee_warning_impression, { + errorType: 'not-enough-balance-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, + }) + expect(AppAnalytics.track).toHaveBeenCalledWith(AppEvents.gas_fee_warning_cta_press, { + errorType: 'not-enough-balance-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, }) expect(navigate).toHaveBeenCalledWith(Screens.FiatExchangeAmount, { tokenId: mockArbEthTokenId, diff --git a/src/earn/EarnEnterAmount.tsx b/src/earn/EarnEnterAmount.tsx index 86d32baf7e0..60291f3af00 100644 --- a/src/earn/EarnEnterAmount.tsx +++ b/src/earn/EarnEnterAmount.tsx @@ -9,6 +9,7 @@ import { EarnEvents, SendEvents } from 'src/analytics/Events' import BackButton from 'src/components/BackButton' import BottomSheet, { BottomSheetModalRefType } from 'src/components/BottomSheet' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' +import GasFeeWarning from 'src/components/GasFeeWarning' import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification' import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView' import { LabelWithInfo } from 'src/components/LabelWithInfo' @@ -27,7 +28,6 @@ import EarnDepositBottomSheet from 'src/earn/EarnDepositBottomSheet' import { usePrepareEnterAmountTransactionsCallback } from 'src/earn/hooks' import { depositStatusSelector } from 'src/earn/selectors' import { getSwapToAmountInDecimals } from 'src/earn/utils' -import { CICOFlow } from 'src/fiatExchanges/types' import ArrowRightThick from 'src/icons/ArrowRightThick' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' @@ -36,7 +36,6 @@ import { hooksApiUrlSelector, positionsWithBalanceSelector } from 'src/positions import { EarnPosition, Position } from 'src/positions/types' import { useSelector } from 'src/redux/hooks' import EnterAmountOptions from 'src/send/EnterAmountOptions' -import { NETWORK_NAMES } from 'src/shared/conts' import { getFeatureGate } from 'src/statsig' import { StatsigFeatureGates } from 'src/statsig/types' import Colors from 'src/styles/colors' @@ -270,10 +269,6 @@ export default function EarnEnterAmount({ route }: Props) { const showLowerAmountError = processedAmounts.token.bignum && processedAmounts.token.bignum.gt(inputToken.balance) - const showNotEnoughBalanceForGasWarning = - !showLowerAmountError && - prepareTransactionsResult && - prepareTransactionsResult.type === 'not-enough-balance-for-gas' const transactionIsPossible = !showLowerAmountError && prepareTransactionsResult && @@ -407,39 +402,11 @@ export default function EarnEnterAmount({ route }: Props) { /> )} - - {showNotEnoughBalanceForGasWarning && ( - { - AppAnalytics.track(EarnEvents.earn_deposit_add_gas_press, { - gasTokenId: feeCurrencies[0].tokenId, - depositTokenId: pool.dataProps.depositTokenId, - networkId: pool.networkId, - providerId: pool.appId, - poolId: pool.positionId, - }) - navigate(Screens.FiatExchangeAmount, { - tokenId: prepareTransactionsResult.feeCurrencies[0].tokenId, - flow: CICOFlow.CashIn, - tokenSymbol: prepareTransactionsResult.feeCurrencies[0].symbol, - }) - }} - style={styles.warning} - testID="EarnEnterAmount/NotEnoughForGasWarning" - /> - )} + {showLowerAmountError && (