Skip to content

Commit

Permalink
feat(swap): add popular tokens to bottom sheet (#4865)
Browse files Browse the repository at this point in the history
### Description

This PR:
- add the popular token dynamic config
- use the dynamic config in the token filters
- add a test

### Test plan

n/a

### Related issues

- Fixes RET-984

### Backwards compatibility

Y
  • Loading branch information
kathaypacific authored Feb 9, 2024
1 parent 291cc72 commit 442e5f2
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/statsig/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const DynamicConfigs = {
configName: StatsigDynamicConfigs.SWAP_CONFIG,
defaultValues: {
maxSlippagePercentage: '0.3',
popularTokenIds: [] as string[],
},
},
[StatsigDynamicConfigs.CICO_TOKEN_INFO]: {
Expand Down
59 changes: 44 additions & 15 deletions src/swap/SwapScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1586,12 +1586,13 @@ describe('SwapScreen', () => {
.mockImplementation((gate) => gate === StatsigFeatureGates.SHOW_SWAP_TOKEN_FILTERS)
})

const expectedAllFromTokens = Object.values(mockStoreTokenBalances).filter(
(token) => token.isSwappable !== false || token.balance !== '0' // include unswappable tokens with balance because it is the "from" token
)

it('should show "my tokens" for the "from" token selection by default', () => {
const expectedAllTokens = Object.values(mockStoreTokenBalances).filter(
(token) => token.isSwappable !== false || token.balance !== '0' // include unswappable tokens with balance because it is the "from" token
)
const mockedZeroBalanceTokens = [mockCeurTokenId, mockCusdTokenId, mockPoofTokenId]
const expectedTokensWithBalance = expectedAllTokens.filter(
const expectedTokensWithBalance = expectedAllFromTokens.filter(
(token) => !mockedZeroBalanceTokens.includes(token.tokenId)
)

Expand All @@ -1611,17 +1612,14 @@ describe('SwapScreen', () => {
// deselect pre-selected filters to show all tokens
fireEvent.press(getByText('tokenBottomSheet.filters.myTokens'))

expectedAllTokens.forEach((token) => {
expectedAllFromTokens.forEach((token) => {
expect(within(tokenBottomSheet).getByText(token.name)).toBeTruthy()
})
})

it('should show "recently swapped" tokens', () => {
const expectedAllTokens = Object.values(mockStoreTokenBalances).filter(
(token) => token.isSwappable !== false || token.balance !== '0' // include unswappable tokens with balance because it is the "from" token
)
const mockedLastSwapped = [mockCeurTokenId, mockCusdTokenId, mockPoofTokenId]
const expectedLastSwapTokens = expectedAllTokens.filter((token) =>
const expectedLastSwapTokens = expectedAllFromTokens.filter((token) =>
mockedLastSwapped.includes(token.tokenId)
)

Expand All @@ -1644,7 +1642,41 @@ describe('SwapScreen', () => {
// de-select last swapped filter
fireEvent.press(getByText('tokenBottomSheet.filters.recentlySwapped'))

expectedAllTokens.forEach((token) => {
expectedAllFromTokens.forEach((token) => {
expect(within(tokenBottomSheet).getByText(token.name)).toBeTruthy()
})
})

it('should show "popular" tokens', () => {
const mockedPopularTokens = [mockUSDCTokenId, mockPoofTokenId]
jest.mocked(getDynamicConfigParams).mockReturnValue({
popularTokenIds: mockedPopularTokens,
showSwap: ['celo-alfajores', 'ethereum-sepolia'],
showBalances: ['celo-alfajores', 'ethereum-sepolia'],
maxSlippagePercentage: '0.3',
})
const expectedPopularTokens = expectedAllFromTokens.filter((token) =>
mockedPopularTokens.includes(token.tokenId)
)

const { swapFromContainer, getByText, tokenBottomSheet } = renderScreen({})

fireEvent.press(within(swapFromContainer).getByTestId('SwapAmountInput/TokenSelect'))
// deselect pre-selected filters to show all tokens
fireEvent.press(getByText('tokenBottomSheet.filters.myTokens'))
// select popular filter
fireEvent.press(getByText('tokenBottomSheet.filters.popular'))

expectedPopularTokens.forEach((token) => {
expect(within(tokenBottomSheet).getByText(token.name)).toBeTruthy()
})
const displayedTokens = within(tokenBottomSheet).getAllByTestId('TokenBalanceItem')
expect(displayedTokens.length).toBe(expectedPopularTokens.length)

// de-select filter
fireEvent.press(getByText('tokenBottomSheet.filters.popular'))

expectedAllFromTokens.forEach((token) => {
expect(within(tokenBottomSheet).getByText(token.name)).toBeTruthy()
})
})
Expand Down Expand Up @@ -1683,13 +1715,10 @@ describe('SwapScreen', () => {
})

it('should show the network filters when there are multiple supported networks', () => {
const expectedAllTokens = Object.values(mockStoreTokenBalances).filter(
(token) => token.isSwappable !== false || token.balance !== '0' // include unswappable tokens with balance because it is the "from" token
)
const expectedEthTokens = expectedAllTokens.filter(
const expectedEthTokens = expectedAllFromTokens.filter(
(token) => token.networkId === NetworkId['ethereum-sepolia']
)
const expectedCeloTokens = expectedAllTokens.filter(
const expectedCeloTokens = expectedAllFromTokens.filter(
(token) => token.networkId === NetworkId['celo-alfajores']
)

Expand Down
11 changes: 7 additions & 4 deletions src/swap/useFilterChips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { useTranslation } from 'react-i18next'
import { FilterChip } from 'src/components/FilterChipsCarousel'
import { TOKEN_MIN_AMOUNT } from 'src/config'
import useSelector from 'src/redux/useSelector'
import { getFeatureGate } from 'src/statsig'
import { StatsigFeatureGates } from 'src/statsig/types'
import { getDynamicConfigParams, getFeatureGate } from 'src/statsig'
import { DynamicConfigs } from 'src/statsig/constants'
import { StatsigDynamicConfigs, StatsigFeatureGates } from 'src/statsig/types'
import { lastSwappedSelector } from 'src/swap/selectors'
import { Field } from 'src/swap/types'
import { TokenBalance } from 'src/tokens/slice'
Expand All @@ -15,7 +16,9 @@ export default function useFilterChip(selectingField: Field | null): FilterChip<
const { t } = useTranslation()
const showSwapTokenFilters = getFeatureGate(StatsigFeatureGates.SHOW_SWAP_TOKEN_FILTERS)
const recentlySwappedTokens = useSelector(lastSwappedSelector)
const popularTokens: string[] = [] // TODO
const popularTokenIds: string[] = getDynamicConfigParams(
DynamicConfigs[StatsigDynamicConfigs.SWAP_CONFIG]
).popularTokenIds
const supportedNetworkIds = getSupportedNetworkIdsForSwap()

const networkIdFilters =
Expand Down Expand Up @@ -48,7 +51,7 @@ export default function useFilterChip(selectingField: Field | null): FilterChip<
{
id: 'popular',
name: t('tokenBottomSheet.filters.popular'),
filterFn: (token: TokenBalance) => popularTokens.includes(token.tokenId),
filterFn: (token: TokenBalance) => popularTokenIds.includes(token.tokenId),
isSelected: selectingField === Field.TO,
},
{
Expand Down

0 comments on commit 442e5f2

Please sign in to comment.