Migrate EVM/Sol/TRX tokens to SQL (#3450)

* fix android CI

* feat: Migrate EVM, Sol and Tron tokens to SQL

* handle rename edgecase

* refactor: enhance token migration logic and improve database handling
This commit is contained in:
David Adegoke
2026-08-20 03:19:05 +02:00
committed by GitHub
parent 887e01a7e7
commit 4cc1495686
23 changed files with 1708 additions and 677 deletions
+96 -96
View File
@@ -26,10 +26,10 @@ class TokenUtilities {
for (final wallet in evmWallets) {
final chain = getTokenNameBasedOnWalletType(wallet.type);
final box = await _openEvmTokensBoxFor(wallet);
final tokens = await Erc20Token.getAllForWallet(wallet.name, _getDefaultChainId(wallet.type));
for (final t in box.values.where((t) => t.enabled)) {
final key = '$chain|${t.contractAddress.toLowerCase()}';
for (final t in tokens.where((t) => t.enabled)) {
final key = "$chain|${t.contractAddress.toLowerCase()}";
if (seen.add(key)) {
unique.add(t);
}
@@ -47,15 +47,17 @@ class TokenUtilities {
final tokens = <SPLToken>[];
for (final wallet in solWallets) {
final box = await _openSolTokensBoxFor(wallet);
tokens.addAll(box.values.where((t) => t.enabled));
final walletTokens = await SPLToken.getAllForWallet(wallet.name);
tokens.addAll(walletTokens.where((t) => t.enabled));
}
final seen = <String>{};
final unique = <SPLToken>[];
for (final token in tokens) {
final key = token.mintAddress.toLowerCase();
if (seen.add(key)) unique.add(token);
if (seen.add(key)) {
unique.add(token);
}
}
return unique;
}
@@ -69,25 +71,31 @@ class TokenUtilities {
final seen = <String>{};
final unique = <TronToken>[];
for (final wallet in tronWallets) {
final box = await _openTronTokensBoxFor(wallet);
for (final t in box.values.where((t) => t.enabled)) {
final walletTokens = await TronToken.getAllForWallet(wallet.name);
for (final t in walletTokens.where((t) => t.enabled)) {
final key = t.contractAddress.toLowerCase();
if (seen.add(key)) unique.add(t);
if (seen.add(key)) {
unique.add(t);
}
}
}
return unique;
}
static List<Erc20Token> loadDefaultEvmTokensForSwap() {
if (evm == null) return [];
if (evm == null) {
return [];
}
final tokens = <Erc20Token>[];
final seen = <String>{};
for (final chain in evm!.getAllChains()) {
for (final token in evm!.getDefaultTokensByChainId(chain.chainId)) {
final key = '${chain.chainId}|${token.contractAddress.toLowerCase()}';
if (seen.add(key)) tokens.add(token);
final key = "${chain.chainId}|${token.contractAddress.toLowerCase()}";
if (seen.add(key)) {
tokens.add(token);
}
}
}
@@ -109,7 +117,9 @@ class TokenUtilities {
for (final t in [...defaultTokens, ...userTokens]) {
final key = '${t.tag ?? 'ETH'}|${t.contractAddress.toLowerCase()}';
if (seen.add(key)) result.add(t);
if (seen.add(key)) {
result.add(t);
}
}
return result;
@@ -124,7 +134,9 @@ class TokenUtilities {
for (final t in [...defaultTokens, ...userTokens]) {
final key = t.mintAddress.toLowerCase();
if (seen.add(key)) result.add(t);
if (seen.add(key)) {
result.add(t);
}
}
return result;
@@ -139,7 +151,9 @@ class TokenUtilities {
for (final t in [...defaultTokens, ...userTokens]) {
final key = t.contractAddress.toLowerCase();
if (seen.add(key)) result.add(t);
if (seen.add(key)) {
result.add(t);
}
}
return result;
@@ -153,17 +167,34 @@ class TokenUtilities {
required WalletType walletType,
required String address,
}) async {
if (address.isEmpty) return null;
if (address.isEmpty) {
return null;
}
final lower = address.toLowerCase();
final tokens = await getAvailableTokensForNetwork(walletType);
for (final t in tokens) {
if (t is Erc20Token && t.contractAddress.toLowerCase() == lower) return t;
if (t is SPLToken && t.mintAddress.toLowerCase() == lower) return t;
if (t is TronToken && t.contractAddress.toLowerCase() == lower) return t;
if (t is Erc20Token && t.contractAddress.toLowerCase() == lower) {
return t;
}
if (t is SPLToken && t.mintAddress.toLowerCase() == lower) {
return t;
}
if (t is TronToken && t.contractAddress.toLowerCase() == lower) {
return t;
}
}
return null;
}
static int _getDefaultChainId(WalletType walletType) => switch (walletType) {
WalletType.ethereum => 1,
WalletType.polygon => 137,
WalletType.base => 8453,
WalletType.arbitrum => 42161,
WalletType.bsc => 56,
_ => 1,
};
static Future<int?> findEvmChainIdForContract(
String contractAddress, {
int? excludingChainId,
@@ -195,45 +226,10 @@ class TokenUtilities {
return null;
}
static Future<Box<Erc20Token>> _openEvmTokensBoxFor(WalletInfo walletInfo) async {
final walletKey = walletInfo.name.replaceAll(' ', '_');
final boxName = _getErc20TokensBoxName(walletKey, walletInfo.type);
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<Erc20Token>(boxName);
}
return CakeHive.openBox<Erc20Token>(boxName);
}
static String _getErc20TokensBoxName(String walletKey, WalletType walletType) {
return switch (walletType) {
WalletType.ethereum => '${walletKey}_${Erc20Token.ethereumBoxName}',
WalletType.polygon => '${walletKey}_${Erc20Token.polygonBoxName}',
WalletType.base => '${walletKey}_${Erc20Token.baseBoxName}',
WalletType.arbitrum => '${walletKey}_${Erc20Token.arbitrumBoxName}',
WalletType.bsc => '${walletKey}_${Erc20Token.bscBoxName}',
_ => '${walletKey}_${Erc20Token.ethereumBoxName}',
};
}
static Future<Box<SPLToken>> _openSolTokensBoxFor(WalletInfo wallet) async {
final boxName = '${wallet.name.replaceAll(' ', '_')}_${SPLToken.boxName}';
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<SPLToken>(boxName);
}
return CakeHive.openBox<SPLToken>(boxName);
}
static Future<Box<TronToken>> _openTronTokensBoxFor(WalletInfo walletInfo) async {
final boxName = '${walletInfo.name.replaceAll(' ', '_')}_${TronToken.boxName}';
if (CakeHive.isBoxOpen(boxName)) {
return CakeHive.box<TronToken>(boxName);
}
return CakeHive.openBox<TronToken>(boxName);
}
static Erc20Token? findErc20Token(CryptoCurrency currency, WalletBase wallet) {
if (currency is Erc20Token) return currency;
if (currency is Erc20Token) {
return currency;
}
// More of a fallback for us
for (final balanceCurrency in wallet.balance.keys) {
@@ -246,10 +242,14 @@ class TokenUtilities {
}
static Erc20Token? findErc20TokenForSwap(CryptoCurrency currency) {
if (currency is Erc20Token) return currency;
if (currency is Erc20Token) {
return currency;
}
for (final token in loadDefaultEvmTokensForSwap()) {
if (_matchesCurrency(token, currency)) return token;
if (_matchesCurrency(token, currency)) {
return token;
}
}
return null;
}
@@ -276,19 +276,19 @@ class TokenUtilities {
final title = currency.title.toLowerCase();
final tag = currency.tag?.toLowerCase();
return title == 'eth' ||
title == 'ethereum' ||
title == 'matic' ||
title == 'polygon' ||
title == 'base' ||
title == 'arbitrum' ||
title == 'bnb' ||
title == 'bsc' ||
title == 'avax' ||
title == 'avalanche' ||
tag == 'polygon' ||
tag == 'bsc' ||
tag == 'avalanche';
return title == "eth" ||
title == "ethereum" ||
title == "matic" ||
title == "polygon" ||
title == "base" ||
title == "arbitrum" ||
title == "bnb" ||
title == "bsc" ||
title == "avax" ||
title == "avalanche" ||
tag == "polygon" ||
tag == "bsc" ||
tag == "avalanche";
}
static int getChainId(CryptoCurrency currency) {
@@ -298,37 +298,37 @@ class TokenUtilities {
// Only check EVM registry for currencies that might be EVM-related
final isPotentialEVM = isNativeToken(currency) ||
(tag != null &&
(tag == 'ETH' || tag == 'POL' || tag == 'BASE' || tag == 'ARB' || tag == 'BSC'));
(tag == "ETH" || tag == "POL" || tag == "BASE" || tag == "ARB" || tag == "BSC"));
if (isPotentialEVM) {
// Try by tag first if available (e.g., 'POL', 'BASE', 'ARB')
if (tag != null) {
final chainId = evm?.getChainIdByTag(tag);
if (chainId != null) return chainId;
if (chainId != null) {
return chainId;
}
}
// Try by title (case-insensitive)
final titleChainId = evm?.getChainIdByTitle(title);
if (titleChainId != null) return titleChainId;
if (titleChainId != null) {
return titleChainId;
}
}
// Fallback to hardcoded values for chains not in registry yet
// Avalanche C-Chain
if (title == 'avalanche' || title == 'avax' || tag == 'AVALANCHE') {
if (title == "avalanche" || title == "avax" || tag == "AVALANCHE") {
return 43114;
}
// Optimism
if (title == 'optimism' || title == 'op' || tag == 'OPTIMISM') {
if (title == "optimism" || title == "op" || tag == "OPTIMISM") {
return 10;
}
// Fantom Opera
if (title == 'fantom' || title == 'ftm' || tag == 'FANTOM') {
if (title == "fantom" || title == "ftm" || tag == "FANTOM") {
return 250;
}
// Default to Ethereum mainnet
return 1;
}
@@ -395,7 +395,7 @@ class TokenUtilities {
}
for (final currency in CryptoCurrency.all) {
if (currency.tag?.toLowerCase() == 'sol') {
if (currency.tag?.toLowerCase() == "sol") {
if (currency is SPLToken) {
final mintAddress = currency.mintAddress.toLowerCase();
if (addedAddresses.add(mintAddress)) {
@@ -406,10 +406,7 @@ class TokenUtilities {
}
}
}
}
// Handle Tron network
else if (network == WalletType.tron) {
} else if (network == WalletType.tron) {
final userTronTokens = await loadAllUniqueTronTokens();
for (final token in userTronTokens) {
final contractAddress = token.contractAddress.toLowerCase();
@@ -426,7 +423,7 @@ class TokenUtilities {
}
for (final currency in CryptoCurrency.all) {
if (currency.tag?.toLowerCase() == 'trx') {
if (currency.tag?.toLowerCase() == "trx") {
if (currency is TronToken) {
final contractAddress = currency.contractAddress.toLowerCase();
if (addedAddresses.add(contractAddress)) {
@@ -442,31 +439,34 @@ class TokenUtilities {
return allTokens;
}
static bool _matchesCurrency(CryptoCurrency a, CryptoCurrency b) {
return a.title.toUpperCase() == b.title.toUpperCase() &&
(a.tag?.toUpperCase() == b.tag?.toUpperCase());
}
static bool _matchesCurrency(CryptoCurrency a, CryptoCurrency b) =>
a.title.toUpperCase() == b.title.toUpperCase() &&
(a.tag?.toUpperCase() == b.tag?.toUpperCase());
static Future<List<CryptoCurrency>> _getUserTokensForNetwork(CryptoCurrency baseCurrency) async {
final walletType = cryptoCurrencyOrTokenToWalletType(baseCurrency);
if (walletType == null) return [];
if (walletType == null) {
return [];
}
if (isEVMCompatibleChain(walletType)) {
final tokens = await TokenUtilities.loadAllUniqueEvmTokens();
return tokens.where((token) {
if (baseCurrency.tag == null) return token.tag == baseCurrency.title;
if (baseCurrency.tag == null) {
return token.tag == baseCurrency.title;
}
return token.tag?.toLowerCase() == baseCurrency.tag?.toLowerCase();
}).toList();
}
if (walletType == WalletType.solana) {
return await loadAllUniqueSolTokens();
return loadAllUniqueSolTokens();
}
if (walletType == WalletType.tron) {
return await loadAllUniqueTronTokens();
return loadAllUniqueTronTokens();
}
return [];