Skip to content

Commit

Permalink
feat(send): show fees on confirmation screen for all tokens (#4527)
Browse files Browse the repository at this point in the history
### Description

For the new send flow, pass down prepared tx and fee amount to send
confirmation and display fees using those instead of the estimates from
redux. Replaces FeeDrawer with LineItemRow since dek fee doesn't need to
be displayed.

Using preparedTx for sendPayment will be in a follow up.

### Test plan

Unit tests, manual



https://github.com/valora-inc/wallet/assets/5062591/dbba8fe2-f173-43c3-a256-bf10981b3936



### Related issues

- Part of ACT-968

### Backwards compatibility

N/A
  • Loading branch information
satish-ravi authored Nov 27, 2023
1 parent 0b76f3a commit 27de3d2
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 191 deletions.
4 changes: 4 additions & 0 deletions src/navigator/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,6 +35,9 @@ interface SendConfirmationParams {
origin: SendOrigin
transactionData: TransactionDataInput
isFromScan: boolean
preparedTransaction?: SerializableTransactionRequest
feeAmount?: string
feeTokenId?: string
}

export type StackParamList = {
Expand Down
44 changes: 31 additions & 13 deletions src/send/SendConfirmation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -176,7 +191,7 @@ describe('SendConfirmation', () => {
}

it('renders correctly', async () => {
const tree = renderScreen()
const tree = renderScreen({}, mockScreenPropsWithPreparedTx)
expect(tree).toMatchSnapshot()
})

Expand All @@ -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')
Expand Down Expand Up @@ -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: {
Expand All @@ -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: {
Expand Down Expand Up @@ -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(`
Expand All @@ -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([])
})
})
52 changes: 23 additions & 29 deletions src/send/SendConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -101,6 +100,8 @@ function SendConfirmation(props: Props) {
comment: commentFromParams,
tokenId,
},
feeAmount,
feeTokenId,
} = props.route.params

const newSendScreen = getFeatureGate(StatsigFeatureGates.USE_NEW_SEND_FLOW)
Expand Down Expand Up @@ -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 (
<View style={styles.feeContainer}>
{newSendScreen ? (
<FeeDrawer
testID={'feeDrawer/SendConfirmation'}
isEstimate={true}
securityFee={securityFeeInToken}
showDekfee={!isDekRegistered}
dekFee={dekFeeInToken}
feeLoading={feeEstimate?.loading || storedDekFee?.loading}
feeHasError={feeEstimate?.error || storedDekFee?.error}
totalFee={totalFeeInFeeToken}
showLocalAmount={false}
tokenId={feeTokenInfo?.tokenId}
/>
feeAmount && (
<LineItemRow
testID="SendConfirmation/fee"
title={t('feeEstimate')}
amount={
<TokenDisplay
amount={new BigNumber(feeAmount)}
tokenId={feeTokenId}
showLocalAmount={false}
/>
}
/>
)
) : (
<LegacyFeeDrawer
testID={'feeDrawer/SendConfirmation'}
Expand Down Expand Up @@ -240,7 +234,7 @@ function SendConfirmation(props: Props) {
}

const onSend = () => {
// 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))
Expand Down
10 changes: 8 additions & 2 deletions src/send/SendEnterAmount.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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'),
Expand Down Expand Up @@ -445,6 +446,11 @@ describe('SendEnterAmount', () => {
tokenAddress: mockCeloAddress,
tokenAmount: new BigNumber(8),
},
feeAmount: '0.006',
feeTokenId: mockCeloTokenId,
preparedTransaction: getSerializablePreparedTransaction(
mockPrepareTransactionsResultPossible.transactions[0]
),
})
})

Expand Down
11 changes: 10 additions & 1 deletion src/send/SendEnterAmount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<StackParamList, Screens.SendEnterAmount>
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 27de3d2

Please sign in to comment.