diff --git a/locales/base/translation.json b/locales/base/translation.json index 1ed632fa50e..289c786e642 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -2831,5 +2831,19 @@ "selectToken": "Select token", "fiatPriceUnavailable": "Price unavailable", "tokenDescription": "{{tokenName}} on {{tokenNetwork}}" + }, + "gasFeeWarning": { + "title": "You need more {{tokenSymbol}} for gas fees", + "title_Dapp": "You have an insufficient gas token balance", + "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", + "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" } } diff --git a/src/analytics/Events.tsx b/src/analytics/Events.tsx index 36a7bd873cd..ecf5c6368ac 100644 --- a/src/analytics/Events.tsx +++ b/src/analytics/Events.tsx @@ -322,8 +322,8 @@ export enum QrScreenEvents { } export enum FeeEvents { - estimate_fee_failed = 'estimate_fee_failed', - estimate_fee_success = 'estimate_fee_success', + gas_fee_warning_impression = 'gas_fee_warning_impression', + gas_fee_warning_cta_press = 'gas_fee_warning_cta_press', } export enum TransactionEvents { @@ -661,7 +661,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 650116ac342..fb0df6d507d 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' @@ -629,15 +630,17 @@ interface SendEventsProperties { } interface FeeEventsProperties { - [FeeEvents.estimate_fee_failed]: { - feeType: string - tokenAddress: string - error: string + [FeeEvents.gas_fee_warning_impression]: { + flow: GasFeeWarningFlow + errorType: 'need-decrease-spend-amount-for-gas' | 'not-enough-balance-for-gas' + tokenId: string + networkId: NetworkId } - [FeeEvents.estimate_fee_success]: { - feeType: string - tokenAddress: string - usdFee: string + [FeeEvents.gas_fee_warning_cta_press]: { + flow: GasFeeWarningFlow + errorType: 'need-decrease-spend-amount-for-gas' | 'not-enough-balance-for-gas' + tokenId: string + networkId: NetworkId } } @@ -1629,7 +1632,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 9d47c75268f..1413204fe92 100644 --- a/src/analytics/docs.ts +++ b/src/analytics/docs.ts @@ -346,8 +346,8 @@ export const eventDocs: Record = { [QrScreenEvents.qr_screen_copy_address]: ``, [QrScreenEvents.qr_scanner_open]: `When unique "QR scanner" button is pressed`, [QrScreenEvents.qr_scanned]: `When a QR code has been successfully scanned`, - [FeeEvents.estimate_fee_failed]: ``, - [FeeEvents.estimate_fee_success]: ``, + [FeeEvents.gas_fee_warning_impression]: `When the gas fee warning is shown to the user`, + [FeeEvents.gas_fee_warning_cta_press]: `When the user presses the CTA on the gas fee warning`, [TransactionEvents.transaction_start]: `when a transaction is about to be submitted to the blockchain`, [TransactionEvents.transaction_gas_estimated]: `when gas is estimated for a transaction or an already estimated gas is used in a transaction about to be submitted (only for contract-kit)`, [TransactionEvents.transaction_hash_received]: `when a hash is received for a transaction`, @@ -589,7 +589,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`, @@ -659,7 +658,10 @@ 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`, + // [FeeEvents.estimate_fee_failed]: ``, + // [FeeEvents.estimate_fee_success]: ``, } diff --git a/src/components/GasFeeWarning.test.tsx b/src/components/GasFeeWarning.test.tsx new file mode 100644 index 00000000000..2fd89f8ca6c --- /dev/null +++ b/src/components/GasFeeWarning.test.tsx @@ -0,0 +1,126 @@ +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 { FeeEvents } from 'src/analytics/Events' +import GasFeeWarning from 'src/components/GasFeeWarning' +import { NetworkId } from 'src/transactions/types' +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, {"context":"Send","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Send","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Send"}'} + ${'sending with insufficient CELO'} | ${'Send'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"context":"Send","tokenSymbol":"CELO"}'} | ${undefined} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'swapping max amount of ETH'} | ${'Swap'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"context":"Swap","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Swap","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Swap"}'} + ${'swapping with insufficient CELO'} | ${'Swap'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"context":"Swap","tokenSymbol":"CELO"}'} | ${undefined} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'withdrawing max amount of ETH'} | ${'Withdraw'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"context":"Withdraw","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Withdraw","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Withdraw"}'} + ${'withdrawing with insufficient CELO'} | ${'Withdraw'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"context":"Withdraw","tokenSymbol":"CELO"}'} | ${undefined} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'depositing max amount of ETH'} | ${'Deposit'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"context":"Deposit","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionMaxAmount, {"context":"Deposit","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.ctaAction, {"context":"Deposit"}'} + ${'depositing with insufficient CELO'} | ${'Deposit'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"context":"Deposit","tokenSymbol":"CELO"}'} | ${undefined} | ${'gasFeeWarning.ctaBuy, {"tokenSymbol":"CELO"}'} + ${'dapp transaction with max amount of ETH'} | ${'Dapp'} | ${mockPreparedTransactionNeedDecreaseEth} | ${mockArbEthTokenId} | ${'gasFeeWarning.title, {"context":"Dapp","tokenSymbol":"ETH"}'} | ${'gasFeeWarning.descriptionDapp, {"tokenSymbol":"ETH"}'} | ${undefined} + ${'dapp transaction with insufficient CELO'} | ${'Dapp'} | ${mockPreparedTransactionNotEnoughCelo} | ${mockCeloTokenId} | ${'gasFeeWarning.title, {"context":"Dapp","tokenSymbol":"CELO"}'} | ${'gasFeeWarning.descriptionDapp, {"tokenSymbol":"CELO"}'} | ${undefined} + `( + 'renders error correctly when $scenario', + ({ flow, prepareTransactionsResult, feeCurrencyTokenId, title, description, ctaLabel }) => { + const store = createMockStore() + const onPressSmallerAmount = jest.fn() + const { getByTestId, getByText } = render( + + + + ) + expect(getByTestId('GasFeeWarning')).toBeTruthy() + expect(AppAnalytics.track).toHaveBeenCalledTimes(1) + expect(AppAnalytics.track).toHaveBeenCalledWith(FeeEvents.gas_fee_warning_impression, { + flow, + errorType: prepareTransactionsResult.type, + tokenId: feeCurrencyTokenId, + networkId: + feeCurrencyTokenId === mockArbEthTokenId + ? NetworkId['arbitrum-sepolia'] + : NetworkId['celo-alfajores'], + }) + expect(getByText(title)).toBeTruthy() + expect(description ? getByText(description) : true).toBeTruthy() + expect(ctaLabel ? getByText(ctaLabel) : true).toBeTruthy() + if (ctaLabel) { + fireEvent.press(getByText(ctaLabel)) + } + expect(onPressSmallerAmount).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..b975940541f --- /dev/null +++ b/src/components/GasFeeWarning.tsx @@ -0,0 +1,124 @@ +import React, { useEffect, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet } from 'react-native' +import AppAnalytics from 'src/analytics/AppAnalytics' +import { FeeEvents } 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, + onPressSmallerAmount, +}: { + prepareTransactionsResult?: PreparedTransactionsResult + flow: GasFeeWarningFlow + onPressSmallerAmount?: (amount: string) => void +}) { + const { t } = useTranslation() + + const feeCurrency = prepareTransactionsResult + ? prepareTransactionsResult.type === 'not-enough-balance-for-gas' + ? prepareTransactionsResult.feeCurrencies[0] + : prepareTransactionsResult.feeCurrency + : undefined + + useEffect(() => { + if (feeCurrency && prepareTransactionsResult && prepareTransactionsResult.type !== 'possible') { + AppAnalytics.track(FeeEvents.gas_fee_warning_impression, { + flow, + errorType: prepareTransactionsResult.type, + tokenId: feeCurrency.tokenId, + networkId: feeCurrency.networkId, + }) + } + }, [flow, prepareTransactionsResult, feeCurrency]) + + const { title, description, ctaLabel, onPressCta } = useMemo(() => { + if ( + !feeCurrency || + !prepareTransactionsResult || + prepareTransactionsResult.type === 'possible' + ) { + return {} + } + const title = t('gasFeeWarning.title', { + context: flow, + tokenSymbol: feeCurrency.symbol, + }) + const trackCtaAnalytics = () => { + AppAnalytics.track(FeeEvents.gas_fee_warning_cta_press, { + flow, + tokenId: feeCurrency.tokenId, + errorType: prepareTransactionsResult.type, + networkId: feeCurrency.networkId, + }) + } + if (flow === 'Dapp') { + return { + title, + description: t('gasFeeWarning.descriptionDapp', { tokenSymbol: feeCurrency.symbol }), + ctaLabel: undefined, + onPressCta: undefined, + } + } else if (prepareTransactionsResult.type === 'not-enough-balance-for-gas') { + return { + title, + ctaLabel: t('gasFeeWarning.ctaBuy', { tokenSymbol: feeCurrency.symbol }), + onPressCta: () => { + trackCtaAnalytics() + navigate(Screens.FiatExchangeAmount, { + tokenId: feeCurrency.tokenId, + flow: CICOFlow.CashIn, + tokenSymbol: feeCurrency.symbol, + }) + }, + } + } else { + return { + title, + description: t('gasFeeWarning.descriptionMaxAmount', { + context: flow, + tokenSymbol: feeCurrency.symbol, + }), + ctaLabel: t('gasFeeWarning.ctaAction', { context: flow }), + onPressCta: () => { + trackCtaAnalytics() + onPressSmallerAmount?.(prepareTransactionsResult.decreasedSpendAmount.toString()) + }, + } + } + }, [flow, prepareTransactionsResult, feeCurrency]) + + if (!title) { + return false + } + + return ( + + ) +} + +const styles = StyleSheet.create({ + warning: { + marginTop: Spacing.Regular16, + paddingHorizontal: Spacing.Regular16, + borderRadius: 16, + }, +}) + +export default GasFeeWarning diff --git a/src/components/InLineNotification.tsx b/src/components/InLineNotification.tsx index 05bfb8f4683..4ee716a916d 100644 --- a/src/components/InLineNotification.tsx +++ b/src/components/InLineNotification.tsx @@ -82,7 +82,7 @@ export function InLineNotification({ )} {!!title && {title}} - {description} + {!!description && {description}} diff --git a/src/earn/EarnEnterAmount.test.tsx b/src/earn/EarnEnterAmount.test.tsx index 739d9587c13..e66ced060cc 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 { EarnEvents, FeeEvents } from 'src/analytics/Events' import EarnEnterAmount from 'src/earn/EarnEnterAmount' import { usePrepareEnterAmountTransactionsCallback } from 'src/earn/hooks' import { Status as EarnStatus } from 'src/earn/slice' @@ -18,6 +18,7 @@ import { SwapTransaction } from 'src/swap/types' import { TokenBalance } from 'src/tokens/slice' import { NetworkId } from 'src/transactions/types' import { + PreparedTransactionsNeedDecreaseSpendAmountForGas, PreparedTransactionsNotEnoughBalanceForGas, PreparedTransactionsPossible, } from 'src/viem/prepareTransactions' @@ -96,6 +97,20 @@ const mockPreparedTransactionNotEnough: PreparedTransactionsNotEnoughBalanceForG ], } +const mockPreparedTransactionDecreaseSpend: 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), +} + const mockArbFeeCurrencies: TokenBalance[] = [ { ...mockTokenBalances[mockArbEthTokenId], @@ -928,7 +943,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, @@ -945,18 +960,20 @@ 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, + await waitFor(() => expect(getByTestId('GasFeeWarning')).toBeTruthy()) + fireEvent.press(getByText('gasFeeWarning.ctaBuy, {"tokenSymbol":"ETH"}')) + expect(AppAnalytics.track).toHaveBeenCalledTimes(2) + expect(AppAnalytics.track).toHaveBeenCalledWith(FeeEvents.gas_fee_warning_impression, { + errorType: 'not-enough-balance-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, + networkId: NetworkId['arbitrum-sepolia'], + }) + expect(AppAnalytics.track).toHaveBeenCalledWith(FeeEvents.gas_fee_warning_cta_press, { + errorType: 'not-enough-balance-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, networkId: NetworkId['arbitrum-sepolia'], - poolId: mockEarnPositions[0].positionId, - providerId: mockEarnPositions[0].appId, - depositTokenId: mockArbUsdcTokenId, }) expect(navigate).toHaveBeenCalledWith(Screens.FiatExchangeAmount, { tokenId: mockArbEthTokenId, @@ -965,6 +982,42 @@ describe('EarnEnterAmount', () => { }) }) + it('should show gas warning error when prepareTransactionsResult is type need-decrease-spend-amount-for-gas, and tapping cta behaves as expected', async () => { + jest.mocked(usePrepareEnterAmountTransactionsCallback).mockReturnValue({ + prepareTransactionsResult: { + prepareTransactionsResult: mockPreparedTransactionDecreaseSpend, + swapTransaction: undefined, + }, + refreshPreparedTransactions: jest.fn(), + clearPreparedTransactions: jest.fn(), + prepareTransactionError: undefined, + isPreparingTransactions: false, + }) + const { getByTestId, getByText } = render( + + + + ) + + await waitFor(() => expect(getByTestId('GasFeeWarning')).toBeTruthy()) + fireEvent.press(getByText('gasFeeWarning.ctaAction, {"context":"Deposit"}')) + expect(AppAnalytics.track).toHaveBeenCalledTimes(2) + expect(AppAnalytics.track).toHaveBeenCalledWith(FeeEvents.gas_fee_warning_impression, { + errorType: 'need-decrease-spend-amount-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, + networkId: NetworkId['arbitrum-sepolia'], + }) + expect(AppAnalytics.track).toHaveBeenCalledWith(FeeEvents.gas_fee_warning_cta_press, { + errorType: 'need-decrease-spend-amount-for-gas', + flow: 'Deposit', + tokenId: mockArbEthTokenId, + networkId: NetworkId['arbitrum-sepolia'], + }) + // Deposit value should now be decreasedSpendAmount from mockPreparedTransactionDecreaseSpend, which is 1 + expect(getByTestId('EarnEnterAmount/Deposit/Crypto')).toHaveTextContent('1.00 USDC') + }) + it('should show the FeeDetailsBottomSheet when the user taps the fee details icon', async () => { jest.mocked(usePrepareEnterAmountTransactionsCallback).mockReturnValue({ prepareTransactionsResult: { diff --git a/src/earn/EarnEnterAmount.tsx b/src/earn/EarnEnterAmount.tsx index e0f195ae8eb..cb5389c6e88 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' @@ -286,10 +285,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 && @@ -423,39 +418,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 && (