Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silly-feet-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@reservoir0x/relay-kit-ui': patch
---

Optimize token list merging and sorting logic in TokenSelector
85 changes: 48 additions & 37 deletions packages/ui/src/components/common/TokenSelector/TokenSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { bitcoin } from '../../../utils/bitcoin.js'
import { evmDeadAddress } from '@reservoir0x/relay-sdk'
import { solDeadAddress } from '@reservoir0x/relay-sdk'
import { bitcoinDeadAddress } from '@reservoir0x/relay-sdk'
import { mergeTokenLists } from '../../../utils/tokens.js'

export type TokenSelectorProps = {
openState?: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
Expand Down Expand Up @@ -263,7 +264,7 @@ const TokenSelector: FC<TokenSelectorProps> = ({
suggestedTokenQuery
? {
tokens: suggestedTokenQuery,
limit: 20,
limit: 30,
depositAddressOnly
}
: undefined,
Expand Down Expand Up @@ -299,47 +300,44 @@ const TokenSelector: FC<TokenSelectorProps> = ({
return mergedList
}, [tokenList, externalTokenList])

// Filter out unconfigured chains and append Relay Chain to each currency
// Enhance token list with Relay chain data, balances and sort by usd value/balances
const enhancedCurrencyList = useMemo(() => {
const _tokenList =
combinedTokenList && (combinedTokenList as any).length
? (combinedTokenList as CurrencyList[])
: undefined

// Filter suggested tokens by chain if needed
const filteredSuggestedTokens = chainFilter.id
? suggestedTokens
?.map((tokenList) =>
tokenList.filter((token) => token.chainId === chainFilter.id)
)
.filter((tokenList) => tokenList.length > 0)
? suggestedTokens?.map((tokenList) =>
tokenList.filter((token) => token.chainId === chainFilter.id)
)
: suggestedTokens

let list =
context === 'from' &&
useDefaultTokenList &&
chainFilter.id === undefined &&
filteredSuggestedTokens &&
filteredSuggestedTokens.length > 0
? filteredSuggestedTokens
: combinedTokenList

const ethTokens = _tokenList?.find(
(list) => list[0] && list[0].groupID === 'ETH'
// Only merge suggested tokens when using default list
const list = useDefaultTokenList
? mergeTokenLists([filteredSuggestedTokens, combinedTokenList])
: combinedTokenList || []

// Prioritize ETH and USDC tokens
const ethTokens = list.find(
(tokenList) => tokenList[0] && tokenList[0].groupID === 'ETH'
)
const usdcTokens = _tokenList?.find(
(list) => list[0] && list[0].groupID === 'USDC'
const usdcTokens = list.find(
(tokenList) => tokenList[0] && tokenList[0].groupID === 'USDC'
)
if (list && suggestedTokens) {
list = list?.filter(
(tokenList) =>
tokenList[0] &&
tokenList[0].groupID !== 'ETH' &&
tokenList[0].groupID !== 'USDC'
)
list = [ethTokens ?? [], usdcTokens ?? []].concat(list)
}

const mappedList = list?.map((currencyList) => {
// Remove ETH/USDC from main list and add them to the front
const filteredList = list.filter(
(tokenList) =>
tokenList[0] &&
tokenList[0].groupID !== 'ETH' &&
tokenList[0].groupID !== 'USDC'
)

const sortedList = [
...(ethTokens ? [ethTokens] : []),
...(usdcTokens ? [usdcTokens] : []),
...filteredList
]

// Map and enhance the currency list
const mappedList = sortedList?.map((currencyList) => {
const filteredList = currencyList
.map((currency) => {
const relayChain = configuredChains.find(
Expand Down Expand Up @@ -394,8 +392,21 @@ const TokenSelector: FC<TokenSelectorProps> = ({
totalValueUsd
}
})
.filter((list) => list !== undefined)
.sort((a, b) => (b?.totalValueUsd ?? 0) - (a?.totalValueUsd ?? 0))
.filter((list): list is NonNullable<typeof list> => list !== undefined)
.sort((a, b) => {
// First sort by USD value if available
if (a.totalValueUsd !== b.totalValueUsd) {
return b.totalValueUsd - a.totalValueUsd
}
// Then sort by balance if USD value is equal or undefined
if (a.totalBalance !== b.totalBalance) {
return Number(b.totalBalance - a.totalBalance)
}
// Finally prioritize verified tokens
const aVerified = a.chains[0]?.metadata?.verified ?? false
const bVerified = b.chains[0]?.metadata?.verified ?? false
return bVerified === aVerified ? 0 : bVerified ? 1 : -1
})
}, [
context,
combinedTokenList,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ export const SetCurrencyStep: FC<SetCurrencyProps> = ({
}}
/>
<Text style="subtitle3" color="subtle">
Popular Tokens
Suggested Tokens
</Text>
</>
) : null}
Expand Down
24 changes: 24 additions & 0 deletions packages/ui/src/utils/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CurrencyList } from '@reservoir0x/relay-kit-hooks'
import type { Token } from '../types/index.js'
import { ASSETS_RELAY_API } from '@reservoir0x/relay-sdk'
import type { paths, RelayChain } from '@reservoir0x/relay-sdk'
Expand Down Expand Up @@ -42,3 +43,26 @@ export const findBridgableToken = (chain?: RelayChain, token?: Token) => {
}
return null
}

export const mergeTokenLists = (lists: (CurrencyList[] | undefined)[]) => {
const mergedList: CurrencyList[] = []
const seenTokens = new Set<string>()

lists.forEach((list) => {
if (!list) return

list.forEach((currencyList) => {
const currency = currencyList[0]
if (!currency) return

const tokenKey = `${currency.chainId}:${currency.address?.toLowerCase()}`

if (!seenTokens.has(tokenKey)) {
seenTokens.add(tokenKey)
mergedList.push(currencyList)
}
})
})

return mergedList
}
Loading