Skip to content

Commit

Permalink
refactor: fix lp tokens segregation
Browse files Browse the repository at this point in the history
  • Loading branch information
shoom3301 committed Oct 22, 2024
1 parent a0bab7b commit 9c5d5b5
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'

import { NATIVE_CURRENCIES } from '@cowprotocol/common-const'
import { EnrichedOrder, EthflowData, OrderClass, SupportedChainId as ChainId } from '@cowprotocol/cow-sdk'
import { TokensByAddress, useAllTokens } from '@cowprotocol/tokens'
import { TokensByAddress, useAllActiveTokens } from '@cowprotocol/tokens'
import { useIsSafeWallet, useWalletInfo } from '@cowprotocol/wallet'

import { Order, OrderStatus } from 'legacy/state/orders/actions'
Expand Down Expand Up @@ -32,7 +32,7 @@ const statusMapping: Record<OrderTransitionStatus, OrderStatus | undefined> = {
function _transformOrderBookOrderToStoreOrder(
order: EnrichedOrder,
chainId: ChainId,
allTokens: TokensByAddress
allTokens: TokensByAddress,
): Order | undefined {
const {
uid: id,
Expand Down Expand Up @@ -63,7 +63,7 @@ function _transformOrderBookOrderToStoreOrder(
console.warn(
`OrdersFromApiUpdater::Tokens not found for order ${id}: sellToken ${
!inputToken ? sellToken : 'found'
} - buyToken ${!outputToken ? buyToken : 'found'}`
} - buyToken ${!outputToken ? buyToken : 'found'}`,
)
return
}
Expand Down Expand Up @@ -110,7 +110,7 @@ function _getInputToken(
isEthFlow: boolean,
chainId: ChainId,
sellToken: string,
allTokens: TokensByAddress
allTokens: TokensByAddress,
): ReturnType<typeof getTokenFromMapping> {
return isEthFlow ? NATIVE_CURRENCIES[chainId] : getTokenFromMapping(sellToken, chainId, allTokens)
}
Expand Down Expand Up @@ -141,7 +141,7 @@ export function OrdersFromApiUpdater(): null {
const clearOrderStorage = useClearOrdersStorage()

const { account, chainId } = useWalletInfo()
const allTokens = useAllTokens()
const allTokens = useAllActiveTokens()
const tokensAreLoaded = useMemo(() => Object.keys(allTokens).length > 0, [allTokens])
const addOrUpdateOrders = useAddOrUpdateOrders()
const updateApiOrders = useSetAtom(apiOrdersAtom)
Expand Down Expand Up @@ -176,7 +176,7 @@ export function OrdersFromApiUpdater(): null {
console.error(`OrdersFromApiUpdater::Failed to fetch orders`, e)
}
},
[addOrUpdateOrders, ordersFromOrderBook, getTokensForOrdersList, isSafeWallet]
[addOrUpdateOrders, ordersFromOrderBook, getTokensForOrdersList, isSafeWallet],
)

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
useAddList,
useAddUserToken,
useAllListsList,
useAllTokens,
useAllActiveTokens,
useFavoriteTokens,
useUnsupportedTokens,
useUserAddedTokens,
Expand Down Expand Up @@ -75,7 +75,7 @@ export function SelectTokenWidget({ displayLpTokenLists }: SelectTokenWidgetProp
const addCustomTokenLists = useAddList((source) => addListAnalytics('Success', source))
const importTokenCallback = useAddUserToken()

const allTokens = useAllTokens()
const allTokens = useAllActiveTokens()
const favoriteTokens = useFavoriteTokens()
const userAddedTokens = useUserAddedTokens()
const allTokenLists = useAllListsList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useMemo } from 'react'

import { LpToken, NATIVE_CURRENCIES } from '@cowprotocol/common-const'
import type { SupportedChainId } from '@cowprotocol/cow-sdk'
import { useAllTokens } from '@cowprotocol/tokens'
import { useAllActiveTokens } from '@cowprotocol/tokens'

import ms from 'ms.macro'

Expand All @@ -24,7 +24,7 @@ export interface BalancesAndAllowancesUpdaterProps {
export function BalancesAndAllowancesUpdater({ account, chainId }: BalancesAndAllowancesUpdaterProps) {
const setBalances = useSetAtom(balancesAtom)

const allTokens = useAllTokens()
const allTokens = useAllActiveTokens()
const { data: nativeTokenBalance } = useNativeTokenBalance(account)

const tokenAddresses = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { TokenWithLogo } from '@cowprotocol/common-const'

import { activeTokensAtom } from '../../state/tokens/allTokensAtom'


export function useAllTokens(): TokenWithLogo[] {
export function useAllActiveTokens(): TokenWithLogo[] {
return useAtomValue(activeTokensAtom)
}
23 changes: 12 additions & 11 deletions libs/tokens/src/hooks/tokens/useAllLpTokens.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import { useAtomValue } from 'jotai'
import { useAtomValue } from 'jotai/index'

import { LpToken, SWR_NO_REFRESH_OPTIONS } from '@cowprotocol/common-const'

import useSWR from 'swr'

import { lpTokensByCategoryAtom } from '../../state/tokens/allTokensAtom'
import { activeTokensAtom, inactiveTokensAtom } from '../../state/tokens/allTokensAtom'
import { TokenListCategory } from '../../types'

const fallbackData: LpToken[] = []

export function useAllLpTokens(categories: TokenListCategory[] | null): LpToken[] {
const lpTokensByCategory = useAtomValue(lpTokensByCategoryAtom)
const activeTokens = useAtomValue(activeTokensAtom)
const inactiveTokens = useAtomValue(inactiveTokensAtom)

return useSWR(
categories ? [lpTokensByCategory, categories] : null,
([lpTokensByCategory, categories]) => {
return categories.reduce<LpToken[]>((acc, category) => {
if (category === TokenListCategory.LP || category === TokenListCategory.COW_AMM_LP) {
acc.push(...lpTokensByCategory[category])
}
categories ? [activeTokens, inactiveTokens, categories] : null,
([activeTokens, inactiveTokens, categories]) => {
const allTokens = [...activeTokens, ...inactiveTokens]
const selectOnlyCoWAmm = categories?.length === 1 && categories.includes(TokenListCategory.COW_AMM_LP)

return acc
}, [])
return allTokens.filter((token) => {
const isLp = token instanceof LpToken
return isLp ? (selectOnlyCoWAmm ? token.isCowAmm : true) : false
}) as LpToken[]
},
{ ...SWR_NO_REFRESH_OPTIONS, fallbackData },
).data
Expand Down
2 changes: 1 addition & 1 deletion libs/tokens/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type { TokenSearchResponse } from './hooks/tokens/useSearchToken'
// Hooks
export { useAllListsList } from './hooks/lists/useAllListsList'
export { useAddList } from './hooks/lists/useAddList'
export { useAllTokens } from './hooks/tokens/useAllTokens'
export { useAllActiveTokens } from './hooks/tokens/useAllActiveTokens'
export { useVirtualLists } from './hooks/lists/useVirtualLists'
export { useFavoriteTokens } from './hooks/tokens/favorite/useFavoriteTokens'
export { useUserAddedTokens } from './hooks/tokens/userAdded/useUserAddedTokens'
Expand Down
133 changes: 39 additions & 94 deletions libs/tokens/src/state/tokens/allTokensAtom.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { atom } from 'jotai'

import { LpToken, NATIVE_CURRENCIES, TokenWithLogo } from '@cowprotocol/common-const'
import { NATIVE_CURRENCIES, TokenWithLogo } from '@cowprotocol/common-const'
import { TokenInfo } from '@cowprotocol/types'

import { favoriteTokensAtom } from './favoriteTokensAtom'
import { userAddedTokensAtom } from './userAddedTokensAtom'

import { LP_TOKEN_LIST_CATEGORIES, TokenListCategory, TokensMap } from '../../types'
import { TokenListCategory, TokensMap } from '../../types'
import { lowerCaseTokensMap } from '../../utils/lowerCaseTokensMap'
import { parseTokenInfo } from '../../utils/parseTokenInfo'
import { tokenMapToListWithLogo } from '../../utils/tokenMapToListWithLogo'
Expand All @@ -21,22 +21,9 @@ export interface TokensBySymbol {
[address: string]: TokenWithLogo[]
}

type TokenCategoryByMap = Record<TokenListCategory, TokensMap>

type LpTokensByCategory = {
[TokenListCategory.LP]: LpToken[]
[TokenListCategory.COW_AMM_LP]: LpToken[]
}

interface TokensState {
activeTokens: TokenCategoryByMap
inactiveTokens: TokenCategoryByMap
}

const DEFAULT_TOKEN_CATEGORY_BY_MAP = {
[TokenListCategory.ERC20]: {},
[TokenListCategory.LP]: {},
[TokenListCategory.COW_AMM_LP]: {},
activeTokens: TokensMap
inactiveTokens: TokensMap
}

const tokensStateAtom = atom<TokensState>((get) => {
Expand All @@ -49,69 +36,38 @@ const tokensStateAtom = atom<TokensState>((get) => {
const isListEnabled = listsEnabledState[list.source]

list.list.tokens.forEach((token) => {
const category = list.category || TokenListCategory.ERC20
const tokenInfo = parseTokenInfo(chainId, token)
const tokenAddressKey = tokenInfo?.address.toLowerCase()

if (!tokenInfo || !tokenAddressKey) return

const category = list.category || TokenListCategory.ERC20
if (category === TokenListCategory.LP) {
tokenInfo.isLpToken = true
}

if (category === TokenListCategory.COW_AMM_LP) {
tokenInfo.isLpToken = true
tokenInfo.isCoWAmmToken = true
}

if (isListEnabled) {
if (!acc.activeTokens[category][tokenAddressKey]) {
acc.activeTokens[category][tokenAddressKey] = tokenInfo
if (!acc.activeTokens[tokenAddressKey]) {
acc.activeTokens[tokenAddressKey] = tokenInfo
}
} else {
if (!acc.inactiveTokens[category][tokenAddressKey]) {
acc.inactiveTokens[category][tokenAddressKey] = tokenInfo
if (!acc.inactiveTokens[tokenAddressKey]) {
acc.inactiveTokens[tokenAddressKey] = tokenInfo
}
}
})

return acc
},
{ activeTokens: { ...DEFAULT_TOKEN_CATEGORY_BY_MAP }, inactiveTokens: { ...DEFAULT_TOKEN_CATEGORY_BY_MAP } },
{ activeTokens: {}, inactiveTokens: {} },
)
})

const lpTokensMapAtom = atom((get) => {
const { chainId } = get(environmentAtom)
const listsStatesList = get(listsStatesListAtom)

return listsStatesList.reduce<TokenCategoryByMap>(
(acc, list) => {
const category = !list.category || !LP_TOKEN_LIST_CATEGORIES.includes(list.category) ? undefined : list.category

if (!category) {
return acc
}

list.list.tokens.forEach((token) => {
const tokenInfo = parseTokenInfo(chainId, token)
const tokenAddressKey = tokenInfo?.address.toLowerCase()

if (!tokenInfo || !tokenAddressKey) return

acc[category][tokenAddressKey] = tokenInfo
})

return acc
},
{ [TokenListCategory.COW_AMM_LP]: {}, [TokenListCategory.LP]: {} } as TokenCategoryByMap,
)
})

export const lpTokensByCategoryAtom = atom<LpTokensByCategory>((get) => {
const { chainId } = get(environmentAtom)
const lpTokensMap = get(lpTokensMapAtom)
const getTokensByCategory = (category: TokenListCategory) =>
tokenMapToListWithLogo(lpTokensMap[category], category, chainId) as LpToken[]

return {
[TokenListCategory.LP]: getTokensByCategory(TokenListCategory.LP),
[TokenListCategory.COW_AMM_LP]: getTokensByCategory(TokenListCategory.COW_AMM_LP),
}
})

/**
* Returns a list of tokens that are active and sorted alphabetically
* The list includes: native token, user added tokens, favorite tokens and tokens from active lists
Expand All @@ -123,46 +79,35 @@ export const activeTokensAtom = atom<TokenWithLogo[]>((get) => {
const favoriteTokensState = get(favoriteTokensAtom)

const tokensMap = get(tokensStateAtom)
const lpTokensByCategory = get(lpTokensByCategoryAtom)
const nativeToken = NATIVE_CURRENCIES[chainId]

return [
// Native, user added and favorite tokens
...tokenMapToListWithLogo(
{
[nativeToken.address.toLowerCase()]: nativeToken as TokenInfo,
...lowerCaseTokensMap(userAddedTokens[chainId]),
...lowerCaseTokensMap(favoriteTokensState[chainId]),
},
TokenListCategory.ERC20,
chainId,
),
// Tokens from active lists
...Object.keys(tokensMap.activeTokens).reduce<TokenWithLogo[]>((acc, _category) => {
const category = _category as TokenListCategory
const categoryMap = tokensMap.activeTokens[category]

acc.push(...tokenMapToListWithLogo(categoryMap, category, chainId))
return acc
}, []),
// LP tokens
...(enableLpTokensByDefault
? lpTokensByCategory[TokenListCategory.LP].concat(lpTokensByCategory[TokenListCategory.COW_AMM_LP])
: []),
]
return tokenMapToListWithLogo(
{
[nativeToken.address.toLowerCase()]: nativeToken as TokenInfo,
...tokensMap.activeTokens,
...lowerCaseTokensMap(userAddedTokens[chainId]),
...lowerCaseTokensMap(favoriteTokensState[chainId]),
...(enableLpTokensByDefault
? Object.keys(tokensMap.inactiveTokens).reduce<TokensMap>((acc, key) => {
const token = tokensMap.inactiveTokens[key]

if (token.isLpToken) {
acc[key] = token
}

return acc
}, {})
: null),
},
chainId,
)
})

export const inactiveTokensAtom = atom<TokenWithLogo[]>((get) => {
const { chainId } = get(environmentAtom)
const tokensMap = get(tokensStateAtom)

return Object.keys(tokensMap.inactiveTokens).reduce<TokenWithLogo[]>((acc, _category) => {
const category = _category as TokenListCategory
const categoryMap = tokensMap.inactiveTokens[category]

acc.push(...tokenMapToListWithLogo(categoryMap, category, chainId))
return acc
}, [])
return tokenMapToListWithLogo(tokensMap.inactiveTokens, chainId)
})

export const tokensByAddressAtom = atom<TokensByAddress>((get) => {
Expand Down
14 changes: 5 additions & 9 deletions libs/tokens/src/utils/tokenMapToListWithLogo.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { LpToken, TokenWithLogo } from '@cowprotocol/common-const'

import { TokenListCategory, TokensMap } from '../types'
import { TokensMap } from '../types'

/**
* Convert a tokens map to a list of tokens and sort them alphabetically
*/
export function tokenMapToListWithLogo(
tokenMap: TokensMap,
category: TokenListCategory,
chainId: number,
): TokenWithLogo[] {
const isCoWAMM = category === TokenListCategory.COW_AMM_LP

export function tokenMapToListWithLogo(tokenMap: TokensMap, chainId: number): TokenWithLogo[] {
return Object.values(tokenMap)
.filter((token) => token.chainId === chainId)
.sort((a, b) => a.symbol.localeCompare(b.symbol))
.map((token) =>
token.tokens ? LpToken.fromTokenToLp(token, isCoWAMM) : TokenWithLogo.fromToken(token, token.logoURI),
token.isLpToken
? LpToken.fromTokenToLp(token, !!token.isCoWAmmToken)
: TokenWithLogo.fromToken(token, token.logoURI),
)
}
2 changes: 2 additions & 0 deletions libs/types/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ export type TokenInfo = {
symbol: string
logoURI?: string
tokens?: string[]
isLpToken?: boolean
isCoWAmmToken?: boolean
}

0 comments on commit 9c5d5b5

Please sign in to comment.