diff --git a/src/libs/portfolio/helpers.ts b/src/libs/portfolio/helpers.ts index 78020b4be..5b838f6b9 100644 --- a/src/libs/portfolio/helpers.ts +++ b/src/libs/portfolio/helpers.ts @@ -2,11 +2,14 @@ import { Contract, ZeroAddress } from 'ethers' import IERC20 from '../../../contracts/compiled/IERC20.json' import gasTankFeeTokens from '../../consts/gasTankFeeTokens' +import humanizerInfo from '../../consts/humanizer/humanizerInfo.json' +import { networks } from '../../consts/networks' import { PINNED_TOKENS } from '../../consts/pinnedTokens' import { Account, AccountId } from '../../interfaces/account' import { NetworkId } from '../../interfaces/network' import { RPCProvider } from '../../interfaces/provider' import { isSmartAccount } from '../account/account' +import { HumanizerMeta } from '../humanizer/interfaces' import { CustomToken } from './customToken' import { AdditionalPortfolioNetworkResult, @@ -347,6 +350,44 @@ export const tokenFilter = ( return isInAdditionalHints || pinnedRequested } +const getTokenSafetyLevel = (token: TokenResult): TokenResult['flags']['safetyLevel'] => { + let safetyLevel: TokenResult['flags']['safetyLevel'] = 'unknown' + + const hasFlags = token.flags.canTopUpGasTank || token.flags.isFeeToken || token.flags.onGasTank + + if (token.address === ZeroAddress || hasFlags) return 'trusted' + + const canTokenBeChecked = networks.some((network) => network.id === token.networkId) + + // If the token is on a custom network we can't check if it's spoof + // thus we can't trust it + if (!canTokenBeChecked) return undefined + + Object.values(humanizerInfo.knownAddresses as unknown as HumanizerMeta).some((value) => { + // Skip non-token values + if (!value.token) return false + + // The token is found in the humanizer + if (value.address.toLowerCase() === token.address.toLowerCase()) { + safetyLevel = 'trusted' + return true + } + + // The token is found in the humanizer with a different address + if (value.token?.symbol?.toLowerCase() === token.symbol.toLowerCase()) { + safetyLevel = 'spoof' + // Don't break the loop, as we want to check if there's a trusted token with the same symbol + } + + return false + }) + + // As long as the token isn't spoof and there is a price we can trust it + if (safetyLevel === 'unknown' && token.priceIn.length) return 'trusted' + + return safetyLevel +} + /** * Filter the TokenResult[] by certain criteria (please refer to `tokenFilter` for more details) * and set the token.isHidden flag. @@ -364,6 +405,7 @@ export const processTokens = ( return tokenResults.reduce((tokens, tokenResult) => { const token = { ...tokenResult } + const safetyLevel = getTokenSafetyLevel(token) const preference = tokenPreferences?.find((tokenPreference) => { return tokenPreference.address === token.address && tokenPreference.networkId === network.id @@ -373,6 +415,8 @@ export const processTokens = ( token.isHidden = preference.isHidden } + token.flags.safetyLevel = safetyLevel + if (tokenFilter(token, nativeToken!, network, hasNonZeroTokens, additionalHints, !!preference)) tokens.push(token) diff --git a/src/libs/portfolio/interfaces.ts b/src/libs/portfolio/interfaces.ts index e151d72a6..f9109bed5 100644 --- a/src/libs/portfolio/interfaces.ts +++ b/src/libs/portfolio/interfaces.ts @@ -23,6 +23,7 @@ export type TokenResult = Omit & { rewardsType: 'wallet-vesting' | 'wallet-rewards' | null canTopUpGasTank: boolean isFeeToken: boolean + safetyLevel?: 'trusted' | 'unknown' | 'spoof' } }