Skip to content

Conversation

@CharlVS
Copy link
Collaborator

@CharlVS CharlVS commented Jun 4, 2025

Komodo Wallet v0.9.1 Release Notes

This release includes critical bug fixes for Trezor hardware wallet functionality and introduces a comprehensive redesign of the wallet assets page with improved mobile experience and enhanced portfolio management features.

✨ New Features

🎨 Redesigned Assets Page

  • Tabbed Navigation System: Completely redesigned interface replacing the overlay chart system with clean tabs:
    • Assets tab (default) - Displays user's coin holdings
    • Portfolio Growth tab - Dedicated view for growth charts
    • Profit & Loss tab - Separate view for P&L analysis
  • Enhanced Mobile Experience:
    • New statistics carousel optimized for mobile devices
    • Improved coin list items with better balance and 24h change display
    • Platform-specific padding and spacing adjustments
  • Responsive Design: Seamless adaptation between mobile and desktop layouts

📊 Real-time Data Integration

  • Added 24h price change tracking across all wallet views
  • Integrated real-time balance updates with percentage changes
  • Enhanced portfolio calculations including:
    • Total portfolio balance tracking
    • 24h change in USD
    • 24h percentage change monitoring

🐛 Bug Fixes

  • Trezor Login Issues - Fixed critical bugs in the Trezor hardware wallet login flow that were preventing users from accessing their wallets

🏗️ Technical Improvements

  • Implemented NestedScrollView for improved scroll behavior with tab navigation
  • Refactored CoinListView into a separate widget for cleaner code architecture
  • Added cached price retrieval methods to reduce unnecessary API calls
  • Migrated CexPrice to use SDK types for better compatibility

Full Changelog: 0.9.0...0.9.1

@CharlVS CharlVS self-assigned this Jun 4, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 4, 2025

Walkthrough

This update addresses a critical bug with Trezor hardware wallet login by updating password handling and coin activation logic. Password utility functions are refactored to use centralized security utilities. Documentation is updated to reflect the new release, and the project version is incremented to 0.9.1.

Changes

Files/Group Change Summary
lib/shared/utils/password.dart Replaced custom password generation and validation with calls to centralized security utility methods.
lib/bloc/trezor_init_bloc/trezor_init_bloc.dart Updated Trezor login to generate passwords dynamically and activate default coins; adjusted method signatures.
CHANGELOG.md, pubspec.yaml Added release notes for v0.9.1 and incremented version number; upgraded http dependency version.
docs/FLUTTER_VERSION.md Reworded Flutter version support statement for clarity and future-proofing.
lib/model/coin.dart Removed exclusion of SegWit coins from Trezor support in hasTrezorSupport getter logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant TrezorInitBloc
    participant SecurityUtils
    participant KdfSdk

    User->>App: Initiate Trezor login
    App->>TrezorInitBloc: _loginToTrezorWallet({walletName, password?})
    alt password not provided
        TrezorInitBloc->>SecurityUtils: generatePasswordSecure(16)
        SecurityUtils-->>TrezorInitBloc: Generated password
    end
    TrezorInitBloc->>KdfSdk: addActivatedCoins(enabledByDefaultTrezorCoins)
    TrezorInitBloc->>App: Complete login process
Loading

Possibly related PRs

Suggested labels

bug, P0

Suggested reviewers

  • smk762

Poem

🐇
A bug with Trezor, now set free,
Passwords strong as they can be!
Coins awake with just one tap,
Docs and versions neatly wrapped.
Hop along, the wallet's bright—
0.9.1 shines in the night!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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 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 for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

github-actions bot commented Jun 4, 2025

Visit the preview URL for this PR (updated for commit c0cd153):

https://walletrc--pull-2743-merge-ncur58lv.web.app

(expires Fri, 20 Jun 2025 11:49:07 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: f66a4ff03faa546f12f0ae5a841bd9eff2714dcc

* fix pwd validation fail on hidden wallet

* fix default pass & ensure auto active coins register

* use secure random password

* refactor: migrate password generation to SDK

---------

Co-authored-by: CharlVS <[email protected]>
@CharlVS CharlVS marked this pull request as ready for review June 4, 2025 13:30
@CharlVS CharlVS added the QA Ready for QA Testing label Jun 4, 2025
smk762

This comment was marked as outdated.

@CharlVS CharlVS marked this pull request as draft June 5, 2025 15:57
* fix(trezor): persist generated password and login in HD mode

* chore(deps): update SDK to f63bebb

* refactor(trezor): add error handling to password storage & retrieval
@CharlVS CharlVS marked this pull request as ready for review June 6, 2025 22:11
@CharlVS CharlVS requested a review from smk762 June 6, 2025 22:12
@CharlVS CharlVS dismissed smk762’s stale review June 6, 2025 22:13

Fixed (again)

Copy link
Contributor

@DeckerSU DeckerSU left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase the branch on the top of current dev. In current state it doesn't include build fixes.

@DeckerSU DeckerSU self-requested a review June 10, 2025 22:17
Copy link
Contributor

@DeckerSU DeckerSU left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

It looks like everything works as expected in most cases. I ran tests using both the web (PC) and desktop (linux) versions. For the web tests, I used a Trezor Safe 3 device with Google Chrome Version 137.0.7151.68 (Official Build) (64-bit).

Everything worked fine across various scenarios, except one: when the Trezor was set up with SLIP39 (20 words) and no PIN, I encountered the following error:

"Internal: bad magic in v1 read: 0x19f0 instead of 0x3f2323"

I tried disconnecting and reconnecting the Trezor, but the error persisted. Then, I re-initialized the Trezor with BIP39 (12 words) and no PIN—it worked fine. I also restored it again with SLIP39 (20 words) and no PIN, and that time it also worked as expected.

So, I approve the changes, but I strongly recommend running additional tests just to be sure that users won’t encounter the same issue under specific conditions.

One more thing: I noticed that if you choose Trezor, BTC using Native SegWit (bc1) addresses can't be added as a coin to the list. This is the default address format for Trezor, so it means users who hold their BTC in Native SegWit addresses may not be able to access their funds using Komodo Wallet.

Copy link
Collaborator

@smk762 smk762 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed I was able to access Trezor on both linux desktop and brave browser.
Non-blocking issue added at #2766

Confirm no segwit variants found (BTC, LTC, DGB) in activation list. Issue for this created at #2767

The bad magic error @DeckerSU reported has been converted to an issue at GLEECBTC/komodo-defi-framework#2488

Though some work remains to be done with Trezor, the reported login issue is resolved. Approved.

smk762 and others added 2 commits June 12, 2025 13:53
…d UX (#2771)

- Restructure wallet page with tab-based navigation (Assets, Portfolio Growth, Profit & Loss)
- Replace single scroll view with NestedScrollView for better tab content management
- Move portfolio charts from overlay to dedicated tabs for clearer organization
- Enhance mobile coin list items with dedicated balance/24h change layout
- Add statistics carousel for mobile wallet overview replacing wrap layout
- Integrate 24h price tracking across coin items and portfolio calculations
- Update wallet overview with real-time balance and copy functionality
- Improve responsive design with platform-specific padding and layouts
- Refactor CoinListView into separate widget for cleaner code structure
- Change "Currency" header to "Portfolio" and update action button text

BREAKING CHANGE: PortfolioGrowthChartLoadSuccess state now requires totalBalance,
totalChange24h, and percentageChange24h parameters
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
lib/views/wallet/wallet_page/common/asset_list_item.dart (1)

31-43: 🛠️ Refactor suggestion

⚠️ Potential issue

priceChangePercentage24h is never used

The new value is accepted by AssetListItem but not forwarded to AssetListItemMobile / AssetListItemDesktop.
Either pass it through or render it directly here; otherwise this field is dead code and the UI won’t show the 24 h change.

-        ? AssetListItemMobile(
-            assetId: assetId,
-            backgroundColor: backgroundColor,
-            onTap: onTap,
-          )
+        ? AssetListItemMobile(
+            assetId: assetId,
+            backgroundColor: backgroundColor,
+            onTap: onTap,
+            priceChangePercentage24h: priceChangePercentage24h,
+          )
...
-        : AssetListItemDesktop(
-            assetId: assetId,
-            backgroundColor: backgroundColor,
-            onTap: onTap,
-          );
+        : AssetListItemDesktop(
+            assetId: assetId,
+            backgroundColor: backgroundColor,
+            onTap: onTap,
+            priceChangePercentage24h: priceChangePercentage24h,
+          );
♻️ Duplicate comments (1)
lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart (1)

224-233: Same inconsistency as above inside the periodic update – totals are derived from the original coins, not the filtered supportedCoins actually displayed.

🧹 Nitpick comments (11)
lib/bloc/cex_market_data/price_chart/models/time_period.dart (1)

26-28: Prefer a getter for formatted to keep API symmetry and avoid redundant parentheses

In the analogous PriceChartPeriod enum, formatted is exposed as a getter. Using a method here introduces an unnecessary discrepancy and forces callers to add () when switching between the two types.

-  // TODO: Localize
-  String formatted() => name;
+  // TODO: Localize
+  String get formatted => name;

No functional change, but it tightens consistency across the model layer and marginally improves call-site ergonomics.

lib/views/wallet/wallet_page/wallet_main/active_coins_list.dart (1)

59-69: Avoid firing Bloc events from the list builder

itemBuilder runs every build; emitting CoinsPubkeysRequested here can spam the bloc during fast scrolling or rebuilds.
Prefetch pubkeys before rendering, or cache the request status to ensure each coin triggers at most one fetch.

lib/views/wallet/wallet_page/wallet_main/wallet_manage_section.dart (1)

96-106: Hard-coded text breaks i18n.

The mobile header 'Portfolio' and button label 'Add assets' bypass the existing easy_localization setup.
Replace them with the corresponding LocaleKeys entries as done for the desktop flow to keep translations in sync.

lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_repository.dart (1)

545-553: Nit: method name leakage.

updatePrices() is a thin alias for fetchCurrentPrices() and adds no semantic value.
Consider removing it or turning it into a polling scheduler so the public API does more than forward the call.

lib/views/wallet/wallet_page/common/expandable_coin_list_item.dart (2)

120-191: Multiple visible strings are not localised.

'Balance', '24h %' and several others are hard-coded. This regresses localisation coverage introduced elsewhere.
Expose them via LocaleKeys before release.


248-285: Untranslated “Swap” label.

The chip caption 'Swap' should also come from the localisation bundle to avoid English-only UI.

lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart (1)

81-85: Currency formatting is fixed to USD.

NumberFormat.currency(symbol: '\$') ignores the user’s selected fiat.
Use the active fiat symbol (or locale) from settings to avoid confusing non-USD users.

lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart (2)

171-174: Duplicate cache refresh wastes bandwidth
updatePrices() is already called a few lines earlier inside the periodic loop, yet it’s invoked again here before every chart build. Remove one call to avoid doubling network traffic.


244-289: Edge-cases in helper math

  1. coin.id.symbol.configSymbol.toUpperCase() risks key mismatches if the cached-price map uses raw symbols (e.g. “BTC”) while configSymbol already returns upper-case. Safer:
final price = portfolioGrowthRepository.getCachedPrice(coin.id.symbol);
  1. _calculateTotalBalance forces a floor of 0.01, which can fabricate money for dust portfolios. Prefer displaying <\$0.01 in the UI instead of inflating the value.

  2. Percent change calculation divides by totalBalance <= 0.01. Using the synthetic 0.01 from (2) can output misleading 100 % swings.

lib/views/wallet/wallet_page/wallet_main/wallet_main.dart (2)

112-213: Hard-coded 340 px height breaks on tablets & desktops
The growth / P&L charts are wrapped in fixed-height containers. On tall screens they look cramped; on very small devices they may overflow. Let the chart decide its own height or use AspectRatio:

- height: 340,
+ height: isMobile ? 280 : 420,

or remove the explicit height altogether.


73-73: TabController always initialised with 3 tabs
For non-logged-in users the UI shows only one page; still creating a 3-length TabController is harmless but unnecessary. You can lazily instantiate it only when AuthorizeMode.logIn.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c9e9a68 and c0cd153.

📒 Files selected for processing (17)
  • lib/bloc/app_bloc_root.dart (1 hunks)
  • lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart (4 hunks)
  • lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_repository.dart (2 hunks)
  • lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_state.dart (1 hunks)
  • lib/bloc/cex_market_data/price_chart/models/price_chart_interval.dart (1 hunks)
  • lib/bloc/cex_market_data/price_chart/models/time_period.dart (1 hunks)
  • lib/bloc/coins_bloc/coins_repo.dart (1 hunks)
  • lib/model/cex_price.dart (1 hunks)
  • lib/shared/widgets/coin_balance.dart (1 hunks)
  • lib/views/wallet/wallet_page/common/asset_list_item.dart (1 hunks)
  • lib/views/wallet/wallet_page/common/assets_list.dart (1 hunks)
  • lib/views/wallet/wallet_page/common/expandable_coin_list_item.dart (5 hunks)
  • lib/views/wallet/wallet_page/wallet_main/active_coins_list.dart (1 hunks)
  • lib/views/wallet/wallet_page/wallet_main/wallet_main.dart (5 hunks)
  • lib/views/wallet/wallet_page/wallet_main/wallet_manage_section.dart (3 hunks)
  • lib/views/wallet/wallet_page/wallet_main/wallet_overview.dart (4 hunks)
  • packages/komodo_ui_kit/lib/src/display/statistic_card.dart (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • lib/bloc/coins_bloc/coins_repo.dart
  • lib/shared/widgets/coin_balance.dart
  • packages/komodo_ui_kit/lib/src/display/statistic_card.dart
  • lib/bloc/cex_market_data/price_chart/models/price_chart_interval.dart
⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: Build Desktop (linux)
  • GitHub Check: Build Desktop (windows)
  • GitHub Check: Build Desktop (macos)
  • GitHub Check: unit_tests
  • GitHub Check: osv_scan
  • GitHub Check: Build Mobile (iOS)
  • GitHub Check: Test web-app-macos
  • GitHub Check: Build Mobile (Android)
  • GitHub Check: validate_code_guidelines
  • GitHub Check: Test web-app-linux-profile
  • GitHub Check: build_and_preview
🔇 Additional comments (6)
lib/bloc/app_bloc_root.dart (1)

195-199: Confirm symbol change intent

Switching the initial PriceChartStarted symbols to ['BTC'] shifts the default focus away from KMD.
Please verify this is a deliberate product decision and that BTC price data is always available at startup.

lib/views/wallet/wallet_page/common/assets_list.dart (1)

48-52: Guard against missing map entries

priceChangePercentages[asset.id] can be null.
If downstream widgets assume a non-null double, add a default (e.g. ?? 0).

lib/views/wallet/wallet_page/wallet_main/active_coins_list.dart (1)

59-63: Confirm Flutter version supports SliverList.builder

SliverList.builder is only available from Flutter 3.10+.
If you target older SDKs, revert to SliverChildBuilderDelegate to keep compatibility.

lib/model/cex_price.dart (2)

1-5: Good cleanup

Re-using SDK definitions via typedefs removes duplicated models.


6-10: Edge-case for unknown provider

cexDataProvider() falls back to unknown, which relies on that enum member existing in the SDK. Confirm it’s still present after the SDK upgrade to avoid runtime errors.

lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart (1)

175-178: Totals may disagree with the plotted data
_calculateTotal* functions run on all coins, while the chart just built might exclude unsupported / inactive coins. This can show a balance that does not match the chart line. Pass the exact coin list used to build chart instead of the original coins argument.

Also applies to: 183-186

Comment on lines +22 to 32
required this.totalBalance,
required this.totalChange24h,
required this.percentageChange24h,
});

final ChartData portfolioGrowth;
final double percentageIncrease;
final double totalBalance;
final double totalChange24h;
final double percentageChange24h;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid storing money in double.

double introduces rounding errors that quickly surface in financial UIs (e.g. $0.01 sometimes renders as 0.009999).
If practical, migrate these three monetary fields to a fixed-precision type (Decimal, BigInt cents, or a dedicated Money class) and format at the edge.

🤖 Prompt for AI Agents
In lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_state.dart between
lines 22 and 32, the fields totalBalance, totalChange24h, and
percentageChange24h are currently typed as double, which can cause rounding
errors in financial calculations. To fix this, change these fields to use a
fixed-precision type such as Decimal, BigInt representing cents, or a dedicated
Money class. Update all related code to handle this new type and ensure
formatting to string for display happens only at the UI layer.

Comment on lines +525 to +538
Future<double> calculateTotalChange24h(List<Coin> coins) async {
// Fetch current prices including 24h change data
final prices = await _coinsRepository.fetchCurrentPrices() ?? {};

// Calculate the 24h change by summing the change percentage of each coin
// multiplied by its USD balance and divided by 100 (to convert percentage to decimal)
double totalChange = 0.0;
for (final coin in coins) {
final price = prices[coin.id.symbol.configSymbol.toUpperCase()];
final change24h = price?.change24h ?? 0.0;
final usdBalance = coin.lastKnownUsdBalance(_sdk) ?? 0.0;
totalChange += (change24h * usdBalance / 100);
}
return totalChange;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

calculateTotalChange24h triggers an extra network call per invocation.

Every call fetches fresh prices, even though updatePrices() intentionally pre-loads and caches them.
This defeats caching and can hammer the market-data service.

-final prices = await _coinsRepository.fetchCurrentPrices() ?? {};
+final prices = _coinsRepository.getCachedPrices() ?? 
+              await _coinsRepository.fetchCurrentPrices() ?? {};

Alternatively pass the already-cached map from the caller.

📝 Committable suggestion

‼️ 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.

Suggested change
Future<double> calculateTotalChange24h(List<Coin> coins) async {
// Fetch current prices including 24h change data
final prices = await _coinsRepository.fetchCurrentPrices() ?? {};
// Calculate the 24h change by summing the change percentage of each coin
// multiplied by its USD balance and divided by 100 (to convert percentage to decimal)
double totalChange = 0.0;
for (final coin in coins) {
final price = prices[coin.id.symbol.configSymbol.toUpperCase()];
final change24h = price?.change24h ?? 0.0;
final usdBalance = coin.lastKnownUsdBalance(_sdk) ?? 0.0;
totalChange += (change24h * usdBalance / 100);
}
return totalChange;
Future<double> calculateTotalChange24h(List<Coin> coins) async {
// Fetch current prices including 24h change data
- final prices = await _coinsRepository.fetchCurrentPrices() ?? {};
+ final prices = _coinsRepository.getCachedPrices()
+ ?? await _coinsRepository.fetchCurrentPrices() ?? {};
// Calculate the 24h change by summing the change percentage of each coin
// multiplied by its USD balance and divided by 100 (to convert percentage to decimal)
double totalChange = 0.0;
for (final coin in coins) {
final price = prices[coin.id.symbol.configSymbol.toUpperCase()];
final change24h = price?.change24h ?? 0.0;
final usdBalance = coin.lastKnownUsdBalance(_sdk) ?? 0.0;
totalChange += (change24h * usdBalance / 100);
}
return totalChange;
}
🤖 Prompt for AI Agents
In lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_repository.dart
around lines 525 to 538, the calculateTotalChange24h method fetches current
prices on every call, causing unnecessary network requests and bypassing the
existing caching done by updatePrices(). To fix this, modify the method to
accept the cached prices map as a parameter from the caller instead of fetching
it internally, and use this passed-in map to calculate the total 24h change,
thereby avoiding redundant network calls and leveraging the cached data.

Comment on lines +124 to +128
Stream<Object?>.periodic(event.updateFrequency).asyncMap((_) async {
// Update prices before fetching chart data
await portfolioGrowthRepository.updatePrices();
return _fetchPortfolioGrowthChart(event);
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Un-throttled price polling may flood the CEX API
updatePrices() is executed on every tick of Stream.periodic. If event.updateFrequency is small (e.g. a few seconds), the wallet will hammer the price endpoint continuously, even when the UI is backgrounded. Consider one of:

-Stream<Object?>.periodic(event.updateFrequency)…
+Stream<Object?>.periodic(event.updateFrequency.clamp(
+  const Duration(seconds: 30),  // minimum polling interval
+  const Duration(minutes: 5),   // upper bound
+))…

or move price refreshing to a debounced/background service.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In lib/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart around
lines 124 to 128, the current implementation calls updatePrices() on every tick
of Stream.periodic, which can flood the CEX API if the updateFrequency is small.
To fix this, implement a throttling or debouncing mechanism to limit how often
updatePrices() is called, or move the price refreshing logic to a background
service that respects app lifecycle states, ensuring it does not run
aggressively when the UI is backgrounded.

Comment on lines +378 to +427
class CoinListView extends StatelessWidget {
const CoinListView({
super.key,
required this.mode,
required this.searchPhrase,
required this.withBalance,
required this.onActiveCoinItemTap,
required this.onAssetItemTap,
});

final AuthorizeMode mode;
final String searchPhrase;
final bool withBalance;
final Function(Coin) onActiveCoinItemTap;
final Function(Coin) onAssetItemTap;

@override
Widget build(BuildContext context) {
switch (mode) {
case AuthorizeMode.logIn:
return ActiveCoinsList(
searchPhrase: searchPhrase,
withBalance: withBalance,
onCoinItemTap: onActiveCoinItemTap,
);
case AuthorizeMode.hiddenLogin:
case AuthorizeMode.noLogin:
return AssetsList(
useGroupedView: true,
assets: context
.read<CoinsBloc>()
.state
.coins
.values
.map((coin) => coin.assetId)
.toList(),
withBalance: false,
searchPhrase: searchPhrase,
onAssetItemTap: (assetId) => onAssetItemTap(
context
.read<CoinsBloc>()
.state
.coins
.values
.firstWhere((coin) => coin.assetId == assetId),
),
);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

firstWhere can throw for unknown asset IDs
CoinListView converts an assetId back to a Coin with firstWhere, which throws if the coin set changes between build and tap. Use a null-safe lookup:

- .firstWhere((coin) => coin.assetId == assetId),
+ .firstWhereOrNull((coin) => coin.assetId == assetId) ??
+   Coin.unknown(assetId),

(or guard against null).

📝 Committable suggestion

‼️ 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.

Suggested change
class CoinListView extends StatelessWidget {
const CoinListView({
super.key,
required this.mode,
required this.searchPhrase,
required this.withBalance,
required this.onActiveCoinItemTap,
required this.onAssetItemTap,
});
final AuthorizeMode mode;
final String searchPhrase;
final bool withBalance;
final Function(Coin) onActiveCoinItemTap;
final Function(Coin) onAssetItemTap;
@override
Widget build(BuildContext context) {
switch (mode) {
case AuthorizeMode.logIn:
return ActiveCoinsList(
searchPhrase: searchPhrase,
withBalance: withBalance,
onCoinItemTap: onActiveCoinItemTap,
);
case AuthorizeMode.hiddenLogin:
case AuthorizeMode.noLogin:
return AssetsList(
useGroupedView: true,
assets: context
.read<CoinsBloc>()
.state
.coins
.values
.map((coin) => coin.assetId)
.toList(),
withBalance: false,
searchPhrase: searchPhrase,
onAssetItemTap: (assetId) => onAssetItemTap(
context
.read<CoinsBloc>()
.state
.coins
.values
.firstWhere((coin) => coin.assetId == assetId),
),
);
}
}
}
class CoinListView extends StatelessWidget {
const CoinListView({
super.key,
required this.mode,
required this.searchPhrase,
required this.withBalance,
required this.onActiveCoinItemTap,
required this.onAssetItemTap,
});
final AuthorizeMode mode;
final String searchPhrase;
final bool withBalance;
final Function(Coin) onActiveCoinItemTap;
final Function(Coin) onAssetItemTap;
@override
Widget build(BuildContext context) {
switch (mode) {
case AuthorizeMode.logIn:
return ActiveCoinsList(
searchPhrase: searchPhrase,
withBalance: withBalance,
onCoinItemTap: onActiveCoinItemTap,
);
case AuthorizeMode.hiddenLogin:
case AuthorizeMode.noLogin:
return AssetsList(
useGroupedView: true,
assets: context
.read<CoinsBloc>()
.state
.coins
.values
.map((coin) => coin.assetId)
.toList(),
withBalance: false,
searchPhrase: searchPhrase,
onAssetItemTap: (assetId) => onAssetItemTap(
context
.read<CoinsBloc>()
.state
.coins
.values
- .firstWhere((coin) => coin.assetId == assetId),
+ .firstWhereOrNull((coin) => coin.assetId == assetId) ??
+ Coin.unknown(assetId),
),
);
}
}
}
🤖 Prompt for AI Agents
In lib/views/wallet/wallet_page/wallet_main/wallet_main.dart between lines 378
and 427, the use of firstWhere to find a Coin by assetId can throw an exception
if the assetId is not found due to changes in the coin set between build and
tap. To fix this, replace firstWhere with a null-safe lookup method such as
firstWhereOrNull or use a try-catch block to handle the case where no matching
Coin is found, ensuring the onAssetItemTap callback is only called with a valid
Coin or guarded against null values.

@ca333 ca333 merged commit 19dff7d into main Jun 14, 2025
13 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

QA Ready for QA Testing release candidate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants