fix: crypto amount calculation logic with max decimals (#2697)
* fix: crypto amount calculation logic with max decimals * refactor: remove `_setCryptoNumMaximumFractionDigits` and `_cryptoNumberFormat` to simplify `OutputBase` logic
Konstantin Ullrich committed
Dec 1, 2025 at 21:43 UTC
86f30a216cdd82d9fd8bde88fdebf0fbcd468b71
2 files changed
+21
-48
cw_core/lib/crypto_currency.dart
+14
-3
@@ -1,6 +1,9 @@
1
import 'package:cw_core/currency.dart';
2
import 'package:cw_core/enumerable_item.dart';
3
import 'package:collection/collection.dart';
4
+import 'package:cw_core/parse_fixed.dart';
5
+
6
+import 'format_fixed.dart';
7
8
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implements Currency {
9
const CryptoCurrency({
@@ -394,7 +397,15 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
397
@override
398
String toString() => title;
399
397
- bool titleAndTagEqual(CryptoCurrency other) {
398
- return title == other.title && tag == other.tag;
399
- }
400
+ bool titleAndTagEqual(CryptoCurrency other) => title == other.title && tag == other.tag;
401
+
402
+ /// Format the raw amount into its decimal representation eg. turn Sats into Bitcoin
403
+ String formatAmount(BigInt amount, {int? fractionalDigits, bool trimZeros = true}) =>
404
+ formatFixed(amount, decimals, fractionalDigits: fractionalDigits, trimZeros: trimZeros);
405
+
406
+ /// Parse the [value] and turn it into the smallest denomination eg. turn Bitcoin into Sats
407
+ BigInt parseAmount(String value) => parseFixed(value, decimals);
408
+
409
+ /// Try parsing the [value] and turn it into the smallest denomination eg. turn Bitcoin into Sats
410
+ BigInt? tryParseAmount(String value) => tryParseFixed(value, decimals);
411
}
lib/view_model/send/output.dart
+7
-45
@@ -19,7 +19,6 @@ import 'package:cw_core/transaction_history.dart';
19
import 'package:cw_core/transaction_info.dart';
20
import 'package:cw_core/utils/print_verbose.dart';
21
import 'package:flutter/material.dart';
22
-import 'package:intl/intl.dart';
22
import 'package:mobx/mobx.dart';
23
import 'package:cw_core/wallet_base.dart';
24
import 'package:cake_wallet/monero/monero.dart';
@@ -42,8 +41,7 @@ class Output = OutputBase with _$Output;
41
abstract class OutputBase with Store {
42
OutputBase(
43
this._wallet, this._settingsStore, this._fiatConversationStore, this.cryptoCurrencyHandler)
45
- : _cryptoNumberFormat = NumberFormat(cryptoNumberPattern),
46
- key = UniqueKey(),
44
+ : key = UniqueKey(),
45
sendAll = false,
46
cryptoAmount = '',
47
cryptoFullBalance = '',
@@ -53,7 +51,6 @@ abstract class OutputBase with Store {
51
extractedAddress = '',
52
estimatedFee = '0.0',
53
parsedAddress = ParsedAddress(addresses: []) {
56
- _setCryptoNumMaximumFractionDigits();
54
autorun((_) {
55
final status = _wallet.syncStatus;
56
printV("Sync status changed to $status. Recalculating fees");
@@ -289,7 +286,6 @@ abstract class OutputBase with Store {
286
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
287
final SettingsStore _settingsStore;
288
final FiatConversionStore _fiatConversationStore;
292
- final NumberFormat _cryptoNumberFormat;
289
290
@action
291
void setSendAll(String fullBalance) {
@@ -302,7 +298,6 @@ abstract class OutputBase with Store {
298
void updateWallet(
299
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> newWallet) {
300
_wallet = newWallet;
305
- _setCryptoNumMaximumFractionDigits();
301
}
302
303
@action
@@ -355,11 +350,12 @@ abstract class OutputBase with Store {
350
@action
351
void _updateCryptoAmount() {
352
try {
358
- final crypto = double.parse(fiatAmount.replaceAll(',', '.')) /
359
- _fiatConversationStore.prices[cryptoCurrencyHandler()]!;
360
- final cryptoAmountTmp = _cryptoNumberFormat.format(crypto);
361
- if (cryptoAmount != cryptoAmountTmp) {
362
- cryptoAmount = cryptoAmountTmp;
353
+ final crypto = (double.parse(fiatAmount.replaceAll(',', '.')) /
354
+ _fiatConversationStore.prices[cryptoCurrencyHandler()]!)
355
+ .toStringAsFixed(cryptoCurrencyHandler().decimals);
356
+
357
+ if (cryptoAmount != crypto) {
358
+ cryptoAmount = crypto;
359
}
360
} catch (e) {
361
cryptoAmount = '';
@@ -375,40 +371,6 @@ abstract class OutputBase with Store {
371
return fields;
372
}
373
378
- void _setCryptoNumMaximumFractionDigits() {
379
- var maximumFractionDigits = 0;
380
-
381
- switch (_wallet.type) {
382
- case WalletType.monero:
383
- case WalletType.ethereum:
384
- case WalletType.polygon:
385
- case WalletType.base:
386
- case WalletType.arbitrum:
387
- case WalletType.solana:
388
- case WalletType.tron:
389
- case WalletType.haven:
390
- case WalletType.zano:
391
- case WalletType.nano:
392
- case WalletType.decred:
393
- maximumFractionDigits = 12;
394
- break;
395
- case WalletType.bitcoin:
396
- case WalletType.litecoin:
397
- case WalletType.bitcoinCash:
398
- case WalletType.dogecoin:
399
- maximumFractionDigits = 8;
400
- break;
401
- case WalletType.wownero:
402
- maximumFractionDigits = 11;
403
- break;
404
- case WalletType.none:
405
- case WalletType.banano:
406
- break;
407
- }
408
-
409
- _cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits;
410
- }
411
-
374
Future<void> fetchParsedAddress(BuildContext context) async {
375
final domain = address;
376
final currency = cryptoCurrencyHandler();