Repository: KomodoPlatform/komodo-wallet
Pull request #2579: feat: Rework unauthenticated assets list
Author: login: CharlVS
Labels: name: QA
description: Ready for QA Testing
Created: 2025-03-28T12:39:12Z | Merged: 2025-03-31T12:25:15Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Redesigned asset display elements now feature unified visuals for logos, titles, and details.
  - Enhanced wallet listings offer adaptive layouts for mobile and desktop, including expandable grouping for assets sharing similar tickers.
  - Improved chart dimensions provide clearer visualization of asset trends.

- **Chores**
  - Internal improvements streamline price update logic and update dependency configurations for enhanced performance and stability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/firebase.json b/firebase.json
index c2d711b738..3c3e731ab5 100644
--- a/firebase.json
+++ b/firebase.json
@@ -14,4 +14,4 @@
       }
     ]
   }
-}
+}
\ No newline at end of file
diff --git a/lib/bloc/coins_bloc/coins_bloc.dart b/lib/bloc/coins_bloc/coins_bloc.dart
index 3117433181..9625579fa1 100644
--- a/lib/bloc/coins_bloc/coins_bloc.dart
+++ b/lib/bloc/coins_bloc/coins_bloc.dart
@@ -3,6 +3,7 @@ import 'dart:async';
 import 'package:bloc/bloc.dart';
 import 'package:bloc_concurrency/bloc_concurrency.dart';
 import 'package:equatable/equatable.dart';
+import 'package:flutter/foundation.dart';
 import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
 import 'package:komodo_defi_types/komodo_defi_types.dart';
 import 'package:logging/logging.dart';
@@ -13,6 +14,7 @@ import 'package:web_dex/model/cex_price.dart';
 import 'package:web_dex/model/coin.dart';
 import 'package:web_dex/model/kdf_auth_metadata_extension.dart';
 import 'package:web_dex/model/wallet.dart';
+import 'package:web_dex/shared/utils/utils.dart';
 
 part 'coins_event.dart';
 part 'coins_state.dart';
@@ -277,43 +279,36 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
     CoinsPricesUpdated event,
     Emitter<CoinsState> emit,
   ) async {
-    bool changed = false;
     final prices = await _coinsRepo.fetchCurrentPrices();
-
     if (prices == null) {
       _log.severe('Coin prices list empty/null');
       return;
     }
-
-    final coins = Map<String, Coin>.of(state.coins);
-    for (final entry in state.coins.entries) {
-      final coin = entry.value;
-      final CexPrice? usdPrice =
-          prices[coin.id.symbol.configSymbol.toUpperCase()];
-
-      if (usdPrice != coin.usdPrice) {
-        changed = true;
-        // Create new coin instance with updated price
-        coins[entry.key] = coin.copyWith(usdPrice: usdPrice);
-      }
+    final didPricesChange = !mapEquals(state.prices, prices);
+    if (!didPricesChange) {
+      _log.info('Coin prices list unchanged');
+      return;
     }
 
-    if (changed) {
-      final newWalletCoins = state.walletCoins.map(
-        (String coinId, Coin coin) => MapEntry<String, Coin>(
-          coinId,
-          coin.copyWith(usdPrice: coins[coinId]!.usdPrice),
-        ),
-      );
-      emit(
-        state.copyWith(
-          coins: coins,
-          walletCoins: {...state.walletCoins, ...newWalletCoins},
-        ),
-      );
+    Map<String, Coin> updateCoinsWithPrices(Map<String, Coin> coins) {
+      final map = coins.map((key, coin) {
+        final price = prices[coin.id.id];
+        if (price != null) {
+          return MapEntry(key, coin.copyWith(usdPrice: price));
+        }
+        return MapEntry(key, coin);
+      });
+
+      return Map.of(map).unmodifiable();
     }
 
-    _log.info('Coin CEX prices updated');
+    emit(
+      state.copyWith(
+        prices: prices.unmodifiable(),
+        coins: updateCoinsWithPrices(state.coins),
+        walletCoins: updateCoinsWithPrices(state.walletCoins),
+      ),
+    );
   }
 
   Future<void> _onLogin(
@@ -560,3 +555,5 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
     }
   }
 }
+
+//
diff --git a/lib/bloc/coins_bloc/coins_state.dart b/lib/bloc/coins_bloc/coins_state.dart
index ae1e9d196e..95a2a34140 100644
--- a/lib/bloc/coins_bloc/coins_state.dart
+++ b/lib/bloc/coins_bloc/coins_state.dart
@@ -6,6 +6,7 @@ class CoinsState extends Equatable {
     required this.walletCoins,
     required this.loginActivationFinished,
     required this.pubkeys,
+    required this.prices,
   });
 
   factory CoinsState.initial() => const CoinsState(
@@ -13,22 +14,25 @@ class CoinsState extends Equatable {
         walletCoins: {},
         loginActivationFinished: false,
         pubkeys: {},
+        prices: {},
       );
 
   final Map<String, Coin> coins;
   final Map<String, Coin> walletCoins;
   final bool loginActivationFinished;
   final Map<String, AssetPubkeys> pubkeys;
+  final Map<String, CexPrice> prices;
 
   @override
   List<Object> get props =>
-      [coins, walletCoins, loginActivationFinished, pubkeys];
+      [coins, walletCoins, loginActivationFinished, pubkeys, prices];
 
   CoinsState copyWith({
     Map<String, Coin>? coins,
     Map<String, Coin>? walletCoins,
     bool? loginActivationFinished,
     Map<String, AssetPubkeys>? pubkeys,
+    Map<String, CexPrice>? prices,
   }) {
     return CoinsState(
       coins: coins ?? this.coins,
@@ -36,14 +40,38 @@ class CoinsState extends Equatable {
       loginActivationFinished:
           loginActivationFinished ?? this.loginActivationFinished,
       pubkeys: pubkeys ?? this.pubkeys,
+      prices: prices ?? this.prices,
     );
   }
 
-  // TODO! Migrate to SDK
+  /// Gets the price for a given asset ID
+  CexPrice? getPriceForAsset(AssetId assetId) {
+    return prices[assetId.symbol.configSymbol.toUpperCase()];
+  }
+
+  /// Gets the 24h price change percentage for a given asset ID
+  double? get24hChangeForAsset(AssetId assetId) {
+    return getPriceForAsset(assetId)?.change24h;
+  }
+
+  /// Calculates the USD price for a given amount of a coin
+  ///
+  /// [amount] The amount of the coin as a string
+  /// [coinAbbr] The abbreviation/symbol of the coin
+  ///
+  /// Returns null if:
+  /// - The coin is not found in the state
+  /// - The amount cannot be parsed to a double
+  /// - The coin does not have a USD price
+  ///
+  /// Note: This will be migrated to use the SDK's price functionality in the future.
+  /// See the MarketDataManager in the SDK for the new implementation.
+  @Deprecated('Use sdk.prices.fiatPrice(assetId) * amount instead')
   double? getUsdPriceByAmount(String amount, String coinAbbr) {
     final Coin? coin = coins[coinAbbr];
     final double? parsedAmount = double.tryParse(amount);
-    final double? usdPrice = coin?.usdPrice?.price;
+    final CexPrice? cexPrice = prices[coinAbbr.toUpperCase()];
+    final double? usdPrice = cexPrice?.price;
 
     if (coin == null || usdPrice == null || parsedAmount == null) {
       return null;
diff --git a/lib/main.dart b/lib/main.dart
index eec617899f..0ac2f26c12 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -11,7 +11,6 @@ import 'package:komodo_cex_market_data/komodo_cex_market_data.dart';
 import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
 import 'package:path/path.dart' as p;
 import 'package:path_provider/path_provider.dart';
-import 'package:universal_html/html.dart' as html;
 import 'package:web_dex/app_config/app_config.dart';
 import 'package:web_dex/app_config/package_information.dart';
 import 'package:web_dex/bloc/app_bloc_observer.dart';
@@ -121,8 +120,7 @@ PerformanceMode? _getPerformanceModeFromUrl() {
       : null;
 
   if (kIsWeb) {
-    final url = html.window.location.href;
-    final uri = Uri.parse(url);
+    final uri = Uri.base;
     maybeEnvPerformanceMode =
         uri.queryParameters['demo_mode_performance'] ?? maybeEnvPerformanceMode;
   }
diff --git a/lib/model/cex_price.dart b/lib/model/cex_price.dart
index 84d4b893b2..ee2e6f9540 100644
--- a/lib/model/cex_price.dart
+++ b/lib/model/cex_price.dart
@@ -1,6 +1,19 @@
-import 'package:collection/collection.dart';
 import 'package:equatable/equatable.dart';
 
+enum CexDataProvider {
+  binance,
+  coingecko,
+  coinpaprika,
+  nomics,
+  unknown,
+}
+
+CexDataProvider cexDataProvider(String string) {
+  return CexDataProvider.values.firstWhere(
+      (e) => e.toString().split('.').last == string,
+      orElse: () => CexDataProvider.unknown);
+}
+
 class CexPrice extends Equatable {
   const CexPrice({
     required this.ticker,
@@ -67,17 +80,3 @@ class CexPrice extends Equatable {
         changeProvider,
       ];
 }
-
-enum CexDataProvider {
-  binance,
-  coingecko,
-  coinpaprika,
-  nomics,
-  unknown,
-}
-
-CexDataProvider cexDataProvider(String string) {
-  return CexDataProvider.values
-          .firstWhereOrNull((e) => e.toString().split('.').last == string) ??
-      CexDataProvider.unknown;
-}
diff --git a/lib/shared/utils/extensions/collection_extensions.dart b/lib/shared/utils/extensions/collection_extensions.dart
new file mode 100644
index 0000000000..c7c0af8b58
--- /dev/null
+++ b/lib/shared/utils/extensions/collection_extensions.dart
@@ -0,0 +1,37 @@
+import 'dart:collection';
+
+/// Extension to make Lists unmodifiable.
+extension UnmodifiableListExtension<E> on Iterable<E> {
+  /// Returns an unmodifiable view of this iterable.
+  ///
+  /// NB! This references the original iterable, so if the original iterable is
+  /// modified, the unmodifiable view will reflect those changes.
+  ///
+  /// This is useful for preventing modifications to the list while still
+  /// allowing read access.
+  ///
+  /// This won't protect against modifications to the elements of the iterable
+  /// if they are mutable.
+  Iterable<E> unmodifiable() => UnmodifiableListView<E>(this);
+}
+
+/// Extension to make Maps unmodifiable.
+///
+/// NB! This references the original map, so if the original map is
+/// modified, the unmodifiable view will reflect those changes.
+///
+/// This is useful for preventing modifications to the map while still
+/// allowing read access.
+///
+/// This won't protect against modifications to the elements of the map
+/// if they are mutable.
+extension UnmodifiableMapExtension<K, V> on Map<K, V> {
+  /// Returns an unmodifiable view of this map.
+  ///
+  /// This is useful for preventing modifications to the map while still
+  /// allowing read access.
+  ///
+  /// This won't protect against modifications to the elements of the map
+  /// if they are mutable.
+  Map<K, V> unmodifiable() => UnmodifiableMapView<K, V>(this);
+}
diff --git a/lib/shared/utils/utils.dart b/lib/shared/utils/utils.dart
index b23371277d..f8e1b4f367 100644
--- a/lib/shared/utils/utils.dart
+++ b/lib/shared/utils/utils.dart
@@ -17,10 +17,12 @@ import 'package:web_dex/model/wallet.dart';
 import 'package:web_dex/performance_analytics/performance_analytics.dart';
 import 'package:web_dex/services/logger/get_logger.dart';
 import 'package:web_dex/shared/constants.dart';
+
 export 'package:web_dex/shared/utils/extensions/async_extensions.dart';
-export 'package:web_dex/shared/utils/prominent_colors.dart';
-export 'package:web_dex/shared/utils/extensions/sdk_extensions.dart';
+export 'package:web_dex/shared/utils/extensions/collection_extensions.dart';
 export 'package:web_dex/shared/utils/extensions/legacy_coin_migration_extensions.dart';
+export 'package:web_dex/shared/utils/extensions/sdk_extensions.dart';
+export 'package:web_dex/shared/utils/prominent_colors.dart';
 
 void copyToClipBoard(BuildContext context, String str) {
   final themeData = Theme.of(context);
diff --git a/lib/shared/widgets/asset_item/asset_item.dart b/lib/shared/widgets/asset_item/asset_item.dart
new file mode 100644
index 0000000000..85b60a5560
--- /dev/null
+++ b/lib/shared/widgets/asset_item/asset_item.dart
@@ -0,0 +1,46 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:komodo_ui/komodo_ui.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_body.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+
+/// A widget that displays an asset with its logo and information.
+///
+/// This replaces the previous CoinItem and works with AssetId instead of Coin.
+class AssetItem extends StatelessWidget {
+  const AssetItem({
+    super.key,
+    required this.assetId,
+    this.amount,
+    this.size = AssetItemSize.medium,
+    this.subtitleText,
+  });
+
+  final AssetId? assetId;
+  final double? amount;
+  final AssetItemSize size;
+  final String? subtitleText;
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisSize: MainAxisSize.min,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        AssetIcon(
+          assetId,
+          size: size.assetLogo,
+        ),
+        SizedBox(width: 8),
+        Flexible(
+          child: AssetItemBody(
+            assetId: assetId,
+            amount: amount,
+            size: size,
+            subtitleText: subtitleText,
+          ),
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/shared/widgets/asset_item/asset_item_body.dart b/lib/shared/widgets/asset_item/asset_item_body.dart
new file mode 100644
index 0000000000..9666502d00
--- /dev/null
+++ b/lib/shared/widgets/asset_item/asset_item_body.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_subtitle.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_title.dart';
+
+/// A widget that displays an asset's title and subtitle information.
+///
+/// This replaces the previous CoinItemBody component and works with AssetId instead of Coin.
+class AssetItemBody extends StatelessWidget {
+  const AssetItemBody({
+    super.key,
+    required this.assetId,
+    this.amount,
+    this.size = AssetItemSize.medium,
+    this.subtitleText,
+  });
+
+  final AssetId? assetId;
+  final double? amount;
+  final AssetItemSize size;
+  final String? subtitleText;
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        SizedBox(height: size.verticalSpacing),
+        AssetItemTitle(assetId: assetId, size: size, amount: amount),
+        SizedBox(height: size.verticalSpacing),
+        AssetItemSubtitle(
+          assetId: assetId,
+          size: size,
+          amount: amount,
+          text: subtitleText,
+        ),
+      ],
+    );
+  }
+}
diff --git a/lib/shared/widgets/asset_item/asset_item_size.dart b/lib/shared/widgets/asset_item/asset_item_size.dart
new file mode 100644
index 0000000000..3064ff0dbe
--- /dev/null
+++ b/lib/shared/widgets/asset_item/asset_item_size.dart
@@ -0,0 +1,45 @@
+/// Defines size configurations for an AssetItem component.
+class AssetItemSize {
+  const AssetItemSize({
+    required this.assetLogo,
+    required this.title,
+    required this.subtitle,
+    required this.verticalSpacing,
+  });
+
+  /// Size preset for a large asset item
+  static const AssetItemSize large = AssetItemSize(
+    assetLogo: 34.0,
+    title: 14.0,
+    subtitle: 12.0,
+    verticalSpacing: 6,
+  );
+
+  /// Size preset for a medium asset item
+  static const AssetItemSize medium = AssetItemSize(
+    assetLogo: 30.0,
+    title: 13.0,
+    subtitle: 11.0,
+    verticalSpacing: 3.0,
+  );
+
+  /// Size preset for a small asset item
+  static const AssetItemSize small = AssetItemSize(
+    assetLogo: 26.0,
+    title: 11.0,
+    subtitle: 10.0,
+    verticalSpacing: 3.0,
+  );
+
+  /// Size of the asset logo
+  final double assetLogo;
+
+  /// Text size for the title
+  final double title;
+
+  /// Text size for the subtitle
+  final double subtitle;
+
+  /// Standard spacing between elements
+  final double verticalSpacing;
+}
diff --git a/lib/shared/widgets/asset_item/asset_item_subtitle.dart b/lib/shared/widgets/asset_item/asset_item_subtitle.dart
new file mode 100644
index 0000000000..5acb92ab30
--- /dev/null
+++ b/lib/shared/widgets/asset_item/asset_item_subtitle.dart
@@ -0,0 +1,36 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+
+/// A widget that displays an asset's subtitle, typically showing the ticker symbol.
+///
+/// This replaces the previous CoinItemSubtitle component and works with AssetId instead of Coin.
+class AssetItemSubtitle extends StatelessWidget {
+  const AssetItemSubtitle({
+    super.key,
+    required this.assetId,
+    required this.size,
+    this.amount,
+    this.text,
+  });
+
+  final AssetId? assetId;
+  final AssetItemSize size;
+  final double? amount;
+  final String? text;
+
+  @override
+  Widget build(BuildContext context) {
+    final String subtitleText =
+        text ?? (assetId != null ? assetId!.symbol.configSymbol : 'Unknown');
+
+    return Text(
+      subtitleText,
+      style: TextStyle(
+        fontSize: size.subtitle,
+        height: 1,
+        color: Theme.of(context).colorScheme.onSurfaceVariant,
+      ),
+    );
+  }
+}
diff --git a/lib/shared/widgets/asset_item/asset_item_title.dart b/lib/shared/widgets/asset_item/asset_item_title.dart
new file mode 100644
index 0000000000..b4417f154b
--- /dev/null
+++ b/lib/shared/widgets/asset_item/asset_item_title.dart
@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+
+/// A widget that displays an asset's title, typically the asset name.
+///
+/// This replaces the previous CoinItemTitle component and works with AssetId instead of Coin.
+class AssetItemTitle extends StatelessWidget {
+  const AssetItemTitle({
+    super.key,
+    required this.assetId,
+    required this.size,
+    this.amount,
+  });
+
+  final AssetId? assetId;
+  final AssetItemSize size;
+  final double? amount;
+
+  @override
+  Widget build(BuildContext context) {
+    return DefaultTextStyle.merge(
+      style: Theme.of(context).textTheme.titleMedium?.copyWith(
+            fontSize: size.title,
+            height: 1,
+          ),
+      child: Text(assetId?.name ?? 'Unknown Asset'),
+    );
+  }
+}
diff --git a/lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart b/lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart
index 68d987a24b..500e5b55e7 100644
--- a/lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart
+++ b/lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart
@@ -23,9 +23,9 @@ class CoinSparkline extends StatelessWidget {
           return const SizedBox.shrink();
         } else {
           return LimitedBox(
-            maxWidth: 120,
+            maxWidth: 130,
             child: SizedBox(
-              height: 24,
+              height: 35,
               child: SparklineChart(
                 data: snapshot.data!,
                 positiveLineColor: Colors.green,
diff --git a/lib/views/wallet/wallet_page/common/asset_list_item.dart b/lib/views/wallet/wallet_page/common/asset_list_item.dart
new file mode 100644
index 0000000000..1a5a4c9d7d
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/asset_list_item.dart
@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/common/screen.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/asset_list_item_desktop.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/asset_list_item_mobile.dart';
+
+/// A widget that displays an asset in a list item format with different layouts for mobile and desktop.
+///
+/// This replaces the previous CoinListItem component and works with AssetId instead of Coin.
+class AssetListItem extends StatelessWidget {
+  const AssetListItem({
+    super.key,
+    required this.assetId,
+    required this.backgroundColor,
+    required this.onTap,
+    this.isActivating = false,
+  });
+
+  final AssetId assetId;
+  final Color backgroundColor;
+  final void Function(AssetId) onTap;
+  final bool isActivating;
+
+  @override
+  Widget build(BuildContext context) {
+    return Opacity(opacity: isActivating ? 0.3 : 1, child: _buildItem());
+  }
+
+  Widget _buildItem() {
+    return isMobile
+        ? AssetListItemMobile(
+            assetId: assetId,
+            backgroundColor: backgroundColor,
+            onTap: onTap,
+          )
+        : AssetListItemDesktop(
+            assetId: assetId,
+            backgroundColor: backgroundColor,
+            onTap: onTap,
+          );
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart b/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart
new file mode 100644
index 0000000000..d08b1a5fd0
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart
@@ -0,0 +1,80 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:komodo_ui/komodo_ui.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
+
+/// A widget that displays an asset in a list item format optimized for desktop devices.
+///
+/// This replaces the previous CoinListItemDesktop component and works with AssetId instead of Coin.
+class AssetListItemDesktop extends StatelessWidget {
+  const AssetListItemDesktop({
+    super.key,
+    required this.assetId,
+    required this.backgroundColor,
+    required this.onTap,
+    this.priceChangePercentage24h,
+  });
+
+  final AssetId assetId;
+  final Color backgroundColor;
+  final void Function(AssetId) onTap;
+
+  /// The 24-hour price change percentage for the asset
+  final double? priceChangePercentage24h;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(10),
+      ),
+      clipBehavior: Clip.antiAlias,
+      child: Material(
+        color: backgroundColor,
+        child: InkWell(
+          onTap: () => onTap(assetId),
+          child: Container(
+            padding: const EdgeInsets.symmetric(
+              horizontal: 16.0,
+              vertical: 16,
+            ),
+            child: Row(
+              children: [
+                Expanded(
+                  child: Container(
+                    constraints: const BoxConstraints(
+                      maxWidth: 200,
+                    ),
+                    alignment: Alignment.centerLeft,
+                    child: AssetItem(
+                      assetId: assetId,
+                      size: AssetItemSize.large,
+                    ),
+                  ),
+                ),
+                // Spacer(),
+                Expanded(
+                  child: Padding(
+                    padding: const EdgeInsets.symmetric(horizontal: 12.0),
+                    child: TrendPercentageText(
+                      percentage: priceChangePercentage24h ?? 0,
+                      showIcon: true,
+                      iconSize: 16,
+                      precision: 2,
+                    ),
+                  ),
+                ),
+                Expanded(
+                  flex: 2,
+                  child: CoinSparkline(coinId: assetId.id),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/asset_list_item_mobile.dart b/lib/views/wallet/wallet_page/common/asset_list_item_mobile.dart
new file mode 100644
index 0000000000..2981e12cb2
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/asset_list_item_mobile.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+
+/// A widget that displays an asset in a list item format optimized for mobile devices.
+///
+/// This replaces the previous CoinListItemMobile component and works with AssetId instead of Coin.
+class AssetListItemMobile extends StatelessWidget {
+  const AssetListItemMobile({
+    super.key,
+    required this.assetId,
+    required this.backgroundColor,
+    required this.onTap,
+  });
+
+  final AssetId assetId;
+  final Color backgroundColor;
+  final void Function(AssetId) onTap;
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: () => onTap(assetId),
+      child: Container(
+        color: backgroundColor,
+        padding: const EdgeInsets.symmetric(
+          horizontal: 16.0,
+          vertical: 8.0,
+        ),
+        child: Row(
+          children: [
+            Expanded(
+              child: AssetItem(
+                assetId: assetId,
+                size: AssetItemSize.medium,
+              ),
+            ),
+            const Icon(Icons.chevron_right),
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/assets_list.dart b/lib/views/wallet/wallet_page/common/assets_list.dart
new file mode 100644
index 0000000000..3d6b394285
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/assets_list.dart
@@ -0,0 +1,114 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/asset_list_item.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart';
+
+/// A widget that displays a list of assets.
+///
+/// This replaces the previous AllCoinsList component and works with AssetId instead of Coin.
+class AssetsList extends StatelessWidget {
+  const AssetsList({
+    super.key,
+    required this.assets,
+    required this.onAssetItemTap,
+    this.withBalance = false,
+    this.searchPhrase = '',
+    this.useGroupedView = false,
+    this.priceChangePercentages = const {},
+  });
+
+  final List<AssetId> assets;
+  final Function(AssetId) onAssetItemTap;
+  final bool withBalance;
+  final String searchPhrase;
+  final bool useGroupedView;
+  final Map<String, double> priceChangePercentages;
+
+  @override
+  Widget build(BuildContext context) {
+    if (useGroupedView) {
+      return _buildGroupedView(context);
+    }
+    return _buildFlatView(context);
+  }
+
+  Widget _buildFlatView(BuildContext context) {
+    final filteredAssets = _filterAssets();
+
+    return SliverList(
+      delegate: SliverChildBuilderDelegate(
+        (BuildContext context, int index) {
+          final asset = filteredAssets[index];
+          final Color backgroundColor = index.isEven
+              ? Theme.of(context).colorScheme.surface
+              : Theme.of(context).colorScheme.onSurface;
+
+          return AssetListItem(
+            assetId: asset,
+            backgroundColor: backgroundColor,
+            onTap: onAssetItemTap,
+          );
+        },
+        childCount: filteredAssets.length,
+      ),
+    );
+  }
+
+  Widget _buildGroupedView(BuildContext context) {
+    final groupedAssets = _groupAssetsByTicker();
+    final groups = groupedAssets.entries.toList();
+
+    return SliverList.separated(
+      itemBuilder: (BuildContext context, int index) {
+        final group = groups[index];
+        final Color backgroundColor = index.isEven
+            ? Theme.of(context).colorScheme.surface
+            : Theme.of(context).colorScheme.onSurface;
+
+        return GroupedAssetTickerItem(
+          key: Key(group.key),
+          assets: group.value,
+          backgroundColor: backgroundColor,
+          onTap: onAssetItemTap,
+        );
+      },
+      separatorBuilder: (BuildContext context, int index) {
+        return const SizedBox(height: 4);
+      },
+      itemCount: groups.length,
+    );
+  }
+
+  /// Groups assets by their ticker symbol
+  Map<String, List<AssetId>> _groupAssetsByTicker() {
+    final filteredAssets = _filterAssets();
+    final groupedAssets = <String, List<AssetId>>{};
+
+    for (final asset in filteredAssets) {
+      final symbol = asset.symbol.configSymbol;
+      groupedAssets.putIfAbsent(symbol, () => []).add(asset);
+    }
+
+    return Map.fromEntries(
+      groupedAssets.entries.toList()..sort((a, b) => a.key.compareTo(b.key)),
+    );
+  }
+
+  /// Filters assets based on search phrase
+  List<AssetId> _filterAssets() {
+    if (searchPhrase.isEmpty) {
+      return assets;
+    }
+
+    return assets.where((asset) {
+      final name = asset.name.toLowerCase();
+      final symbol = asset.symbol.configSymbol.toLowerCase();
+      final id = asset.id.toLowerCase();
+      final searchLower = searchPhrase.toLowerCase();
+
+      return name.contains(searchLower) ||
+          symbol.contains(searchLower) ||
+          id.contains(searchLower);
+    }).toList();
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart b/lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart
index 393679caeb..738ce7aff3 100644
--- a/lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart
+++ b/lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart
@@ -7,8 +7,6 @@ import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
 import 'package:web_dex/generated/codegen_loader.g.dart';
 import 'package:web_dex/model/coin.dart';
 import 'package:web_dex/shared/ui/ui_simple_border_button.dart';
-import 'package:web_dex/shared/widgets/coin_balance.dart';
-import 'package:web_dex/shared/widgets/coin_fiat_change.dart';
 import 'package:web_dex/shared/widgets/coin_fiat_price.dart';
 import 'package:web_dex/shared/widgets/coin_item/coin_item.dart';
 import 'package:web_dex/shared/widgets/coin_item/coin_item_size.dart';
@@ -17,11 +15,11 @@ import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_
 
 class CoinListItemDesktop extends StatelessWidget {
   const CoinListItemDesktop({
-    Key? key,
+    super.key,
     required this.coin,
     required this.backgroundColor,
     required this.onTap,
-  }) : super(key: key);
+  });
 
   final Coin coin;
   final Color backgroundColor;
@@ -36,7 +34,7 @@ class CoinListItemDesktop extends StatelessWidget {
         color: backgroundColor,
       ),
       child: Material(
-        type: MaterialType.transparency,
+        // type: MaterialType.transparency,
         child: InkWell(
           borderRadius: BorderRadius.circular(10),
           hoverColor: theme.custom.zebraHoverColor,
@@ -44,7 +42,7 @@ class CoinListItemDesktop extends StatelessWidget {
           child: Container(
             padding: const EdgeInsets.fromLTRB(0, 10, 16, 10),
             child: Row(
-              key: Key('active-coin-item-${(coin.abbr).toLowerCase()}'),
+              key: Key('coin-item-${(coin.abbr).toLowerCase()}'),
               children: [
                 Expanded(
                   flex: 5,
@@ -73,28 +71,6 @@ class CoinListItemDesktop extends StatelessWidget {
                     ],
                   ),
                 ),
-                Expanded(
-                  flex: 5,
-                  child: coin.isSuspended
-                      ? _SuspendedMessage(
-                          key: Key('suspended-asset-message-${coin.abbr}'),
-                          coin: coin,
-                          isReEnabling: coin.isActivating,
-                        )
-                      : CoinBalance(
-                          key: Key('balance-asset-${coin.abbr}'),
-                          coin: coin,
-                        ),
-                ),
-                Expanded(
-                  flex: 2,
-                  child: coin.isSuspended
-                      ? const SizedBox.shrink()
-                      : CoinFiatChange(
-                          coin,
-                          style: const TextStyle(fontSize: _fontSize),
-                        ),
-                ),
                 Expanded(
                   flex: 2,
                   child: coin.isSuspended
@@ -106,8 +82,7 @@ class CoinListItemDesktop extends StatelessWidget {
                 ),
                 Expanded(
                   flex: 2,
-                  child:
-                      CoinSparkline(coinId: coin.abbr), // Using CoinSparkline
+                  child: CoinSparkline(coinId: coin.abbr),
                 ),
               ],
             ),
@@ -120,7 +95,6 @@ class CoinListItemDesktop extends StatelessWidget {
 
 class _SuspendedMessage extends StatelessWidget {
   const _SuspendedMessage({
-    super.key,
     required this.coin,
     required this.isReEnabling,
   });
diff --git a/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart b/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart
new file mode 100644
index 0000000000..5f84883ada
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart
@@ -0,0 +1,285 @@
+import 'dart:math' show min;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:komodo_ui/komodo_ui.dart';
+import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
+import 'package:web_dex/common/screen.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
+
+/// A widget that displays a group of assets sharing the same ticker symbol.
+///
+/// Shows the same layout as AssetListItemDesktop but adds an expansion button
+/// to show related assets that share the same ticker symbol.
+class GroupedAssetTickerItem extends StatefulWidget {
+  const GroupedAssetTickerItem({
+    super.key,
+    required this.assets,
+    required this.backgroundColor,
+    required this.onTap,
+    this.expanded,
+    this.isActivating = false,
+  });
+
+  final List<AssetId> assets;
+  final Color backgroundColor;
+  final void Function(AssetId)? onTap;
+  final bool? expanded;
+  final bool isActivating;
+
+  @override
+  State<GroupedAssetTickerItem> createState() => _GroupedAssetTickerItemState();
+}
+
+class _GroupedAssetTickerItemState extends State<GroupedAssetTickerItem> {
+  late bool _isExpanded;
+
+  @override
+  void initState() {
+    super.initState();
+    _isExpanded = widget.expanded ?? false;
+  }
+
+  @override
+  void didUpdateWidget(GroupedAssetTickerItem oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (widget.expanded != null && widget.expanded != oldWidget.expanded) {
+      setState(() {
+        _isExpanded = widget.expanded!;
+      });
+    }
+  }
+
+  void _toggleExpanded() {
+    setState(() {
+      _isExpanded = !_isExpanded;
+    });
+  }
+
+  AssetId get _primaryAsset => widget.assets.first;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+    return Opacity(
+      opacity: widget.isActivating ? 0.3 : 1,
+      child: Material(
+        color: widget.backgroundColor,
+        borderRadius: BorderRadius.circular(10),
+        clipBehavior: Clip.hardEdge,
+        type: MaterialType.card,
+        child: InkWell(
+          onTap:
+              widget.onTap == null ? null : () => widget.onTap!(_primaryAsset),
+          child: Column(
+            children: [
+              Container(
+                padding: const EdgeInsets.symmetric(
+                  horizontal: 16.0,
+                  vertical: 16,
+                ),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    Expanded(
+                      flex: 3,
+                      child: AssetItem(
+                        assetId: _primaryAsset,
+                        size: AssetItemSize.large,
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: BlocBuilder<CoinsBloc, CoinsState>(
+                        builder: (context, state) {
+                          final price = state.getPriceForAsset(_primaryAsset);
+                          final formattedPrice = price?.price != null
+                              ? NumberFormat.currency(
+                                  symbol: '\$',
+                                  decimalDigits: 2,
+                                ).format(price!.price)
+                              : '';
+                          return Text(
+                            formattedPrice,
+                            style: theme.textTheme.bodyMedium,
+                            overflow: TextOverflow.ellipsis,
+                          );
+                        },
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: BlocBuilder<CoinsBloc, CoinsState>(
+                        builder: (context, state) {
+                          return TrendPercentageText(
+                            textStyle: theme.textTheme.bodyMedium?.copyWith(
+                              fontWeight: FontWeight.w500,
+                            ),
+                            percentage:
+                                state.get24hChangeForAsset(_primaryAsset) ?? 0,
+                            showIcon: true,
+                            iconSize: 16,
+                            precision: 2,
+                          );
+                        },
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: ConstrainedBox(
+                        constraints: const BoxConstraints(
+                          maxWidth: 130,
+                          maxHeight: 35,
+                        ),
+                        child: CoinSparkline(coinId: _primaryAsset.id),
+                      ),
+                    ),
+                    ConstrainedBox(
+                      constraints: const BoxConstraints(minWidth: 48),
+                      child: !(widget.assets.length > 1)
+                          ? null
+                          : IconButton(
+                              iconSize: 32,
+                              icon: Icon(
+                                _isExpanded
+                                    ? Icons.expand_less
+                                    : Icons.expand_more,
+                                color: theme.colorScheme.secondary,
+                              ),
+                              onPressed: _toggleExpanded,
+                              tooltip: _isExpanded
+                                  ? 'Hide related assets'
+                                  : 'Show related assets',
+                            ),
+                    ),
+                  ],
+                ),
+              ),
+              if (_isExpanded)
+                _ExpandedView(
+                    assets: widget.assets, theme: theme, onTap: widget.onTap),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class _ExpandedView extends StatelessWidget {
+  const _ExpandedView({
+    required this.assets,
+    required this.theme,
+    required this.onTap,
+  });
+
+  final List<AssetId> assets;
+  final ThemeData theme;
+  final void Function(AssetId)? onTap;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      padding: const EdgeInsets.only(
+        left: 16.0,
+        right: 16.0,
+        bottom: 16.0,
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          const Divider(),
+          Padding(
+            padding: const EdgeInsets.only(bottom: 8.0, top: 8.0),
+            child: Text(
+              'Available on Networks:',
+              style: theme.textTheme.bodySmall?.copyWith(
+                color: theme.colorScheme.secondary,
+              ),
+            ),
+          ),
+          _AssetIconsRow(assets: assets, onTap: onTap),
+        ],
+      ),
+    );
+  }
+}
+
+class _AssetIconsRow extends StatelessWidget {
+  const _AssetIconsRow({
+    required this.assets,
+    required this.onTap,
+  });
+
+  final List<AssetId> assets;
+  final void Function(AssetId)? onTap;
+
+  @override
+  Widget build(BuildContext context) {
+    final relatedAssets = assets.length > 1 ? assets.toList() : assets;
+
+    return Wrap(
+      spacing: 12.0,
+      runSpacing: 12.0,
+      children: relatedAssets.map((asset) {
+        return _AssetIconItem(
+          asset: asset,
+          onTap: onTap,
+        );
+      }).toList(),
+    );
+  }
+}
+
+class _AssetIconItem extends StatelessWidget {
+  const _AssetIconItem({
+    required this.asset,
+    required this.onTap,
+  });
+
+  final AssetId asset;
+  final void Function(AssetId)? onTap;
+
+  @override
+  Widget build(BuildContext context) {
+    final size = isMobile ? 50.0 : 70.0;
+    final theme = Theme.of(context);
+
+    return Tooltip(
+      message: asset.id,
+      child: InkWell(
+        onTap: onTap == null ? null : () => onTap!(asset),
+        borderRadius: BorderRadius.circular(8.0),
+        child: Container(
+          height: size,
+          constraints: BoxConstraints(
+            minWidth: size,
+          ),
+          child: Padding(
+            padding: const EdgeInsets.symmetric(horizontal: 8.0),
+            child: Column(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                AssetIcon.ofTicker(
+                  asset.subClass.iconTicker,
+                  size: min(36, size * 0.6),
+                ),
+                const SizedBox(height: 2),
+                Text(
+                  asset.subClass.formatted,
+                  style: theme.textTheme.labelSmall,
+                  maxLines: 1,
+                  overflow: TextOverflow.ellipsis,
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/grouped_assets_list.dart b/lib/views/wallet/wallet_page/common/grouped_assets_list.dart
new file mode 100644
index 0000000000..ba2de7b140
--- /dev/null
+++ b/lib/views/wallet/wallet_page/common/grouped_assets_list.dart
@@ -0,0 +1,81 @@
+import 'package:flutter/material.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart';
+
+/// A widget that displays a list of assets grouped by their ticker symbols.
+///
+/// This is an alternative to the [AssetsList] component that groups assets
+/// with the same ticker symbol together and allows expanding to see all
+/// related assets.
+class GroupedAssetsList extends StatelessWidget {
+  const GroupedAssetsList({
+    super.key,
+    required this.assets,
+    required this.onAssetItemTap,
+    this.searchPhrase = '',
+  });
+
+  /// The complete list of assets to display
+  final List<AssetId> assets;
+
+  /// Callback function when an asset is tapped
+  final Function(AssetId) onAssetItemTap;
+
+  /// Optional search phrase to filter assets
+  final String searchPhrase;
+
+  @override
+  Widget build(BuildContext context) {
+    final groupedAssets = _groupAssetsByTicker();
+
+    return SliverList.separated(
+      separatorBuilder: (BuildContext context, int index) {
+        return const SizedBox(height: 8);
+      },
+      itemBuilder: (BuildContext context, int index) {
+        final ticker = groupedAssets.keys.elementAt(index);
+        final assetGroup = groupedAssets[ticker]!;
+
+        final Color backgroundColor = index.isEven
+            ? Theme.of(context).colorScheme.surface
+            : Theme.of(context).colorScheme.onSurface;
+
+        return GroupedAssetTickerItem(
+          key: Key(ticker),
+          assets: assetGroup,
+          backgroundColor: backgroundColor,
+          onTap: onAssetItemTap,
+        );
+      },
+      itemCount: groupedAssets.length,
+    );
+  }
+
+  /// Groups assets by their ticker symbol
+  Map<String, List<AssetId>> _groupAssetsByTicker() {
+    final filteredAssets = _filterAssets();
+    final groupedAssets = <String, List<AssetId>>{};
+
+    for (final asset in filteredAssets) {
+      final ticker = asset.symbol.configSymbol;
+      groupedAssets.putIfAbsent(ticker, () => []).add(asset);
+    }
+
+    return groupedAssets;
+  }
+
+  /// Filters assets based on search phrase
+  List<AssetId> _filterAssets() {
+    if (searchPhrase.isEmpty) {
+      return assets;
+    }
+
+    return assets.where((asset) {
+      final name = asset.name.toLowerCase();
+      final symbol = asset.symbol.configSymbol.toLowerCase();
+      final searchLower = searchPhrase.toLowerCase();
+
+      return name.contains(searchLower) || symbol.contains(searchLower);
+    }).toList();
+  }
+}
diff --git a/lib/views/wallet/wallet_page/common/wallet_coins_list.dart b/lib/views/wallet/wallet_page/common/wallet_coins_list.dart
index 51fe67c738..b01780b633 100644
--- a/lib/views/wallet/wallet_page/common/wallet_coins_list.dart
+++ b/lib/views/wallet/wallet_page/common/wallet_coins_list.dart
@@ -1,36 +1,35 @@
 import 'package:flutter/material.dart';
-import 'package:web_dex/model/coin.dart';
-import 'package:web_dex/views/wallet/wallet_page/common/coin_list_item.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/asset_list_item.dart';
 
-class WalletCoinsList extends StatelessWidget {
-  const WalletCoinsList({
-    Key? key,
-    required this.coins,
-    required this.onCoinItemTap,
-  }) : super(key: key);
+class KnownAssetsList extends StatelessWidget {
+  const KnownAssetsList({
+    super.key,
+    required this.assets,
+    required this.onAssetItemTap,
+  });
 
-  final List<Coin> coins;
-  final Function(Coin) onCoinItemTap;
+  final List<AssetId> assets;
+  final void Function(AssetId) onAssetItemTap;
 
   @override
   Widget build(BuildContext context) {
-    return SliverList(
-        key: const Key('wallet-page-coins-list'),
-        delegate: SliverChildBuilderDelegate(
-          (BuildContext context, int index) {
-            final Coin coin = coins[index];
-            final bool isEven = (index + 1) % 2 == 0;
-            final Color backgroundColor = isEven
-                ? Theme.of(context).colorScheme.surface
-                : Theme.of(context).colorScheme.onSurface;
-            return CoinListItem(
-              key: Key('wallet-coin-list-item-${coin.abbr.toLowerCase()}'),
-              coin: coin,
-              backgroundColor: backgroundColor,
-              onTap: onCoinItemTap,
-            );
-          },
-          childCount: coins.length,
-        ));
+    return SliverList.separated(
+      key: const Key('wallet-page-coins-list'),
+      itemBuilder: (BuildContext context, int index) {
+        final asset = assets[index];
+        final Color backgroundColor = index.isEven
+            ? Theme.of(context).colorScheme.surface
+            : Theme.of(context).colorScheme.onSurface;
+        return AssetListItem(
+            assetId: asset,
+            backgroundColor: backgroundColor,
+            onTap: (assetId) => onAssetItemTap(assetId));
+      },
+      separatorBuilder: (BuildContext context, int index) {
+        return const SizedBox(height: 8);
+      },
+      itemCount: assets.length,
+    );
   }
 }
diff --git a/lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart b/lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart
index df8a1079cd..01ddd3d95a 100644
--- a/lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart
+++ b/lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart
@@ -9,14 +9,14 @@ import 'package:web_dex/views/wallet/wallet_page/common/wallet_coins_list.dart';
 
 class AllCoinsList extends StatefulWidget {
   const AllCoinsList({
-    Key? key,
+    super.key,
     required this.searchPhrase,
     required this.withBalance,
-    required this.onCoinItemTap,
-  }) : super(key: key);
+    required this.onCoinSelected,
+  });
   final String searchPhrase;
   final bool withBalance;
-  final Function(Coin) onCoinItemTap;
+  final Function(Coin) onCoinSelected;
 
   @override
   _AllCoinsListState createState() => _AllCoinsListState();
@@ -74,9 +74,13 @@ class _AllCoinsListState extends State<AllCoinsList> {
                       ),
                     ),
                   )
-                : WalletCoinsList(
-                    coins: displayedCoins,
-                    onCoinItemTap: widget.onCoinItemTap,
+                : KnownAssetsList(
+                    assets: displayedCoins.map((c) => c.id).toList(),
+                    onAssetItemTap: (id) {
+                      final coin =
+                          displayedCoins.firstWhere((coin) => coin.id == id);
+                      widget.onCoinSelected(coin);
+                    },
                   );
       },
     );
diff --git a/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart b/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart
index 9143536053..85395b6d32 100644
--- a/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart
+++ b/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart
@@ -30,8 +30,8 @@ import 'package:web_dex/views/common/pages/page_layout.dart';
 import 'package:web_dex/views/dex/dex_helpers.dart';
 import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/animated_portfolio_charts.dart';
 import 'package:web_dex/views/wallet/wallet_page/charts/coin_prices_chart.dart';
+import 'package:web_dex/views/wallet/wallet_page/common/assets_list.dart';
 import 'package:web_dex/views/wallet/wallet_page/wallet_main/active_coins_list.dart';
-import 'package:web_dex/views/wallet/wallet_page/wallet_main/all_coins_list.dart';
 import 'package:web_dex/views/wallet/wallet_page/wallet_main/wallet_manage_section.dart';
 import 'package:web_dex/views/wallet/wallet_page/wallet_main/wallet_overview.dart';
 import 'package:web_dex/views/wallets_manager/wallets_manager_events_factory.dart';
@@ -139,6 +139,9 @@ class _WalletMainState extends State<WalletMain>
                         mode: authStateMode,
                       ),
                     ),
+                    SliverToBoxAdapter(
+                      child: SizedBox(height: 8),
+                    ),
                     _buildCoinList(authStateMode),
                   ],
                 ),
@@ -219,10 +222,25 @@ class _WalletMainState extends State<WalletMain>
         );
       case AuthorizeMode.hiddenLogin:
       case AuthorizeMode.noLogin:
-        return AllCoinsList(
+        return AssetsList(
+          useGroupedView: true,
+          assets: context
+              .read<CoinsBloc>()
+              .state
+              .coins
+              .values
+              .map((coin) => coin.assetId)
+              .toList(),
+          withBalance: false,
           searchPhrase: _searchKey,
-          withBalance: _showCoinWithBalance,
-          onCoinItemTap: _onCoinItemTap,
+          onAssetItemTap: (assetId) => _onAssetItemTap(
+            context
+                .read<CoinsBloc>()
+                .state
+                .coins
+                .values
+                .firstWhere((coin) => coin.assetId == assetId),
+          ),
         );
     }
   }
@@ -249,6 +267,11 @@ class _WalletMainState extends State<WalletMain>
     _popupDispatcher!.show();
   }
 
+  void _onAssetItemTap(Coin coin) {
+    _popupDispatcher = _createPopupDispatcher();
+    _popupDispatcher!.show();
+  }
+
   PopupDispatcher _createPopupDispatcher() {
     final TakerBloc takerBloc = context.read<TakerBloc>();
     final BridgeBloc bridgeBloc = context.read<BridgeBloc>();
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index f793cd6771..7b628da70f 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -209,7 +209,7 @@ SPEC CHECKSUMS:
   FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
   GoogleAppMeasurement: fc0817122bd4d4189164f85374e06773b9561896
   GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
-  komodo_defi_framework: f0b88d0dfa7907a9ece425bf4f1435cd94cfdc73
+  komodo_defi_framework: c01710e1bf0eaa1fad46b3e4ba596f24dba394fd
   local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
   mobile_scanner: 07710d6b9b2c220ae899de2d7ecf5d77ffa56333
   nanopb: fad817b59e0457d11a5dfbde799381cd727c1275


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (12)</summary><blockquote>

<details>
<summary>lib/views/wallet/wallet_page/common/asset_list_item.dart (1)</summary><blockquote>

`1-42`: **Well-implemented responsive design pattern**

The widget correctly implements different layouts for mobile and desktop platforms, and properly handles visual state through the opacity property. The code is clean and follows good object-oriented practices with clear separation of concerns.

One minor suggestion: Consider passing the `isMobile` value through a function parameter or using a provider pattern instead of directly accessing what appears to be a global variable, which would improve testability.

</blockquote></details>
<details>
<summary>lib/shared/widgets/asset_item/asset_item_subtitle.dart (1)</summary><blockquote>

`1-36`: **Solid implementation with one unused parameter**

The widget is well-structured with good handling of null values for `assetId` and `text`. The documentation clearly explains its purpose and relationship to the previous implementation.

However, there's an unused parameter in the class:


```diff
  const AssetItemSubtitle({
    super.key,
    required this.assetId,
    required this.size,
-   this.amount,
    this.text,
  });

  final AssetId? assetId;
  final AssetItemSize size;
- final double? amount;
  final String? text;
```

The `amount` parameter is defined but not used anywhere in the `build` method. Consider removing it to simplify the API if it's not needed.

</blockquote></details>
<details>
<summary>lib/shared/widgets/asset_item/asset_item.dart (1)</summary><blockquote>

`1-46`: **Well-designed component with good separation of concerns**

The widget properly delegates rendering to specialized sub-components, following good Flutter practices. The documentation clearly explains how this component replaces the previous `CoinItem`.


One minor suggestion: consider making the spacing between the icon and body configurable rather than hardcoding `SizedBox(width: 8)`. This could be done through a parameter or by using theme spacing values.

</blockquote></details>
<details>
<summary>lib/views/wallet/wallet_page/common/assets_list.dart (2)</summary><blockquote>

`43-44`: **Consider using a more appropriate color token for alternating backgrounds.**

Using `colorScheme.onSurface` as a background color might not be semantically correct according to Material Design guidelines. This color token is typically meant for content that appears on surfaces, not for the surface itself.

```diff
- final Color backgroundColor = index.isEven
-     ? Theme.of(context).colorScheme.surface
-     : Theme.of(context).colorScheme.onSurface;
+ final Color backgroundColor = index.isEven
+     ? Theme.of(context).colorScheme.surface
+     : Theme.of(context).colorScheme.surfaceVariant;
```

---

`61-81`: **Good implementation of the grouped view with separation.**

The grouped view implementation with the `SliverList.separated` provides proper visual spacing between groups. However, consider adding similar separation for the flat view for consistency in the UI.

Consider modifying the flat view implementation to use `SliverList.separated` as well:

```diff
- return SliverList(
-   delegate: SliverChildBuilderDelegate(
-     (BuildContext context, int index) {
-       final asset = filteredAssets[index];
-       final Color backgroundColor = index.isEven
-           ? Theme.of(context).colorScheme.surface
-           : Theme.of(context).colorScheme.onSurface;
-
-       return AssetListItem(
-         assetId: asset,
-         backgroundColor: backgroundColor,
-         onTap: onAssetItemTap,
-       );
-     },
-     childCount: filteredAssets.length,
-   ),
- );
+ return SliverList.separated(
+   itemBuilder: (BuildContext context, int index) {
+     final asset = filteredAssets[index];
+     final Color backgroundColor = index.isEven
+         ? Theme.of(context).colorScheme.surface
+         : Theme.of(context).colorScheme.surfaceVariant;
+     
+     return AssetListItem(
+       assetId: asset,
+       backgroundColor: backgroundColor,
+       onTap: onAssetItemTap,
+     );
+   },
+   separatorBuilder: (BuildContext context, int index) {
+     return const SizedBox(height: 2); // Smaller separation for flat items
+   },
+   itemCount: filteredAssets.length,
+ );
```

</blockquote></details>
<details>
<summary>lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart (2)</summary><blockquote>

`57-57`: **Remove commented-out code.**

There's a commented-out `// Spacer()` that should be removed. Keeping commented code can lead to confusion and clutter.

```diff
-                // Spacer(),
```

---

`58-72`: **Consider a more balanced layout for the row elements.**

The current implementation uses three `Expanded` widgets with different flex factors. Consider revising the layout to ensure consistent proportions or use more specific constraints for better control over the layout.

```diff
-                Expanded(
-                  child: Padding(
-                    padding: const EdgeInsets.symmetric(horizontal: 12.0),
-                    child: TrendPercentageText(
-                      percentage: priceChangePercentage24h ?? 0,
-                      showIcon: true,
-                      iconSize: 16,
-                      precision: 2,
-                    ),
-                  ),
-                ),
-                Expanded(
-                  flex: 2,
-                  child: CoinSparkline(coinId: assetId.id),
-                ),
+                SizedBox(
+                  width: 120,
+                  child: Padding(
+                    padding: const EdgeInsets.symmetric(horizontal: 12.0),
+                    child: TrendPercentageText(
+                      percentage: priceChangePercentage24h ?? 0,
+                      showIcon: true,
+                      iconSize: 16,
+                      precision: 2,
+                    ),
+                  ),
+                ),
+                Expanded(
+                  child: CoinSparkline(coinId: assetId.id),
+                ),
```

</blockquote></details>
<details>
<summary>pubspec.yaml (1)</summary><blockquote>

`59-60`: **Fix trailing space on line 60.**

There's a trailing space on line 60 that should be removed for consistent formatting.

```diff
 args: ^2.7.0 # dart.dev
-  
+
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

[error] 60-60: trailing spaces

(trailing-spaces)

</details>

</details>

</blockquote></details>
<details>
<summary>lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart (4)</summary><blockquote>

`98-104`: **Consider handling price formatting more robustly**

The current price formatting logic could be improved for better maintainability and readability.


```diff
final price = state.getPriceForAsset(_primaryAsset);
-final formattedPrice = price?.price != null
-    ? NumberFormat.currency(
-        symbol: '\$',
-        decimalDigits: 2,
-      ).format(price!.price)
-    : '';
+final formattedPrice = price?.price == null
+    ? ''
+    : NumberFormat.currency(
+        symbol: '\$',
+        decimalDigits: 2,
+      ).format(price.price);
```

---

`137-138`: **Consider safeguarding against invalid coinId**

The `CoinSparkline` widget is directly using `_primaryAsset.id` which might cause issues if the ID format changes or is invalid.


```diff
-child: CoinSparkline(coinId: _primaryAsset.id),
+child: CoinSparkline(
+  coinId: _primaryAsset.id,
+  fallbackWidget: Container(height: 35),
+),
```

Add a `fallbackWidget` parameter to `CoinSparkline` to handle cases where the chart can't be displayed.

---

`218-219`: **Simplify the relatedAssets assignment**

The conditional expression is unnecessary since both branches result in the same list of assets.


```diff
-final relatedAssets = assets.length > 1 ? assets.toList() : assets;
+final relatedAssets = assets;
```

---

`262-264`: **Improve readability of icon size calculation**

The calculation for the icon size is a bit complex inline. Consider extracting it to a variable for better readability.


```diff
+final iconSize = min(36, size * 0.6);
AssetIcon.ofTicker(
  asset.subClass.iconTicker,
-  size: min(36, size * 0.6),
+  size: iconSize,
),
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d513ddc357d05892e3c45b59261d2b8ec83959f9 and c79f8348dcbe0ba695488489f19f2c5bce2db500.

</details>

<details>
<summary>⛔ Files ignored due to path filters (3)</summary>

* `macos/Podfile.lock` is excluded by `!**/*.lock`
* `packages/komodo_ui_kit/pubspec.lock` is excluded by `!**/*.lock`
* `pubspec.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (25)</summary>

* `firebase.json` (1 hunks)
* `lib/bloc/coins_bloc/coins_bloc.dart` (4 hunks)
* `lib/bloc/coins_bloc/coins_state.dart` (1 hunks)
* `lib/main.dart` (1 hunks)
* `lib/model/cex_price.dart` (1 hunks)
* `lib/shared/utils/extensions/collection_extensions.dart` (1 hunks)
* `lib/shared/utils/utils.dart` (1 hunks)
* `lib/shared/widgets/asset_item/asset_item.dart` (1 hunks)
* `lib/shared/widgets/asset_item/asset_item_body.dart` (1 hunks)
* `lib/shared/widgets/asset_item/asset_item_size.dart` (1 hunks)
* `lib/shared/widgets/asset_item/asset_item_subtitle.dart` (1 hunks)
* `lib/shared/widgets/asset_item/asset_item_title.dart` (1 hunks)
* `lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/asset_list_item.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/asset_list_item_mobile.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/assets_list.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart` (3 hunks)
* `lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/grouped_assets_list.dart` (1 hunks)
* `lib/views/wallet/wallet_page/common/wallet_coins_list.dart` (1 hunks)
* `lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart` (2 hunks)
* `lib/views/wallet/wallet_page/wallet_main/wallet_main.dart` (4 hunks)
* `packages/komodo_persistence_layer/pubspec.yaml` (1 hunks)
* `pubspec.yaml` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.35.1)</summary>

<details>
<summary>pubspec.yaml</summary>

[error] 60-60: trailing spaces

(trailing-spaces)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: Build Desktop (linux)
* GitHub Check: Test web-app-macos
* GitHub Check: Build Mobile (Android)
* GitHub Check: Build Desktop (windows)
* GitHub Check: Build Mobile (iOS)

</details>

<details>
<summary>🔇 Additional comments (44)</summary><blockquote>

<details>
<summary>packages/komodo_persistence_layer/pubspec.yaml (1)</summary>

`12-12`: **Simplified dependency management - good improvement!**

Switching from a Git dependency to a direct version specification for Hive improves build reproducibility and speeds up dependency resolution. This change aligns with best practices for Dart package management.

</details>
<details>
<summary>firebase.json (1)</summary>

`17-17`: **Added proper EOF newline - good practice!**

Adding a newline at the end of the file ensures compliance with POSIX standards and improves compatibility with various text processing tools.

</details>
<details>
<summary>lib/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart (1)</summary>

`26-29`: **Improved sparkline chart dimensions for better visualization**

The increased dimensions (width from 120 to 130, height from 24 to 35) provide more space for the sparkline chart, improving visibility and readability. This change aligns well with the new asset-based UI components being introduced in this PR.

</details>
<details>
<summary>lib/shared/widgets/asset_item/asset_item_size.dart (1)</summary>

`1-45`: **Well-structured size configuration class with clear presets**

This new class provides a clean, type-safe approach to defining size configurations for asset items. The three preset sizes (large, medium, small) with well-documented parameters will ensure UI consistency across the application. The approach promotes maintainability and reusability for the new asset-based UI components.

</details>
<details>
<summary>lib/shared/widgets/asset_item/asset_item_title.dart (1)</summary>

`1-30`: **Clean implementation with good documentation!**

The implementation follows Flutter's best practices with proper null handling for `assetId`, clear documentation about its purpose and relationship to the previous component, and appropriate text styling based on theme and size parameters.

</details>
<details>
<summary>lib/shared/widgets/asset_item/asset_item_body.dart (1)</summary>

`1-42`: **Well-structured component with good separation of concerns**

The `AssetItemBody` widget is well-designed with proper composition using `AssetItemTitle` and `AssetItemSubtitle`. The component maintains a clean hierarchy with appropriate vertical spacing and follows Flutter's best practices for widget implementation.

</details>
<details>
<summary>lib/main.dart (1)</summary>

`123-124`: **Good simplification of URL handling**

Replacing the previous implementation (which likely used `html.window.location.href`) with `Uri.base` is a good improvement that removes the dependency on the `universal_html` package while maintaining the same functionality.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/asset_list_item_mobile.dart (1)</summary>

`1-45`: **Clean and well-structured implementation**

This is a well-implemented mobile asset list item that nicely replaces the previous coin-based implementation. The code follows Flutter best practices with clear documentation and proper widget structure. I appreciate the clear class documentation that explains how this component replaces the previous `CoinListItemMobile`.

</details>
<details>
<summary>lib/model/cex_price.dart (1)</summary>

`3-15`: **Improved error handling with null safety**

The new implementation of `CexDataProvider` and `cexDataProvider` is more robust. The function now uses `firstWhere` with an `orElse` parameter instead of the previous `firstWhereOrNull`, ensuring a default value is always returned rather than potentially returning null. This approach simplifies client code by eliminating the need to handle null values.

</details>
<details>
<summary>lib/shared/utils/utils.dart (1)</summary>

`20-25`: **Exports reorganized to improve utility module organization.**

The export statements have been reorganized to include the new collection extensions and ensure consistent SDK extension inclusion. This change maintains a clean API surface for importing utility functions throughout the application.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/assets_list.dart (2)</summary>

`6-26`: **New AssetsList widget implementation looks good.**

The widget properly handles displaying assets in either flat or grouped views, with appropriate parameters for customization. Good job replacing the legacy `AllCoinsList` with this more modern implementation using `AssetId`.

---

`97-113`: **Efficient search implementation with proper case normalization.**

The filtering logic correctly normalizes case for case-insensitive searching and checks multiple asset properties (name, symbol, and ID) to provide comprehensive search results.

</details>
<details>
<summary>lib/shared/utils/extensions/collection_extensions.dart (2)</summary>

`3-16`: **Well-implemented unmodifiable list extension with thorough documentation.**

The `UnmodifiableListExtension` provides a convenient way to create immutable views of collections. The documentation clearly explains the behavior and limitations, particularly noting that the view references the original collection.

---

`18-37`: **Well-implemented unmodifiable map extension with thorough documentation.**

The `UnmodifiableMapExtension` provides a clear and convenient way to create immutable views of maps. The documentation properly warns about the limitations, ensuring developers understand that the elements themselves remain mutable.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/asset_list_item_desktop.dart (1)</summary>

`8-26`: **Good implementation of desktop-optimized asset list item.**

The class properly replaces the legacy `CoinListItemDesktop` component, adapting to work with `AssetId` instead of `Coin`. The parameters and structure are clear and well-organized.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/grouped_assets_list.dart (4)</summary>

`1-26`: **Well-structured widget with clear API design.**

The `GroupedAssetsList` widget has a clean, well-documented API with required and optional parameters properly defined. The documentation comments are informative and explain the purpose of the widget and its relation to the `AssetsList` component.

---

`27-52`: **Well-implemented build method with good separation of UI and logic.**

The build method efficiently delegates to helper methods for data manipulation and focuses on UI construction. The background color alternation based on index provides good visual distinction between groups.

---

`54-65`: **Thoughtful implementation of asset grouping logic.**

The `_groupAssetsByTicker` method effectively organizes assets by their ticker symbols, and the use of `putIfAbsent` ensures that each ticker has its corresponding list of assets without redundant code.

---

`67-80`: **Efficient filtering implementation with case-insensitive search.**

The filtering logic correctly handles both empty search cases and performs case-insensitive matching against both asset names and symbols.

</details>
<details>
<summary>lib/views/wallet/wallet_page/wallet_main/all_coins_list.dart (3)</summary>

`11-16`: **Constructor updated to use modern Flutter syntax.**

The change to use `super.key` follows modern Flutter best practices. The parameter rename from `onCoinItemTap` to `onCoinSelected` is more descriptive of the actual action being performed.

---

`19-19`: **Function signature updated for better semantic clarity.**

Changing the callback function signature to clearly indicate that a coin is being selected rather than tapped improves the API's semantic clarity.

---

`77-84`: **Widget transition from coins to assets model.**

The widget has been updated from `WalletCoinsList` to `KnownAssetsList` as part of the refactoring to an asset-based model. The transformation of coins to asset IDs and the callback implementation to convert back is handled correctly.

</details>
<details>
<summary>lib/bloc/coins_bloc/coins_bloc.dart (4)</summary>

`6-7`: **Appropriate imports added for new functionality.**

The addition of Foundation's `mapEquals` and utility imports supports the improved implementation of the price update logic.

---

`287-291`: **Simplified price update check using mapEquals.**

The new implementation effectively uses `mapEquals` to efficiently determine if prices have changed, avoiding unnecessary state updates and providing clearer logging.

---

`293-303`: **Well-designed helper function for updating coin prices.**

The `updateCoinsWithPrices` helper function cleanly encapsulates the logic for updating coin prices, making the code more maintainable. The use of functional programming patterns with `map` and returning an unmodifiable map is a good practice.

---

`305-311`: **Concise state update with immutable data structures.**

The state update now clearly separates the prices update from the coins update and uses immutable data structures (via `unmodifiable()`), which is good practice for state management in Flutter/Dart.

</details>
<details>
<summary>lib/views/wallet/wallet_page/wallet_main/wallet_main.dart (4)</summary>

`33-33`: **Import updated to support new asset-based UI components.**

The import statement has been updated to include the new `assets_list.dart` file, which is part of the refactoring to an asset-based model.

---

`142-144`: **Added spacing for improved UI layout.**

The addition of a `SliverToBoxAdapter` with a small height improves the visual spacing between UI elements, enhancing the overall look and feel of the wallet page.

---

`225-244`: **Refactored to use asset-based list view with grouped display.**

The implementation now uses `AssetsList` with `useGroupedView: true` to display assets in a grouped manner. The transformation from coins to assets and the callback handling is properly implemented.

---

`270-273`: **Added dedicated method for asset item tap handling.**

The new `_onAssetItemTap` method provides a clear handler for asset taps, which is more consistent with the asset-based model being implemented. The method correctly creates and shows a popup dispatcher just like the coin-based version.

</details>
<details>
<summary>lib/bloc/coins_bloc/coins_state.dart (4)</summary>

`9-9`: **Architectural improvement: Adding centralized price state.**

The addition of a `prices` field to the `CoinsState` class provides a centralized storage for price information. This is a good architectural improvement that helps maintain a single source of truth for price data.



Also applies to: 17-17, 24-24, 28-28

---

`35-35`: **LGTM: Correctly implemented immutability support.**

The `prices` parameter has been properly added to the `copyWith` method, maintaining the immutability pattern and ensuring state updates are handled correctly.



Also applies to: 43-43

---

`47-55`: **Well-designed helper methods for asset price retrieval.**

These new helper methods (`getPriceForAsset` and `get24hChangeForAsset`) provide a clean API for accessing price information, improving code readability and maintainability throughout the codebase.

---

`57-75`: **Good transition with proper deprecation notice.**

The updated `getUsdPriceByAmount` method now uses the new price structure while maintaining backward compatibility. The deprecation notice with a clear migration path is excellent practice for API evolution.

</details>
<details>
<summary>pubspec.yaml (2)</summary>

`59-59`: **Consider standardizing version constraint style.**

The PR updates several dependencies to use caret notation (`^`) allowing for compatible minor updates. This is a good practice for most dependencies, but ensure this aligns with your team's dependency management strategy.



Also applies to: 68-68, 138-138

---

`114-114`: **Simplification of Hive dependency declarations.**

Replacing Git references with direct version specifications is a good simplification that makes the dependency structure clearer and potentially improves build reproducibility.



Also applies to: 117-117

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/wallet_coins_list.dart (2)</summary>

`2-13`: **Good migration from coin-based to asset-based architecture.**

The renaming from `WalletCoinsList` to `KnownAssetsList` and the transition from `Coin` to `AssetId` objects align with the broader architectural shift in the application. This provides a more standardized approach to handling assets.

---

`17-33`: **Improved list implementation with better separation.**

The switch to `SliverList.separated` with an explicit separator builder improves the UI by providing consistent spacing between items. The implementation is clean and follows Flutter best practices.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart (3)</summary>

`18-18`: **Updated to modern Flutter constructor syntax.**

Using `super.key` is the recommended approach in newer Flutter versions, improving code readability.



Also applies to: 22-22

---

`37-37`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify the effect of commenting out MaterialType.**

The Material `type` property has been commented out. This might affect the visual appearance of the component, particularly with regard to ink effects.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for other Material widgets in the codebase to see what type is typically used
rg -A 1 "Material\(" --glob "*.dart" | rg -B 1 "type:"
```

Length of output: 3560

---



</details>

**Design Validation: Confirm the Visual Impact of the Material Type Change**

We verified that nearly all similar Material widget instances explicitly set the `type` property to `MaterialType.transparency` (e.g., in `main_menu_desktop_item.dart`, `orderbook_table_item.dart`, etc.), while in `coin_list_item_desktop.dart` this property is commented out. This means that this component will use Flutter’s default `MaterialType` (typically `canvas`), which could affect the ink splash and ripple effects.

- **Action Required:** Please double-check that the default behavior is intentional for this component. If the transparent effect is desired to maintain visual consistency, consider either reintroducing the `type: MaterialType.transparency` or explicitly confirming that the change does not adversely affect the component's design and ink effects.

---

`45-46`: <details>
<summary>❓ Verification inconclusive</summary>

**Simplified UI with removed components.**

The UI has been streamlined, removing certain components like coin balance and fiat change displays. This aligns with the transition to the new asset-based architecture, but ensure this doesn't result in loss of important functionality for users.




Also applies to: 74-86

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if these features have been moved to the new asset-based components
fd asset_list_item ".dart$" | xargs cat | rg -A 10 -B 10 "CoinFiatChange|CoinBalance"
```

Length of output: 181

---

**Attention: Re-verify asset migration for coin UI components.**

The UI in `lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart` has been streamlined to remove coin balance and fiat change displays in line with the asset-based architecture. To ensure that no important functionality has been lost, please verify that the coin-related features have been migrated into the new asset-based components (e.g., files whose names include “asset_list_item”). 

Below is a revised shell script to help locate any residual references to `CoinFiatChange` or `CoinBalance` in those components. Please run it manually and review the output:



- **Next Steps:**
  - Confirm that any critical functionality tied to coin balance and fiat change is implemented in the new asset-based components.
  - If the script shows that these references exist appropriately, then the removal from this view is safe.
  - If the script finds no references—and if you believe this might hide missing functionality—please re-assess the migration plan.



---

🏁 Script executed:

```shell

#!/bin/bash
# Check within dart files in asset-based components for references to CoinFiatChange or CoinBalance.
fd -e dart "asset_list_item" | while read file; do
    echo "File: $file"
    rg -n "CoinFiatChange|CoinBalance" "$file" || echo "No coin-related UI components in $file"
done
```

Length of output: 1527

---



</details>

**Action Required: Confirm Intentional Removal of Coin Balance and Fiat Change Functionality**

After verifying the asset-based components:
- We searched for references to both `CoinFiatChange` and `CoinBalance` in files like `asset_list_item.dart`, `asset_list_item_desktop.dart`, and `asset_list_item_mobile.dart` and found none.
- This indicates that the coin balance and fiat change displays have indeed been removed in line with the new asset based architecture.

Please ensure that:
- The removal of these UI components from `lib/views/wallet/wallet_page/common/coin_list_item_desktop.dart` (Lines 45-46 and 74-86) is intentional and does not result in the loss of critical functionality for users.
- Any needed coin-related information or functionality is either unnecessary under the new architecture or has been provided through other means.

</details>
<details>
<summary>lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart (3)</summary>

`17-35`: **Well-documented widget with clear parameters**

The class documentation and parameter definitions are excellent. The widget is well-designed with appropriate required and optional parameters.

---

`37-62`: **Good state management implementation**

The state class properly handles initialization and updates to the expanded state, with clean toggling functionality.

---

`17-279`: **Overall excellent implementation of the GroupedAssetTickerItem widget**

The widget is well-designed with robust state management, responsive UI, and clear separation of concerns. The expandable functionality for showing related assets is implemented cleanly, and the UI is consistent with the app's theme.

A few minor improvements could make it even better:
1. Add error handling for network failures when fetching prices
2. Consider adding animations for the expand/collapse transition
3. Implement accessibility features like semantic labels

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-29T15:04:24Z
user: login: ShantanuSharma9873
body: **Tested the build and below are the observations:**
- The new coin listing layout looks good, the graph on the right, and the "**Available on Networks**" dropdown shows coin logos and details.
- In the "**Available on Networks**" dropdown, the pointer doesn’t turn into a **hand** when hovering directly on the coin, rather it does only when hovering beside it on the same line.
- These features are only visible when the wallet is logged out. Logged-in view still shows the old UI with a disabled button.
**Logout View:**
![image](https://github.com/user-attachments/assets/8014e964-a0d2-4682-929b-4fa06d931559)
**Logged In View:**
![image](https://github.com/user-attachments/assets/b57b8f66-310a-4121-a002-55d2b6c2af5c)

**Mobile View Issues:** Tested on iPhone 16 
- The 'Backup Seed Phrase' message is nowhere displayed in any if the tabs.
![image](https://github.com/user-attachments/assets/f8bf30bb-38cb-41ae-bf04-10638cb71289)
- The 'Assets Management' button text is not clearly visible when the wallet is in Light Mode.
![image](https://github.com/user-attachments/assets/6a59577f-656a-4483-bb99-19e363ec476e)
- The Name of the Wallet is no where displayed, Simply '**Wallet**' text is displayed at the top for all the wallets having different names.
![image](https://github.com/user-attachments/assets/6278f425-5c1e-4719-9f20-b33d3e43c1fa)
- The Seed Phrase displayed in the capsule boxes are not center aligned, the text looks too much at the bottom and not in the center of the capsule. (Minor Issue)
![image](https://github.com/user-attachments/assets/e3d1901b-2a7d-4a21-8370-ca7b042a3efd)
- Unable to click on the '**Hide 0 Balance Assets**' checkbox in the Mobile mode. 
![image](https://github.com/user-attachments/assets/94afd1ea-1afb-420b-a6f1-2a178334a95e)
- The Coin balance and the 24% both gets merged in the Mobile view.
![image](https://github.com/user-attachments/assets/283702fd-668b-485d-98aa-3143051d5801)
- The coin price displayed on the chart moves along with the screen when scrolled down. 
![image](https://github.com/user-attachments/assets/4cf9aeec-a5b9-4fdb-985c-8d5c8bebdeeb)
- The '**Currency**' heading and the **Coin Search** both gets merged with the coin listing 
![image](https://github.com/user-attachments/assets/35c976c6-7097-40be-87c3-a65747b84243)
- The Coin price and its time frame chart gets outside the screen from Right and the Coin price gets cut from the left side. 
![image](https://github.com/user-attachments/assets/402cdd68-a0f7-40c1-ba82-838497b911a7)




state: COMMENTED
author_association: NONE
submitted_at: 2025-03-30T11:11:44Z

Review comments:
diff_hunk: @@ -0,0 +1,279 @@
+import 'dart:math' show min;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:komodo_ui/komodo_ui.dart';
+import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
+import 'package:web_dex/common/screen.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
+
+/// A widget that displays a group of assets sharing the same ticker symbol.
+///
+/// Shows the same layout as AssetListItemDesktop but adds an expansion button
+/// to show related assets that share the same ticker symbol.
+class GroupedAssetTickerItem extends StatefulWidget {
+  const GroupedAssetTickerItem({
+    super.key,
+    required this.assets,
+    required this.backgroundColor,
+    required this.onTap,
+    this.expanded,
+    this.isActivating = false,
+  });
+
+  final List<AssetId> assets;
+  final Color backgroundColor;
+  final void Function(AssetId)? onTap;
+  final bool? expanded;
+  final bool isActivating;
+
+  @override
+  State<GroupedAssetTickerItem> createState() => _GroupedAssetTickerItemState();
+}
+
+class _GroupedAssetTickerItemState extends State<GroupedAssetTickerItem> {
+  late bool _isExpanded;
+
+  @override
+  void initState() {
+    super.initState();
+    _isExpanded = widget.expanded ?? false;
+  }
+
+  @override
+  void didUpdateWidget(GroupedAssetTickerItem oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    if (widget.expanded != null && widget.expanded != oldWidget.expanded) {
+      setState(() {
+        _isExpanded = widget.expanded!;
+      });
+    }
+  }
+
+  void _toggleExpanded() {
+    setState(() {
+      _isExpanded = !_isExpanded;
+    });
+  }
+
+  AssetId get _primaryAsset => widget.assets.first;
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);  
+    return Opacity(
+      opacity: widget.isActivating ? 0.3 : 1,
+      child: Material(
+        color: widget.backgroundColor,
+        borderRadius: BorderRadius.circular(10),
+        clipBehavior: Clip.hardEdge,
+        type: MaterialType.card,
+        child: InkWell(
+          onTap:
+              widget.onTap == null ? null : () => widget.onTap!(_primaryAsset),
+          child: Column(
+            children: [
+              Container(
+                padding: const EdgeInsets.symmetric(
+                  horizontal: 16.0,
+                  vertical: 16,
+                ),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    Expanded(
+                      flex: 3,
+                      child: AssetItem(
+                        assetId: _primaryAsset,
+                        size: AssetItemSize.large,
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: BlocBuilder<CoinsBloc, CoinsState>(
+                        builder: (context, state) {
+                          final price = state.getPriceForAsset(_primaryAsset);
+                          final formattedPrice = price?.price != null
+                              ? NumberFormat.currency(
+                                  symbol: '\$',
+                                  decimalDigits: 2,
+                                ).format(price!.price)
+                              : '';
+                          return Text(
+                            formattedPrice,
+                            style: theme.textTheme.bodyMedium,
+                            overflow: TextOverflow.ellipsis,
+                          );
+                        },
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: BlocBuilder<CoinsBloc, CoinsState>(
+                        builder: (context, state) {
+                          return TrendPercentageText(
+                            textStyle: theme.textTheme.bodyMedium?.copyWith(
+                              fontWeight: FontWeight.w500,
+                            ),
+                            percentage:
+                                state.get24hChangeForAsset(_primaryAsset) ?? 0,
+                            showIcon: true,
+                            iconSize: 16,
+                            precision: 2,
+                          );
+                        },
+                      ),
+                    ),
+                    Expanded(
+                      flex: 2,
+                      child: ConstrainedBox(
+                        constraints: const BoxConstraints(
+                          maxWidth: 130,
+                          maxHeight: 35,
+                        ),
+                        child: CoinSparkline(coinId: _primaryAsset.id),
+                      ),
+                    ),
+                    ConstrainedBox(
+                      constraints: const BoxConstraints(minWidth: 48),
+                      child: !(widget.assets.length > 1)
+                          ? null
+                          : IconButton(
+                              iconSize: 32,
+                              icon: Icon(
+                                _isExpanded
+                                    ? Icons.expand_less
+                                    : Icons.expand_more,
+                                color: theme.colorScheme.secondary,
+                              ),
+                              onPressed: _toggleExpanded,
+                              tooltip: _isExpanded
+                                  ? 'Hide related assets'
+                                  : 'Show related assets',
+                            ),
+                    ),
+                  ],
+                ),
+              ),
+              if (_isExpanded)
+                _ExpandedView(assets: widget.assets, theme: theme),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class _ExpandedView extends StatelessWidget {
+  const _ExpandedView({
+    required this.assets,
+    required this.theme,
+  });
+
+  final List<AssetId> assets;
+  final ThemeData theme;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      padding: const EdgeInsets.only(
+        left: 16.0,
+        right: 16.0,
+        bottom: 16.0,
+      ),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          const Divider(),
+          Padding(
+            padding: const EdgeInsets.only(bottom: 8.0, top: 8.0),
+            child: Text(
+              'Available on Networks:',
+              style: theme.textTheme.bodySmall?.copyWith(
+                color: theme.colorScheme.secondary,
+              ),
+            ),
+          ),
+          _AssetIconsRow(assets: assets),
+        ],
+      ),
+    );
+  }
+}
+
+class _AssetIconsRow extends StatelessWidget {
+  const _AssetIconsRow({
+    required this.assets,
+  });
+
+  final List<AssetId> assets;
+
+  @override
+  Widget build(BuildContext context) {
+    final relatedAssets = assets.length > 1 ? assets.toList() : assets;
+
+    return Wrap(
+      spacing: 12.0,
+      runSpacing: 12.0,
+      children: relatedAssets.map((asset) {
+        return _AssetIconItem(asset: asset);
+      }).toList(),
+    );
+  }
+}
+
+class _AssetIconItem extends StatelessWidget {
+  const _AssetIconItem({
+    required this.asset,
+  });
+
+  final AssetId asset;
+
+  @override
+  Widget build(BuildContext context) {
+    final size = isMobile ? 50.0 : 70.0;
+    final theme = Theme.of(context);
+
+    return Tooltip(
+      message: asset.id,
+      child: InkWell(
+        onTap: null,
+        borderRadius: BorderRadius.circular(8.0),
path: lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add meaningful onTap handler for asset icons**

Currently, the asset icons show an InkWell effect but don't do anything when tapped (`onTap: null`), which could confuse users.


Consider implementing a navigation action to view details for that specific asset:

```diff
child: InkWell(
-  onTap: null,
+  onTap: () {
+    // Navigate to asset details or perform some action
+    Navigator.of(context).push(
+      MaterialPageRoute(
+        builder: (context) => AssetDetailsPage(assetId: asset),
+      ),
+    );
+  },
  borderRadius: BorderRadius.circular(8.0),
```

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-29T15:04:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,279 @@
+import 'dart:math' show min;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:komodo_defi_types/komodo_defi_types.dart';
+import 'package:komodo_ui/komodo_ui.dart';
+import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
+import 'package:web_dex/common/screen.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
+import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
+import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
+
path: lib/views/wallet/wallet_page/common/grouped_asset_ticker_item.dart
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Good imports organization, but missing NumberFormat**

The imports are well-organized, but there's a missing import for `NumberFormat` which is used in lines 100-103 for currency formatting.


Add this import at the top of the file:

```diff
import 'dart:math' show min;

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:intl/intl.dart' show NumberFormat;
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:komodo_ui/komodo_ui.dart';
import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
import 'package:web_dex/common/screen.dart';
import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import 'dart:math' show min;

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart' show NumberFormat;
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:komodo_ui/komodo_ui.dart';
import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart';
import 'package:web_dex/common/screen.dart';
import 'package:web_dex/shared/widgets/asset_item/asset_item.dart';
import 'package:web_dex/shared/widgets/asset_item/asset_item_size.dart';
import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/coin_sparkline.dart';
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-29T15:04:23Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-28T12:39:20Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on base/target branches other than the default branch.
> 
> 
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request introduces minor formatting improvements and refactors key logic in the application. It adds a newline to `firebase.json` and simplifies the price update flow in the CoinsBloc using `mapEquals`. The state now includes a new `prices` property with helper methods. The URL logic in the main file is updated to remove an obsolete dependency. Numerous new asset-based UI components have been added to replace legacy coin widgets, and dependency declarations in the pubspec files have been streamlined.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `firebase.json` | Added a newline at the end for consistent formatting. |
| `lib/bloc/coins_bloc/coins_bloc.dart`<br>`lib/bloc/coins_bloc/coins_state.dart` | Refactored price update logic using `mapEquals` and a helper function; added a new `prices` property and helper methods in state. |
| `lib/main.dart` | Removed the `universal_html` import and updated URL handling to use `Uri.base` for performance mode detection. |
| `lib/model/cex_price.dart` | Revised `CexDataProvider` enum and function to use `firstWhere` with a default fallback to `unknown`. |
| `lib/shared/utils/extensions/collection_extensions.dart`<br>`lib/shared/utils/utils.dart` | Added extensions for unmodifiable lists/maps and reorganized export statements (removed `prominent_colors.dart`, added SDK and collection extensions). |
| `lib/shared/widgets/asset_item/*` | Introduced new asset widget suite (`AssetItem`, `AssetItemBody`, `AssetItemTitle`, `AssetItemSubtitle`, `AssetItemSize`) to replace legacy coin components with an `AssetId`-based approach. |
| `lib/views/wallet/wallet_page/common/*`<br>`lib/views/wallet/wallet_page/wallet_main/*` | Implemented new asset-based wallet UI components (list items, grouped views) and refactored tap callbacks from coins to assets. |
| `packages/komodo_persistence_layer/pubspec.yaml`<br>`pubspec.yaml` | Updated dependency declarations: simplified Git references for `hive` and `hive_flutter`, and updated versions for `args`, `video_player`, and `shared_preferences`. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant U as User
    participant AL as AssetsList
    participant WM as WalletMain
    participant PD as Popup Dispatcher

    U->>AL: Tap on an asset item
    AL->>WM: Invoke onAssetItemTap callback with AssetId
    WM->>WM: Retrieve corresponding Coin object
    WM->>PD: Trigger asset details popup
    PD-->>U: Display asset details
```

## Suggested labels
`enhancement`

## Suggested reviewers
- AndrewDelaney
- smk762

## Poem
> I'm a rabbit, hopping through the code,  
> Delighting in changes on every road.  
> New assets bloom like carrots bright,  
> Logic refined with clean insight.  
> I dance in joy with a happy leap—  
> Code so fresh, it’s ours to keep! 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=KomodoPlatform/komodo-wallet&utm_content=2579):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-28T12:46:50Z
body: Visit the preview URL for this PR (updated for commit 4372191):

[https://walletrc--pull-2579-merge-2abk2zrl.web.app](https://walletrc--pull-2579-merge-2abk2zrl.web.app)

<sub>(expires Mon, 07 Apr 2025 12:23:46 GMT)</sub>

<sub>🔥 via [Firebase Hosting GitHub Action](https://github.com/marketplace/actions/deploy-to-firebase-hosting) 🌎</sub>

<sub>Sign: f66a4ff03faa546f12f0ae5a841bd9eff2714dcc</sub>
author_association: NONE
user: login: CharlVS
created_at: 2025-03-29T14:46:01Z
body: @coderabbitai review
author_association: COLLABORATOR
user: login: coderabbitai[bot]
created_at: 2025-03-29T14:46:07Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>
author_association: CONTRIBUTOR
user: login: CharlVS
created_at: 2025-03-31T11:26:02Z
body: Thank you for the excellent review, @ShantanuSharma9873. I will resolve the issues reported this week in a separate PR. I can see the issues mentioned also as in-app bug reports, but if you haven't already, please add all your found issues as in-app reports.
author_association: COLLABORATOR