From fa5149af7f2ab8273f4af094afb2d80a987bbd0c Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Sat, 7 Feb 2026 12:53:43 +0100 Subject: [PATCH] Generic Token Fixes (#2873) * feat: Automatically detect wallet tokens for EVM chains * feat: Disable potential tg scam tokens by default * feat: Add homoglyph normalization to detect spoofing attacks in token symbols * refactor: Improve scam token detection and apply to automatically fetched EVM tokens * refactor: Enhance scam detection for automatically detected tokens in evm wallets * feat: Add fiat check to scam checks on automatically imported token and disable tokens that fail the check * feat: Add fiat check to scam checks on automatically imported token and disable tokens that fail the check * feat: Add fiat check to scam checks on automatically imported token and disable tokens that fail the check * feat: Enable whitelisted tokens with balance when importing wallet tokens * feat: Disable imported tokens with balance less than 0.1 usd * feat: Disable imported tokens with balance less than 0.1 usd - Update configure file * fixes: - default tokens marked as potential scam - ui overflow for token name on balance page - add more filters for spam tokens --- cw_evm/lib/evm_chain_wallet.dart | 20 ++++-- .../screens/dashboard/edit_token_page.dart | 23 +++--- .../pages/balance/balance_row_widget.dart | 72 +++++++++---------- 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/cw_evm/lib/evm_chain_wallet.dart b/cw_evm/lib/evm_chain_wallet.dart index c44042077..c3ecd5ba1 100644 --- a/cw_evm/lib/evm_chain_wallet.dart +++ b/cw_evm/lib/evm_chain_wallet.dart @@ -442,8 +442,6 @@ abstract class EVMChainWalletBase } bool isTokenPropertiesSuspicious(Erc20Token token) { - final baseCurrencySymbols = CryptoCurrency.all.map((e) => e.title.toUpperCase()).toList(); - bool isTokenWhitelisted = getDefaultTokenContractAddresses .any((element) => element.toLowerCase() == token.contractAddress.toLowerCase()); @@ -459,10 +457,18 @@ abstract class EVMChainWalletBase 'http', 'https', '.com', + '.org', + '.top', + '.live', 'airdrop', + 'reward', + 'distribution', 'www', '.xyz', '🎁', + 'bot', + 'claim', + 'reward', ]; final hasSuspiciousData = suspiciousStrings.any( @@ -472,11 +478,10 @@ abstract class EVMChainWalletBase normalizedTitle.toLowerCase().contains(element), ); - // Check if the token symbol is the same as any of the base currencies symbols (ETH, SOL, POL, TRX, etc). - // If it is, then it's probably a scam unless it's in the whitelist. - final hasSuspiciousSymbol = baseCurrencySymbols.contains(normalizedSymbol); + final nativeSymbol = currency.title.toUpperCase(); + final hasSuspiciousSymbol = normalizedSymbol == nativeSymbol && !isTokenWhitelisted; - return hasSuspiciousData || (hasSuspiciousSymbol && !isTokenWhitelisted); + return hasSuspiciousData || hasSuspiciousSymbol; } Future _checkForExistingScamTokens() async { @@ -1287,6 +1292,9 @@ abstract class EVMChainWalletBase } Future addErc20Token(Erc20Token token) async { + final isSuspicious = isTokenPropertiesSuspicious(token); + token.isPotentialScam = token.isPotentialScam || isSuspicious; + String? iconPath; if ((token.iconPath == null || token.iconPath!.isEmpty) && !token.isPotentialScam) { diff --git a/lib/src/screens/dashboard/edit_token_page.dart b/lib/src/screens/dashboard/edit_token_page.dart index 7399ac0bd..f436845fe 100644 --- a/lib/src/screens/dashboard/edit_token_page.dart +++ b/lib/src/screens/dashboard/edit_token_page.dart @@ -230,19 +230,18 @@ class _EditTokenPageBodyState extends State { ); bool isPotentialScam = hasPotentialError && !isWhitelisted; - final tokenSymbol = _tokenSymbolController.text.toUpperCase(); - // check if the token symbol is the same as any of the base currencies symbols (ETH, SOL, POL, TRX, etc): - // if it is, then it's probably a scam unless it's in the whitelist - - // ugh, should it be commented out? - // because there are some tokens that has the name of original currencies - // like: 0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6 - - final baseCurrencySymbols = - CryptoCurrency.all.map((e) => e.title.toUpperCase()).toList(); - if (baseCurrencySymbols.contains(tokenSymbol.trim().toUpperCase()) && - !isWhitelisted) { + // check if the token symbol is the same as the native token symbol + // to prevent token impersonation + // (e.g. fake ETH on Ethereum, fake SOL on Solana) + final tokenSymbol = _tokenSymbolController.text + .trim() + .toUpperCase(); + final nativeSymbol = widget.homeSettingsViewModel + .nativeToken + .title + .toUpperCase(); + if (tokenSymbol == nativeSymbol && !isWhitelisted) { isPotentialScam = true; } diff --git a/lib/src/screens/dashboard/pages/balance/balance_row_widget.dart b/lib/src/screens/dashboard/pages/balance/balance_row_widget.dart index e2fa67063..b9e170599 100644 --- a/lib/src/screens/dashboard/pages/balance/balance_row_widget.dart +++ b/lib/src/screens/dashboard/pages/balance/balance_row_widget.dart @@ -173,46 +173,44 @@ class BalanceRowWidget extends StatelessWidget { ], ), ), - SizedBox( - //width: min(MediaQuery.of(context).size.width * 0.2, 100), - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - CakeImageWidget( - imageUrl: currency.iconPath, - height: 40, - width: 40, - errorWidget: Container( - height: 30.0, - width: 30.0, - child: Center( - child: Text( - currency.title.substring(0, min(currency.title.length, 2)), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 11, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.surfaceContainer, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CakeImageWidget( + imageUrl: currency.iconPath, + height: 40, + width: 40, + errorWidget: Container( + height: 30.0, + width: 30.0, + child: Center( + child: Text( + currency.title.substring(0, min(currency.title.length, 2)), + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 11, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).colorScheme.surfaceContainer, + ), ), - const SizedBox(height: 10), - Text( - currency.title, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontSize: 16, - fontWeight: FontWeight.w700, - color: Theme.of(context).colorScheme.onSurface, - height: 1, - ), - ), - ], - ), + ), + const SizedBox(height: 10), + Text( + currency.title, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Theme.of(context).colorScheme.onSurface, + height: 1, + ), + ), + ], ), ), ],