feat: harden spam detection for solana and evm (#3322)

* feat: harden spam detection for solana and evm

* fix: guard scam/impersonator tokens from auto-enabling on EVM and Solana

* feat: enhance token property validation for scam detection
This commit is contained in:
David Adegoke
2026-07-10 14:52:19 +03:00
committed by GitHub
parent c9e0ac85d4
commit a7073df422
5 changed files with 361 additions and 128 deletions
+24 -22
View File
@@ -692,29 +692,18 @@ class CWEVM extends EVM {
);
}
Future<({double usdValue, bool hasValidFiatPrice})> _getTokenUsdValueAndFiatCheck(
Erc20Token token,
BigInt balanceWei,
) async {
Future<double> _fetchFiatApiPriceForToken(Erc20Token token) async {
try {
final settingsStore = getIt.get<SettingsStore>();
final torOnly = settingsStore.fiatApiMode == FiatApiMode.torOnly;
final price = await FiatConversionService.fetchPrice(
return await FiatConversionService.fetchPrice(
crypto: token,
fiat: FiatCurrency.usd,
torOnly: torOnly,
);
final hasValidFiatPrice = price > 0;
final decimals = token.decimal;
final balance = balanceWei.toDouble() / math.pow(10, decimals);
final usdValue = balance * price;
return (usdValue: usdValue, hasValidFiatPrice: hasValidFiatPrice);
} catch (e) {
return (usdValue: 0.0, hasValidFiatPrice: false);
} catch (_) {
return 0.0;
}
}
@@ -730,21 +719,34 @@ class CWEVM extends EVM {
final List<Future<void>> tokenChecks = [];
final whitelistedContracts =
wallet.getDefaultTokenContractAddresses.map((a) => a.toLowerCase()).toSet();
for (final item in result.newTokens) {
tokenChecks.add((() async {
final token = item.token;
final isPropertiesSuspicious = wallet.isTokenPropertiesSuspicious(token);
final isWhitelisted = whitelistedContracts.contains(token.contractAddress.toLowerCase());
final fiatResult = await _getTokenUsdValueAndFiatCheck(
token,
item.balanceWei,
);
final isSpam = isPropertiesSuspicious || !fiatResult.hasValidFiatPrice;
final moralisPrice = item.moralisUsdPrice;
final moralisValue = item.moralisUsdValue ?? 0.0;
final hasMoralisPrice = moralisPrice != null && moralisPrice > 0;
final fiatApiPrice = await _fetchFiatApiPriceForToken(token);
final hasFiatApiPrice = fiatApiPrice > 0;
final isImpersonator =
hasFiatApiPrice && !hasMoralisPrice && !isWhitelisted && !item.verifiedContract;
final isSpam = isPropertiesSuspicious ||
token.isPotentialScam ||
isImpersonator ||
(!hasMoralisPrice && !hasFiatApiPrice);
token.isPotentialScam = isSpam;
token.enabled = (fiatResult.usdValue >= _minTokenUsdValue) && !isSpam;
token.enabled =
hasMoralisPrice && hasFiatApiPrice && (moralisValue >= _minTokenUsdValue) && !isSpam;
await wallet.addErc20Token(token);
})());
+3 -1
View File
@@ -387,9 +387,11 @@ class CWSolana extends Solana {
tokenChecks.add((() async {
final token = item.token;
final isPropertiesSuspicious = wallet.isTokenPropertiesSuspicious(token);
final fiatResult = await _getTokenUsdValueAndFiatCheck(token, item.balance);
final isSpam = !fiatResult.hasValidFiatPrice;
final isSpam = isPropertiesSuspicious || !fiatResult.hasValidFiatPrice;
token.isPotentialScam = isSpam;
token.enabled = (fiatResult.usdValue >= _minTokenUsdValue) && !isSpam;