@lifi/widget
Version:
LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.
175 lines (153 loc) • 4.88 kB
text/typescript
import type { TokenExtended, WalletTokenExtended } from '@lifi/sdk'
import { formatUnits } from 'viem'
import type { TokenAmount, TokenAmountExtended } from '../types/token.js'
import type { WidgetTokens } from '../types/widget.js'
const sortByBalances = (a: TokenAmount, b: TokenAmount) =>
Number.parseFloat(formatUnits(b.amount ?? 0n, b.decimals)) *
Number.parseFloat(b.priceUSD ?? '0') -
Number.parseFloat(formatUnits(a.amount ?? 0n, a.decimals)) *
Number.parseFloat(a.priceUSD ?? '0')
const sortByVolume = (a: TokenExtended, b: TokenExtended) =>
(b.volumeUSD24H ?? 0) - (a.volumeUSD24H ?? 0)
export const processTokenBalances = (
isBalanceLoading: boolean,
noCategories: boolean,
configTokens?: WidgetTokens,
selectedChainId?: number,
tokens?: TokenExtended[],
tokensWithBalances?: TokenAmount[]
) => {
if (isBalanceLoading) {
if (noCategories) {
const sortedTokens = [...(tokens ?? [])].sort(sortByVolume)
return {
processedTokens: sortedTokens,
withCategories: false,
}
} else {
return processedTypedTokens(
tokens ?? [],
[],
selectedChainId,
configTokens
)
}
}
const sortedTokensWithBalances = [...(tokensWithBalances ?? [])].sort(
sortByBalances
)
const tokensWithBalancesSet = new Set(
sortedTokensWithBalances.map(
(token) => `${token.chainId}-${token.address.toLowerCase()}`
)
)
const tokensWithoutBalances =
tokens
?.filter((token) => {
const tokenKey = `${token.chainId}-${token.address.toLowerCase()}`
return !tokensWithBalancesSet.has(tokenKey)
})
.sort(sortByVolume) ?? []
if (noCategories) {
return {
processedTokens: [...sortedTokensWithBalances, ...tokensWithoutBalances],
withCategories: false,
}
} else {
return processedTypedTokens(
tokensWithoutBalances,
sortedTokensWithBalances,
selectedChainId,
configTokens
)
}
}
// NB: only for non-all-networks
const processedTypedTokens = (
tokens: TokenAmount[],
tokensWithBalances: TokenAmount[],
selectedChainId?: number,
configTokens?: WidgetTokens
) => {
const filteredTokensMap = new Map(
tokens.map((token) => [token.address, token])
)
const featuredTokensFromConfig: TokenAmount[] = []
const popularTokensFromConfig: TokenAmount[] = []
;(['popular', 'featured'] as const).forEach((tokenType) => {
const typedTokens = configTokens?.[tokenType]?.filter(
(token) => token.chainId === selectedChainId
)
typedTokens?.forEach((token) => {
const tokenAmount = { ...token } as TokenAmount
tokenAmount[tokenType] = true
// Config tokens are explicitly set by integrator, mark as verified
tokenAmount.verified = true
const match = filteredTokensMap.get(token.address)
if (match?.priceUSD) {
tokenAmount.priceUSD = match.priceUSD
}
if (!token.logoURI && match?.logoURI) {
tokenAmount.logoURI = match.logoURI
}
if (tokenType === 'popular') {
popularTokensFromConfig.push(tokenAmount)
} else {
featuredTokensFromConfig.push(tokenAmount)
}
})
})
// Filter out config-added tokens from main list
const configTokenAddresses = new Set(
[...popularTokensFromConfig, ...featuredTokensFromConfig].map((t) =>
t.address.toLowerCase()
)
)
const remainingTokens = tokens.filter(
(token) => !configTokenAddresses.has(token.address.toLowerCase())
)
const otherTokens: TokenAmount[] = []
for (const token of remainingTokens) {
if (token.featured) {
featuredTokensFromConfig.push(token)
} else if (token.popular) {
popularTokensFromConfig.push(token)
} else {
otherTokens.push(token)
}
}
const sortedFeaturedTokens = [...featuredTokensFromConfig].sort(sortByVolume)
const sortedPopularTokens = [...popularTokensFromConfig].sort(sortByVolume)
const sortedOtherTokens = [...otherTokens].sort(sortByVolume)
return {
processedTokens: [
...sortedFeaturedTokens,
...tokensWithBalances,
...sortedPopularTokens,
...sortedOtherTokens,
],
withCategories: Boolean(
featuredTokensFromConfig?.length || popularTokensFromConfig?.length
),
}
}
export const isSearchMatch = (
token: TokenExtended | TokenAmountExtended,
search?: string
) => {
if (!search) {
return true
}
const searchLowerCase = search.toLowerCase()
return (
token.name?.toLowerCase().includes(searchLowerCase) ||
token.symbol
?.replaceAll('₮', 'T')
.toLowerCase()
.includes(searchLowerCase) ||
token.address?.toLowerCase().includes(searchLowerCase)
)
}
export const isSupportedToken = (token: WalletTokenExtended) => {
return token.name && token.symbol && token.priceUSD && token.chainId
}