diff --git a/src/navigator/types.tsx b/src/navigator/types.tsx index 5318c4c3fcd..0034a058d48 100644 --- a/src/navigator/types.tsx +++ b/src/navigator/types.tsx @@ -21,6 +21,7 @@ import { AssetTabType } from 'src/tokens/Assets' import { AssetViewType } from 'src/tokens/TokenBalances' import { Network, TokenTransaction } from 'src/transactions/types' import { CiCoCurrency, Currency } from 'src/utils/currencies' +import { SerializableTransactionRequest } from 'src/viem/preparedTransactionSerialization' import { WalletConnectRequestType } from 'src/walletConnect/types' // Typed nested navigator params @@ -34,6 +35,9 @@ interface SendConfirmationParams { origin: SendOrigin transactionData: TransactionDataInput isFromScan: boolean + preparedTransaction?: SerializableTransactionRequest + feeAmount?: string + feeTokenId?: string } export type StackParamList = { diff --git a/src/send/SendConfirmation.test.tsx b/src/send/SendConfirmation.test.tsx index e613bfa6561..4462c68ed56 100644 --- a/src/send/SendConfirmation.test.tsx +++ b/src/send/SendConfirmation.test.tsx @@ -67,6 +67,21 @@ const mockScreenProps = getMockStackScreenProps(Screens.SendConfirmation, { isFromScan: false, }) +const mockScreenPropsWithPreparedTx = getMockStackScreenProps(Screens.SendConfirmation, { + transactionData: { + ...mockTokenTransactionData, + }, + origin: SendOrigin.AppSendFlow, + isFromScan: false, + preparedTransaction: { + from: '0xfrom', + to: '0xto', + data: '0xdata', + }, + feeAmount: '0.004', + feeTokenId: mockCeloTokenId, +}) + const mockInviteScreenProps = getMockStackScreenProps(Screens.SendConfirmation, { transactionData: mockTokenInviteTransactionData, origin: SendOrigin.AppSendFlow, @@ -176,7 +191,7 @@ describe('SendConfirmation', () => { } it('renders correctly', async () => { - const tree = renderScreen() + const tree = renderScreen({}, mockScreenPropsWithPreparedTx) expect(tree).toMatchSnapshot() }) @@ -198,16 +213,10 @@ describe('SendConfirmation', () => { expect(getElementText(totalComponent)).toEqual('₱1.36') }) - it('renders correctly for send payment confirmation, sending cUSD, fee in CELO (new UI)', async () => { - const { getByText, getByTestId } = renderScreen() - - fireEvent.press(getByText('feeEstimate')) - - await act(() => { - jest.runAllTimers() - }) + it('renders correctly for send payment confirmation with fees from props (new UI)', async () => { + const { getByTestId } = renderScreen({}, mockScreenPropsWithPreparedTx) - const feeComponent = getByTestId('feeDrawer/SendConfirmation/totalFee') + const feeComponent = getByTestId('LineItemRow/SendConfirmation/fee') expect(getElementText(feeComponent)).toEqual('0.004 CELO') const totalComponent = getByTestId('TotalLineItem/Total') @@ -258,7 +267,8 @@ describe('SendConfirmation', () => { expect(getElementText(totalComponent)).toEqual('₱1.36') }) - it('shows --- for fee when fee estimate fails', async () => { + it('shows --- for fee when fee estimate fails (old UI)', async () => { + jest.mocked(getFeatureGate).mockReturnValue(false) const { queryByTestId, getByText } = renderScreen({ fees: { estimates: { @@ -276,7 +286,8 @@ describe('SendConfirmation', () => { expect(getByText('---')).toBeTruthy() }) - it('shows loading for fee while fee estimate loads', async () => { + it('shows loading for fee while fee estimate loads (old UI)', async () => { + jest.mocked(getFeatureGate).mockReturnValue(false) const { queryByTestId, getByTestId } = renderScreen({ fees: { estimates: { @@ -503,7 +514,8 @@ describe('SendConfirmation', () => { ) }) - it('dispatches fee estimation if not already done', async () => { + it('dispatches fee estimation if not already done for old send flow', async () => { + jest.mocked(getFeatureGate).mockReturnValue(false) const { store } = renderScreen({ fees: { estimates: emptyFees } }) expect(store.getActions()).toMatchInlineSnapshot(` @@ -525,4 +537,10 @@ describe('SendConfirmation', () => { ] `) }) + + it('does not dispatch fee estimate action for new send flow', async () => { + const { store } = renderScreen({ fees: { estimates: emptyFees } }) + + expect(store.getActions()).toEqual([]) + }) }) diff --git a/src/send/SendConfirmation.tsx b/src/send/SendConfirmation.tsx index 91e18d34002..bf35e92d437 100644 --- a/src/send/SendConfirmation.tsx +++ b/src/send/SendConfirmation.tsx @@ -13,8 +13,8 @@ import BackButton from 'src/components/BackButton' import CommentTextInput from 'src/components/CommentTextInput' import ContactCircle from 'src/components/ContactCircle' import Dialog from 'src/components/Dialog' -import FeeDrawer from 'src/components/FeeDrawer' import LegacyFeeDrawer from 'src/components/LegacyFeeDrawer' +import LineItemRow from 'src/components/LineItemRow' import ReviewFrame from 'src/components/ReviewFrame' import ShortenedAddress from 'src/components/ShortenedAddress' import TextButton from 'src/components/TextButton' @@ -48,8 +48,7 @@ import { StatsigFeatureGates } from 'src/statsig/types' import colors from 'src/styles/colors' import fontStyles, { typeScale } from 'src/styles/fonts' import { iconHitslop } from 'src/styles/variables' -import { useTokenInfo, useTokenInfoByAddress } from 'src/tokens/hooks' -import { celoAddressSelector } from 'src/tokens/selectors' +import { useTokenInfo } from 'src/tokens/hooks' import { tokenSupportsComments } from 'src/tokens/utils' import { Network } from 'src/transactions/types' import { Currency } from 'src/utils/currencies' @@ -101,6 +100,8 @@ function SendConfirmation(props: Props) { comment: commentFromParams, tokenId, }, + feeAmount, + feeTokenId, } = props.route.params const newSendScreen = getFeatureGate(StatsigFeatureGates.USE_NEW_SEND_FLOW) @@ -144,54 +145,47 @@ function SendConfirmation(props: Props) { }) } - // TODO (ACT-922): Update all fee-related code below to work with native tokens const feeEstimates = useSelector(feeEstimatesSelector) const feeType = FeeType.SEND const feeEstimate = tokenAddress ? feeEstimates[tokenAddress]?.[feeType] : undefined - // TODO (ACT-922): Actually disable Ethereum sends if no fee information exists + // TODO (satish): check and use preparedTransaction const disableSend = isSending || (!feeEstimate?.feeInfo && tokenNetwork === Network.Celo) useEffect(() => { - if (!feeEstimate && tokenAddress) { + if (!newSendScreen && !feeEstimate && tokenAddress) { dispatch(estimateFee({ feeType, tokenAddress })) } - }, [feeEstimate]) + }, [feeEstimate, newSendScreen]) useEffect(() => { - if (!isDekRegistered && tokenAddress) { + if (!newSendScreen && !isDekRegistered && tokenAddress) { dispatch(estimateFee({ feeType: FeeType.REGISTER_DEK, tokenAddress })) } - }, [isDekRegistered]) + }, [isDekRegistered, newSendScreen]) const securityFeeInUsd = feeEstimate?.usdFee ? new BigNumber(feeEstimate.usdFee) : undefined const storedDekFee = tokenAddress ? feeEstimates[tokenAddress]?.[FeeType.REGISTER_DEK] : undefined const dekFeeInUsd = storedDekFee?.usdFee ? new BigNumber(storedDekFee.usdFee) : undefined const totalFeeInUsd = securityFeeInUsd?.plus(dekFeeInUsd ?? 0) - const celoAddress = useSelector(celoAddressSelector) - const feeTokenAddress = feeEstimate?.feeInfo?.feeCurrency ?? celoAddress - const feeTokenInfoFromEstimate = useTokenInfoByAddress(feeTokenAddress) - const feeTokenInfo = newSendScreen ? feeTokenInfoFromEstimate : tokenInfo - const securityFeeInToken = securityFeeInUsd?.dividedBy(feeTokenInfo?.priceUsd ?? 0) - const dekFeeInToken = dekFeeInUsd?.dividedBy(feeTokenInfo?.priceUsd ?? 0) - const totalFeeInFeeToken = totalFeeInUsd?.dividedBy(feeTokenInfo?.priceUsd ?? 0) const FeeContainer = () => { return ( {newSendScreen ? ( - + feeAmount && ( + + } + /> + ) ) : ( { - // TODO (ACT-922): Remove Celo network check once we have Ethereum fees + // TODO (satish): Use preparedTransaction for new send flow if (!feeEstimate?.feeInfo && tokenNetwork === Network.Celo) { // This should never happen because the confirm button is disabled if this happens. dispatch(showError(ErrorMessages.SEND_PAYMENT_FAILED)) diff --git a/src/send/SendEnterAmount.test.tsx b/src/send/SendEnterAmount.test.tsx index 576d74c8a39..079b62deb87 100644 --- a/src/send/SendEnterAmount.test.tsx +++ b/src/send/SendEnterAmount.test.tsx @@ -11,8 +11,11 @@ import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { RecipientType } from 'src/recipients/recipient' import SendEnterAmount from 'src/send/SendEnterAmount' +import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions' import { getSupportedNetworkIdsForSend } from 'src/tokens/utils' import { NetworkId } from 'src/transactions/types' +import { PreparedTransactionsPossible } from 'src/viem/prepareTransactions' +import { getSerializablePreparedTransaction } from 'src/viem/preparedTransactionSerialization' import MockedNavigator from 'test/MockedNavigator' import { createMockStore } from 'test/utils' import { @@ -24,8 +27,6 @@ import { mockPoofTokenId, mockTokenBalances, } from 'test/values' -import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions' -import { PreparedTransactionsPossible } from 'src/viem/prepareTransactions' jest.mock('src/tokens/utils', () => ({ ...jest.requireActual('src/tokens/utils'), @@ -445,6 +446,11 @@ describe('SendEnterAmount', () => { tokenAddress: mockCeloAddress, tokenAmount: new BigNumber(8), }, + feeAmount: '0.006', + feeTokenId: mockCeloTokenId, + preparedTransaction: getSerializablePreparedTransaction( + mockPrepareTransactionsResultPossible.transactions[0] + ), }) }) diff --git a/src/send/SendEnterAmount.tsx b/src/send/SendEnterAmount.tsx index 89d988b1f4b..20e2c7c78a8 100644 --- a/src/send/SendEnterAmount.tsx +++ b/src/send/SendEnterAmount.tsx @@ -45,6 +45,7 @@ import { TokenBalance } from 'src/tokens/slice' import { getSupportedNetworkIdsForSend } from 'src/tokens/utils' import Logger from 'src/utils/Logger' import { getFeeCurrencyAndAmount } from 'src/viem/prepareTransactions' +import { getSerializablePreparedTransaction } from 'src/viem/preparedTransactionSerialization' import { walletAddressSelector } from 'src/web3/selectors' type Props = NativeStackScreenProps @@ -152,7 +153,10 @@ function SendEnterAmount({ route }: Props) { } const onReviewPress = () => { - // TODO(ACT-955): pass fees as props for confirmation screen + if (!sendIsPossible) { + // should never happen because button is disabled if send is not possible + throw new Error('Send is not possible') + } navigate(Screens.SendConfirmation, { origin, isFromScan, @@ -164,6 +168,11 @@ function SendEnterAmount({ route }: Props) { tokenAddress: token.address!, tokenAmount: parsedAmount, }, + preparedTransaction: getSerializablePreparedTransaction( + prepareTransactionsResult.transactions[0] + ), + feeAmount: feeAmount?.toString(), + feeTokenId: feeCurrency?.tokenId, }) ValoraAnalytics.track(SendEvents.send_amount_continue, { origin, diff --git a/src/send/__snapshots__/SendConfirmation.test.tsx.snap b/src/send/__snapshots__/SendConfirmation.test.tsx.snap index ffcb419916b..375c2adb381 100644 --- a/src/send/__snapshots__/SendConfirmation.test.tsx.snap +++ b/src/send/__snapshots__/SendConfirmation.test.tsx.snap @@ -344,161 +344,64 @@ exports[`SendConfirmation renders correctly 1`] = ` } } > - - + - - - - feeEstimate - - - - - - - - - - - - - - 0.004 - CELO - - - - + feeEstimate + + + + 0.004 + CELO + +