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
10 changes: 10 additions & 0 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -484,19 +484,29 @@
"withdrawPreview": "Preview Withdrawal",
"withdrawPreviewZhtlcNote": "ZHTLC transactions can take a while to generate.\nPlease stay on this page until the preview is ready, otherwise you will need to start over.",
"withdrawPreviewError": "Error occurred while fetching withdrawal preview",
"withdrawDestination": "Destination",
"withdrawNetworkDetails": "Network details",
"withdrawHighFee": "High fee",
"withdrawPreviewExpiresIn": "Preview expires in {}s",
"withdrawPreviewRefreshing": "Refreshing preview...",
"withdrawTronBandwidthUsed": "Bandwidth (NET) used",
"withdrawTronBandwidthFee": "Bandwidth fee",
"withdrawTronBandwidthSource": "Bandwidth source",
"withdrawTronEnergyUsed": "Energy used",
"withdrawTronEnergyFee": "Energy fee",
"withdrawTronEnergySource": "Energy source",
"withdrawTronAccountActivationFee": "Account activation fee",
"withdrawTronFeeSummary": "TRON fee summary",
"withdrawTronFeePaidIn": "Paid in {}",
"withdrawTronBandwidthCovered": "Covered by free NET bandwidth",
"withdrawTronEnergyCovered": "Covered by free energy",
"withdrawTronResourceNotUsed": "Not used",
"withdrawTronFeeSummaryCharged": "Network will charge {} {}.",
"withdrawTronFeeSummaryCovered": "No {} fee will be charged (covered by resources).",
"withdrawTronPreviewExpired": "This TRON transaction preview expired. Regenerate it to continue.",
"withdrawTronPreviewRefreshFailed": "This TRON transaction preview expired and could not be refreshed.",
"withdrawTronPreviewRegenerate": "Regenerate",
"withdrawAwaitingConfirmations": "Awaiting confirmations",
"txHistoryFetchError": "Error fetching tx history from the endpoint. Unsupported type: {}",
"txHistoryNoTransactions": "Transactions are not available",
"maxGapLimitReached": "Maximum gap limit reached - please use existing unused addresses first",
Expand Down
8 changes: 7 additions & 1 deletion lib/bloc/bridge_form/bridge_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/data_from_service.dart';
import 'package:web_dex/model/dex_form_error.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';
import 'package:web_dex/model/trade_preimage.dart';
import 'package:web_dex/model/typedef.dart';
import 'package:web_dex/model/wallet.dart';
Expand Down Expand Up @@ -672,7 +673,12 @@ class BridgeBloc extends Bloc<BridgeEvent, BridgeState> {
path: 'bridge_bloc::_getFeesData',
isError: true,
);
return DataFromService(error: TextError(error: 'Failed to request fees'));
return DataFromService(
error: TextError(
error: formatKdfUserFacingError(e),
technicalDetails: extractKdfTechnicalDetails(e),
),
);
}
}

Expand Down
6 changes: 5 additions & 1 deletion lib/bloc/bridge_form/bridge_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/data_from_service.dart';
import 'package:web_dex/model/dex_form_error.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';
import 'package:web_dex/model/trade_preimage.dart';
import 'package:web_dex/shared/utils/formatters.dart';
import 'package:web_dex/shared/utils/utils.dart';
Expand Down Expand Up @@ -139,7 +140,10 @@ class BridgeValidator {
isError: true,
);
return DataFromService(
error: TextError(error: 'Failed to request trade preimage'),
error: TextError(
error: formatKdfUserFacingError(e),
technicalDetails: extractKdfTechnicalDetails(e),
),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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/constants.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';

part 'portfolio_growth_event.dart';
part 'portfolio_growth_state.dart';
Expand Down Expand Up @@ -354,7 +355,10 @@ class PortfolioGrowthBloc
);
emit(
GrowthChartLoadFailure(
error: TextError(error: 'Failed to load portfolio growth'),
error: TextError(
error: formatKdfUserFacingError(error),
technicalDetails: extractKdfTechnicalDetails(error),
),
selectedPeriod: event.selectedPeriod,
totalCoins: totalCoins,
coinsWithKnownBalance: coinsWithKnownBalance,
Expand Down
6 changes: 5 additions & 1 deletion lib/bloc/cex_market_data/profit_loss/profit_loss_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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/constants.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';

part 'profit_loss_event.dart';
part 'profit_loss_state.dart';
Expand Down Expand Up @@ -291,7 +292,10 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
_log.shout('Failed to load portfolio profit/loss', error, stackTrace);
emit(
ProfitLossLoadFailure(
error: TextError(error: 'Failed to load portfolio profit/loss'),
error: TextError(
error: formatKdfUserFacingError(error),
technicalDetails: extractKdfTechnicalDetails(error),
),
selectedPeriod: event.selectedPeriod,
),
);
Expand Down
32 changes: 1 addition & 31 deletions lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,37 +228,7 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
return LocaleKeys.connectionToServersFailing.tr(args: [assetId]);
}

if (error is SdkError) {
return _localizedSdkError(error);
}

if (error is MmRpcException) {
return error.localizedMessage;
}

if (error is GeneralErrorResponse) {
return error.localizedMessage;
}

final raw = error.toString().trim();
if (raw.isEmpty) {
return LocaleKeys.somethingWrong.tr();
}

const exceptionPrefix = 'Exception: ';
if (raw.startsWith(exceptionPrefix)) {
final message = raw.substring(exceptionPrefix.length).trim();
if (message.isNotEmpty) {
return message;
}
}

return raw;
}

String _localizedSdkError(SdkError error) {
final localized = error.messageKey.tr(args: error.messageArgs);
return localized == error.messageKey ? error.fallbackMessage : localized;
return formatKdfUserFacingError(error);
}

bool _isNetworkLikeError(Object error) {
Expand Down
27 changes: 24 additions & 3 deletions lib/bloc/coins_bloc/coins_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -808,17 +808,38 @@ class CoinsRepo {
_invalidateActivatedAssetsCache();
}

double? getUsdPriceByAmount(String amount, String coinAbbr) {
/// Calculates USD value for a numeric [amount] of [coinAbbr].
///
/// Prefer this method over [getUsdPriceByAmount] to avoid string parsing
/// issues (e.g. accidentally passing display-formatted values like
/// `"1.1 TRX"`).
double? getUsdPriceForAmount(num amount, String coinAbbr) {
final Coin? coin = getCoin(coinAbbr);
final double? parsedAmount = double.tryParse(amount);
final double parsedAmount = amount.toDouble();
final double? usdPrice = coin?.usdPrice?.price?.toDouble();

if (coin == null || usdPrice == null || parsedAmount == null) {
if (coin == null || usdPrice == null) {
return null;
}
return parsedAmount * usdPrice;
}

@Deprecated(
'Use getUsdPriceForAmount(num amount, String coinAbbr) to avoid '
'string-parsing bugs from display-formatted values.',
)
double? getUsdPriceByAmount(String amount, String coinAbbr) {
final double? parsedAmount = double.tryParse(amount);
if (parsedAmount == null) {
_log.warning(
'Invalid amount "$amount" passed to getUsdPriceByAmount for $coinAbbr. '
'Use getUsdPriceForAmount() with a numeric value.',
);
return null;
}
return getUsdPriceForAmount(parsedAmount, coinAbbr);
}

/// Fetches current prices for a broad set of assets
///
/// This method is used to fetch prices for a broad set of assets so unauthenticated users
Expand Down
24 changes: 15 additions & 9 deletions lib/bloc/coins_bloc/coins_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,34 @@ class CoinsState extends Equatable {
return getPriceForAsset(assetId)?.change24h?.toDouble();
}

/// 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
/// Calculates the USD price for a given numeric [amount] of [coinAbbr].
///
/// 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) {
double? getUsdPriceForAmount(num amount, String coinAbbr) {
final Coin? coin = coins[coinAbbr];
final double? parsedAmount = double.tryParse(amount);
final double parsedAmount = amount.toDouble();
final CexPrice? cexPrice = prices[coinAbbr.toUpperCase()];
final double? usdPrice = cexPrice?.price?.toDouble();

if (coin == null || usdPrice == null || parsedAmount == null) {
if (coin == null || usdPrice == null) {
return null;
}
return parsedAmount * usdPrice;
}

/// Backward-compatible string overload.
@Deprecated(
'Use getUsdPriceForAmount(num amount, String coinAbbr) to avoid '
'string-parsing bugs from display-formatted values.',
)
double? getUsdPriceByAmount(String amount, String coinAbbr) {
final double? parsedAmount = double.tryParse(amount);
if (parsedAmount == null) return null;
return getUsdPriceForAmount(parsedAmount, coinAbbr);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ class CustomTokenImportBloc

final balanceInfo = await _coinsRepo.tryGetBalanceInfo(tokenData.id);
final balance = balanceInfo.spendable;
final usdBalance = _coinsRepo.getUsdPriceByAmount(
balance.toString(),
final usdBalance = _coinsRepo.getUsdPriceForAmount(
balance.toDouble(),
tokenData.id.id,
);

Expand Down
8 changes: 7 additions & 1 deletion lib/bloc/taker_form/taker_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/data_from_service.dart';
import 'package:web_dex/model/dex_form_error.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';
import 'package:web_dex/model/trade_preimage.dart';
import 'package:web_dex/model/wallet.dart';
import 'package:web_dex/shared/utils/utils.dart';
Expand Down Expand Up @@ -608,7 +609,12 @@ class TakerBloc extends Bloc<TakerEvent, TakerState> {
path: 'taker_bloc::_getFeesData',
isError: true,
);
return DataFromService(error: TextError(error: 'Failed to request fees'));
return DataFromService(
error: TextError(
error: formatKdfUserFacingError(e),
technicalDetails: extractKdfTechnicalDetails(e),
),
);
}
}

Expand Down
6 changes: 5 additions & 1 deletion lib/bloc/taker_form/taker_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/data_from_service.dart';
import 'package:web_dex/model/dex_form_error.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';
import 'package:web_dex/model/trade_preimage.dart';
import 'package:web_dex/shared/utils/formatters.dart';
import 'package:web_dex/shared/utils/utils.dart';
Expand Down Expand Up @@ -333,7 +334,10 @@ class TakerValidator {
isError: true,
);
return DataFromService(
error: TextError(error: 'Failed to request trade preimage'),
error: TextError(
error: formatKdfUserFacingError(e),
technicalDetails: extractKdfTechnicalDetails(e),
),
);
}
}
Expand Down
25 changes: 2 additions & 23 deletions lib/bloc/transaction_history/transaction_history_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import 'dart:async';

import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:komodo_defi_sdk/komodo_defi_sdk.dart';
import 'package:komodo_defi_sdk/src/activation/activation_exceptions.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_event.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_state.dart';
import 'package:web_dex/generated/codegen_loader.g.dart';
import 'package:web_dex/model/coin.dart';
import 'package:web_dex/model/text_error.dart';
import 'package:web_dex/shared/utils/extensions/transaction_extensions.dart';
import 'package:web_dex/shared/utils/kdf_error_display.dart';
import 'package:web_dex/shared/utils/utils.dart';

class TransactionHistoryBloc
Expand All @@ -28,26 +26,7 @@ class TransactionHistoryBloc
final KomodoDefiSdk _sdk;
StreamSubscription<List<Transaction>>? _historySubscription;

String _errorMessageFrom(Object error) {
if (error is SdkError) {
final localized = error.messageKey.tr(args: error.messageArgs);
return localized == error.messageKey ? error.fallbackMessage : localized;
}

if (error is ActivationFailedException && error.originalError is SdkError) {
final sdkError = error.originalError as SdkError;
final localized = sdkError.messageKey.tr(args: sdkError.messageArgs);
return localized == sdkError.messageKey
? sdkError.fallbackMessage
: localized;
}

if (error is ActivationFailedException) {
return 'Asset activation failed: ${error.message}';
}

return LocaleKeys.somethingWrong.tr();
}
String _errorMessageFrom(Object error) => formatKdfUserFacingError(error);

@override
Future<void> close() async {
Expand Down
Loading
Loading