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
This commit is contained in:
David Adegoke
2026-02-07 12:53:43 +01:00
committed by GitHub
parent d8a6766090
commit fa5149af7f
3 changed files with 60 additions and 55 deletions
+14 -6
View File
@@ -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<void> _checkForExistingScamTokens() async {
@@ -1287,6 +1292,9 @@ abstract class EVMChainWalletBase
}
Future<void> addErc20Token(Erc20Token token) async {
final isSuspicious = isTokenPropertiesSuspicious(token);
token.isPotentialScam = token.isPotentialScam || isSuspicious;
String? iconPath;
if ((token.iconPath == null || token.iconPath!.isEmpty) && !token.isPotentialScam) {
+11 -12
View File
@@ -230,19 +230,18 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
);
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;
}
@@ -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,
),
),
],
),
),
],