From 1b55e25097598db5853523cfa9e0f3819d05f62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?qwang1113=20=28=E7=8E=8B=E5=BC=BA=29?= <15064266+qwang1113@users.noreply.github.com> Date: Tue, 21 Feb 2023 10:54:51 +0800 Subject: [PATCH] Fix/query evm balance rpcBatch fallback & update token security & batch api for query token details, OK-17217, OK-17458, OK-17536 (#2596) * feat: update token security field * fix: query evm balance rpcBatch fallback, OK-17217 * feat: batch token details --- .../src/provider/chains/eth/geth.ts | 19 +- packages/components/src/Token/index.tsx | 39 ++-- .../entity/SimpleDbEntityPresetTokens.ts | 14 ++ packages/engine/src/index.ts | 26 +-- packages/engine/src/managers/goplus.ts | 30 ++- packages/engine/src/types/token.ts | 14 +- packages/kit-bg/src/services/ServicePrice.ts | 7 +- packages/kit-bg/src/services/ServiceRevoke.ts | 22 ++- packages/kit-bg/src/services/ServiceToken.ts | 184 +++++++++++++----- packages/kit/src/hooks/useTokens.ts | 7 +- packages/kit/src/store/reducers/tokens.ts | 9 +- .../kit/src/views/AnnualReport/Welcome.tsx | 5 +- .../kit/src/views/ManageTokens/AddToken.tsx | 10 +- .../kit/src/views/ManageTokens/Listing.tsx | 3 +- .../helpers/TokenSecurityModalWrapper.tsx | 3 +- packages/kit/src/views/ManageTokens/types.ts | 8 +- packages/kit/src/views/Revoke/index.tsx | 3 +- .../src/views/Wallet/AssetsList/TokenCell.tsx | 20 +- .../kit/src/views/Wallet/AssetsList/index.tsx | 7 +- 19 files changed, 281 insertions(+), 149 deletions(-) diff --git a/packages/blockchain-libs/src/provider/chains/eth/geth.ts b/packages/blockchain-libs/src/provider/chains/eth/geth.ts index 48dd1c8b631..c7225f3c610 100644 --- a/packages/blockchain-libs/src/provider/chains/eth/geth.ts +++ b/packages/blockchain-libs/src/provider/chains/eth/geth.ts @@ -143,13 +143,18 @@ class Geth extends BaseClient { ] : ['eth_getBalance', [i.address, Geth.__LAST_BLOCK__]], ); - - const resp: Array = await this.rpc.batchCall( - calls, - undefined, - undefined, - true, - ); + let resp: Array; + try { + resp = await this.rpc.batchCall(calls, undefined, undefined, true); + } catch (error) { + // https://onekeyhq.atlassian.net/browse/OK-17217 + resp = await Promise.all( + calls.slice(0, 11).map((c) => this.rpc.call(c[0], c[1])), + ); + if (calls.length > 11) { + resp.push(...calls.slice(11).map(() => undefined)); + } + } return resp.map((i) => { let balance; diff --git a/packages/components/src/Token/index.tsx b/packages/components/src/Token/index.tsx index 3d736b28ffb..52f9856750e 100644 --- a/packages/components/src/Token/index.tsx +++ b/packages/components/src/Token/index.tsx @@ -12,9 +12,9 @@ import { useIntl } from 'react-intl'; import { parseNetworkId } from '@onekeyhq/engine/src/managers/network'; import type { Token as IToken } from '@onekeyhq/engine/src/types/token'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import { useNavigation, useNetwork } from '@onekeyhq/kit/src/hooks'; import { ModalRoutes, RootRoutes } from '@onekeyhq/kit/src/routes/types'; -import { useTokenSecurityInfo } from '@onekeyhq/kit/src/views/ManageTokens/hooks'; import { ManageTokenRoutes } from '@onekeyhq/kit/src/views/ManageTokens/types'; import { OnekeyNetwork } from '@onekeyhq/shared/src/config/networkIds'; import platformEnv from '@onekeyhq/shared/src/platformEnv'; @@ -71,20 +71,16 @@ export const SecurityIcon: FC<{ token: Partial; size: number }> = ({ token, size, }) => { - const { security, networkId, tokenIdOnNetwork, address } = token; - const { data } = useTokenSecurityInfo( - networkId ?? '', - tokenIdOnNetwork ?? address ?? '', - ); - if (!security || !data?.hasSecurity) { + const { riskLevel } = token; + if (!riskLevel || riskLevel < TokenRiskLevel.VERIFIED) { return null; } - if (data?.danger?.length) { + if (riskLevel === TokenRiskLevel.DANGER) { return ( ); } - if (data?.warn?.length) { + if (riskLevel === TokenRiskLevel.WARN) { return ( ); @@ -101,10 +97,16 @@ export const TokenVerifiedIcon: FC<{ const navigation = useNavigation(); const icon = useMemo(() => { - if (token && String(token?.security) === 'true') { + if (!token?.riskLevel) { + if (token?.isNative) { + return ; + } + return null; + } + if (token?.riskLevel > TokenRiskLevel.VERIFIED) { return ; } - if (String(token?.verified) === 'true' || token?.isNative) { + if (token?.riskLevel === TokenRiskLevel.VERIFIED) { return ; } return null; @@ -127,9 +129,11 @@ export const TokenVerifiedIcon: FC<{ navigation.navigate(RootRoutes.Modal, { screen: ModalRoutes.ManageToken, params: { - screen: token?.security - ? ManageTokenRoutes.TokenRiskDetail - : ManageTokenRoutes.VerifiedToken, + screen: + typeof token?.riskLevel === 'number' && + token?.riskLevel > TokenRiskLevel.VERIFIED + ? ManageTokenRoutes.TokenRiskDetail + : ManageTokenRoutes.VerifiedToken, params: { token: { ...token, @@ -140,7 +144,7 @@ export const TokenVerifiedIcon: FC<{ }); }, [navigation, token, intl]); - if (!token || (!token.verified && !token.security && !token?.isNative)) { + if (!token || (!token.riskLevel && !token?.isNative)) { return null; } @@ -314,10 +318,7 @@ const Token: FC = ({ numberOfLines: 1, ...nameProps, }); - if ( - !showTokenVerifiedIcon || - (!token?.verified && !token?.security && !token?.isNative) - ) { + if (!showTokenVerifiedIcon || (!token?.riskLevel && !token?.isNative)) { return dom; } return ( diff --git a/packages/engine/src/dbs/simple/entity/SimpleDbEntityPresetTokens.ts b/packages/engine/src/dbs/simple/entity/SimpleDbEntityPresetTokens.ts index 4ea0d397cc8..a7820aed0cc 100644 --- a/packages/engine/src/dbs/simple/entity/SimpleDbEntityPresetTokens.ts +++ b/packages/engine/src/dbs/simple/entity/SimpleDbEntityPresetTokens.ts @@ -1,4 +1,6 @@ // import { Token as ServerToken, top50 } from '@onekey/token-50-token-list'; +import { uniqBy } from 'lodash'; + import { SEPERATOR } from '@onekeyhq/shared/src/engine/engineConsts'; import { formatServerToken, isValidTokenId } from '../../../managers/token'; @@ -124,6 +126,18 @@ export class SimpleDbEntityTokens extends SimpleDbEntityBase & { logoURI?: string; - security?: boolean; }) | undefined; const { impl, chainId } = parseNetworkId(networkId); @@ -1257,6 +1258,16 @@ class Engine { const vault = await this.getChainOnlyVault(networkId); try { [tokenInfo] = await vault.fetchTokenInfos([tokenIdOnNetwork]); + if (tokenInfo) { + const info = await fetchSecurityInfo({ + networkId, + address: tokenIdOnNetwork, + apiName: GoPlusSupportApis.token_security, + }); + Object.assign(tokenInfo, { + riskLevel: info ? getRiskLevel(info) : TokenRiskLevel.UNKNOWN, + }); + } } catch (e) { debugLogger.common.error(`fetchTokenInfos error`, { params: [tokenIdOnNetwork], @@ -1267,17 +1278,6 @@ class Engine { if (!tokenInfo) { throw new Error('findToken ERROR: token not found.'); } - const { hasSecurity } = await getTokenRiskyItems({ - apiName: GoPlusSupportApis.token_security, - networkId, - address: tokenIdOnNetwork, - }); - if (hasSecurity) { - tokenInfo = { - ...tokenInfo, - security: true, - }; - } return { id: tokenId, networkId, diff --git a/packages/engine/src/managers/goplus.ts b/packages/engine/src/managers/goplus.ts index 06f6df5f44d..5c28e23340f 100644 --- a/packages/engine/src/managers/goplus.ts +++ b/packages/engine/src/managers/goplus.ts @@ -6,6 +6,7 @@ import type { LocaleIds } from '@onekeyhq/components/src/locale'; import { OnekeyNetwork } from '@onekeyhq/shared/src/config/networkIds'; import { GoPlusSupportApis } from '../types/goplus'; +import { TokenRiskLevel } from '../types/token'; import { fetchData } from './token'; @@ -163,13 +164,13 @@ export const tokenSecurityRiskItems: Record< ], warn: ['form__high_sell_tax', ['form__high_sell_tax_desc', { 0: '10%' }]], }, - 'is_mintable': { - warn: [ - 'form__token_can_be_issued_additionall', - 'form__token_can_be_issued_additionall_desc', - ], - safe: ['form__no_additional_issuance', 'form__no_additional_issuance_desc'], - }, + // 'is_mintable': { + // warn: [ + // 'form__token_can_be_issued_additionall', + // 'form__token_can_be_issued_additionall_desc', + // ], + // safe: ['form__no_additional_issuance', 'form__no_additional_issuance_desc'], + // }, } as const; export type CheckParams = { @@ -191,7 +192,7 @@ export const dangerItems: [keyof GoPlusTokenSecurity, CheckItemFunc][] = [ export const warnItems: [keyof GoPlusTokenSecurity, CheckItemFunc][] = [ ['is_proxy', (data) => data === '1'], - ['is_mintable', (data) => data === '1'], + // ['is_mintable', (data) => data === '1'], ['can_take_back_ownership', (data) => data === '1'], ['hidden_owner', (data) => data === '1'], ['external_call', (data) => data === '1'], @@ -384,3 +385,16 @@ export const getGpChainId = (networkId: string) => { return 'tron'; } }; + +export const getRiskLevel = (info: GoPlusTokenSecurity) => { + if (isTrustToken(info)) { + return TokenRiskLevel.VERIFIED; + } + if (dangerItems.filter(([k, func]) => func(info?.[k])).length) { + return TokenRiskLevel.DANGER; + } + if (warnItems.filter(([k, func]) => func(info?.[k])).length) { + return TokenRiskLevel.WARN; + } + return TokenRiskLevel.UNKNOWN; +}; diff --git a/packages/engine/src/types/token.ts b/packages/engine/src/types/token.ts index 419cdefcf38..2c33f3b562e 100644 --- a/packages/engine/src/types/token.ts +++ b/packages/engine/src/types/token.ts @@ -2,6 +2,13 @@ import type { LocaleIds } from '@onekeyhq/components/src/locale'; import type { HasName } from './base'; +export enum TokenRiskLevel { + UNKNOWN = 0, + VERIFIED = 1, + WARN, + DANGER, +} + export type ServerToken = { name: string; symbol: string; @@ -10,8 +17,6 @@ export type ServerToken = { logoURI: string; impl: string; status: 'LISTED' | 'DRAFT' | 'TRASH'; - verified: boolean; - security: boolean; addToIndex: boolean; chainId: string; source: string[]; @@ -20,6 +25,7 @@ export type ServerToken = { isNative?: boolean; onramperId?: string; moonpayId?: string; + riskLevel?: TokenRiskLevel; }; export type Token = HasName & { @@ -36,13 +42,13 @@ export type Token = HasName & { coingeckoId?: string; swftId?: string; marketCap?: number; - verified?: boolean; - security?: boolean; addToIndex?: boolean; autoDetected?: boolean; sendAddress?: string; onramperId?: string; moonpayId?: string; + + riskLevel?: TokenRiskLevel; }; export type Tool = { diff --git a/packages/kit-bg/src/services/ServicePrice.ts b/packages/kit-bg/src/services/ServicePrice.ts index 2f54cc1c90a..580cbd3fe88 100644 --- a/packages/kit-bg/src/services/ServicePrice.ts +++ b/packages/kit-bg/src/services/ServicePrice.ts @@ -69,7 +69,7 @@ export default class ServicePrice extends ServiceBase { }; const datas = await this.getCgkTokenPrice(params); if (Object.keys(datas).length > 0) { - dispatch(setTokenPriceMap({ prices: datas })); + dispatch(setTokenPriceMap({ prices: datas, vsCurrency })); } return datas; } @@ -88,10 +88,11 @@ export default class ServicePrice extends ServiceBase { for (const tokenId of tokenIds) { const key = tokenId ? `${networkId}-${tokenId}` : networkId; const price = tokenPricesInCache?.[key]; + const updatedAt = price?.[`updatedAt--${vsCurrency}`]; if ( - price?.updatedAt && + updatedAt && price[vsCurrency] && - now - price.updatedAt <= PRICE_EXPIRED_TIME + now - updatedAt <= PRICE_EXPIRED_TIME ) { cachePrices[key] = price; cachedTokenIds.push(tokenId); diff --git a/packages/kit-bg/src/services/ServiceRevoke.ts b/packages/kit-bg/src/services/ServiceRevoke.ts index 71b11f8462f..6eade1191cc 100644 --- a/packages/kit-bg/src/services/ServiceRevoke.ts +++ b/packages/kit-bg/src/services/ServiceRevoke.ts @@ -93,7 +93,7 @@ export default class ServiceRevoke extends ServiceBase { if (!provider) { return; } - return new multicall.MulticallProvider(provider, { verbose: true }); + return new multicall.MulticallProvider(provider, { verbose: false }); } @backgroundMethod() @@ -439,7 +439,8 @@ export default class ServiceRevoke extends ServiceBase { @backgroundMethod() async fetchERC20TokenAllowences(networkId: string, address: string) { - const { engine } = this.backgroundApi; + const { servicePrice } = this.backgroundApi; + const vsCurrency = appSelector((s) => s.settings.selectedFiatMoneySymbol); const events = await this.getTransferEvents(networkId, address); const res = await this.getERC20TokenApprovals(networkId, address, events); const allowanceList = await Promise.all( @@ -463,15 +464,16 @@ export default class ServiceRevoke extends ServiceBase { const addresses = result .map((r) => r.token.address?.toLowerCase()) .filter((a) => !!a) as string[]; - const priceAndCharts = await engine.getPricesAndCharts( - networkId, - addresses, - false, + const pricesMap = await servicePrice.getCgkTokenPrice({ + platform: networkId, + contractAddresses: addresses, + }); + const prices = Object.fromEntries( + Object.entries(pricesMap).map(([k, value]) => { + const v = value?.[vsCurrency]; + return [k, v]; + }), ); - const prices: Record = {}; - for (const [id, price] of Object.entries(priceAndCharts[0])) { - prices[id] = price.toString(); - } return { allowance: result, prices, diff --git a/packages/kit-bg/src/services/ServiceToken.ts b/packages/kit-bg/src/services/ServiceToken.ts index 588595d0234..252cb33a44e 100644 --- a/packages/kit-bg/src/services/ServiceToken.ts +++ b/packages/kit-bg/src/services/ServiceToken.ts @@ -1,5 +1,5 @@ import BigNumber from 'bignumber.js'; -import { debounce, isEmpty, uniq } from 'lodash'; +import { debounce, isEmpty, uniq, xor } from 'lodash'; import memoizee from 'memoizee'; import { @@ -10,16 +10,22 @@ import simpleDb from '@onekeyhq/engine/src/dbs/simple/simpleDb'; import type { CheckParams } from '@onekeyhq/engine/src/managers/goplus'; import { checkSite, + fetchSecurityInfo, getAddressRiskyItems, + getRiskLevel, getTokenRiskyItems, } from '@onekeyhq/engine/src/managers/goplus'; import { fetchTokenSource, fetchTools, + formatServerToken, getBalanceKey, } from '@onekeyhq/engine/src/managers/token'; import { AccountType } from '@onekeyhq/engine/src/types/account'; -import type { Token } from '@onekeyhq/engine/src/types/token'; +import type { GoPlusTokenSecurity } from '@onekeyhq/engine/src/types/goplus'; +import { GoPlusSupportApis } from '@onekeyhq/engine/src/types/goplus'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; +import type { ServerToken, Token } from '@onekeyhq/engine/src/types/token'; import { setTools } from '@onekeyhq/kit/src/store/reducers/data'; import type { TokenBalanceValue } from '@onekeyhq/kit/src/store/reducers/tokens'; import { @@ -33,6 +39,7 @@ import { backgroundMethod, bindThis, } from '@onekeyhq/shared/src/background/backgroundDecorators'; +import { fetchData } from '@onekeyhq/shared/src/background/backgroundUtils'; import { AppEventBusNames, appEventBus, @@ -108,25 +115,20 @@ export default class ServiceToken extends ServiceBase { ); const { selectedFiatMoneySymbol } = appSelector((s) => s.settings); const actions: any[] = []; - const [balances, autodetectedTokens = []] = await this.fetchTokenBalance({ + const [, autodetectedTokens = []] = await this.fetchTokenBalance({ activeAccountId, activeNetworkId, tokenIds: tokens.map((token) => token.tokenIdOnNetwork), }); const accountTokens = tokens.concat(autodetectedTokens); // check token prices - await servicePrice.fetchSimpleTokenPrice({ + servicePrice.fetchSimpleTokenPrice({ networkId: activeNetworkId, accountId: activeAccountId, tokenIds: accountTokens.map((t) => t.tokenIdOnNetwork), vsCurrency: selectedFiatMoneySymbol, }); actions.push( - setAccountTokensBalances({ - activeAccountId, - activeNetworkId, - tokensBalance: balances, - }), setAccountTokens({ activeAccountId, activeNetworkId, @@ -291,11 +293,20 @@ export default class ServiceToken extends ServiceBase { if (isExists) { return; } + const info = await fetchSecurityInfo({ + networkId, + address, + apiName: GoPlusSupportApis.token_security, + }); + const riskLevel = info ? getRiskLevel(info) : TokenRiskLevel.UNKNOWN; const result = await engine.quickAddToken( accountId, networkId, address, logoURI, + { + riskLevel, + }, ); await this.fetchAccountTokens({ activeAccountId: accountId, @@ -344,7 +355,13 @@ export default class ServiceToken extends ServiceBase { networkId: string, accountId: string, withMain = true, - ): Promise<[Record, Token[] | undefined]> { + ): Promise< + [ + Record, + Token[] | undefined, + Record, + ] + > { const { engine } = this.backgroundApi; const vault = await engine.getVault({ networkId, accountId }); @@ -368,6 +385,10 @@ export default class ServiceToken extends ServiceBase { networkId, ); const allAccountTokens: Token[] = []; + const tokens = await this.batchTokenDetail( + networkId, + balancesFromApi.map((b) => b.address), + ); for (const { address, balance, @@ -377,37 +398,104 @@ export default class ServiceToken extends ServiceBase { (b) => (+b.balance > 0 || !b.address) && !removedTokens.includes(b.address), )) { - try { - let token = await engine.findToken({ - networkId, - tokenIdOnNetwork: address, - }); - if (!token) { - token = await engine.quickAddToken(accountId, networkId, address); - } - if (token) { - // only record new token balances - // other token balances still get from RPC for accuracy - Object.assign(ret, { - [getBalanceKey({ - address, - sendAddress, - })]: { - balance, - blockHeight, - }, - }); - allAccountTokens.push({ - ...token, + const token = tokens[address]; + if (token) { + // only record new token balances + // other token balances still get from RPC for accuracy + Object.assign(ret, { + [getBalanceKey({ + address, sendAddress, - autoDetected: !accountTokens.some((t) => t.address === address), - }); - } - } catch (e) { - // pass + })]: { + balance, + blockHeight, + }, + }); + allAccountTokens.push({ + ...token, + sendAddress, + autoDetected: !accountTokens.some((t) => t.address === address), + }); + } + } + return [ret, allAccountTokens, tokens]; + } + + async batchTokenDetail( + networkId: string, + addresses: string[], + tokensMap: Record = {}, + ) { + const addressMap: Record = addresses.reduce( + (memo, n) => ({ + ...memo, + [n]: 1, + }), + {}, + ); + const localTokens: Token[] = ( + await this.backgroundApi.engine.getTokens( + networkId, + undefined, + true, + true, + ) + ).filter((t) => addressMap[t?.address ?? '']); + + const serverAddress = xor( + addresses, + localTokens.map((t) => t.address ?? ''), + ); + + let serverTokens: Token[] = []; + if (serverAddress.length) { + serverTokens = ( + await fetchData( + `/token/detail/batch`, + { + networkId, + addresses: serverAddress, + }, + [], + 'POST', + ) + ).map(formatServerToken); + } + + const result: Record = [ + ...serverTokens, + ...localTokens, + ].reduce( + (sum, n) => ({ + ...sum, + [n.address ?? '']: n, + }), + {}, + ); + + const restAddress = xor( + serverAddress, + serverTokens.map((t) => t.address), + ); + const rpcTokens: Token[] = []; + for (const address of restAddress) { + const detail = await this.backgroundApi.engine.findToken({ + networkId, + tokenIdOnNetwork: address ?? '', + }); + if (detail) { + result[address ?? ''] = detail; + rpcTokens.push(detail); } } - return [ret, allAccountTokens]; + await simpleDb.token.insertTokens(networkId, [ + ...serverTokens, + ...rpcTokens, + ]); + return { + ...tokensMap, + ...result, + }; } @backgroundMethod() @@ -416,6 +504,7 @@ export default class ServiceToken extends ServiceBase { accountId: string, tokensToGet: string[], withMain = true, + tokensMap: Record = {}, ): Promise<[Record, Token[] | undefined]> { const { engine } = this.backgroundApi; const network = await engine.getNetwork(networkId); @@ -440,14 +529,15 @@ export default class ServiceToken extends ServiceBase { }); } const balanceList = balances.slice(withMain ? 1 : 0); + const tokens = await this.batchTokenDetail( + networkId, + tokensToGet, + tokensMap, + ); for (let i = 0; i < balanceList.length; i += 1) { const balance = balanceList[i]; - - const tokenId1 = tokensToGet[i]; - const token = await engine.findToken({ - networkId, - tokenIdOnNetwork: tokenId1, - }); + const tokenAddress = tokensToGet[i]; + const token = tokens[tokenAddress]; const decimals = token?.decimals; if ( token && @@ -457,7 +547,7 @@ export default class ServiceToken extends ServiceBase { const bal = balance.div(new BigNumber(10).pow(decimals)).toFixed(); Object.assign(ret, { [getBalanceKey({ - address: tokenId1, + address: tokenAddress, })]: { balance: bal, blockHeight, @@ -496,9 +586,10 @@ export default class ServiceToken extends ServiceBase { let serverBalances: Record = {}; let rpcBalances: Record = {}; let autodetectedTokens: Token[] = []; + let tokensMap: Record = {}; if (balanceSupprtedNetwork.includes(networkId)) { try { - [serverBalances, autodetectedTokens = []] = + [serverBalances, autodetectedTokens = [], tokensMap] = await this.getAccountBalanceFromServerApi( networkId, accountId, @@ -521,6 +612,7 @@ export default class ServiceToken extends ServiceBase { accountId, tokensToGet, withMain, + tokensMap, ); } catch (e) { debugLogger.common.error( diff --git a/packages/kit/src/hooks/useTokens.ts b/packages/kit/src/hooks/useTokens.ts index e7a48ee0986..a5570c20623 100644 --- a/packages/kit/src/hooks/useTokens.ts +++ b/packages/kit/src/hooks/useTokens.ts @@ -6,6 +6,7 @@ import { useAsync } from 'react-async-hook'; import { getBalanceKey } from '@onekeyhq/engine/src/managers/token'; import type { Token } from '@onekeyhq/engine/src/types/token'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import { OnekeyNetwork } from '@onekeyhq/shared/src/config/networkIds'; import backgroundApiProxy from '../background/instance/backgroundApiProxy'; @@ -122,7 +123,11 @@ export function useAccountTokens( if (hideSmallBalance && new B(t.usdValue).isLessThan(1)) { return false; } - if (hideRiskTokens && t.security) { + if ( + hideRiskTokens && + t.riskLevel && + t.riskLevel > TokenRiskLevel.VERIFIED + ) { return false; } if (putMainTokenOnTop && (t.isNative || !t.address)) { diff --git a/packages/kit/src/store/reducers/tokens.ts b/packages/kit/src/store/reducers/tokens.ts index 00f3e435eac..fde8161957a 100644 --- a/packages/kit/src/store/reducers/tokens.ts +++ b/packages/kit/src/store/reducers/tokens.ts @@ -19,9 +19,7 @@ export type TokenPrices = Record; export type SimpleTokenPrices = Record< string, number | PriceLoading | NoPriceData -> & { - updatedAt?: number; -}; +>; type NetworkId = string; type AccountId = string; @@ -63,6 +61,7 @@ type TokenBalancePayloadAction = { type TokenPrivePayloadAction = { prices: Record; + vsCurrency: string; }; export const tokensSlice = createSlice({ @@ -70,14 +69,14 @@ export const tokensSlice = createSlice({ initialState, reducers: { setTokenPriceMap(state, action: PayloadAction) { - const { prices } = action.payload; + const { prices, vsCurrency } = action.payload; if (!state.tokenPriceMap) state.tokenPriceMap = {}; Object.keys(prices).forEach((key) => { const cachePrice = state.tokenPriceMap[key] || {}; state.tokenPriceMap[key] = { ...cachePrice, ...prices[key], - updatedAt: Date.now(), + [`updatedAt--${vsCurrency}`]: Date.now(), }; }); }, diff --git a/packages/kit/src/views/AnnualReport/Welcome.tsx b/packages/kit/src/views/AnnualReport/Welcome.tsx index cb2b365f005..894091eae55 100644 --- a/packages/kit/src/views/AnnualReport/Welcome.tsx +++ b/packages/kit/src/views/AnnualReport/Welcome.tsx @@ -18,6 +18,7 @@ import type { LocaleIds } from '@onekeyhq/components/src/locale'; import { shortenAddress } from '@onekeyhq/components/src/utils'; import { getBalanceKey } from '@onekeyhq/engine/src/managers/token'; import type { NFTAsset } from '@onekeyhq/engine/src/types/nft'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import bg1 from '@onekeyhq/kit/assets/annual/1.jpg'; import bgLoading from '@onekeyhq/kit/assets/annual/bg_loading.png'; import bgStart from '@onekeyhq/kit/assets/annual/bg_start.png'; @@ -84,7 +85,9 @@ const AnnualLoading: FC = () => { activeNetworkId: networkId, activeAccountId: accountId, }); - const accountTokens = res.filter((n) => !n.security); + const accountTokens = res.filter( + (n) => !n.riskLevel || n.riskLevel <= TokenRiskLevel.VERIFIED, + ); const prices = await servicePrice.fetchSimpleTokenPrice({ networkId, accountId, diff --git a/packages/kit/src/views/ManageTokens/AddToken.tsx b/packages/kit/src/views/ManageTokens/AddToken.tsx index 77696f4b8ae..184b620eaf4 100644 --- a/packages/kit/src/views/ManageTokens/AddToken.tsx +++ b/packages/kit/src/views/ManageTokens/AddToken.tsx @@ -19,6 +19,7 @@ import type { ModalProps } from '@onekeyhq/components/src/Modal'; import { TokenVerifiedIcon } from '@onekeyhq/components/src/Token'; import { getBalanceKey } from '@onekeyhq/engine/src/managers/token'; import type { Token as TokenType } from '@onekeyhq/engine/src/types/token'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import debugLogger from '@onekeyhq/shared/src/logger/debugLogger'; import type { WatchAssetParameters } from '@onekeyhq/shared/src/providerApis/ProviderApiEthereum/ProviderApiEthereum.types'; @@ -29,7 +30,6 @@ import useDappApproveAction from '../../hooks/useDappApproveAction'; import useDappParams from '../../hooks/useDappParams'; import { wait } from '../../utils/helper'; -import { useTokenSecurityInfo } from './hooks'; import { ManageTokenRoutes } from './types'; import type { ManageTokenRoutesParams } from './types'; @@ -53,7 +53,6 @@ export type IViewTokenModalProps = ModalProps; const useRouteParams = () => { const routeProps = useRoute(); const { params } = routeProps; - const { network } = useActiveWalletAccount(); let token: Partial; if ('query' in params) { const query: WatchAssetParameters = JSON.parse(params.query); @@ -70,10 +69,6 @@ const useRouteParams = () => { } else { token = params; } - const { data } = useTokenSecurityInfo( - network?.id ?? '', - token.tokenIdOnNetwork ?? token?.address ?? '', - ); if ('query' in params) { const query: WatchAssetParameters = JSON.parse(params.query); const { address, symbol, decimals, image, sendAddress } = query.options; @@ -83,8 +78,7 @@ const useRouteParams = () => { symbol: symbol ?? '', decimal: decimals ?? 0, logoURI: image ?? '', - security: data?.hasSecurity, - verified: data?.hasSecurity === false, + riskLevel: token.riskLevel ?? TokenRiskLevel.UNKNOWN, sendAddress: sendAddress ?? undefined, }; } diff --git a/packages/kit/src/views/ManageTokens/Listing.tsx b/packages/kit/src/views/ManageTokens/Listing.tsx index 5e113529d75..3052b3d8244 100644 --- a/packages/kit/src/views/ManageTokens/Listing.tsx +++ b/packages/kit/src/views/ManageTokens/Listing.tsx @@ -380,10 +380,9 @@ const ListRenderToken: FC = ({ item }) => { address: item.tokenIdOnNetwork, decimal: item.decimals, logoURI: item.logoURI, - verified: item.verified, - security: item?.security, source: item.source || '', sendAddress: item.sendAddress, + riskLevel: item.riskLevel, }); }, [navigation, item, isOwned]); diff --git a/packages/kit/src/views/ManageTokens/helpers/TokenSecurityModalWrapper.tsx b/packages/kit/src/views/ManageTokens/helpers/TokenSecurityModalWrapper.tsx index 57bfb5c102f..0d84108057e 100644 --- a/packages/kit/src/views/ManageTokens/helpers/TokenSecurityModalWrapper.tsx +++ b/packages/kit/src/views/ManageTokens/helpers/TokenSecurityModalWrapper.tsx @@ -3,11 +3,12 @@ import { formatMessage } from '@onekeyhq/components/src/Provider'; import { generateNetworkIdByChainId } from '@onekeyhq/engine/src/managers/network'; import { GoPlusSupportApis } from '@onekeyhq/engine/src/types/goplus'; import type { Token } from '@onekeyhq/engine/src/types/token'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import backgroundApiProxy from '../../../background/instance/backgroundApiProxy'; export const notifyIfRiskToken = (token: Partial) => { - if (String(token?.security) !== 'true') { + if (!token.riskLevel || token.riskLevel <= TokenRiskLevel.VERIFIED) { return; } let networkId = token?.networkId; diff --git a/packages/kit/src/views/ManageTokens/types.ts b/packages/kit/src/views/ManageTokens/types.ts index 417d7f8df33..ef33181f935 100644 --- a/packages/kit/src/views/ManageTokens/types.ts +++ b/packages/kit/src/views/ManageTokens/types.ts @@ -1,5 +1,5 @@ import type { PriceAlertItem } from '@onekeyhq/engine/src/managers/notification'; -import type { Token } from '@onekeyhq/engine/src/types/token'; +import type { Token, TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; export enum ManageTokenRoutes { Listing = 'ListTokensModal', @@ -22,10 +22,9 @@ export type ManageTokenRoutesParams = { address: string; decimal: number; logoURI: string; - verified?: boolean; - security?: boolean; source?: string; sendAddress?: string; + riskLevel: TokenRiskLevel; } | { query: string }; [ManageTokenRoutes.ActivateToken]: { @@ -42,10 +41,9 @@ export type ManageTokenRoutesParams = { address: string; decimal: number; logoURI: string; - verified?: boolean; source?: string; - security?: boolean; sendAddress?: string; + riskLevel?: TokenRiskLevel; }; [ManageTokenRoutes.VerifiedToken]: { token: Partial; diff --git a/packages/kit/src/views/Revoke/index.tsx b/packages/kit/src/views/Revoke/index.tsx index b07fd733d17..881f9c7d935 100644 --- a/packages/kit/src/views/Revoke/index.tsx +++ b/packages/kit/src/views/Revoke/index.tsx @@ -19,6 +19,7 @@ import type { ERC721TokenAllowance, } from '@onekeyhq/engine/src/managers/revoke'; import { toFloat } from '@onekeyhq/engine/src/managers/revoke'; +import { TokenRiskLevel } from '@onekeyhq/engine/src/types/token'; import { IMPL_EVM } from '@onekeyhq/shared/src/engine/engineConsts'; import platformEnv from '@onekeyhq/shared/src/platformEnv'; @@ -90,7 +91,7 @@ const RevokePage: FC = () => { ?.filter( ({ token }) => filters.includeUnverifiedTokens || - (token.verified && !token.security), + token.riskLevel === TokenRiskLevel.VERIFIED, ) .filter(({ token, balance }) => { if (filters.includeZeroBalancesTokens) { diff --git a/packages/kit/src/views/Wallet/AssetsList/TokenCell.tsx b/packages/kit/src/views/Wallet/AssetsList/TokenCell.tsx index de6fc062e0a..0285763b296 100644 --- a/packages/kit/src/views/Wallet/AssetsList/TokenCell.tsx +++ b/packages/kit/src/views/Wallet/AssetsList/TokenCell.tsx @@ -22,10 +22,10 @@ import { useSimpleTokenPriceInfo, useSimpleTokenPriceValue, } from '../../../hooks/useManegeTokenPrice'; -import { useSingleToken, useTokenBalance } from '../../../hooks/useTokens'; +import { useTokenBalance } from '../../../hooks/useTokens'; import { calculateGains, getPreBaseValue } from '../../../utils/priceUtils'; -interface TokenCellProps { +type TokenCellProps = TokenType & { borderTopRadius?: string | number; borderRadius?: string | number; borderTopWidth?: string | number; @@ -39,10 +39,9 @@ interface TokenCellProps { tokenIdOnNetwork: string; sendAddress?: string; autoDetected?: boolean; -} +}; const TokenCell: FC = ({ accountId, - networkId, hidePriceInfo, borderTopRadius, borderRadius, @@ -51,17 +50,14 @@ const TokenCell: FC = ({ onPress, borderColor = 'border-subdued', bg = 'surface-default', - ...tokenItem + ...token }) => { const isVerticalLayout = useIsVerticalLayout(); - const { token } = useSingleToken(networkId, tokenItem.tokenIdOnNetwork); + const { networkId } = token; const balance = useTokenBalance({ accountId, networkId, - token: { - ...token, - ...tokenItem, - }, + token, fallback: '0', }); const { network } = useActiveSideAccount({ accountId, networkId }); @@ -116,10 +112,10 @@ const TokenCell: FC = ({ (t: TokenType) => { onPress?.({ ...t, - sendAddress: tokenItem?.sendAddress, + sendAddress: token?.sendAddress, }); }, - [onPress, tokenItem?.sendAddress], + [onPress, token?.sendAddress], ); if (!token) { diff --git a/packages/kit/src/views/Wallet/AssetsList/index.tsx b/packages/kit/src/views/Wallet/AssetsList/index.tsx index 771c45b3eb8..c1fed2360eb 100644 --- a/packages/kit/src/views/Wallet/AssetsList/index.tsx +++ b/packages/kit/src/views/Wallet/AssetsList/index.tsx @@ -34,6 +34,7 @@ import TokenCell from './TokenCell'; import type { SimplifiedToken } from '../../../store/reducers/tokens'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { Token } from '@onekeyhq/engine/src/types/token'; type NavigationProps = NativeStackNavigationProp< RootRoutesParams, @@ -117,7 +118,7 @@ function AssetsList({ ); const onTokenCellPress = useCallback( - (item: SimplifiedToken) => { + (item: Token) => { if (onTokenPress) { onTokenPress({ token: item }); return; @@ -138,12 +139,11 @@ function AssetsList({ [account?.id, networkId, navigation, onTokenPress], ); - const renderListItem: FlatListProps['renderItem'] = ({ + const renderListItem: FlatListProps['renderItem'] = ({ item, index, }) => ( );