-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(feedV2): generic deposit and withdraw types (#6189)
### Description Introduce new generic types for the TX feed for deposits and withdrawals. `DEPOSIT` replaces `EARN_DEPOSIT` and `EARN_SWAP_DEPOSIT`. `WITHDRAW` replaces `EARN_WITHDRAW`. Why? 1. This allows directly supporting all the withdrawals and deposits for all dapps that Zerion already knows about. 2. We can do this now without too much effort and keep a "simple" backend implementation for the new `getWalletTransactions` This is just for the new feed API with Zerion. ### Test plan - Tests pass ### Related issues - Related to RET-1204 - Also see this Slack [thread](https://valora-app.slack.com/archives/C029Z1QMD7B/p1729847488933829) for context ### Backwards compatibility Yes ### Network scalability If a new NetworkId and/or Network are added in the future, the changes in this PR will: - [x] Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added)
- Loading branch information
1 parent
811e881
commit 6581faf
Showing
11 changed files
with
858 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { render } from '@testing-library/react-native' | ||
import * as React from 'react' | ||
import { Provider } from 'react-redux' | ||
import DepositOrWithdrawFeedItem from 'src/transactions/feed/DepositOrWithdrawFeedItem' | ||
import { NetworkId, TokenTransactionTypeV2, TransactionStatus } from 'src/transactions/types' | ||
import { createMockStore } from 'test/utils' | ||
import { mockCeloTokenId, mockCusdTokenId } from 'test/values' | ||
|
||
describe('DepositOrWithdrawFeedItem', () => { | ||
const store = createMockStore() | ||
|
||
const depositTransaction = { | ||
type: TokenTransactionTypeV2.Deposit as const, | ||
networkId: NetworkId['celo-mainnet'], | ||
timestamp: 1234567890, | ||
block: '123456', | ||
transactionHash: '0x123', | ||
fees: [], | ||
appName: 'Some Dapp', | ||
inAmount: { | ||
value: '100', | ||
tokenId: mockCeloTokenId, | ||
}, | ||
outAmount: { | ||
value: '100', | ||
tokenId: mockCusdTokenId, | ||
}, | ||
status: TransactionStatus.Complete, | ||
} | ||
|
||
const withdrawTransaction = { | ||
...depositTransaction, | ||
type: TokenTransactionTypeV2.Withdraw as const, | ||
} | ||
|
||
it('renders deposit correctly', () => { | ||
const { getByText, getByTestId } = render( | ||
<Provider store={store}> | ||
<DepositOrWithdrawFeedItem transaction={depositTransaction} /> | ||
</Provider> | ||
) | ||
|
||
expect(getByText('transactionFeed.depositTitle')).toBeTruthy() | ||
expect(getByText('transactionFeed.depositSubtitle, {"txAppName":"Some Dapp"}')).toBeTruthy() | ||
expect(getByTestId('DepositOrWithdrawFeedItem/DEPOSIT-amount-crypto')).toBeTruthy() | ||
expect(getByTestId('DepositOrWithdrawFeedItem/DEPOSIT-amount-local')).toBeTruthy() | ||
}) | ||
|
||
it('renders withdraw correctly', () => { | ||
const { getByText, getByTestId } = render( | ||
<Provider store={store}> | ||
<DepositOrWithdrawFeedItem transaction={withdrawTransaction} /> | ||
</Provider> | ||
) | ||
|
||
expect(getByText('transactionFeed.withdrawTitle')).toBeTruthy() | ||
expect(getByText('transactionFeed.withdrawSubtitle, {"txAppName":"Some Dapp"}')).toBeTruthy() | ||
expect(getByTestId('DepositOrWithdrawFeedItem/WITHDRAW-amount-crypto')).toBeTruthy() | ||
expect(getByTestId('DepositOrWithdrawFeedItem/WITHDRAW-amount-local')).toBeTruthy() | ||
}) | ||
|
||
it('displays app name when available', () => { | ||
const { getByText } = render( | ||
<Provider store={store}> | ||
<DepositOrWithdrawFeedItem transaction={depositTransaction} /> | ||
</Provider> | ||
) | ||
|
||
expect(getByText('transactionFeed.depositSubtitle, {"txAppName":"Some Dapp"}')).toBeTruthy() | ||
}) | ||
|
||
it('does not display app name when not available', () => { | ||
const transactionWithoutProvider = { | ||
...depositTransaction, | ||
appName: undefined, | ||
} | ||
|
||
const { queryByText } = render( | ||
<Provider store={store}> | ||
<DepositOrWithdrawFeedItem transaction={transactionWithoutProvider} /> | ||
</Provider> | ||
) | ||
|
||
expect(queryByText('transactionFeed.depositSubtitle, {"txAppName":"Some Dapp"}')).toBeNull() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import BigNumber from 'bignumber.js' | ||
import React from 'react' | ||
import { useTranslation } from 'react-i18next' | ||
import { StyleSheet, Text, View } from 'react-native' | ||
import AppAnalytics from 'src/analytics/AppAnalytics' | ||
import { HomeEvents } from 'src/analytics/Events' | ||
import TokenDisplay from 'src/components/TokenDisplay' | ||
import Touchable from 'src/components/Touchable' | ||
import { navigate } from 'src/navigator/NavigationService' | ||
import { Screens } from 'src/navigator/Screens' | ||
import Colors from 'src/styles/colors' | ||
import { typeScale } from 'src/styles/fonts' | ||
import { Spacing } from 'src/styles/styles' | ||
import variables from 'src/styles/variables' | ||
import TransactionFeedItemImage from 'src/transactions/feed/TransactionFeedItemImage' | ||
import { DepositOrWithdraw, TokenTransactionTypeV2 } from 'src/transactions/types' | ||
|
||
interface DescriptionProps { | ||
transaction: DepositOrWithdraw | ||
} | ||
|
||
function Description({ transaction }: DescriptionProps) { | ||
const { t } = useTranslation() | ||
const txAppName = transaction.appName | ||
let title | ||
let subtitle | ||
|
||
switch (transaction.type) { | ||
case TokenTransactionTypeV2.Deposit: | ||
title = t('transactionFeed.depositTitle') | ||
subtitle = t('transactionFeed.depositSubtitle', { | ||
context: !txAppName ? 'noTxAppName' : undefined, | ||
txAppName, | ||
}) | ||
break | ||
case TokenTransactionTypeV2.Withdraw: | ||
title = t('transactionFeed.withdrawTitle') | ||
subtitle = t('transactionFeed.withdrawSubtitle', { | ||
context: !txAppName ? 'noTxAppName' : undefined, | ||
txAppName, | ||
}) | ||
break | ||
} | ||
|
||
return ( | ||
<View style={styles.contentContainer}> | ||
<Text style={styles.title} testID={'DepositOrWithdrawFeedItem/title'} numberOfLines={1}> | ||
{title} | ||
</Text> | ||
{!!txAppName && ( | ||
<Text | ||
style={styles.subtitle} | ||
testID={'DepositOrWithdrawFeedItem/subtitle'} | ||
numberOfLines={1} | ||
> | ||
{subtitle} | ||
</Text> | ||
)} | ||
</View> | ||
) | ||
} | ||
|
||
interface AmountDisplayProps { | ||
transaction: DepositOrWithdraw | ||
isLocal: boolean | ||
} | ||
|
||
function AmountDisplay({ transaction, isLocal }: AmountDisplayProps) { | ||
let amountValue | ||
let tokenId | ||
|
||
switch (transaction.type) { | ||
case TokenTransactionTypeV2.Deposit: | ||
amountValue = new BigNumber(-transaction.outAmount.value) | ||
tokenId = transaction.outAmount.tokenId | ||
break | ||
case TokenTransactionTypeV2.Withdraw: | ||
amountValue = new BigNumber(transaction.inAmount.value) | ||
tokenId = transaction.inAmount.tokenId | ||
break | ||
} | ||
|
||
const textStyle = isLocal | ||
? styles.amountSubtitle | ||
: [ | ||
styles.amountTitle, | ||
transaction.type === TokenTransactionTypeV2.Withdraw && { color: Colors.accent }, | ||
] | ||
|
||
return ( | ||
<TokenDisplay | ||
amount={amountValue} | ||
tokenId={tokenId} | ||
showLocalAmount={isLocal} | ||
showSymbol={true} | ||
showExplicitPositiveSign={!isLocal} | ||
hideSign={!!isLocal} | ||
style={textStyle} | ||
testID={`DepositOrWithdrawFeedItem/${transaction.type}-amount-${isLocal ? 'local' : 'crypto'}`} | ||
/> | ||
) | ||
} | ||
|
||
interface AmountProps { | ||
transaction: DepositOrWithdraw | ||
} | ||
|
||
function Amount({ transaction }: AmountProps) { | ||
return ( | ||
<View style={styles.amountContainer}> | ||
<AmountDisplay transaction={transaction} isLocal={false} /> | ||
<AmountDisplay transaction={transaction} isLocal={true} /> | ||
</View> | ||
) | ||
} | ||
|
||
interface Props { | ||
transaction: DepositOrWithdraw | ||
} | ||
|
||
export default function DepositOrWithdrawFeedItem({ transaction }: Props) { | ||
return ( | ||
<Touchable | ||
testID={`DepositOrWithdrawFeedItem/${transaction.transactionHash}`} | ||
onPress={() => { | ||
// TODO: we'll add the type in a subsequent PR | ||
AppAnalytics.track(HomeEvents.transaction_feed_item_select) | ||
navigate(Screens.TransactionDetailsScreen, { transaction }) | ||
}} | ||
> | ||
<View style={styles.container}> | ||
<TransactionFeedItemImage | ||
status={transaction.status} | ||
transactionType={transaction.type} | ||
networkId={transaction.networkId} | ||
/> | ||
<Description transaction={transaction} /> | ||
<Amount transaction={transaction} /> | ||
</View> | ||
</Touchable> | ||
) | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flexDirection: 'row', | ||
paddingVertical: Spacing.Small12, | ||
paddingHorizontal: variables.contentPadding, | ||
alignItems: 'center', | ||
}, | ||
contentContainer: { | ||
flex: 1, | ||
paddingHorizontal: variables.contentPadding, | ||
}, | ||
title: { | ||
...typeScale.labelMedium, | ||
color: Colors.black, | ||
}, | ||
subtitle: { | ||
...typeScale.bodySmall, | ||
color: Colors.gray4, | ||
}, | ||
amountContainer: { | ||
maxWidth: '50%', | ||
}, | ||
amountTitle: { | ||
...typeScale.labelMedium, | ||
color: Colors.black, | ||
flexWrap: 'wrap', | ||
textAlign: 'right', | ||
}, | ||
amountSubtitle: { | ||
...typeScale.bodySmall, | ||
color: Colors.gray4, | ||
flexWrap: 'wrap', | ||
textAlign: 'right', | ||
}, | ||
}) |
Oops, something went wrong.