Skip to content

Commit

Permalink
Fix/query evm balance rpcBatch fallback & update token security & bat…
Browse files Browse the repository at this point in the history
…ch 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
  • Loading branch information
qwang1113 authored Feb 21, 2023
1 parent 8bff2e9 commit 1b55e25
Show file tree
Hide file tree
Showing 19 changed files with 281 additions and 149 deletions.
19 changes: 12 additions & 7 deletions packages/blockchain-libs/src/provider/chains/eth/geth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,18 @@ class Geth extends BaseClient {
]
: ['eth_getBalance', [i.address, Geth.__LAST_BLOCK__]],
);

const resp: Array<string | undefined> = await this.rpc.batchCall(
calls,
undefined,
undefined,
true,
);
let resp: Array<string | undefined>;
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<string>(c[0], c[1])),
);
if (calls.length > 11) {
resp.push(...calls.slice(11).map(() => undefined));
}
}
return resp.map((i) => {
let balance;

Expand Down
39 changes: 20 additions & 19 deletions packages/components/src/Token/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -71,20 +71,16 @@ export const SecurityIcon: FC<{ token: Partial<IToken>; 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 (
<Icon size={size} name="ShieldExclamationMini" color="icon-critical" />
);
}
if (data?.warn?.length) {
if (riskLevel === TokenRiskLevel.WARN) {
return (
<Icon size={size} name="ExclamationTriangleMini" color="icon-warning" />
);
Expand All @@ -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 <Icon size={size} name="BadgeCheckMini" color="icon-success" />;
}
return null;
}
if (token?.riskLevel > TokenRiskLevel.VERIFIED) {
return <SecurityIcon token={token} size={size} />;
}
if (String(token?.verified) === 'true' || token?.isNative) {
if (token?.riskLevel === TokenRiskLevel.VERIFIED) {
return <Icon size={size} name="BadgeCheckMini" color="icon-success" />;
}
return null;
Expand All @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -314,10 +318,7 @@ const Token: FC<TokenProps> = ({
numberOfLines: 1,
...nameProps,
});
if (
!showTokenVerifiedIcon ||
(!token?.verified && !token?.security && !token?.isNative)
) {
if (!showTokenVerifiedIcon || (!token?.riskLevel && !token?.isNative)) {
return dom;
}
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -124,6 +126,18 @@ export class SimpleDbEntityTokens extends SimpleDbEntityBase<ISimpleDbEntityToke
return this.localTokens.addTokenToUnknownAccount(token);
}

async insertTokens(networkId: string, tokens: Token[]) {
const savedTokens = await this.getRawData();
const data = {
...(savedTokens || {}),
[networkId]: uniqBy(
[...(savedTokens?.[networkId] || []), ...tokens],
'address',
),
};
return this.setRawData(data);
}

async removeTokenFromAccount(accountId: string, tokenId: string) {
return this.localTokens.removeToken(accountId, tokenId);
}
Expand Down
26 changes: 13 additions & 13 deletions packages/engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import {
derivationPathTemplates,
getDefaultPurpose,
} from './managers/derivation';
import { getTokenRiskyItems } from './managers/goplus';
import { fetchSecurityInfo, getRiskLevel } from './managers/goplus';
import { implToCoinTypes } from './managers/impl';
import {
fromDBNetworkToNetwork,
Expand All @@ -88,6 +88,7 @@ import { AccountType } from './types/account';
import { CredentialType } from './types/credential';
import { GoPlusSupportApis } from './types/goplus';
import { HistoryEntryStatus } from './types/history';
import { TokenRiskLevel } from './types/token';
import {
WALLET_TYPE_EXTERNAL,
WALLET_TYPE_HD,
Expand All @@ -114,6 +115,7 @@ import type {
} from './types/account';
import type { BackupObject, ImportableHDWallet } from './types/backup';
import type { DevicePayload } from './types/device';
import type { GoPlusTokenSecurity } from './types/goplus';
import type {
HistoryEntry,
HistoryEntryMeta,
Expand Down Expand Up @@ -1241,7 +1243,6 @@ class Engine {
let tokenInfo:
| (Pick<Token, 'name' | 'symbol' | 'decimals'> & {
logoURI?: string;
security?: boolean;
})
| undefined;
const { impl, chainId } = parseNetworkId(networkId);
Expand All @@ -1257,6 +1258,16 @@ class Engine {
const vault = await this.getChainOnlyVault(networkId);
try {
[tokenInfo] = await vault.fetchTokenInfos([tokenIdOnNetwork]);
if (tokenInfo) {
const info = await fetchSecurityInfo<GoPlusTokenSecurity>({
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],
Expand All @@ -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,
Expand Down
30 changes: 22 additions & 8 deletions packages/engine/src/managers/goplus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 = {
Expand All @@ -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'],
Expand Down Expand Up @@ -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;
};
14 changes: 10 additions & 4 deletions packages/engine/src/types/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[];
Expand All @@ -20,6 +25,7 @@ export type ServerToken = {
isNative?: boolean;
onramperId?: string;
moonpayId?: string;
riskLevel?: TokenRiskLevel;
};

export type Token = HasName & {
Expand All @@ -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 = {
Expand Down
7 changes: 4 additions & 3 deletions packages/kit-bg/src/services/ServicePrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down
22 changes: 12 additions & 10 deletions packages/kit-bg/src/services/ServiceRevoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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(
Expand 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<string, string> = {};
for (const [id, price] of Object.entries(priceAndCharts[0])) {
prices[id] = price.toString();
}
return {
allowance: result,
prices,
Expand Down
Loading

0 comments on commit 1b55e25

Please sign in to comment.