From c1dbcdd8f990da7e83d9bf0328a97d8cad6995c8 Mon Sep 17 00:00:00 2001 From: CharlVS <77973576+CharlVS@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:14:41 +0200 Subject: [PATCH 1/2] fix: Fix UTXO coins being incorrectly tagged as Smart Chain (#242) - Updated CoinSubClass.parse() to prioritize exact enum name matches - Added separate phase for exact ticker matches to resolve ambiguity - Changed formatted getter for utxo to return 'Native' instead of 'UTXO' - This fixes UTXO coins (zcash, ltc, doge) being incorrectly categorized as Smart Chain Both utxo and smartChain had the same ticker 'UTXO', causing the parser to match smartChain first due to enum order. The new parsing logic ensures 'UTXO' from coins_config.json correctly maps to CoinSubClass.utxo. --- .../lib/src/coin_classes/coin_subclasses.dart | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart b/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart index ea8a97b5..e9ecc5d2 100644 --- a/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart +++ b/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart @@ -157,19 +157,34 @@ enum CoinSubClass { final sanitizedValue = value.toLowerCase().replaceAll(regex, ''); + // First, try to find exact enum name match (highest priority) + try { + return CoinSubClass.values.firstWhere((e) { + final enumName = e.toString().split('.').last.toLowerCase(); + return enumName == sanitizedValue; + }); + } catch (_) { + // If no exact match, continue with other matching strategies + } + + // Second, try to find exact ticker match + try { + return CoinSubClass.values.firstWhere((e) { + final tickerLower = e.ticker.toLowerCase(); + return tickerLower == sanitizedValue; + }); + } catch (_) { + // If no exact ticker match, continue with other matching strategies + } + return CoinSubClass.values.firstWhere((e) { - // Exit early if exact match to default to previous behavior and avoid - // unnecessary checks. - final matchesValue = e.toString().toLowerCase().contains(sanitizedValue); + // Check if enum name contains the value + final enumName = e.toString().split('.').last.toLowerCase(); + final matchesValue = enumName.contains(sanitizedValue); if (matchesValue) { return true; } - final matchesTicker = e.ticker.toLowerCase().contains(sanitizedValue); - if (matchesTicker) { - return true; - } - final matchesTokenStandardSuffix = e.tokenStandardSuffix?.toLowerCase().contains(sanitizedValue) ?? false; @@ -239,7 +254,7 @@ enum CoinSubClass { case CoinSubClass.matic: return 'Polygon'; case CoinSubClass.utxo: - return 'UTXO'; + return 'Native'; case CoinSubClass.smartBch: return 'SmartBCH'; case CoinSubClass.erc20: From ec40963cb023056dd71e82ba143d7bcbec863cdf Mon Sep 17 00:00:00 2001 From: CharlVS <77973576+CharlVS@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:10:31 +0200 Subject: [PATCH 2/2] fix(types): SMART_CHAIN ticker and robust parsing in CoinSubClass - smartChain.ticker -> SMART_CHAIN\n- Sanitize ticker for exact/partial matches\n- Restore partial ticker matching\n- Prefer utxo on exact "UTXO"\n- Catch StateError and deduplicate enum name extraction --- .../lib/src/coin_classes/coin_subclasses.dart | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart b/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart index e9ecc5d2..f97aea10 100644 --- a/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart +++ b/packages/komodo_defi_types/lib/src/coin_classes/coin_subclasses.dart @@ -30,6 +30,11 @@ enum CoinSubClass { zhtlc, unknown; + static String _enumNameLower(CoinSubClass e) { + // Normalize enum value to its lowercased name without the enum prefix + return e.toString().split('.').last.toLowerCase(); + } + // TODO: verify all the tickers. String get ticker { switch (this) { @@ -49,8 +54,9 @@ enum CoinSubClass { case CoinSubClass.avx20: return 'AVAX'; case CoinSubClass.utxo: - case CoinSubClass.smartChain: return 'UTXO'; + case CoinSubClass.smartChain: + return 'SMART_CHAIN'; case CoinSubClass.moonriver: return 'MOVR'; case CoinSubClass.ethereumClassic: @@ -144,7 +150,10 @@ enum CoinSubClass { /// Parse a string to a coin subclass. /// - /// Attempts to match the string to a coin subclass by: + /// Attempts to match the string to a coin subclass with the following + /// precedence: + /// - Exact enum name match (highest priority) + /// - Exact ticker match (with tie-breakers, e.g. 'UTXO' -> utxo) /// - Partial match to the subclass name /// - Partial match to the subclass ticker /// - Partial match to the subclass token standard suffix @@ -159,32 +168,47 @@ enum CoinSubClass { // First, try to find exact enum name match (highest priority) try { - return CoinSubClass.values.firstWhere((e) { - final enumName = e.toString().split('.').last.toLowerCase(); - return enumName == sanitizedValue; - }); - } catch (_) { + return CoinSubClass.values.firstWhere( + (e) => _enumNameLower(e) == sanitizedValue, + ); + // ignore: avoid_catching_errors + } on StateError { // If no exact match, continue with other matching strategies } - // Second, try to find exact ticker match - try { - return CoinSubClass.values.firstWhere((e) { - final tickerLower = e.ticker.toLowerCase(); - return tickerLower == sanitizedValue; - }); - } catch (_) { - // If no exact ticker match, continue with other matching strategies + // Second, try to find exact ticker match (sanitized) + final exactTickerMatches = CoinSubClass.values + .where( + (e) => e.ticker.toLowerCase().replaceAll(regex, '') == sanitizedValue, + ) + .toList(); + if (exactTickerMatches.isNotEmpty) { + // Tie-breaker for duplicated tickers. Both smartChain and utxo return + // 'UTXO' as ticker; prefer utxo to avoid mislabeling. + if (sanitizedValue == 'utxo') { + return CoinSubClass.utxo; + } + + return exactTickerMatches.first; } return CoinSubClass.values.firstWhere((e) { // Check if enum name contains the value - final enumName = e.toString().split('.').last.toLowerCase(); + final enumName = _enumNameLower(e); final matchesValue = enumName.contains(sanitizedValue); if (matchesValue) { return true; } + // Check if ticker contains the value (partial ticker match, sanitized) + final matchesTicker = e.ticker + .toLowerCase() + .replaceAll(regex, '') + .contains(sanitizedValue); + if (matchesTicker) { + return true; + } + final matchesTokenStandardSuffix = e.tokenStandardSuffix?.toLowerCase().contains(sanitizedValue) ?? false; @@ -199,7 +223,7 @@ enum CoinSubClass { static CoinSubClass? tryParse(String value) { try { return parse(value); - } catch (_) { + } on StateError { return null; } }