Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Komodo Wallet v0.9.1 Release Notes

This is a hotfix release that addresses critical issues with Trezor hardware wallet login functionality.

## πŸ› Bug Fixes

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

**Full Changelog**: [0.9.0...0.9.1](https://github.com/KomodoPlatform/komodo-wallet/compare/0.9.0...0.9.1)

---

# Komodo Wallet v0.9.0 Release Notes

We are excited to announce Komodo Wallet v0.9.0. This release introduces HD wallet functionality, cross-platform fiat on-ramp improvements, a new feedback provider, and numerous bug fixes and dependency upgrades.
Expand Down
6 changes: 3 additions & 3 deletions app_theme/lib/src/dark/theme_global_dark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ ThemeData get themeGlobalDark {
fontFamily: 'Manrope',
scaffoldBackgroundColor: colorScheme.onSurface,
cardColor: colorScheme.surface,
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(18)),
Expand All @@ -83,7 +83,7 @@ ThemeData get themeGlobalDark {
iconTheme: IconThemeData(color: colorScheme.primary),
progressIndicatorTheme:
ProgressIndicatorThemeData(color: colorScheme.primary),
dialogTheme: const DialogTheme(
dialogTheme: const DialogThemeData(
backgroundColor: Color.fromRGBO(14, 16, 27, 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Expand Down Expand Up @@ -144,7 +144,7 @@ ThemeData get themeGlobalDark {
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
),
),
tabBarTheme: TabBarTheme(
tabBarTheme: TabBarThemeData(
labelColor: textColor,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
Expand Down
6 changes: 3 additions & 3 deletions app_theme/lib/src/light/theme_global_light.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ ThemeData get themeGlobalLight {
fontFamily: 'Manrope',
scaffoldBackgroundColor: colorScheme.onSurface,
cardColor: colorScheme.surface,
cardTheme: CardTheme(
cardTheme: CardThemeData(
color: colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(18)),
Expand All @@ -67,7 +67,7 @@ ThemeData get themeGlobalLight {
iconTheme: IconThemeData(color: colorScheme.primary),
progressIndicatorTheme:
ProgressIndicatorThemeData(color: colorScheme.primary),
dialogTheme: const DialogTheme(
dialogTheme: const DialogThemeData(
backgroundColor: Color.fromRGBO(255, 255, 255, 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Expand Down Expand Up @@ -152,7 +152,7 @@ ThemeData get themeGlobalLight {
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
),
),
tabBarTheme: TabBarTheme(
tabBarTheme: TabBarThemeData(
labelColor: textColor,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
Expand Down
8 changes: 6 additions & 2 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@
"cancelAll": "Cancel all",
"type": "Type",
"sell": "Sell",
"sellCrypto": "Sell Crypto",
"sellCryptoDescription": "Select a cryptocurrency to sell through our integrated Bitrefill service.",
"buy": "Buy",
"changingWalletPassword": "Changing your wallet password",
"changingWalletPasswordDescription": "This password will be used for logging in to your wallet",
Expand Down Expand Up @@ -639,7 +641,7 @@
"decimals": "Decimals",
"onlySendToThisAddress": "Only send {} to this address",
"scanTheQrCode": "Scan the QR code on any mobile device wallet",
"swapAddress": "Swap Address",
"tradingAddress": "Trading Address",
"addresses": "Addresses",
"creating": "Creating",
"createAddress": "Create Address",
Expand Down Expand Up @@ -671,5 +673,7 @@
"previewWithdrawal": "Preview Withdrawal",
"createNewAddress": "Create New Address",
"searchAddresses": "Search addresses",
"chart": "Chart"
"chart": "Chart",
"tradingDisabledTooltip": "Trading features are currently disabled",
"tradingDisabled": "Trading is currently unavailable"
}
2 changes: 1 addition & 1 deletion docs/FLUTTER_VERSION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Supported Flutter Version

This project supports Flutter `3.29.2` (latest stable release). We aim to keep the project up-to-date with the most recent stable Flutter versions.
This project supports Flutter `3.29.2`. We aim to keep the project up-to-date with the latest `stable` Flutter release.

## Recommended Approach: Multiple Flutter Versions

Expand Down
32 changes: 32 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldb_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Generated file, do not edit.
#

import lldb

def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()

# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'

error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return

def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")
5 changes: 5 additions & 0 deletions ios/Flutter/ephemeral/flutter_lldbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#

command script import --relative-to-command-file flutter_lldb_helper.py
18 changes: 9 additions & 9 deletions lib/app_config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ final Uri discordSupportChannelUrl = Uri.parse(
'https://discord.com/channels/412898016371015680/429676282196787200');
final Uri discordInviteUrl = Uri.parse('https://komodoplatform.com/discord');

// Temporary feature flag to allow merging of the PR
// TODO: Remove this flag after the feature is finalized
/// Const to define if Bitrefill integration is enabled in the app.
const bool isBitrefillIntegrationEnabled = false;

/// Const to define if trading is enabled in the app. Trading is only permitted
/// with test coins for development purposes while the regulatory compliance
/// framework is being developed.
/// Const to define whether to show trading warning dialogs and notices.
/// This can be used to control the display of trading-related warnings
/// throughout the application.
///
///! You are solely responsible for any losses/damage that may occur. Komodo
///! Platform does not condone the use of this app for trading purposes and
///! unequivocally forbids it.
const bool kIsWalletOnly = !kDebugMode;
///! You are solely responsible for any losses/damage that may occur due to
///! compliance issues, bugs, or other unforeseen circumstances. Komodo
///! Platform and its legal entities do not condone the use of this app for
///! trading purposes where it is not legally compliant.
const bool kShowTradingWarning = false;

const Duration kPerformanceLogInterval = Duration(minutes: 1);

Expand Down
11 changes: 10 additions & 1 deletion lib/bloc/app_bloc_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ import 'package:web_dex/bloc/settings/settings_bloc.dart';
import 'package:web_dex/bloc/settings/settings_repository.dart';
import 'package:web_dex/bloc/system_health/system_clock_repository.dart';
import 'package:web_dex/bloc/system_health/system_health_bloc.dart';
import 'package:web_dex/bloc/trading_status/trading_status_bloc.dart';
import 'package:web_dex/bloc/taker_form/taker_bloc.dart';
import 'package:web_dex/bloc/trading_status/trading_status_repository.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_bloc.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_repo.dart';
import 'package:web_dex/bloc/trezor_bloc/trezor_repo.dart';
Expand Down Expand Up @@ -177,6 +179,7 @@ class AppBlocRoot extends StatelessWidget {
RepositoryProvider(
create: (_) => KmdRewardsBloc(coinsRepository, mm2Api),
),
RepositoryProvider(create: (_) => TradingStatusRepository()),
],
child: MultiBlocProvider(
providers: [
Expand All @@ -194,7 +197,7 @@ class AppBlocRoot extends StatelessWidget {
komodoDefiSdk,
)..add(
const PriceChartStarted(
symbols: ['KMD'],
symbols: ['BTC'],
period: Duration(days: 30),
),
),
Expand Down Expand Up @@ -287,6 +290,12 @@ class AppBlocRoot extends StatelessWidget {
),
),
),
BlocProvider<TradingStatusBloc>(
lazy: false,
create: (context) => TradingStatusBloc(
context.read<TradingStatusRepository>(),
)..add(TradingStatusCheckRequested()),
),
BlocProvider<SystemHealthBloc>(
create: (_) => SystemHealthBloc(SystemClockRepository(), mm2Api)
..add(SystemHealthPeriodicCheckStarted()),
Expand Down
2 changes: 1 addition & 1 deletion lib/bloc/bitrefill/bloc/bitrefill_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class BitrefillBloc extends Bloc<BitrefillEvent, BitrefillState> {
emit(const BitrefillLoadInProgress());
final String url = _bitrefillRepository.embeddedBitrefillUrl(
coinAbbr: event.coin?.abbr,
refundAddress: event.coin?.address,
refundAddress: event.refundAddress ?? event.coin?.address,
);

final List<String> supportedCoins =
Expand Down
5 changes: 3 additions & 2 deletions lib/bloc/bitrefill/bloc/bitrefill_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ sealed class BitrefillEvent extends Equatable {
/// Request to load the bitrefill state with the url and supported coins
/// from the bitrefill provider.
final class BitrefillLoadRequested extends BitrefillEvent {
const BitrefillLoadRequested({this.coin});
const BitrefillLoadRequested({this.coin, this.refundAddress});

final Coin? coin;
final String? refundAddress;

@override
List<Object> get props => [];
List<Object> get props => [coin?.abbr ?? '', refundAddress ?? ''];
}

/// Request to open the Bitrefill widget to make a purchase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:web_dex/bloc/cex_market_data/sdk_auth_activation_extension.dart'
import 'package:web_dex/mm2/mm2_api/rpc/base.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/extensions/legacy_coin_migration_extensions.dart';

part 'portfolio_growth_event.dart';
part 'portfolio_growth_state.dart';
Expand Down Expand Up @@ -120,10 +121,13 @@ class PortfolioGrowthBloc
await emit.forEach(
// computation is omitted, so null-valued events are emitted on a set
// interval.
Stream<Object?>.periodic(event.updateFrequency)
.asyncMap((_) async => _fetchPortfolioGrowthChart(event)),
Stream<Object?>.periodic(event.updateFrequency).asyncMap((_) async {
// Update prices before fetching chart data
await portfolioGrowthRepository.updatePrices();
return _fetchPortfolioGrowthChart(event);
}),
onData: (data) =>
_handlePortfolioGrowthUpdate(data, event.selectedPeriod),
_handlePortfolioGrowthUpdate(data, event.selectedPeriod, event.coins),
onError: (error, stackTrace) {
_log.shout('Failed to load portfolio growth', error, stackTrace);
return GrowthChartLoadFailure(
Expand Down Expand Up @@ -164,10 +168,21 @@ class PortfolioGrowthBloc
return state;
}

// Fetch prices before calculating total change
// This ensures we have the latest prices in the cache
await portfolioGrowthRepository.updatePrices();

final totalBalance = _calculateTotalBalance(coins);
final totalChange24h = _calculateTotalChange24h(coins);
final percentageChange24h = _calculatePercentageChange24h(coins);

return PortfolioGrowthChartLoadSuccess(
portfolioGrowth: chart,
percentageIncrease: chart.percentageIncrease,
selectedPeriod: event.selectedPeriod,
totalBalance: totalBalance,
totalChange24h: totalChange24h,
percentageChange24h: percentageChange24h,
);
}

Expand Down Expand Up @@ -205,20 +220,71 @@ class PortfolioGrowthBloc
PortfolioGrowthState _handlePortfolioGrowthUpdate(
ChartData growthChart,
Duration selectedPeriod,
List<Coin> coins,
) {
if (growthChart.isEmpty && state is PortfolioGrowthChartLoadSuccess) {
return state;
}

final percentageIncrease = growthChart.percentageIncrease;

// TODO? Include the center value in the bloc state instead of
// calculating it in the UI
final totalBalance = _calculateTotalBalance(coins);
final totalChange24h = _calculateTotalChange24h(coins);
final percentageChange24h = _calculatePercentageChange24h(coins);

return PortfolioGrowthChartLoadSuccess(
portfolioGrowth: growthChart,
percentageIncrease: percentageIncrease,
selectedPeriod: selectedPeriod,
totalBalance: totalBalance,
totalChange24h: totalChange24h,
percentageChange24h: percentageChange24h,
);
}

/// Calculate the total balance of all coins in USD
double _calculateTotalBalance(List<Coin> coins) {
double total = coins.fold(
0,
(prev, coin) => prev + (coin.lastKnownUsdBalance(sdk) ?? 0),
);

// Return at least 0.01 if total is positive but very small
if (total > 0 && total < 0.01) {
return 0.01;
}

return total;
}

/// Calculate the total 24h change in USD value
double _calculateTotalChange24h(List<Coin> coins) {
// 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)
return coins.fold(
0.0,
(sum, coin) {
// Use the price change from the CexPrice if available
final usdBalance = coin.lastKnownUsdBalance(sdk) ?? 0.0;
// Get the coin price from the repository's prices cache
final price = portfolioGrowthRepository
.getCachedPrice(coin.id.symbol.configSymbol.toUpperCase());
final change24h = price?.change24h ?? 0.0;
return sum + (change24h * usdBalance / 100);
},
);
}

/// Calculate the percentage change over 24h for the entire portfolio
double _calculatePercentageChange24h(List<Coin> coins) {
final double totalBalance = _calculateTotalBalance(coins);
final double totalChange = _calculateTotalChange24h(coins);

// Avoid division by zero or very small balances
if (totalBalance <= 0.01) {
return 0.0;
}

// Return the percentage change
return (totalChange / totalBalance) * 100;
}
}
Loading
Loading