WIP use Amount

This commit is contained in:
julian 2023-04-05 16:06:31 -06:00
parent 2936c1b807
commit 81c612ddd7
104 changed files with 2211 additions and 1890 deletions
lib
db
models
pages
pages_desktop_specific
route_generator.dart
services
utilities
widgets
test

View file

@ -3,6 +3,7 @@ import 'package:flutter_native_splash/cli_commands.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/exceptions/main_db/main_db_exception.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';

View file

@ -67,10 +67,10 @@ extension MainDBQueries on MainDB {
final maybeDecimal = Decimal.tryParse(searchTerm);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Format.decimalAmountToSatoshis(
Amount.fromDecimal(
maybeDecimal,
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
}
@ -139,10 +139,10 @@ extension MainDBQueries on MainDB {
final maybeDecimal = Decimal.tryParse(searchTerm);
if (maybeDecimal != null) {
qq = qq.or().valueEqualTo(
Format.decimalAmountToSatoshis(
Amount.fromDecimal(
maybeDecimal,
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
}

View file

@ -1,15 +1,21 @@
import 'dart:convert';
import 'package:decimal/decimal.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
enum Unit {
base,
u,
m,
normal;
}
class Balance {
final Coin coin;
final int total;
final int spendable;
final int blockedTotal;
final int pendingSpendable;
final Amount total;
final Amount spendable;
final Amount blockedTotal;
final Amount pendingSpendable;
Balance({
required this.coin,
@ -19,36 +25,64 @@ class Balance {
required this.pendingSpendable,
});
Decimal getTotal({bool includeBlocked = true}) => Format.satoshisToAmount(
includeBlocked ? total : total - blockedTotal,
coin: coin,
);
// Decimal getTotal({bool includeBlocked = true}) => Format.satoshisToAmount(
// includeBlocked ? total : total - blockedTotal,
// coin: coin,
// );
//
// Decimal getSpendable() => Format.satoshisToAmount(
// spendable,
// coin: coin,
// );
//
// Decimal getPending() => Format.satoshisToAmount(
// pendingSpendable,
// coin: coin,
// );
//
// Decimal getBlocked() => Format.satoshisToAmount(
// blockedTotal,
// coin: coin,
// );
Decimal getSpendable() => Format.satoshisToAmount(
spendable,
coin: coin,
);
Decimal getPending() => Format.satoshisToAmount(
pendingSpendable,
coin: coin,
);
Decimal getBlocked() => Format.satoshisToAmount(
blockedTotal,
coin: coin,
);
String toJsonIgnoreCoin() => jsonEncode(toMap()..remove("coin"));
String toJsonIgnoreCoin() => jsonEncode({
"total": total.toJsonString(),
"spendable": spendable.toJsonString(),
"blockedTotal": blockedTotal.toJsonString(),
"pendingSpendable": pendingSpendable.toJsonString(),
});
// need to fall back to parsing from in due to cached balances being previously
// stored as int values instead of Amounts
factory Balance.fromJson(String json, Coin coin) {
final decoded = jsonDecode(json);
return Balance(
coin: coin,
total: decoded["total"] as int,
spendable: decoded["spendable"] as int,
blockedTotal: decoded["blockedTotal"] as int,
pendingSpendable: decoded["pendingSpendable"] as int,
total: decoded["total"] is String
? Amount.fromSerializedJsonString(decoded["total"] as String)
: Amount(
rawValue: BigInt.from(decoded["total"] as int),
fractionDigits: coin.decimals,
),
spendable: decoded["spendable"] is String
? Amount.fromSerializedJsonString(decoded["spendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["spendable"] as int),
fractionDigits: coin.decimals,
),
blockedTotal: decoded["blockedTotal"] is String
? Amount.fromSerializedJsonString(decoded["blockedTotal"] as String)
: Amount(
rawValue: BigInt.from(decoded["blockedTotal"] as int),
fractionDigits: coin.decimals,
),
pendingSpendable: decoded["pendingSpendable"] is String
? Amount.fromSerializedJsonString(
decoded["pendingSpendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["pendingSpendable"] as int),
fractionDigits: coin.decimals,
),
);
}

View file

@ -1,14 +1,12 @@
import 'dart:convert';
import 'package:decimal/decimal.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
class TokenBalance extends Balance {
TokenBalance({
required this.contractAddress,
required this.decimalPlaces,
required super.total,
required super.spendable,
required super.blockedTotal,
@ -17,37 +15,35 @@ class TokenBalance extends Balance {
});
final String contractAddress;
final int decimalPlaces;
@override
Decimal getTotal({bool includeBlocked = false}) =>
Format.satoshisToEthTokenAmount(
includeBlocked ? total : total - blockedTotal,
decimalPlaces,
);
@override
Decimal getSpendable() => Format.satoshisToEthTokenAmount(
spendable,
decimalPlaces,
);
@override
Decimal getPending() => Format.satoshisToEthTokenAmount(
pendingSpendable,
decimalPlaces,
);
@override
Decimal getBlocked() => Format.satoshisToEthTokenAmount(
blockedTotal,
decimalPlaces,
);
// @override
// Decimal getTotal({bool includeBlocked = false}) =>
// Format.satoshisToEthTokenAmount(
// includeBlocked ? total : total - blockedTotal,
// decimalPlaces,
// );
//
// @override
// Decimal getSpendable() => Format.satoshisToEthTokenAmount(
// spendable,
// decimalPlaces,
// );
//
// @override
// Decimal getPending() => Format.satoshisToEthTokenAmount(
// pendingSpendable,
// decimalPlaces,
// );
//
// @override
// Decimal getBlocked() => Format.satoshisToEthTokenAmount(
// blockedTotal,
// decimalPlaces,
// );
@override
String toJsonIgnoreCoin() => jsonEncode({
"contractAddress": contractAddress,
"decimalPlaces": decimalPlaces,
"total": total,
"spendable": spendable,
"blockedTotal": blockedTotal,
@ -56,15 +52,36 @@ class TokenBalance extends Balance {
factory TokenBalance.fromJson(
String json,
int fractionDigits,
) {
final decoded = jsonDecode(json);
return TokenBalance(
contractAddress: decoded["contractAddress"] as String,
decimalPlaces: decoded["decimalPlaces"] as int,
total: decoded["total"] as int,
spendable: decoded["spendable"] as int,
blockedTotal: decoded["blockedTotal"] as int,
pendingSpendable: decoded["pendingSpendable"] as int,
total: decoded["total"] is String
? Amount.fromSerializedJsonString(decoded["total"] as String)
: Amount(
rawValue: BigInt.from(decoded["total"] as int),
fractionDigits: fractionDigits,
),
spendable: decoded["spendable"] is String
? Amount.fromSerializedJsonString(decoded["spendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["spendable"] as int),
fractionDigits: fractionDigits,
),
blockedTotal: decoded["blockedTotal"] is String
? Amount.fromSerializedJsonString(decoded["blockedTotal"] as String)
: Amount(
rawValue: BigInt.from(decoded["blockedTotal"] as int),
fractionDigits: fractionDigits,
),
pendingSpendable: decoded["pendingSpendable"] is String
? Amount.fromSerializedJsonString(
decoded["pendingSpendable"] as String)
: Amount(
rawValue: BigInt.from(decoded["pendingSpendable"] as int),
fractionDigits: fractionDigits,
),
);
}
}

View file

@ -1,10 +1,12 @@
import 'package:stackwallet/utilities/amount.dart';
class TransactionFilter {
final bool sent;
final bool received;
final bool trade;
final DateTime? from;
final DateTime? to;
final int? amount;
final Amount? amount;
final String keyword;
TransactionFilter({
@ -23,7 +25,7 @@ class TransactionFilter {
bool? trade,
DateTime? from,
DateTime? to,
int? amount,
Amount? amount,
String? keyword,
}) {
return TransactionFilter(

View file

@ -8,12 +8,13 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/coin_control/utxo_card.dart';
import 'package:stackwallet/pages/coin_control/utxo_details_view.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
@ -682,12 +683,14 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
value += element,
);
return Text(
"${Format.satoshisToAmount(
selectedSum,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
"${selectedSum.toAmount(fractionDigits: coin.decimals).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: widget.requestedTotal == null
? STextStyles.w600_14(context)
: STextStyles.w600_14(context).copyWith(
@ -728,12 +731,14 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
style: STextStyles.w600_14(context),
),
Text(
"${Format.satoshisToAmount(
widget.requestedTotal!,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
"${widget.requestedTotal!.toAmount(fractionDigits: coin.decimals).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: STextStyles.w600_14(context),
),
],

View file

@ -2,10 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
@ -123,10 +124,13 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
"${utxo.value.toAmount(fractionDigits: coin.decimals).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)}} ${coin.ticker}",
style: STextStyles.w600_14(context),
),
const SizedBox(

View file

@ -6,9 +6,10 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -239,12 +240,13 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
width: 16,
),
Text(
"${Format.satoshisToAmount(
utxo!.value,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
"${utxo!.value.toAmount(fractionDigits: coin.decimals).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: STextStyles.pageTitleH2(context),
),
],

View file

@ -11,9 +11,9 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -359,14 +359,10 @@ class _ConfirmChangeNowSendViewState
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
ref.watch(
managerProvider.select((value) => value.coin),
"${(transactionInfo["fee"] as int).toAmount(
fractionDigits: ref.watch(
managerProvider
.select((value) => value.coin.decimals),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
@ -400,26 +396,37 @@ class _ConfirmChangeNowSendViewState
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int) +
(transactionInfo["recipientAmt"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
ref.watch(
Builder(
builder: (context) {
final coin = ref.watch(
managerProvider.select((value) => value.coin),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
);
final fee =
(transactionInfo["fee"] as int).toAmount(
fractionDigits: coin.decimals,
);
final amount =
transactionInfo["recipientAmt"] as Amount;
final total = amount + fee;
final locale = ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
);
return Text(
"${total.localizedStringAsFixed(
locale: locale,
)}"
" ${coin.ticker}",
style: STextStyles.itemSubtitle12(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
);
},
),
],
),
@ -570,16 +577,20 @@ class _ConfirmChangeNowSendViewState
final price = ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => value.getPrice(coin)));
final amount = Format.satoshisToAmount(
transactionInfo["recipientAmt"] as int,
coin: coin,
);
final value = price.item1 * amount;
final amount =
transactionInfo["recipientAmt"] as Amount;
final value = (price.item1 * amount.decimal)
.toAmount(fractionDigits: 2);
final currency = ref.watch(prefsChangeNotifierProvider
.select((value) => value.currency));
final locale = ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
);
return Text(
" | ${value.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} $currency",
" | ${value.localizedStringAsFixed(locale: locale)} $currency",
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
@ -592,12 +603,13 @@ class _ConfirmChangeNowSendViewState
],
),
child: Text(
"${Format.satoshiAmountToPrettyString(transactionInfo["recipientAmt"] as int, ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), ref.watch(
managerProvider.select((value) => value.coin),
))} ${ref.watch(
"${(transactionInfo["recipientAmt"] as Amount).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
@ -625,12 +637,17 @@ class _ConfirmChangeNowSendViewState
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(transactionInfo["fee"] as int, ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), ref.watch(
managerProvider.select((value) => value.coin),
))} ${ref.watch(
"${(transactionInfo["fee"] as int).toAmount(fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
)).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
@ -711,21 +728,35 @@ class _ConfirmChangeNowSendViewState
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString((transactionInfo["fee"] as int) + (transactionInfo["recipientAmt"] as int), ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), ref.watch(
managerProvider.select((value) => value.coin),
))} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
Builder(
builder: (context) {
final coin = ref.watch(
managerProvider.select((value) => value.coin),
);
final fee = (transactionInfo["fee"] as int).toAmount(
fractionDigits: coin.decimals,
);
final amount =
transactionInfo["recipientAmt"] as Amount;
final total = amount + fee;
final locale = ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
);
return Text(
"${total.localizedStringAsFixed(
locale: locale,
)}"
" ${coin.ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
);
},
),
],
),

View file

@ -15,11 +15,11 @@ import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dia
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/background.dart';
@ -526,10 +526,11 @@ class _Step4ViewState extends ConsumerState<Step4View> {
walletsChangeNotifierProvider)
.getManager(tuple.item1);
final amount =
Format.decimalAmountToSatoshis(
model.sendAmount,
manager.coin);
final Amount amount =
model.sendAmount.toAmount(
fractionDigits:
manager.coin.decimals,
);
final address =
model.trade!.payInAddress;
@ -565,7 +566,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
final txDataFuture =
manager.prepareSend(
address: address,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate":
FeeRateType.average,
@ -670,12 +671,17 @@ class _Step4ViewState extends ConsumerState<Step4View> {
.useMaterialPageRoute,
builder:
(BuildContext context) {
final coin =
coinFromTickerCaseInsensitive(
model.trade!
.payInCurrency);
return SendFromView(
coin:
coinFromTickerCaseInsensitive(
model.trade!
.payInCurrency),
amount: model.sendAmount,
coin: coin,
amount: model.sendAmount
.toAmount(
fractionDigits:
coin.decimals,
),
address: model
.trade!.payInAddress,
trade: model.trade!,

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -13,15 +12,14 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -45,7 +43,7 @@ class SendFromView extends ConsumerStatefulWidget {
static const String routeName = "/sendFrom";
final Coin coin;
final Decimal amount;
final Amount amount;
final String address;
final Trade trade;
final bool shouldPopRoot;
@ -57,14 +55,10 @@ class SendFromView extends ConsumerStatefulWidget {
class _SendFromViewState extends ConsumerState<SendFromView> {
late final Coin coin;
late final Decimal amount;
late final Amount amount;
late final String address;
late final Trade trade;
String formatAmount(Decimal amount, Coin coin) {
return amount.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
}
@override
void initState() {
coin = widget.coin;
@ -151,7 +145,13 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
Row(
children: [
Text(
"You need to send ${formatAmount(amount, coin)} ${coin.ticker}",
"You need to send ${amount.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
: STextStyles.itemSubtitle(context),
@ -202,7 +202,7 @@ class SendFromCard extends ConsumerStatefulWidget {
}) : super(key: key);
final String walletId;
final Decimal amount;
final Amount amount;
final String address;
final Trade trade;
final bool fromDesktopStep4;
@ -213,13 +213,11 @@ class SendFromCard extends ConsumerStatefulWidget {
class _SendFromCardState extends ConsumerState<SendFromCard> {
late final String walletId;
late final Decimal amount;
late final Amount amount;
late final String address;
late final Trade trade;
Future<void> _send(Manager manager, {bool? shouldSendPublicFiroFunds}) async {
final _amount = Format.decimalAmountToSatoshis(amount, manager.coin);
try {
bool wasCancelled = false;
@ -265,7 +263,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
if (shouldSendPublicFiroFunds == null) {
txDataFuture = manager.prepareSend(
address: address,
satoshiAmount: _amount,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
@ -277,7 +275,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
if (shouldSendPublicFiroFunds) {
txDataFuture = firoWallet.prepareSendPublic(
address: address,
satoshiAmount: _amount,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
@ -286,7 +284,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
} else {
txDataFuture = firoWallet.prepareSend(
address: address,
satoshiAmount: _amount,
amount: amount,
args: {
"feeRate": FeeRateType.average,
// ref.read(feeRateTypeStateProvider)
@ -452,37 +450,11 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
"Use private balance",
style: STextStyles.itemSubtitle(context),
),
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: Future(() =>
(manager.wallet as FiroWallet)
.availablePrivateBalance()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${Format.localizedStringAsFixed(
value: snapshot.data!,
locale: locale,
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance..."
],
style: STextStyles.itemSubtitle(context),
);
}
},
Text(
"${(manager.wallet as FiroWallet).availablePrivateBalance().localizedStringAsFixed(
locale: locale,
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
),
],
),
@ -540,37 +512,11 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
"Use public balance",
style: STextStyles.itemSubtitle(context),
),
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: Future(() =>
(manager.wallet as FiroWallet)
.availablePublicBalance()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${Format.localizedStringAsFixed(
value: snapshot.data!,
locale: locale,
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance..."
],
style: STextStyles.itemSubtitle(context),
);
}
},
Text(
"${(manager.wallet as FiroWallet).availablePublicBalance().localizedStringAsFixed(
locale: locale,
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
),
],
),
@ -652,35 +598,11 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
height: 2,
),
if (!isFiro)
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: Future(() => manager.balance.getTotal()),
builder:
(builderContext, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${Format.localizedStringAsFixed(
value: snapshot.data!,
locale: locale,
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance..."
],
style: STextStyles.itemSubtitle(context),
);
}
},
Text(
"${manager.balance.spendable.localizedStringAsFixed(
locale: locale,
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
),
],
),

View file

@ -9,10 +9,9 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -198,18 +197,6 @@ class _ExchangeProviderOptionsState
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate;
if (estimate.reversed) {
rate = (toAmount /
estimate.estimatedAmount)
.toDecimal(
scaleOnInfinitePrecision: 12);
} else {
rate = (estimate.estimatedAmount /
fromAmount)
.toDecimal(
scaleOnInfinitePrecision: 12);
}
Coin coin;
try {
coin = coinFromTickerCaseInsensitive(
@ -217,18 +204,32 @@ class _ExchangeProviderOptionsState
} catch (_) {
coin = Coin.bitcoin;
}
Amount rate;
if (estimate.reversed) {
rate = (toAmount /
estimate.estimatedAmount)
.toDecimal(
scaleOnInfinitePrecision: 18)
.toAmount(
fractionDigits:
coin.decimals);
} else {
rate = (estimate.estimatedAmount /
fromAmount)
.toDecimal(
scaleOnInfinitePrecision: 18)
.toAmount(
fractionDigits:
coin.decimals);
}
return Text(
"1 ${sendCurrency.ticker.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
"1 ${sendCurrency.ticker.toUpperCase()} ~ ${rate.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale),
),
decimalPlaces:
Constants.decimalPlacesForCoin(
coin),
)} ${receivingCurrency.ticker.toUpperCase()}",
style: STextStyles.itemSubtitle12(
context)
@ -435,18 +436,6 @@ class _ExchangeProviderOptionsState
snapshot.hasData) {
final estimate = snapshot.data?.value;
if (estimate != null) {
Decimal rate;
if (estimate.reversed) {
rate = (toAmount /
estimate.estimatedAmount)
.toDecimal(
scaleOnInfinitePrecision: 12);
} else {
rate = (estimate.estimatedAmount /
fromAmount)
.toDecimal(
scaleOnInfinitePrecision: 12);
}
Coin coin;
try {
coin = coinFromTickerCaseInsensitive(
@ -454,18 +443,32 @@ class _ExchangeProviderOptionsState
} catch (_) {
coin = Coin.bitcoin;
}
Amount rate;
if (estimate.reversed) {
rate = (toAmount /
estimate.estimatedAmount)
.toDecimal(
scaleOnInfinitePrecision: 18)
.toAmount(
fractionDigits: coin.decimals,
);
} else {
rate = (estimate.estimatedAmount /
fromAmount)
.toDecimal(
scaleOnInfinitePrecision: 18)
.toAmount(
fractionDigits: coin.decimals,
);
}
return Text(
"1 ${sendCurrency.ticker.toUpperCase()} ~ ${Format.localizedStringAsFixed(
value: rate,
"1 ${sendCurrency.ticker.toUpperCase()} ~ ${rate.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale),
),
decimalPlaces:
Constants.decimalPlacesForCoin(
coin),
)} ${receivingCurrency.ticker.toUpperCase()}",
style: STextStyles.itemSubtitle12(
context)

View file

@ -20,6 +20,7 @@ import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dar
import 'package:stackwallet/services/exchange/exchange.dart';
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -256,11 +257,11 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
label: "Send from Stack",
buttonHeight: ButtonHeight.l,
onPressed: () {
final amount = sendAmount;
final address = trade.payInAddress;
final coin =
coinFromTickerCaseInsensitive(trade.payInCurrency);
final amount =
sendAmount.toAmount(fractionDigits: coin.decimals);
final address = trade.payInAddress;
Navigator.of(context).pushNamed(
SendFromView.routeName,
@ -339,13 +340,32 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
const SizedBox(
height: 4,
),
SelectableText(
"-${Format.localizedStringAsFixed(value: sendAmount, locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), decimalPlaces: trade.payInCurrency.toLowerCase() == "xmr" ? 12 : 8)} ${trade.payInCurrency.toUpperCase()}",
style: STextStyles.itemSubtitle(context),
),
Builder(builder: (context) {
String text;
try {
final coin = coinFromTickerCaseInsensitive(
trade.payInCurrency);
final amount = sendAmount.toAmount(
fractionDigits: coin.decimals);
text = amount.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
);
} catch (_) {
text = sendAmount.toStringAsFixed(
trade.payInCurrency.toLowerCase() == "xmr"
? 12
: 8);
}
return SelectableText(
"-$text ${trade.payInCurrency.toUpperCase()}",
style: STextStyles.itemSubtitle(context),
);
}),
],
),
if (!isDesktop)

View file

@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -17,25 +16,22 @@ class ConfirmPaynymConnectDialog extends StatelessWidget {
const ConfirmPaynymConnectDialog({
Key? key,
required this.nymName,
required this.locale,
required this.onConfirmPressed,
required this.amount,
required this.coin,
}) : super(key: key);
final String nymName;
final String locale;
final VoidCallback onConfirmPressed;
final int amount;
final Amount amount;
final Coin coin;
String get title => "Connect to $nymName";
String get message => "A one-time connection fee of "
"${Format.satoshisToAmount(
amount,
coin: coin,
).toStringAsFixed(
Constants.decimalPlacesForCoin(coin),
)} ${coin.ticker} "
"${amount.localizedStringAsFixed(locale: locale)} ${coin.ticker} "
"will be charged to connect to this PayNym.\n\nThis fee "
"covers the cost of creating a one-time transaction to create a "
"record on the blockchain. This keeps PayNyms decentralized.";

View file

@ -13,9 +13,11 @@ import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -134,6 +136,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
context: context,
builder: (context) => ConfirmPaynymConnectDialog(
nymName: widget.accountLite.nymName,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
onConfirmPressed: () {
//
print("CONFIRM NOTIF TX: $preparedTx");
@ -156,7 +159,10 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
),
);
},
amount: (preparedTx["amount"] as int) + (preparedTx["fee"] as int),
amount: (preparedTx["amount"] as Amount) +
(preparedTx["fee"] as int).toAmount(
fractionDigits: manager.coin.decimals,
),
coin: manager.coin,
),
);

View file

@ -12,8 +12,10 @@ import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.d
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -102,6 +104,7 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
context: context,
builder: (context) => ConfirmPaynymConnectDialog(
nymName: widget.accountLite.nymName,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
onConfirmPressed: () {
Navigator.of(context, rootNavigator: true).pop();
unawaited(
@ -139,7 +142,10 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
),
);
},
amount: (preparedTx["amount"] as int) + (preparedTx["fee"] as int),
amount: (preparedTx["amount"] as Amount) +
(preparedTx["fee"] as int).toAmount(
fractionDigits: manager.coin.decimals,
),
coin: manager.coin,
),
);

View file

@ -18,10 +18,10 @@ import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -403,12 +403,12 @@ class _ConfirmTransactionViewState
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(transactionInfo["recipientAmt"] as int, ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), ref.watch(
managerProvider.select((value) => value.coin),
))} $unit",
"${(transactionInfo["recipientAmt"] as Amount).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} $unit",
style: STextStyles.itemSubtitle12(context),
textAlign: TextAlign.right,
),
@ -427,12 +427,18 @@ class _ConfirmTransactionViewState
style: STextStyles.smallMed12(context),
),
Text(
"${Format.satoshiAmountToPrettyString(transactionInfo["fee"] as int, ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
), ref.watch(
managerProvider.select((value) => value.coin),
))} ${ref.watch(
"${(transactionInfo["fee"] as int).toAmount(
fractionDigits: ref.watch(
managerProvider.select(
(value) => value.coin.decimals,
),
),
).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: STextStyles.itemSubtitle12(context),
@ -534,7 +540,7 @@ class _ConfirmTransactionViewState
Builder(
builder: (context) {
final amount =
transactionInfo["recipientAmt"] as int;
transactionInfo["recipientAmt"] as Amount;
final coin = ref.watch(
managerProvider.select(
(value) => value.coin,
@ -551,29 +557,25 @@ class _ConfirmTransactionViewState
.getPrice(coin)
.item1;
if (price > Decimal.zero) {
fiatAmount = Format.localizedStringAsFixed(
value: Format.satoshisToAmount(amount,
coin: coin) *
price,
locale: ref
.read(
localeServiceChangeNotifierProvider)
.locale,
decimalPlaces: 2,
);
fiatAmount = (amount.decimal * price)
.toAmount(fractionDigits: 2)
.localizedStringAsFixed(
locale: ref
.read(
localeServiceChangeNotifierProvider)
.locale,
);
}
}
return Row(
children: [
Text(
"${Format.satoshiAmountToPrettyString(
amount,
ref.watch(
"${amount.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
coin,
)} $unit",
style: STextStyles
.desktopTextExtraExtraSmall(
@ -676,19 +678,19 @@ class _ConfirmTransactionViewState
value.getManager(walletId)))
.coin;
final fee = Format.satoshisToAmount(
transactionInfo["fee"] as int,
coin: coin,
final fee =
(transactionInfo["fee"] as int).toAmount(
fractionDigits: coin.decimals,
);
return Text(
"${Format.localizedStringAsFixed(
value: fee,
"${fee.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style:
STextStyles.desktopTextExtraExtraSmall(
@ -855,17 +857,17 @@ class _ConfirmTransactionViewState
.select((value) => value.getManager(walletId)))
.coin;
final fee = Format.satoshisToAmount(
transactionInfo["fee"] as int,
coin: coin,
final fee = (transactionInfo["fee"] as int).toAmount(
fractionDigits: coin.decimals,
);
return Text(
"${Format.localizedStringAsFixed(
value: fee,
locale: ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces: Constants.decimalPlacesForCoin(coin),
"${fee.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
@ -911,34 +913,37 @@ class _ConfirmTransactionViewState
.textConfirmTotalAmount,
),
),
Text(
"${Format.satoshiAmountToPrettyString(
(transactionInfo["fee"] as int) +
(transactionInfo["recipientAmt"] as int),
ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
ref.watch(
managerProvider.select((value) => value.coin),
),
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
)
: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
),
Builder(builder: (context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin));
final fee = (transactionInfo["fee"] as int)
.toAmount(fractionDigits: coin.decimals);
final locale = ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
);
final amount = transactionInfo["recipientAmt"] as Amount;
return Text(
"${(amount + fee).localizedStringAsFixed(
locale: locale,
)} ${ref.watch(
managerProvider.select((value) => value.coin),
).ticker}",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
)
: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
textAlign: TextAlign.right,
);
}),
],
),
),

View file

@ -25,13 +25,13 @@ import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -94,8 +94,8 @@ class _SendViewState extends ConsumerState<SendView> {
final _cryptoFocus = FocusNode();
final _baseFocus = FocusNode();
Decimal? _amountToSend;
Decimal? _cachedAmountToSend;
Amount? _amountToSend;
Amount? _cachedAmountToSend;
String? _address;
String? _privateBalanceString;
@ -106,7 +106,7 @@ class _SendViewState extends ConsumerState<SendView> {
bool _cryptoAmountChangeLock = false;
late VoidCallback onCryptoAmountChanged;
Decimal? _cachedBalance;
Amount? _cachedBalance;
Set<UTXO> selectedUTXOs = {};
@ -118,7 +118,9 @@ class _SendViewState extends ConsumerState<SendView> {
cryptoAmount != ",") {
_amountToSend = cryptoAmount.contains(",")
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
: Decimal.parse(cryptoAmount);
.toAmount(fractionDigits: coin.decimals)
: Decimal.parse(cryptoAmount)
.toAmount(fractionDigits: coin.decimals);
if (_cachedAmountToSend != null &&
_cachedAmountToSend == _amountToSend) {
return;
@ -131,13 +133,13 @@ class _SendViewState extends ConsumerState<SendView> {
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
if (price > Decimal.zero) {
final String fiatAmountString = Format.localizedStringAsFixed(
value: _amountToSend! * price,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: 2,
);
baseAmountController.text = fiatAmountString;
baseAmountController.text = (_amountToSend!.decimal * price)
.toAmount(
fractionDigits: 2,
)
.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
}
} else {
_amountToSend = null;
@ -152,11 +154,8 @@ class _SendViewState extends ConsumerState<SendView> {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null
? 0
: Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
),
? 0.toAmount(fractionDigits: coin.decimals)
: _amountToSend!,
);
});
}
@ -176,24 +175,19 @@ class _SendViewState extends ConsumerState<SendView> {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null
? 0
: Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
),
? 0.toAmount(fractionDigits: coin.decimals)
: _amountToSend!,
);
});
}
});
}
int _currentFee = 0;
late Amount _currentFee;
void _setCurrentFee(String fee, bool shouldSetState) {
final value = Format.decimalAmountToSatoshis(
Decimal.parse(fee),
coin,
);
final value = Decimal.parse(fee).toAmount(fractionDigits: coin.decimals);
if (shouldSetState) {
setState(() => _currentFee = value);
} else {
@ -211,28 +205,28 @@ class _SendViewState extends ConsumerState<SendView> {
return null;
}
void _updatePreviewButtonState(String? address, Decimal? amount) {
void _updatePreviewButtonState(String? address, Amount? amount) {
if (isPaynymSend) {
ref.read(previewTxButtonStateProvider.state).state =
(amount != null && amount > Decimal.zero);
(amount != null && amount > Amount.zero);
} else {
final isValidAddress = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.validateAddress(address ?? "");
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Decimal.zero);
(isValidAddress && amount != null && amount > Amount.zero);
}
}
late Future<String> _calculateFeesFuture;
Map<int, String> cachedFees = {};
Map<int, String> cachedFiroPrivateFees = {};
Map<int, String> cachedFiroPublicFees = {};
Map<Amount, String> cachedFees = {};
Map<Amount, String> cachedFiroPrivateFees = {};
Map<Amount, String> cachedFiroPublicFees = {};
Future<String> calculateFees(int amount) async {
if (amount <= 0) {
Future<String> calculateFees(Amount amount) async {
if (amount <= Amount.zero) {
return "0";
}
@ -269,7 +263,8 @@ class _SendViewState extends ConsumerState<SendView> {
break;
}
int fee;
final String locale = ref.read(localeServiceChangeNotifierProvider).locale;
Amount fee;
if (coin == Coin.monero) {
MoneroTransactionPriority specialMoneroId;
switch (ref.read(feeRateTypeStateProvider.state).state) {
@ -285,8 +280,7 @@ class _SendViewState extends ConsumerState<SendView> {
}
fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!);
cachedFees[amount] = Format.satoshisToAmount(fee, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cachedFees[amount] = fee.localizedStringAsFixed(locale: locale);
return cachedFees[amount]!;
} else if (coin == Coin.firo || coin == Coin.firoTestNet) {
@ -294,23 +288,22 @@ class _SendViewState extends ConsumerState<SendView> {
"Private") {
fee = await manager.estimateFeeFor(amount, feeRate);
cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cachedFiroPrivateFees[amount] =
fee.localizedStringAsFixed(locale: locale);
return cachedFiroPrivateFees[amount]!;
} else {
fee = await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cachedFiroPublicFees[amount] =
fee.localizedStringAsFixed(locale: locale);
return cachedFiroPublicFees[amount]!;
}
} else {
fee = await manager.estimateFeeFor(amount, feeRate);
cachedFees[amount] = Format.satoshisToAmount(fee, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cachedFees[amount] = fee.localizedStringAsFixed(locale: locale);
return cachedFees[amount]!;
}
@ -321,7 +314,7 @@ class _SendViewState extends ConsumerState<SendView> {
final wallet = ref.read(provider).wallet as FiroWallet?;
if (wallet != null) {
Decimal? balance;
Amount? balance;
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
balance = wallet.availablePrivateBalance();
@ -329,8 +322,9 @@ class _SendViewState extends ConsumerState<SendView> {
balance = wallet.availablePublicBalance();
}
return Format.localizedStringAsFixed(
value: balance, locale: locale, decimalPlaces: 8);
return balance.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
}
return null;
@ -345,20 +339,19 @@ class _SendViewState extends ConsumerState<SendView> {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin);
int availableBalance;
final Amount amount = _amountToSend!;
final Amount availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
availableBalance = Format.decimalAmountToSatoshis(
(manager.wallet as FiroWallet).availablePrivateBalance(), coin);
availableBalance =
(manager.wallet as FiroWallet).availablePrivateBalance();
} else {
availableBalance = Format.decimalAmountToSatoshis(
(manager.wallet as FiroWallet).availablePublicBalance(), coin);
availableBalance =
(manager.wallet as FiroWallet).availablePublicBalance();
}
} else {
availableBalance =
Format.decimalAmountToSatoshis(manager.balance.getSpendable(), coin);
availableBalance = manager.balance.spendable;
}
final coinControlEnabled =
@ -462,7 +455,7 @@ class _SendViewState extends ConsumerState<SendView> {
final feeRate = ref.read(feeRateTypeStateProvider);
txDataFuture = wallet.preparePaymentCodeSend(
paymentCode: paymentCode,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate": feeRate,
"UTXOs": (manager.hasCoinControlSupport &&
@ -477,13 +470,13 @@ class _SendViewState extends ConsumerState<SendView> {
"Private") {
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
address: _address!,
satoshiAmount: amount,
amount: amount,
args: {"feeRate": ref.read(feeRateTypeStateProvider)},
);
} else {
txDataFuture = manager.prepareSend(
address: _address!,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
"UTXOs": (manager.hasCoinControlSupport &&
@ -565,12 +558,14 @@ class _SendViewState extends ConsumerState<SendView> {
@override
void initState() {
coin = widget.coin;
ref.refresh(feeSheetSessionCacheProvider);
_currentFee = 0.toAmount(fractionDigits: coin.decimals);
_calculateFeesFuture = calculateFees(0);
_calculateFeesFuture =
calculateFees(0.toAmount(fractionDigits: coin.decimals));
_data = widget.autoFillData;
walletId = widget.walletId;
coin = widget.coin;
clipboard = widget.clipboard;
scanner = widget.barcodeScanner;
@ -676,12 +671,14 @@ class _SendViewState extends ConsumerState<SendView> {
ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
if (_amountToSend == null) {
setState(() {
_calculateFeesFuture = calculateFees(0);
_calculateFeesFuture =
calculateFees(0.toAmount(fractionDigits: coin.decimals));
});
} else {
setState(() {
_calculateFeesFuture = calculateFees(
Format.decimalAmountToSatoshis(_amountToSend!, coin));
_amountToSend!,
);
});
}
});
@ -802,7 +799,7 @@ class _SendViewState extends ConsumerState<SendView> {
coin != Coin.firoTestNet)
? Future(() => ref.watch(
provider.select((value) =>
value.balance.getSpendable())))
value.balance.spendable)))
: ref.watch(publicPrivateBalanceStateProvider.state).state ==
"Private"
? Future(() => (ref
@ -814,7 +811,7 @@ class _SendViewState extends ConsumerState<SendView> {
.wallet as FiroWallet)
.availablePublicBalance()),
builder:
(_, AsyncSnapshot<Decimal> snapshot) {
(_, AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
@ -825,10 +822,9 @@ class _SendViewState extends ConsumerState<SendView> {
return GestureDetector(
onTap: () {
cryptoAmountController.text =
_cachedBalance!.toStringAsFixed(
Constants
.decimalPlacesForCoin(
coin));
_cachedBalance!
.localizedStringAsFixed(
locale: locale);
},
child: Container(
color: Colors.transparent,
@ -837,10 +833,8 @@ class _SendViewState extends ConsumerState<SendView> {
CrossAxisAlignment.end,
children: [
Text(
"${Format.localizedStringAsFixed(
value: _cachedBalance!,
"${_cachedBalance!.localizedStringAsFixed(
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
style:
STextStyles.titleBold12(
@ -851,17 +845,11 @@ class _SendViewState extends ConsumerState<SendView> {
textAlign: TextAlign.right,
),
Text(
"${Format.localizedStringAsFixed(
value: _cachedBalance! *
ref.watch(priceAnd24hChangeNotifierProvider
.select((value) =>
value
.getPrice(
coin)
.item1)),
locale: locale,
decimalPlaces: 2,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
"${(_cachedBalance!.decimal * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1))).toAmount(
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
style: STextStyles.subtitle(
context)
.copyWith(
@ -1134,23 +1122,19 @@ class _SendViewState extends ConsumerState<SendView> {
// autofill amount field
if (results["amount"] !=
null) {
final amount =
final Amount amount =
Decimal.parse(results[
"amount"]!);
"amount"]!)
.toAmount(
fractionDigits:
coin.decimals,
);
cryptoAmountController
.text =
Format
amount
.localizedStringAsFixed(
value: amount,
locale: ref
.read(
localeServiceChangeNotifierProvider)
.locale,
decimalPlaces: Constants
.decimalPlacesForCoin(
coin),
locale: locale,
);
amount.toString();
_amountToSend = amount;
}
@ -1413,24 +1397,21 @@ class _SendViewState extends ConsumerState<SendView> {
"Private") {
cryptoAmountController.text = firoWallet
.availablePrivateBalance()
.toStringAsFixed(
Constants.decimalPlacesForCoin(
coin));
.localizedStringAsFixed(
locale: locale);
} else {
cryptoAmountController.text = firoWallet
.availablePublicBalance()
.toStringAsFixed(
Constants.decimalPlacesForCoin(
coin));
.localizedStringAsFixed(
locale: locale);
}
} else {
cryptoAmountController.text = (ref
.read(provider)
.balance
.getSpendable())
.toStringAsFixed(
Constants.decimalPlacesForCoin(
coin));
cryptoAmountController.text = ref
.read(provider)
.balance
.spendable
.localizedStringAsFixed(
locale: locale);
}
_cryptoAmountChanged();
},
@ -1531,26 +1512,30 @@ class _SendViewState extends ConsumerState<SendView> {
if (baseAmountString.isNotEmpty &&
baseAmountString != "." &&
baseAmountString != ",") {
final baseAmount =
final Amount baseAmount =
baseAmountString.contains(",")
? Decimal.parse(baseAmountString
.replaceFirst(",", "."))
: Decimal.parse(baseAmountString);
.replaceFirst(",", "."))
.toAmount(fractionDigits: 2)
: Decimal.parse(baseAmountString)
.toAmount(fractionDigits: 2);
var _price = ref
final Decimal _price = ref
.read(priceAnd24hChangeNotifierProvider)
.getPrice(coin)
.item1;
if (_price == Decimal.zero) {
_amountToSend = Decimal.zero;
_amountToSend = 0.toAmount(
fractionDigits: coin.decimals);
} else {
_amountToSend = baseAmount <= Decimal.zero
? Decimal.zero
: (baseAmount / _price).toDecimal(
scaleOnInfinitePrecision:
Constants.decimalPlacesForCoin(
coin));
_amountToSend = baseAmount <= Amount.zero
? 0.toAmount(
fractionDigits: coin.decimals)
: (baseAmount.decimal / _price)
.toDouble()
.toAmount(
fractionDigits: coin.decimals);
}
if (_cachedAmountToSend != null &&
_cachedAmountToSend == _amountToSend) {
@ -1562,21 +1547,19 @@ class _SendViewState extends ConsumerState<SendView> {
level: LogLevel.Info);
final amountString =
Format.localizedStringAsFixed(
value: _amountToSend!,
_amountToSend!.localizedStringAsFixed(
locale: ref
.read(
localeServiceChangeNotifierProvider)
.locale,
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = amountString;
_cryptoAmountChangeLock = false;
} else {
_amountToSend = Decimal.zero;
_amountToSend =
0.toAmount(fractionDigits: coin.decimals);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = "";
_cryptoAmountChangeLock = false;
@ -1654,13 +1637,9 @@ class _SendViewState extends ConsumerState<SendView> {
.balance
.spendable;
int? amount;
Amount? amount;
if (_amountToSend != null) {
amount =
Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
);
amount = _amountToSend!;
if (spendable == amount) {
// this is now a send all
@ -1803,10 +1782,13 @@ class _SendViewState extends ConsumerState<SendView> {
builder: (_) =>
TransactionFeeSelectionSheet(
walletId: walletId,
amount: Decimal.tryParse(
cryptoAmountController
.text) ??
Decimal.zero,
amount: (Decimal.tryParse(
cryptoAmountController
.text) ??
Decimal.zero)
.toAmount(
fractionDigits: coin.decimals,
),
updateChosen: (String fee) {
_setCurrentFee(
fee,

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
@ -8,7 +7,6 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart';
class FiroBalanceSelectionSheet extends ConsumerStatefulWidget {
const FiroBalanceSelectionSheet({
@ -153,29 +151,18 @@ class _FiroBalanceSelectionSheetState
const SizedBox(
width: 2,
),
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: Future(
() => firoWallet.availablePrivateBalance()),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle(context),
);
}
},
)
Text(
"${firoWallet.availablePrivateBalance().localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
),
],
),
// ],
@ -245,31 +232,18 @@ class _FiroBalanceSelectionSheetState
const SizedBox(
width: 2,
),
FutureBuilder(
// TODO redo this widget now that its not actually a future
future: Future(
() => firoWallet.availablePublicBalance()),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!.toStringAsFixed(8)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle(context),
);
}
},
)
// ],
// ),
Text(
"${firoWallet.availablePublicBalance().localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
textAlign: TextAlign.left,
),
],
),
),

View file

@ -8,10 +8,10 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -23,9 +23,9 @@ final feeSheetSessionCacheProvider =
});
class FeeSheetSessionCache extends ChangeNotifier {
final Map<int, Decimal> fast = {};
final Map<int, Decimal> average = {};
final Map<int, Decimal> slow = {};
final Map<Amount, Amount> fast = {};
final Map<Amount, Amount> average = {};
final Map<Amount, Amount> slow = {};
void notify() => notifyListeners();
}
@ -40,7 +40,7 @@ class TransactionFeeSelectionSheet extends ConsumerStatefulWidget {
}) : super(key: key);
final String walletId;
final Decimal amount;
final Amount amount;
final Function updateChosen;
final bool isToken;
@ -52,7 +52,7 @@ class TransactionFeeSelectionSheet extends ConsumerStatefulWidget {
class _TransactionFeeSelectionSheetState
extends ConsumerState<TransactionFeeSelectionSheet> {
late final String walletId;
late final Decimal amount;
late final Amount amount;
FeeObject? feeObject;
@ -63,8 +63,8 @@ class _TransactionFeeSelectionSheetState
"Calculating...",
];
Future<Decimal> feeFor({
required int amount,
Future<Amount> feeFor({
required Amount amount,
required FeeRateType feeRateType,
required int feeRate,
required Coin coin,
@ -79,30 +79,21 @@ class _TransactionFeeSelectionSheetState
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.fast.raw!);
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(fee, coin: coin);
final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
}
}
return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
@ -115,30 +106,21 @@ class _TransactionFeeSelectionSheetState
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.regular.raw!);
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(fee, coin: coin);
final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
}
}
return ref.read(feeSheetSessionCacheProvider).average[amount]!;
@ -151,30 +133,21 @@ class _TransactionFeeSelectionSheetState
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.slow.raw!);
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
} else {
final tokenWallet = ref.read(tokenServiceProvider)!;
final fee = await tokenWallet.estimateFeeFor(amount, feeRate);
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(fee, coin: coin);
final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
}
}
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
@ -347,23 +320,25 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.fast,
feeRate: feeObject!.fast,
amount: Format
.decimalAmountToSatoshis(
amount, manager.coin)),
coin: manager.coin,
feeRateType: FeeRateType.fast,
feeRate: feeObject!.fast,
amount: amount,
),
// future: manager.estimateFeeFor(
// Format.decimalAmountToSatoshis(
// amount),
// feeObject!.fast),
builder: (_,
AsyncSnapshot<Decimal> snapshot) {
AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"(~${snapshot.data!.toStringAsFixed(manager.coin.decimals)} ${manager.coin.ticker})",
"(~${snapshot.data!.decimal.toStringAsFixed(
manager.coin.decimals,
)}"
" ${manager.coin.ticker})",
style: STextStyles.itemSubtitle(
context),
textAlign: TextAlign.left,
@ -479,23 +454,23 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.average,
feeRate: feeObject!.medium,
amount: Format
.decimalAmountToSatoshis(
amount, manager.coin)),
coin: manager.coin,
feeRateType: FeeRateType.average,
feeRate: feeObject!.medium,
amount: amount,
),
// future: manager.estimateFeeFor(
// Format.decimalAmountToSatoshis(
// amount),
// feeObject!.fast),
builder: (_,
AsyncSnapshot<Decimal> snapshot) {
AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"(~${snapshot.data!.toStringAsFixed(manager.coin.decimals)} ${manager.coin.ticker})",
"(~${snapshot.data!.decimal.toStringAsFixed(manager.coin.decimals)}"
" ${manager.coin.ticker})",
style: STextStyles.itemSubtitle(
context),
textAlign: TextAlign.left,
@ -612,23 +587,22 @@ class _TransactionFeeSelectionSheetState
if (feeObject != null)
FutureBuilder(
future: feeFor(
coin: manager.coin,
feeRateType: FeeRateType.slow,
feeRate: feeObject!.slow,
amount: Format
.decimalAmountToSatoshis(
amount, manager.coin)),
coin: manager.coin,
feeRateType: FeeRateType.slow,
feeRate: feeObject!.slow,
amount: amount,
),
// future: manager.estimateFeeFor(
// Format.decimalAmountToSatoshis(
// amount),
// feeObject!.fast),
builder: (_,
AsyncSnapshot<Decimal> snapshot) {
AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"(~${snapshot.data!.toStringAsFixed(manager.coin.decimals)} ${manager.coin.ticker})",
"(~${snapshot.data!.decimal.toStringAsFixed(manager.coin.decimals)} ${manager.coin.ticker})",
style: STextStyles.itemSubtitle(
context),
textAlign: TextAlign.left,
@ -686,7 +660,6 @@ class _TransactionFeeSelectionSheetState
String? getAmount(FeeRateType feeRateType, Coin coin) {
try {
final amount = Format.decimalAmountToSatoshis(this.amount, coin);
switch (feeRateType) {
case FeeRateType.fast:
if (ref.read(feeSheetSessionCacheProvider).fast[amount] != null) {

View file

@ -25,7 +25,6 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -34,7 +33,6 @@ import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
@ -102,7 +100,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
Timer? _cryptoAmountChangedFeeUpdateTimer;
Timer? _baseAmountChangedFeeUpdateTimer;
late Future<String> _calculateFeesFuture;
Map<int, String> cachedFees = {};
String cachedFees = "";
void _onTokenSendViewPasteAddressFieldButtonPressed() async {
final ClipboardData? data = await clipboard.getData(Clipboard.kTextPlain);
@ -165,17 +163,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
// autofill amount field
if (results["amount"] != null) {
final amount = Decimal.parse(results["amount"]!);
cryptoAmountController.text = Format.localizedStringAsFixed(
value: amount,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
);
amount.toString();
_amountToSend = Amount.fromDecimal(
amount,
final Amount amount = Decimal.parse(results["amount"]!).toAmount(
fractionDigits: tokenContract.decimals,
);
cryptoAmountController.text = amount.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
_amountToSend = amount;
}
_updatePreviewButtonState(_address, _amountToSend);
@ -243,14 +237,10 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
level: LogLevel.Info);
final amountString = Format.localizedStringAsFixed(
value: _amountToSend!.decimal,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = amountString;
cryptoAmountController.text = _amountToSend!.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
_cryptoAmountChangeLock = false;
} else {
_amountToSend = Amount.zero;
@ -291,13 +281,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
.item1;
if (price > Decimal.zero) {
final String fiatAmountString = Format.localizedStringAsFixed(
value: _amountToSend!.decimal * price,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: 2,
);
baseAmountController.text = fiatAmountString;
baseAmountController.text = (_amountToSend!.decimal * price)
.toAmount(
fractionDigits: 2,
)
.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
}
} else {
_amountToSend = null;
@ -310,9 +300,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
_cryptoAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
if (coin != Coin.epicCash && !_baseFocus.hasFocus) {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null ? 0 : _amountToSend!.raw.toInt(),
);
_calculateFeesFuture = calculateFees();
});
}
});
@ -324,9 +312,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
_baseAmountChangedFeeUpdateTimer = Timer(updateFeesTimerDuration, () {
if (coin != Coin.epicCash && !_cryptoFocus.hasFocus) {
setState(() {
_calculateFeesFuture = calculateFees(
_amountToSend == null ? 0 : _amountToSend!.raw.toInt(),
);
_calculateFeesFuture = calculateFees();
});
}
});
@ -351,15 +337,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
(isValidAddress && amount != null && amount > Amount.zero);
}
Future<String> calculateFees(int amount) async {
if (amount <= 0) {
return "0";
}
if (cachedFees[amount] != null) {
return cachedFees[amount]!;
}
Future<String> calculateFees() async {
final wallet = ref.read(tokenServiceProvider)!;
final feeObject = await wallet.fees;
@ -377,13 +355,12 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
break;
}
int fee;
final Amount fee = wallet.estimateFeeFor(feeRate);
cachedFees = fee.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
fee = await wallet.estimateFeeFor(amount, feeRate);
cachedFees[amount] = Format.satoshisToAmount(fee, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
return cachedFees[amount]!;
return cachedFees;
}
Future<void> _previewTransaction() async {
@ -397,10 +374,6 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
final tokenWallet = ref.read(tokenServiceProvider)!;
final Amount amount = _amountToSend!;
final Amount availableBalance = Amount.fromDecimal(
tokenWallet.balance.getSpendable(),
fractionDigits: tokenContract.decimals,
);
// // confirm send all
// if (amount == availableBalance) {
@ -487,7 +460,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
txDataFuture = tokenWallet.prepareSend(
address: _address!,
satoshiAmount: amount.raw.toInt(),
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
},
@ -560,7 +533,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
void initState() {
ref.refresh(feeSheetSessionCacheProvider);
_calculateFeesFuture = calculateFees(0);
_calculateFeesFuture = calculateFees();
_data = widget.autoFillData;
walletId = widget.walletId;
coin = widget.coin;
@ -703,9 +676,13 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
cryptoAmountController.text = ref
.read(tokenServiceProvider)!
.balance
.getSpendable()
.toStringAsFixed(
tokenContract.decimals);
.spendable
.localizedStringAsFixed(
locale: ref
.read(
localeServiceChangeNotifierProvider)
.locale,
);
},
child: Container(
color: Colors.transparent,
@ -714,7 +691,20 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
CrossAxisAlignment.end,
children: [
Text(
"${ref.read(tokenServiceProvider)!.balance.getSpendable().toStringAsFixed(tokenContract.decimals)} ${tokenContract.symbol}",
"${ref.watch(
tokenServiceProvider.select(
(value) => value!
.balance.spendable
.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
),
),
)} ${tokenContract.symbol}",
style:
STextStyles.titleBold12(context)
.copyWith(
@ -723,22 +713,11 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
textAlign: TextAlign.right,
),
Text(
"${Format.localizedStringAsFixed(
value: ref
.read(
tokenServiceProvider)!
.balance
.getSpendable() *
ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => value
.getTokenPrice(
tokenContract
.address)
.item1)),
locale: locale,
decimalPlaces: 2,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
"${(ref.watch(tokenServiceProvider.select((value) => value!.balance.spendable.decimal)) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getTokenPrice(tokenContract.address).item1))).toAmount(
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
style: STextStyles.subtitle(context)
.copyWith(
fontSize: 8,
@ -1138,9 +1117,14 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
TransactionFeeSelectionSheet(
walletId: walletId,
isToken: true,
amount: Decimal.tryParse(
cryptoAmountController.text) ??
Decimal.zero,
amount: (Decimal.tryParse(
cryptoAmountController
.text) ??
Decimal.zero)
.toAmount(
fractionDigits:
tokenContract.decimals,
),
updateChosen: (String fee) {
setState(() {
_calculateFeesFuture =

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -7,11 +6,9 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
@ -144,40 +141,18 @@ class WalletSyncingOptionsView extends ConsumerWidget {
const SizedBox(
height: 2,
),
FutureBuilder(
future: Future(
() => manager.balance.getTotal()),
builder: (builderContext,
AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${Format.localizedStringAsFixed(
value: snapshot.data!,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) =>
value.locale)),
decimalPlaces: 8,
)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(
context),
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance..."
],
style: STextStyles.itemSubtitle(
context),
);
}
},
),
Text(
"${manager.balance.total.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
)} ${manager.coin.ticker}",
style:
STextStyles.itemSubtitle(context),
)
],
),
const Spacer(),

View file

@ -113,7 +113,13 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
),
const Spacer(),
Text(
"${cachedBalance.getCachedBalance().getTotal()} "
"${cachedBalance.getCachedBalance().total.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} "
"${widget.token.symbol}",
style: STextStyles.itemSubtitle(context),
),

View file

@ -15,10 +15,10 @@ import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/price_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
@ -80,7 +80,13 @@ class TokenSummary extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${balance.getTotal()}"
"${balance.total.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)}"
" ${token.symbol}",
style: STextStyles.pageTitleH1(context),
),
@ -96,17 +102,23 @@ class TokenSummary extends ConsumerWidget {
height: 6,
),
Text(
"${Format.localizedStringAsFixed(
value: ref
.read(tokenServiceProvider)!
.balance
.getSpendable() *
ref.watch(priceAnd24hChangeNotifierProvider.select(
(value) => value.getTokenPrice(token.address).item1)),
locale: ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces: 2,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
"${(balance.total.decimal * ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getTokenPrice(token.address).item1,
),
)).toAmount(
fractionDigits: 2,
).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.currency,
),
)}",
style: STextStyles.subtitle500(context),
),
const SizedBox(

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/balance.dart';
@ -6,6 +5,7 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
@ -112,7 +112,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
BalanceSelector(
title: "Available balance",
coin: coin,
balance: balance.getSpendable(),
balance: balance.spendable,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
@ -138,7 +138,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
BalanceSelector(
title: "Available private balance",
coin: coin,
balance: balanceSecondary.getSpendable(),
balance: balanceSecondary.spendable,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.available;
@ -162,7 +162,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
BalanceSelector(
title: "Full balance",
coin: coin,
balance: balance.getTotal(),
balance: balance.total,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
@ -188,7 +188,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
BalanceSelector(
title: "Full private balance",
coin: coin,
balance: balanceSecondary.getTotal(),
balance: balanceSecondary.total,
onPressed: () {
ref.read(walletBalanceToggleStateProvider.state).state =
WalletBalanceToggleState.full;
@ -217,7 +217,7 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
}
}
class BalanceSelector<T> extends StatelessWidget {
class BalanceSelector<T> extends ConsumerWidget {
const BalanceSelector({
Key? key,
required this.title,
@ -231,14 +231,14 @@ class BalanceSelector<T> extends StatelessWidget {
final String title;
final Coin coin;
final Decimal balance;
final Amount balance;
final VoidCallback onPressed;
final void Function(T?) onChanged;
final T value;
final T? groupValue;
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
return RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
@ -278,7 +278,13 @@ class BalanceSelector<T> extends StatelessWidget {
height: 2,
),
Text(
"${balance.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} ${coin.ticker}",
"${balance.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!

View file

@ -1,26 +1,24 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_sheet.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/balance_refreshed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import '../../../providers/wallet/public_private_balance_state_provider.dart';
class WalletSummaryInfo extends ConsumerStatefulWidget {
const WalletSummaryInfo({
Key? key,
@ -94,7 +92,7 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> {
ref.watch(walletBalanceToggleStateProvider.state).state ==
WalletBalanceToggleState.available;
final Decimal balanceToShow;
final Amount balanceToShow;
String title;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
@ -106,12 +104,11 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> {
final bal = _showPrivate ? firoWallet.balancePrivate : firoWallet.balance;
balanceToShow = _showAvailable ? bal.getSpendable() : bal.getTotal();
balanceToShow = _showAvailable ? bal.spendable : bal.total;
title = _showAvailable ? "Available" : "Full";
title += _showPrivate ? " private balance" : " public balance";
} else {
balanceToShow =
_showAvailable ? balance.getSpendable() : balance.getTotal();
balanceToShow = _showAvailable ? balance.spendable : balance.total;
title = _showAvailable ? "Available balance" : "Full balance";
}
@ -151,10 +148,8 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> {
FittedBox(
fit: BoxFit.scaleDown,
child: SelectableText(
"${Format.localizedStringAsFixed(
value: balanceToShow,
"${balanceToShow.localizedStringAsFixed(
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
style: STextStyles.pageTitleH1(context).copyWith(
fontSize: 24,
@ -166,11 +161,11 @@ class _WalletSummaryInfoState extends ConsumerState<WalletSummaryInfo> {
),
if (externalCalls)
Text(
"${Format.localizedStringAsFixed(
value: priceTuple.item1 * balanceToShow,
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",
"${(priceTuple.item1 * balanceToShow.decimal).toAmount(
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
)} $baseCurrency",
style: STextStyles.subtitle500(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!

View file

@ -13,6 +13,7 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -111,8 +112,7 @@ class _TransactionDetailsViewState extends ConsumerState<AllTransactionsView> {
return false;
}
if (filter.amount != null &&
BigInt.from(filter.amount!) != tx.realAmount.raw) {
if (filter.amount != null && filter.amount! != tx.realAmount) {
return false;
}
@ -956,10 +956,8 @@ class _DesktopTransactionCardRowState
builder: (_) {
final amount = _transaction.realAmount;
return Text(
"$prefix${Format.localizedStringAsFixed(
value: amount.decimal,
"$prefix${amount.localizedStringAsFixed(
locale: locale,
decimalPlaces: coin.decimals,
)} ${coin.ticker}",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
@ -980,11 +978,11 @@ class _DesktopTransactionCardRowState
final amount = _transaction.realAmount;
return Text(
"$prefix${Format.localizedStringAsFixed(
value: amount.decimal * price,
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",
"$prefix${(amount.decimal * price).toAmount(
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
)} $baseCurrency",
style: STextStyles.desktopTextExtraExtraSmall(context),
);
},

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -68,7 +67,7 @@ class _TransactionDetailsViewState
late final Coin coin;
late final Amount amount;
late final Decimal fee;
late final Amount fee;
late final String amountPrefix;
late final String unit;
late final bool isTokenTx;
@ -83,9 +82,8 @@ class _TransactionDetailsViewState
walletId = widget.walletId;
coin = widget.coin;
amount = _transaction
.realAmount; //Format.satoshisToAmount(_transaction.amount, coin: coin);
fee = Format.satoshisToAmount(_transaction.fee, coin: coin);
amount = _transaction.realAmount;
fee = _transaction.fee.toAmount(fractionDigits: coin.decimals);
if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
_transaction.subType == TransactionSubType.mint) {
@ -445,15 +443,12 @@ class _TransactionDetailsViewState
: CrossAxisAlignment.start,
children: [
SelectableText(
"$amountPrefix${Format.localizedStringAsFixed(
value: amount.decimal,
"$amountPrefix${amount.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) =>
value.locale),
),
decimalPlaces:
amount.fractionDigits,
)} $unit",
style: isDesktop
? STextStyles
@ -476,28 +471,26 @@ class _TransactionDetailsViewState
.select((value) =>
value.externalCalls)))
SelectableText(
"$amountPrefix${Format.localizedStringAsFixed(
value: amount.decimal *
ref.watch(
priceAnd24hChangeNotifierProvider
.select((value) => isTokenTx
? value
.getTokenPrice(
_transaction
.otherData!)
.item1
: value
.getPrice(
coin)
.item1),
"$amountPrefix${(amount.decimal * ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => isTokenTx
? value
.getTokenPrice(
_transaction
.otherData!)
.item1
: value
.getPrice(
coin)
.item1),
)).toAmount(fractionDigits: 2).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale,
),
),
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) =>
value.locale),
),
decimalPlaces: 2,
)} ${ref.watch(
)} ${ref.watch(
prefsChangeNotifierProvider
.select(
(value) => value.currency,
@ -896,24 +889,20 @@ class _TransactionDetailsViewState
currentHeight,
coin.requiredConfirmations,
)
? Format.localizedStringAsFixed(
value: fee,
? fee.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) =>
value.locale)),
decimalPlaces:
Constants.decimalPlacesForCoin(
coin))
localeServiceChangeNotifierProvider
.select(
(value) => value.locale),
),
)
: "Pending"
: Format.localizedStringAsFixed(
value: fee,
: fee.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select(
(value) => value.locale)),
decimalPlaces:
Constants.decimalPlacesForCoin(coin));
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
);
if (isTokenTx) {
feeString += " ${coin.ticker}";
}
@ -1517,13 +1506,15 @@ class IconCopyButton extends StatelessWidget {
),
onPressed: () async {
await Clipboard.setData(ClipboardData(text: data));
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
context: context,
),
);
if (context.mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
context: context,
),
);
}
},
child: Padding(
padding: const EdgeInsets.all(5),

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/models/transaction_filter.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/color_theme_provider.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -75,13 +76,12 @@ class _TransactionSearchViewState
_toDateString =
_selectedToDate == null ? "" : Format.formatDate(_selectedToDate!);
// TODO: Fix XMR (modify Format.funcs to take optional Coin parameter)
// final amt = Format.satoshisToAmount(widget.coin == Coin.monero ? )
String amount = "";
if (filterState.amount != null) {
amount = Format.satoshiAmountToPrettyString(filterState.amount!,
ref.read(localeServiceChangeNotifierProvider).locale, widget.coin);
}
final String amount = filterState.amount?.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: widget.coin.decimals,
) ??
"";
_amountTextEditingController.text = amount;
}
@ -966,15 +966,13 @@ class _TransactionSearchViewState
Future<void> _onApplyPressed() async {
final amountText = _amountTextEditingController.text;
Decimal? amountDecimal;
Amount? amount;
if (amountText.isNotEmpty && !(amountText == "," || amountText == ".")) {
amountDecimal = amountText.contains(",")
amount = amountText.contains(",")
? Decimal.parse(amountText.replaceFirst(",", "."))
: Decimal.parse(amountText);
}
int? amount;
if (amountDecimal != null) {
amount = Format.decimalAmountToSatoshis(amountDecimal, widget.coin);
.toAmount(fractionDigits: widget.coin.decimals)
: Decimal.parse(amountText)
.toAmount(fractionDigits: widget.coin.decimals);
}
final TransactionFilter filter = TransactionFilter(

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -38,6 +37,7 @@ import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
@ -346,8 +346,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
);
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
final publicBalance = firoWallet.availablePublicBalance();
if (publicBalance <= Decimal.zero) {
final Amount publicBalance = firoWallet.availablePublicBalance();
if (publicBalance <= Amount.zero) {
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -6,10 +5,10 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -38,8 +37,8 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
late final String walletId;
late final ChangeNotifierProvider<Manager> managerProvider;
Decimal _cachedBalance = Decimal.zero;
Decimal _cachedFiatValue = Decimal.zero;
Amount _cachedBalance = Amount.zero;
Amount _cachedFiatValue = Amount.zero;
@override
void initState() {
@ -223,21 +222,23 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
),
FutureBuilder(
future: Future(() => ref.watch(managerProvider
.select((value) => value.balance.getTotal()))),
builder: (builderContext, AsyncSnapshot<Decimal> snapshot) {
.select((value) => value.balance.total))),
builder: (builderContext, AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
if (snapshot.data != null) {
_cachedBalance = snapshot.data!;
if (externalCalls) {
_cachedFiatValue = _cachedBalance *
ref
.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getPrice(coin),
),
)
.item1;
if (externalCalls && _cachedBalance > Amount.zero) {
_cachedFiatValue = (_cachedBalance.decimal *
ref
.watch(
priceAnd24hChangeNotifierProvider
.select(
(value) => value.getPrice(coin),
),
)
.item1)
.toAmount(fractionDigits: 2);
}
}
}
@ -247,13 +248,13 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${Format.localizedStringAsFixed(
decimalPlaces: 8,
value: _cachedBalance,
"${_cachedBalance.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
decimalPlaces: ref.watch(managerProvider
.select((value) => value.coin.decimals)),
)} ${coin.ticker}",
style: STextStyles.titleBold12(context).copyWith(
fontSize: 16,
@ -269,13 +270,12 @@ class _FavoriteCardState extends ConsumerState<FavoriteCard> {
),
if (externalCalls)
Text(
"${Format.localizedStringAsFixed(
decimalPlaces: 2,
value: _cachedFiatValue,
"${_cachedFiatValue.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale),
),
decimalPlaces: 2,
)} ${ref.watch(
prefsChangeNotifierProvider
.select((value) => value.currency),

View file

@ -7,10 +7,10 @@ import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages/wallets_sheet/wallets_sheet.dart';
import 'package:stackwallet/pages/wallets_view/eth_wallets_overview.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -108,13 +108,12 @@ class WalletListItem extends ConsumerWidget {
final calls =
ref.watch(prefsChangeNotifierProvider).externalCalls;
final priceString = Format.localizedStringAsFixed(
value: tuple.item1,
locale: ref
.watch(localeServiceChangeNotifierProvider.notifier)
.locale,
decimalPlaces: 2,
);
final priceString = tuple.item1
.toAmount(fractionDigits: 2)
.localizedStringAsFixed(
locale: ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)),
);
final double percentChange = tuple.item2;

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -7,10 +6,10 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/pages_desktop_specific/coin_control/utxo_row.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
@ -37,7 +36,7 @@ class DesktopCoinControlUseDialog extends ConsumerStatefulWidget {
}) : super(key: key);
final String walletId;
final Decimal? amountToSend;
final Amount? amountToSend;
@override
ConsumerState<DesktopCoinControlUseDialog> createState() =>
@ -114,12 +113,16 @@ class _DesktopCoinControlUseDialogState
);
}
final selectedSum = Format.satoshisToAmount(
_selectedUTXOs
.map((e) => e.value)
.fold(0, (value, element) => value += element),
coin: coin,
);
final Amount selectedSum = _selectedUTXOs.map((e) => e.value).fold(
Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
(value, element) => value += Amount(
rawValue: BigInt.from(element),
fractionDigits: coin.decimals,
),
);
final enableApply = widget.amountToSend == null
? selectedChanged(_selectedUTXOs)
@ -470,7 +473,7 @@ class _DesktopCoinControlUseDialogState
),
),
Text(
"${widget.amountToSend!.toStringAsFixed(
"${widget.amountToSend!.decimal.toStringAsFixed(
coin.decimals,
)}"
" ${coin.ticker}",
@ -505,7 +508,7 @@ class _DesktopCoinControlUseDialogState
),
),
Text(
"${selectedSum.toStringAsFixed(
"${selectedSum.decimal.toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
style: STextStyles.desktopTextExtraExtraSmall(

View file

@ -4,9 +4,10 @@ import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/coin_control/utxo_details_view.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
@ -143,10 +144,16 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
),
if (!widget.compact)
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
"${Amount(
rawValue: BigInt.from(utxo.value),
fractionDigits: coin.decimals,
).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
textAlign: TextAlign.right,
style: STextStyles.w600_14(context),
),
@ -163,10 +170,16 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${Format.satoshisToAmount(
utxo.value,
coin: coin,
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
"${Amount(
rawValue: BigInt.from(utxo.value),
fractionDigits: coin.decimals,
).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
)} ${coin.ticker}",
textAlign: TextAlign.right,
style: STextStyles.w600_14(context),
),

View file

@ -17,6 +17,7 @@ import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/services/notifications_api.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
@ -182,10 +183,11 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
void sendFromStack() {
final trade = ref.read(desktopExchangeModelProvider)!.trade!;
final amount = Decimal.parse(trade.payInAmount);
final address = trade.payInAddress;
final coin = coinFromTickerCaseInsensitive(trade.payInCurrency);
final amount = Decimal.parse(trade.payInAmount).toAmount(
fractionDigits: coin.decimals,
);
showDialog<void>(
context: context,

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -6,10 +5,8 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
@ -268,7 +265,7 @@ class _DesktopChooseFromStackState
}
}
class BalanceDisplay extends ConsumerStatefulWidget {
class BalanceDisplay extends ConsumerWidget {
const BalanceDisplay({
Key? key,
required this.walletId,
@ -277,65 +274,19 @@ class BalanceDisplay extends ConsumerStatefulWidget {
final String walletId;
@override
ConsumerState<BalanceDisplay> createState() => _BalanceDisplayState();
}
class _BalanceDisplayState extends ConsumerState<BalanceDisplay> {
late final String walletId;
Decimal? _cachedBalance;
static const loopedText = [
"Loading balance ",
"Loading balance. ",
"Loading balance.. ",
"Loading balance..."
];
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
// TODO redo this widget now that its not actually a future
return FutureBuilder(
future: Future(() => manager.balance.getSpendable()),
builder: (context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData &&
snapshot.data != null) {
_cachedBalance = snapshot.data;
}
if (_cachedBalance == null) {
return AnimatedText(
stringsToLoopThrough: loopedText,
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
);
} else {
return Text(
"${Format.localizedStringAsFixed(
value: _cachedBalance!,
locale: locale,
decimalPlaces: 8,
)} ${manager.coin.ticker}",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textAlign: TextAlign.right,
);
}
},
return Text(
"${manager.balance.spendable.localizedStringAsFixed(locale: locale)} "
"${manager.coin.ticker}",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textAlign: TextAlign.right,
);
}
}

View file

@ -10,11 +10,11 @@ import 'package:stackwallet/providers/global/price_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
@ -120,22 +120,15 @@ class _DesktopPaynymSendDialogState
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"${Format.localizedStringAsFixed(
value: !isFiro
? manager.balance.getSpendable()
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (manager.wallet as FiroWallet)
.availablePrivateBalance()
: (manager.wallet as FiroWallet)
.availablePublicBalance(),
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
"${!isFiro ? manager.balance.spendable.localizedStringAsFixed(
locale: locale,
) : ref.watch(
publicPrivateBalanceStateProvider.state,
).state == "Private" ? (manager.wallet as FiroWallet).availablePrivateBalance().localizedStringAsFixed(
locale: locale,
) : (manager.wallet as FiroWallet).availablePublicBalance().localizedStringAsFixed(
locale: locale,
)} ${coin.ticker}",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.right,
),
@ -143,25 +136,7 @@ class _DesktopPaynymSendDialogState
height: 2,
),
Text(
"${Format.localizedStringAsFixed(
value: (!isFiro
? manager.balance.getSpendable()
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (manager.wallet as FiroWallet)
.availablePrivateBalance()
: (manager.wallet as FiroWallet)
.availablePublicBalance()) *
ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getPrice(coin).item1)),
locale: locale,
decimalPlaces: 2,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
"${((!isFiro ? manager.balance.spendable.decimal : ref.watch(publicPrivateBalanceStateProvider.state).state == "Private" ? (manager.wallet as FiroWallet).availablePrivateBalance().decimal : (manager.wallet as FiroWallet).availablePublicBalance().decimal) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1))).toAmount(fractionDigits: 2).localizedStringAsFixed(locale: locale)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
style: STextStyles.baseXS(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!

View file

@ -3,9 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/dialogs/desktop_coin_wallets_dialog.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
@ -205,8 +205,10 @@ class TablePriceInfo extends ConsumerWidget {
),
);
final priceString = Format.localizedStringAsFixed(
value: tuple.item1,
final priceString = Amount.fromDecimal(
tuple.item1,
fractionDigits: 2,
).localizedStringAsFixed(
locale: ref
.watch(
localeServiceChangeNotifierProvider.notifier,

View file

@ -1,5 +1,4 @@
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:decimal/decimal.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -10,11 +9,11 @@ import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_text.dart';
@ -44,8 +43,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
"Calculating...",
];
Future<Decimal> feeFor({
required int amount,
Future<Amount> feeFor({
required Amount amount,
required FeeRateType feeRateType,
required int feeRate,
required Coin coin,
@ -59,24 +58,16 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.fast.raw!);
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
}
return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
@ -89,24 +80,16 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.regular.raw!);
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).average[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
}
return ref.read(feeSheetSessionCacheProvider).average[amount]!;
@ -119,24 +102,16 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.slow.raw!);
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
fee,
coin: coin,
);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate),
coin: coin);
await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
Format.satoshisToAmount(
await manager.estimateFeeFor(amount, feeRate),
coin: coin);
await manager.estimateFeeFor(amount, feeRate);
}
}
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
@ -242,7 +217,7 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
}
final sendAmountProvider =
StateProvider.autoDispose<Decimal>((_) => Decimal.zero);
StateProvider.autoDispose<Amount>((_) => Amount.zero);
class FeeDropDownChild extends ConsumerWidget {
const FeeDropDownChild({
@ -257,8 +232,8 @@ class FeeDropDownChild extends ConsumerWidget {
final FeeObject? feeObject;
final FeeRateType feeRateType;
final String walletId;
final Future<Decimal> Function({
required int amount,
final Future<Amount> Function({
required Amount amount,
required FeeRateType feeRateType,
required int feeRate,
required Coin coin,
@ -322,19 +297,20 @@ class FeeDropDownChild extends ConsumerWidget {
: feeRateType == FeeRateType.slow
? feeObject!.slow
: feeObject!.medium,
amount: Format.decimalAmountToSatoshis(
ref.watch(sendAmountProvider.state).state,
manager.coin,
),
amount: ref.watch(sendAmountProvider.state).state,
),
builder: (_, AsyncSnapshot<Decimal> snapshot) {
builder: (_, AsyncSnapshot<Amount> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${feeRateType.prettyName} (~${snapshot.data!} ${manager.coin.ticker})",
"${feeRateType.prettyName} "
"(~${snapshot.data!.decimal.toStringAsFixed(
manager.coin.decimals,
)} "
"${manager.coin.ticker})",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context)

View file

@ -25,12 +25,12 @@ import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -88,8 +88,8 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
String? _note;
Decimal? _amountToSend;
Decimal? _cachedAmountToSend;
Amount? _amountToSend;
Amount? _cachedAmountToSend;
String? _address;
String? _privateBalanceString;
@ -106,20 +106,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin);
int availableBalance;
final Amount amount = _amountToSend!;
final Amount availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
availableBalance = Format.decimalAmountToSatoshis(
(manager.wallet as FiroWallet).availablePrivateBalance(), coin);
availableBalance =
(manager.wallet as FiroWallet).availablePrivateBalance();
} else {
availableBalance = Format.decimalAmountToSatoshis(
(manager.wallet as FiroWallet).availablePublicBalance(), coin);
availableBalance =
(manager.wallet as FiroWallet).availablePublicBalance();
}
} else {
availableBalance =
Format.decimalAmountToSatoshis(manager.balance.getSpendable(), coin);
availableBalance = manager.balance.spendable;
}
final coinControlEnabled =
@ -268,7 +267,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
final feeRate = ref.read(feeRateTypeStateProvider);
txDataFuture = wallet.preparePaymentCodeSend(
paymentCode: paymentCode,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate": feeRate,
"UTXOs": (manager.hasCoinControlSupport &&
@ -283,7 +282,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
"Private") {
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
address: _address!,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
"UTXOs": (manager.hasCoinControlSupport &&
@ -296,7 +295,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
} else {
txDataFuture = manager.prepareSend(
address: _address!,
satoshiAmount: amount,
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
"UTXOs": (manager.hasCoinControlSupport &&
@ -435,7 +434,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
cryptoAmount != ",") {
_amountToSend = cryptoAmount.contains(",")
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
: Decimal.parse(cryptoAmount);
.toAmount(fractionDigits: coin.decimals)
: Decimal.parse(cryptoAmount)
.toAmount(fractionDigits: coin.decimals);
if (_cachedAmountToSend != null &&
_cachedAmountToSend == _amountToSend) {
return;
@ -448,11 +449,11 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
if (price > Decimal.zero) {
final String fiatAmountString = Format.localizedStringAsFixed(
value: _amountToSend! * price,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: 2,
);
final String fiatAmountString = (_amountToSend!.decimal * price)
.toAmount(fractionDigits: 2)
.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
baseAmountController.text = fiatAmountString;
}
@ -476,17 +477,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
return null;
}
void _updatePreviewButtonState(String? address, Decimal? amount) {
void _updatePreviewButtonState(String? address, Amount? amount) {
if (isPaynymSend) {
ref.read(previewTxButtonStateProvider.state).state =
(amount != null && amount > Decimal.zero);
(amount != null && amount > Amount.zero);
} else {
final isValidAddress = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.validateAddress(address ?? "");
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Decimal.zero);
(isValidAddress && amount != null && amount > Amount.zero);
}
}
@ -498,15 +499,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
final wallet = ref.read(provider).wallet as FiroWallet?;
if (wallet != null) {
Decimal? balance;
Amount? balance;
if (private) {
balance = wallet.availablePrivateBalance();
} else {
balance = wallet.availablePublicBalance();
}
return Format.localizedStringAsFixed(
value: balance, locale: locale, decimalPlaces: 8);
return balance.localizedStringAsFixed(
locale: locale,
decimalPlaces: coin.decimals,
);
}
return null;
@ -577,9 +579,10 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
// autofill amount field
if (results["amount"] != null) {
final amount = Decimal.parse(results["amount"]!);
cryptoAmountController.text = Format.localizedStringAsFixed(
value: amount,
final amount = Decimal.parse(results["amount"]!).toAmount(
fractionDigits: coin.decimals,
);
cryptoAmountController.text = amount.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
);
@ -643,18 +646,20 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
baseAmountString != ",") {
final baseAmount = baseAmountString.contains(",")
? Decimal.parse(baseAmountString.replaceFirst(",", "."))
: Decimal.parse(baseAmountString);
.toAmount(fractionDigits: 2)
: Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
var _price =
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
if (_price == Decimal.zero) {
_amountToSend = Decimal.zero;
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
} else {
_amountToSend = baseAmount <= Decimal.zero
? Decimal.zero
: (baseAmount / _price).toDecimal(
scaleOnInfinitePrecision: Constants.decimalPlacesForCoin(coin));
_amountToSend = baseAmount <= Amount.zero
? Decimal.zero.toAmount(fractionDigits: coin.decimals)
: (baseAmount.decimal / _price)
.toDecimal(scaleOnInfinitePrecision: coin.decimals)
.toAmount(fractionDigits: coin.decimals);
}
if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) {
return;
@ -663,17 +668,16 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
level: LogLevel.Info);
final amountString = Format.localizedStringAsFixed(
value: _amountToSend!,
final amountString = _amountToSend!.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
decimalPlaces: coin.decimals,
);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = amountString;
_cryptoAmountChangeLock = false;
} else {
_amountToSend = Decimal.zero;
_amountToSend = Decimal.zero.toAmount(fractionDigits: coin.decimals);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = "";
_cryptoAmountChangeLock = false;
@ -694,19 +698,24 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
.wallet as FiroWallet;
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
cryptoAmountController.text = (firoWallet.availablePrivateBalance())
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cryptoAmountController.text = firoWallet
.availablePrivateBalance()
.decimal
.toStringAsFixed(coin.decimals);
} else {
cryptoAmountController.text = (firoWallet.availablePublicBalance())
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cryptoAmountController.text = firoWallet
.availablePublicBalance()
.decimal
.toStringAsFixed(coin.decimals);
}
} else {
cryptoAmountController.text = (ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.balance
.getSpendable())
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cryptoAmountController.text = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.balance
.spendable
.decimal
.toStringAsFixed(coin.decimals);
}
}

View file

@ -24,7 +24,6 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/text_styles.dart';
@ -72,7 +71,6 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
late TextEditingController sendToController;
late TextEditingController cryptoAmountController;
late TextEditingController baseAmountController;
// late TextEditingController feeController;
late final SendViewAutoFillData? _data;
@ -82,8 +80,8 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
String? _note;
Decimal? _amountToSend;
Decimal? _cachedAmountToSend;
Amount? _amountToSend;
Amount? _cachedAmountToSend;
String? _address;
bool _addressToggleFlag = false;
@ -94,16 +92,11 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Future<void> previewSend() async {
final tokenWallet = ref.read(tokenServiceProvider)!;
final amount = Amount.fromDecimal(
_amountToSend!,
fractionDigits: tokenWallet.tokenContract.decimals,
);
// todo use Amount class
final availableBalance = tokenWallet.balance.spendable;
final Amount amount = _amountToSend!;
final Amount availableBalance = tokenWallet.balance.spendable;
// confirm send all
if (amount.raw.toInt() == availableBalance) {
if (amount == availableBalance) {
final bool? shouldSendAll = await showDialog<bool>(
context: context,
useSafeArea: false,
@ -233,7 +226,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
txDataFuture = tokenWallet.prepareSend(
address: _address!,
satoshiAmount: amount.raw.toInt(),
amount: amount,
args: {
"feeRate": ref.read(feeRateTypeStateProvider),
},
@ -361,8 +354,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
cryptoAmount != "." &&
cryptoAmount != ",") {
_amountToSend = cryptoAmount.contains(",")
? Decimal.parse(cryptoAmount.replaceFirst(",", "."))
: Decimal.parse(cryptoAmount);
? Decimal.parse(cryptoAmount.replaceFirst(",", ".")).toAmount(
fractionDigits:
ref.read(tokenServiceProvider)!.tokenContract.decimals,
)
: Decimal.parse(cryptoAmount).toAmount(
fractionDigits:
ref.read(tokenServiceProvider)!.tokenContract.decimals,
);
if (_cachedAmountToSend != null &&
_cachedAmountToSend == _amountToSend) {
return;
@ -375,8 +374,10 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
if (price > Decimal.zero) {
final String fiatAmountString = Format.localizedStringAsFixed(
value: _amountToSend! * price,
final String fiatAmountString = Amount.fromDecimal(
_amountToSend!.decimal * price,
fractionDigits: 2,
).localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: 2,
);
@ -403,13 +404,13 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
return null;
}
void _updatePreviewButtonState(String? address, Decimal? amount) {
void _updatePreviewButtonState(String? address, Amount? amount) {
final isValidAddress = ref
.read(walletsChangeNotifierProvider)
.getManager(walletId)
.validateAddress(address ?? "");
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Decimal.zero);
(isValidAddress && amount != null && amount > Amount.zero);
}
Future<void> scanQr() async {
@ -442,12 +443,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
// autofill amount field
if (results["amount"] != null) {
final amount = Decimal.parse(results["amount"]!);
cryptoAmountController.text = Format.localizedStringAsFixed(
value: amount,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
final amount = Decimal.parse(results["amount"]!).toAmount(
fractionDigits:
ref.read(tokenServiceProvider)!.tokenContract.decimals,
);
cryptoAmountController.text = amount.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
);
amount.toString();
_amountToSend = amount;
}
@ -498,23 +501,28 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
}
void fiatTextFieldOnChanged(String baseAmountString) {
final int tokenDecimals =
ref.read(tokenServiceProvider)!.tokenContract.decimals;
if (baseAmountString.isNotEmpty &&
baseAmountString != "." &&
baseAmountString != ",") {
final baseAmount = baseAmountString.contains(",")
? Decimal.parse(baseAmountString.replaceFirst(",", "."))
: Decimal.parse(baseAmountString);
.toAmount(fractionDigits: 2)
: Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
var _price =
final Decimal _price =
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
if (_price == Decimal.zero) {
_amountToSend = Decimal.zero;
_amountToSend = Decimal.zero.toAmount(fractionDigits: tokenDecimals);
} else {
_amountToSend = baseAmount <= Decimal.zero
? Decimal.zero
: (baseAmount / _price).toDecimal(
scaleOnInfinitePrecision: Constants.decimalPlacesForCoin(coin));
_amountToSend = baseAmount <= Amount.zero
? Decimal.zero.toAmount(fractionDigits: tokenDecimals)
: (baseAmount.decimal / _price)
.toDecimal(scaleOnInfinitePrecision: tokenDecimals)
.toAmount(fractionDigits: tokenDecimals);
}
if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) {
return;
@ -523,17 +531,16 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
level: LogLevel.Info);
final amountString = Format.localizedStringAsFixed(
value: _amountToSend!,
final amountString = _amountToSend!.localizedStringAsFixed(
locale: ref.read(localeServiceChangeNotifierProvider).locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
decimalPlaces: tokenDecimals,
);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = amountString;
_cryptoAmountChangeLock = false;
} else {
_amountToSend = Decimal.zero;
_amountToSend = Decimal.zero.toAmount(fractionDigits: tokenDecimals);
_cryptoAmountChangeLock = true;
cryptoAmountController.text = "";
_cryptoAmountChangeLock = false;
@ -543,9 +550,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
}
Future<void> sendAllTapped() async {
cryptoAmountController.text =
(ref.read(tokenServiceProvider)!.balance.getSpendable())
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
cryptoAmountController.text = ref
.read(tokenServiceProvider)!
.balance
.spendable
.decimal
.toStringAsFixed(
ref.read(tokenServiceProvider)!.tokenContract.decimals,
);
}
@override

View file

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -18,6 +17,7 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -162,7 +162,7 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
final publicBalance = firoWallet.availablePublicBalance();
if (publicBalance <= Decimal.zero) {
if (publicBalance <= Amount.zero) {
shouldPop = true;
if (context.mounted) {
Navigator.of(context, rootNavigator: true).pop();

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/balance.dart';
@ -9,9 +8,9 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -86,7 +85,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
: ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).balance));
Decimal balanceToShow;
Amount balanceToShow;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
Balance? balanceSecondary = ref
.watch(
@ -101,18 +100,16 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
WalletBalanceToggleState.available;
if (_showAvailable) {
balanceToShow = showPrivate
? balanceSecondary!.getSpendable()
: balance.getSpendable();
} else {
balanceToShow =
showPrivate ? balanceSecondary!.getTotal() : balance.getTotal();
showPrivate ? balanceSecondary!.spendable : balance.spendable;
} else {
balanceToShow = showPrivate ? balanceSecondary!.total : balance.total;
}
} else {
if (_showAvailable) {
balanceToShow = balance.getSpendable();
balanceToShow = balance.spendable;
} else {
balanceToShow = balance.getTotal();
balanceToShow = balance.total;
}
}
@ -127,8 +124,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${Format.localizedStringAsFixed(
value: balanceToShow,
"${balanceToShow.localizedStringAsFixed(
locale: locale,
decimalPlaces: decimalPlaces,
)} $unit",
@ -137,8 +133,10 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
),
if (externalCalls)
Text(
"${Format.localizedStringAsFixed(
value: priceTuple.item1 * balanceToShow,
"${Amount.fromDecimal(
priceTuple.item1 * balanceToShow.decimal,
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",

View file

@ -1,4 +1,3 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -144,6 +143,7 @@ import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncin
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
@ -1280,7 +1280,7 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case SendFromView.routeName:
if (args is Tuple4<Coin, Decimal, String, Trade>) {
if (args is Tuple4<Coin, Amount, String, Trade>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => SendFromView(

View file

@ -51,8 +51,14 @@ import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 1;
const int DUST_LIMIT = 294;
const int DUST_LIMIT_P2PKH = 546;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(294),
fractionDigits: Coin.particl.decimals,
);
final Amount DUST_LIMIT_P2PKH = Amount(
rawValue: BigInt.from(546),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
@ -155,7 +161,7 @@ class BitcoinWallet extends CoinServiceAPI
// checkChangeAddressForTransactions:
// _checkP2PKHChangeAddressForTransactions,
addDerivation: addDerivation,
dustLimitP2PKH: DUST_LIMIT_P2PKH,
dustLimitP2PKH: DUST_LIMIT_P2PKH.raw.toInt(),
minConfirms: MINIMUM_CONFIRMATIONS,
);
}
@ -1119,7 +1125,7 @@ class BitcoinWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -1149,14 +1155,14 @@ class BitcoinWallet extends CoinServiceAPI
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1468,9 +1474,18 @@ class BitcoinWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2404,8 +2419,11 @@ class BitcoinWallet extends CoinServiceAPI
feeRatePerKB: selectedTxFeeRate,
);
final int roughEstimate =
roughFeeEstimate(spendableOutputs.length, 1, selectedTxFeeRate);
final int roughEstimate = roughFeeEstimate(
spendableOutputs.length,
1,
selectedTxFeeRate,
).raw.toInt();
if (feeForOneOutput < roughEstimate) {
feeForOneOutput = roughEstimate;
}
@ -2476,7 +2494,7 @@ class BitcoinWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2484,7 +2502,7 @@ class BitcoinWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > DUST_LIMIT satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -3063,22 +3081,28 @@ class BitcoinWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance += Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -3087,31 +3111,35 @@ class BitcoinWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
}
}
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(
((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -3125,7 +3153,11 @@ class BitcoinWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -47,7 +47,10 @@ import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 0;
const int DUST_LIMIT = 546;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(546),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
@ -212,10 +215,7 @@ class BitcoinCashWallet extends CoinServiceAPI
@override
Future<int> get maxFee async {
final fee = (await fees).fast;
final satsFee = Format.satoshisToAmount(fee, coin: coin) *
Decimal.fromInt(Constants.satsPerCoin(coin));
return satsFee.floor().toBigInt().toInt();
throw UnimplementedError("Not used in bch");
}
@override
@ -1049,7 +1049,7 @@ class BitcoinCashWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -1078,14 +1078,14 @@ class BitcoinCashWallet extends CoinServiceAPI
}
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final result = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1423,9 +1423,18 @@ class BitcoinCashWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2156,12 +2165,27 @@ class BitcoinCashWallet extends CoinServiceAPI
Set<String> inputAddresses = {};
Set<String> outputAddresses = {};
int totalInputValue = 0;
int totalOutputValue = 0;
Amount totalInputValue = Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
);
Amount totalOutputValue = Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
);
int amountSentFromWallet = 0;
int amountReceivedInWallet = 0;
int changeAmount = 0;
Amount amountSentFromWallet = Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
);
Amount amountReceivedInWallet = Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
);
Amount changeAmount = Amount(
rawValue: BigInt.from(0),
fractionDigits: coin.decimals,
);
// parse inputs
for (final input in txData["vin"] as List) {
@ -2178,13 +2202,13 @@ class BitcoinCashWallet extends CoinServiceAPI
// check matching output
if (prevOut == output["n"]) {
// get value
final value = Format.decimalAmountToSatoshis(
final value = Amount.fromDecimal(
Decimal.parse(output["value"].toString()),
coin,
fractionDigits: coin.decimals,
);
// add value to total
totalInputValue += value;
totalInputValue = totalInputValue + value;
// get input(prevOut) address
final address =
@ -2197,7 +2221,7 @@ class BitcoinCashWallet extends CoinServiceAPI
// if input was from my wallet, add value to amount sent
if (receivingAddresses.contains(address) ||
changeAddresses.contains(address)) {
amountSentFromWallet += value;
amountSentFromWallet = amountSentFromWallet + value;
}
}
}
@ -2207,9 +2231,9 @@ class BitcoinCashWallet extends CoinServiceAPI
// parse outputs
for (final output in txData["vout"] as List) {
// get value
final value = Format.decimalAmountToSatoshis(
final value = Amount.fromDecimal(
Decimal.parse(output["value"].toString()),
coin,
fractionDigits: coin.decimals,
);
// add value to total
@ -2246,7 +2270,7 @@ class BitcoinCashWallet extends CoinServiceAPI
txData["address"] as isar_models.Address;
isar_models.TransactionType type;
int amount;
Amount amount;
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
// tx is sent to self
type = isar_models.TransactionType.sentToSelf;
@ -2301,10 +2325,10 @@ class BitcoinCashWallet extends CoinServiceAPI
scriptPubKeyAddress:
json["scriptPubKey"]?["addresses"]?[0] as String? ??
json['scriptPubKey']['type'] as String,
value: Format.decimalAmountToSatoshis(
value: Amount.fromDecimal(
Decimal.parse(json["value"].toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
outputs.add(output);
}
@ -2316,12 +2340,9 @@ class BitcoinCashWallet extends CoinServiceAPI
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
type: type,
subType: isar_models.TransactionSubType.none,
amount: amount,
amountString: Amount(
rawValue: BigInt.from(amount),
fractionDigits: coin.decimals,
).toJsonString(),
fee: fee,
amount: amount.raw.toInt(),
amountString: amount.toJsonString(),
fee: fee.raw.toInt(),
height: txData["height"] as int?,
isCancelled: false,
isLelantus: false,
@ -2548,7 +2569,7 @@ class BitcoinCashWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2556,7 +2577,7 @@ class BitcoinCashWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > 546 satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -3117,22 +3138,28 @@ class BitcoinCashWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance += Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -3141,19 +3168,19 @@ class BitcoinCashWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
@ -3161,12 +3188,15 @@ class BitcoinCashWallet extends CoinServiceAPI
}
// TODO: correct formula for bch?
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -3180,7 +3210,11 @@ class BitcoinCashWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -16,6 +16,7 @@ import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
import 'package:stackwallet/services/coins/particl/particl_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/prefs.dart';
@ -244,7 +245,7 @@ abstract class CoinServiceAPI {
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
});
@ -299,7 +300,7 @@ abstract class CoinServiceAPI {
bool get isConnected;
Future<int> estimateFeeFor(int satoshiAmount, int feeRate);
Future<Amount> estimateFeeFor(Amount amount, int feeRate);
Future<bool> generateNewAddress();

View file

@ -8,7 +8,6 @@ import 'package:bip39/bip39.dart' as bip39;
import 'package:bitcoindart/bitcoindart.dart';
import 'package:bitcoindart/bitcoindart.dart' as btc_dart;
import 'package:bs58check/bs58check.dart' as bs58check;
import 'package:decimal/decimal.dart';
import 'package:flutter/foundation.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
@ -49,7 +48,10 @@ import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 1;
const int DUST_LIMIT = 1000000;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(1000000),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691";
@ -227,10 +229,7 @@ class DogecoinWallet extends CoinServiceAPI
@override
Future<int> get maxFee async {
final fee = (await fees).fast;
final satsFee = Format.satoshisToAmount(fee, coin: coin) *
Decimal.fromInt(Constants.satsPerCoin(coin));
return satsFee.floor().toBigInt().toInt();
throw UnimplementedError("Not used in dogecoin");
}
@override
@ -915,7 +914,7 @@ class DogecoinWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -944,14 +943,14 @@ class DogecoinWallet extends CoinServiceAPI
}
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final result = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1250,9 +1249,18 @@ class DogecoinWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2241,7 +2249,7 @@ class DogecoinWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2249,7 +2257,7 @@ class DogecoinWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > 546 satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -2830,22 +2838,29 @@ class DogecoinWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance = runningBalance +
Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -2854,19 +2869,19 @@ class DogecoinWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
@ -2874,12 +2889,15 @@ class DogecoinWallet extends CoinServiceAPI
}
// TODO: correct formula for doge?
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -2893,7 +2911,11 @@ class DogecoinWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -33,7 +33,6 @@ import 'package:stackwallet/utilities/default_epicboxes.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
@ -857,20 +856,23 @@ class EpicCashWallet extends CoinServiceAPI
);
@override
Future<Map<String, dynamic>> prepareSend(
{required String address,
required int satoshiAmount,
Map<String, dynamic>? args}) async {
Future<Map<String, dynamic>> prepareSend({
required String address,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
int realfee = await nativeFee(satoshiAmount);
if (balance.spendable == satoshiAmount) {
satoshiAmount = balance.spendable - realfee;
int satAmount = amount.raw.toInt();
int realfee = await nativeFee(satAmount);
if (balance.spendable == amount) {
satAmount = balance.spendable.raw.toInt() - realfee;
}
Map<String, dynamic> txData = {
"fee": realfee,
"addresss": address,
"recipientAmt": satoshiAmount,
"recipientAmt": satAmount,
};
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -1939,9 +1941,13 @@ class EpicCashWallet extends CoinServiceAPI
bool isActive = false;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
int currentFee = await nativeFee(satoshiAmount, ifErrorEstimateFee: true);
return currentFee;
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
int currentFee =
await nativeFee(amount.raw.toInt(), ifErrorEstimateFee: true);
return Amount(
rawValue: BigInt.from(currentFee),
fractionDigits: coin.decimals,
);
}
// not used in epic currently
@ -1973,18 +1979,21 @@ class EpicCashWallet extends CoinServiceAPI
_balance = Balance(
coin: coin,
total: Format.decimalAmountToSatoshis(
total: Amount.fromDecimal(
Decimal.parse(total) + Decimal.parse(awaiting),
coin,
fractionDigits: coin.decimals,
),
spendable: Format.decimalAmountToSatoshis(
spendable: Amount.fromDecimal(
Decimal.parse(spendable),
coin,
fractionDigits: coin.decimals,
),
blockedTotal: 0,
pendingSpendable: Format.decimalAmountToSatoshis(
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
pendingSpendable: Amount.fromDecimal(
Decimal.parse(pending),
coin,
fractionDigits: coin.decimals,
),
);

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:bip39/bip39.dart' as bip39;
import 'package:decimal/decimal.dart';
import 'package:ethereum_addresses/ethereum_addresses.dart';
import 'package:http/http.dart';
import 'package:isar/isar.dart';
@ -34,7 +33,6 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/eth_commons.dart';
import 'package:stackwallet/utilities/extensions/extensions.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart';
@ -88,15 +86,27 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
if (jsonString == null) {
return TokenBalance(
contractAddress: contract.address,
decimalPlaces: contract.decimals,
total: 0,
spendable: 0,
blockedTotal: 0,
pendingSpendable: 0,
total: Amount(
rawValue: BigInt.zero,
fractionDigits: contract.decimals,
),
spendable: Amount(
rawValue: BigInt.zero,
fractionDigits: contract.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: contract.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: contract.decimals,
),
);
}
return TokenBalance.fromJson(
jsonString,
contract.decimals,
);
}
@ -214,18 +224,29 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
// TODO: check if toInt() is ok and if getBalance actually returns enough balance data
_balance = Balance(
coin: coin,
total: ethBalance.getInWei.toInt(),
spendable: ethBalance.getInWei.toInt(),
blockedTotal: 0,
pendingSpendable: 0,
total: Amount.fromDouble(
ethBalance.getValueInUnit(web3.EtherUnit.ether),
fractionDigits: coin.decimals,
),
spendable: Amount.fromDouble(
ethBalance.getValueInUnit(web3.EtherUnit.ether),
fractionDigits: coin.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
);
await updateCachedBalance(_balance!);
}
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
final fee = estimateFee(feeRate, _gasLimit, coin.decimals);
return Format.decimalAmountToSatoshis(Decimal.parse(fee.toString()), coin);
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
return estimateFee(feeRate, _gasLimit, coin.decimals);
}
@override
@ -370,9 +391,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
@override
Future<int> get maxFee async {
final fee = (await fees).fast;
final feeEstimate = await estimateFeeFor(0, fee);
return feeEstimate;
throw UnimplementedError("Not used for eth");
}
@override
@ -421,10 +440,11 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
@override
Future<Map<String, dynamic>> prepareSend(
{required String address,
required int satoshiAmount,
Map<String, dynamic>? args}) async {
Future<Map<String, dynamic>> prepareSend({
required String address,
required Amount amount,
Map<String, dynamic>? args,
}) async {
final feeRateType = args?["feeRate"];
int fee = 0;
final feeObject = await fees;
@ -440,7 +460,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
break;
}
final feeEstimate = await estimateFeeFor(satoshiAmount, fee);
final feeEstimate = await estimateFeeFor(amount, fee);
// bool isSendAll = false;
// final availableBalance = balance.spendable;
@ -453,12 +473,6 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
// satoshiAmount -= feeEstimate;
// }
final decimalAmount = Format.satoshisToAmount(satoshiAmount, coin: coin);
final bigIntAmount = amountToBigInt(
decimalAmount.toDouble(),
Constants.decimalPlacesForCoin(coin),
);
final client = getEthClient();
final myAddress = await currentReceivingAddress;
@ -472,7 +486,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
fee,
),
amountOfGas: BigInt.from(_gasLimit),
value: web3.EtherAmount.inWei(bigIntAmount),
value: web3.EtherAmount.inWei(amount.raw),
);
final nonce = args?["nonce"] as int? ??
@ -494,7 +508,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
fee,
),
maxGas: _gasLimit,
value: web3.EtherAmount.inWei(bigIntAmount),
value: web3.EtherAmount.inWei(amount.raw),
nonce: nonce,
);
@ -502,7 +516,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
"fee": feeEstimate,
"feeInWei": fee,
"address": address,
"recipientAmt": satoshiAmount,
"recipientAmt": amount,
"ethTx": tx,
"chainId": (await client.getChainId()).toInt(),
"nonce": tx.nonce,

View file

@ -707,7 +707,10 @@ Future<dynamic> isolateCreateJoinSplitTransaction(
"txid": txId,
"txHex": txHex,
"value": amount,
"fees": Format.satoshisToAmount(fee, coin: coin).toDouble(),
"fees": Amount(
rawValue: BigInt.from(fee),
fractionDigits: coin.decimals,
).decimal.toDouble(),
"fee": fee,
"vSize": extTx.virtualSize(),
"jmintValue": changeToMint,
@ -716,7 +719,10 @@ Future<dynamic> isolateCreateJoinSplitTransaction(
"height": locktime,
"txType": "Sent",
"confirmed_status": false,
"amount": Format.satoshisToAmount(amount, coin: coin).toDouble(),
"amount": Amount(
rawValue: BigInt.from(amount),
fractionDigits: coin.decimals,
).decimal.toDouble(),
"recipientAmt": amount,
"address": address,
"timestamp": DateTime.now().millisecondsSinceEpoch ~/ 1000,
@ -1030,7 +1036,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Future<Map<String, dynamic>> prepareSendPublic({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -1059,14 +1065,17 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
// check for send all
bool isSendAll = false;
final balance =
Format.decimalAmountToSatoshis(availablePublicBalance(), coin);
if (satoshiAmount == balance) {
final balance = availablePublicBalance();
if (amount == balance) {
isSendAll = true;
}
final txData =
await coinSelection(satoshiAmount, rate, address, isSendAll);
final txData = await coinSelection(
amount.raw.toInt(),
rate,
address,
isSendAll,
);
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
try {
@ -1139,20 +1148,22 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
// check for send all
bool isSendAll = false;
final balance =
Format.decimalAmountToSatoshis(availablePrivateBalance(), coin);
if (satoshiAmount == balance) {
final balance = availablePrivateBalance();
if (amount == balance) {
// print("is send all");
isSendAll = true;
}
dynamic txHexOrError =
await _createJoinSplitTransaction(satoshiAmount, address, isSendAll);
dynamic txHexOrError = await _createJoinSplitTransaction(
amount.raw.toInt(),
address,
isSendAll,
);
Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error);
if (txHexOrError is int) {
// Here, we assume that transaction crafting returned an error
@ -2305,9 +2316,10 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
Future<int> _fetchMaxFee() async {
final balance = availablePrivateBalance();
int spendAmount = (balance * Decimal.fromInt(Constants.satsPerCoin(coin)))
.toBigInt()
.toInt();
int spendAmount =
(balance.decimal * Decimal.fromInt(Constants.satsPerCoin(coin)))
.toBigInt()
.toInt();
int fee = await estimateJoinSplitFee(spendAmount);
return fee;
}
@ -2503,10 +2515,23 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
_balancePrivate = Balance(
coin: coin,
total: intLelantusBalance + unconfirmedLelantusBalance,
spendable: intLelantusBalance,
blockedTotal: 0,
pendingSpendable: unconfirmedLelantusBalance,
total: Amount(
rawValue:
BigInt.from(intLelantusBalance + unconfirmedLelantusBalance),
fractionDigits: coin.decimals,
),
spendable: Amount(
rawValue: BigInt.from(intLelantusBalance),
fractionDigits: coin.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.from(unconfirmedLelantusBalance),
fractionDigits: coin.decimals,
),
);
await updateCachedBalanceSecondary(_balancePrivate!);
@ -2605,8 +2630,10 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
final feesObject = await fees;
final Decimal fastFee =
Format.satoshisToAmount(feesObject.fast, coin: coin);
final Decimal fastFee = Amount(
rawValue: BigInt.from(feesObject.fast),
fractionDigits: coin.decimals,
).decimal;
int firoFee =
(dvSize * fastFee * Decimal.fromInt(100000)).toDouble().ceil();
// int firoFee = (vSize * feesObject.fast * (1 / 1000.0) * 100000000).ceil();
@ -2795,12 +2822,18 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
"txid": txId,
"txHex": txHex,
"value": amount - fee,
"fees": Format.satoshisToAmount(fee, coin: coin).toDouble(),
"fees": Amount(
rawValue: BigInt.from(fee),
fractionDigits: coin.decimals,
).decimal.toDouble(),
"publicCoin": "",
"height": height,
"txType": "Sent",
"confirmed_status": false,
"amount": Format.satoshisToAmount(amount, coin: coin).toDouble(),
"amount": Amount(
rawValue: BigInt.from(amount),
fractionDigits: coin.decimals,
).decimal.toDouble(),
"timestamp": DateTime.now().millisecondsSinceEpoch ~/ 1000,
"subType": "mint",
"mintsMap": mintsMap,
@ -3040,9 +3073,9 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
}
await firoUpdateLelantusCoins(coins);
final amount = Format.decimalAmountToSatoshis(
final amount = Amount.fromDecimal(
Decimal.parse(transactionInfo["amount"].toString()),
coin,
fractionDigits: coin.decimals,
);
// add the send transaction
@ -3059,15 +3092,12 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
: transactionInfo["subType"] == "join"
? isar_models.TransactionSubType.join
: isar_models.TransactionSubType.none,
amount: amount,
amountString: Amount(
rawValue: BigInt.from(amount),
fractionDigits: Coin.firo.decimals,
).toJsonString(),
fee: Format.decimalAmountToSatoshis(
amount: amount.raw.toInt(),
amountString: amount.toJsonString(),
fee: Amount.fromDecimal(
Decimal.parse(transactionInfo["fees"].toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
height: transactionInfo["height"] as int?,
isCancelled: false,
isLelantus: true,
@ -3154,9 +3184,18 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -3608,10 +3647,10 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
scriptPubKeyAddress:
json["scriptPubKey"]?["addresses"]?[0] as String? ??
json['scriptPubKey']['type'] as String,
value: Format.decimalAmountToSatoshis(
value: Amount.fromDecimal(
Decimal.parse(json["value"].toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
outs.add(output);
}
@ -3692,10 +3731,22 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
final currentChainHeight = await chainHeight;
final List<isar_models.UTXO> outputArray = [];
int satoshiBalanceTotal = 0;
int satoshiBalancePending = 0;
int satoshiBalanceSpendable = 0;
int satoshiBalanceBlocked = 0;
Amount satoshiBalanceTotal = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount satoshiBalancePending = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount satoshiBalanceSpendable = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount satoshiBalanceBlocked = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
for (int i = 0; i < fetchedUtxoList.length; i++) {
for (int j = 0; j < fetchedUtxoList[i].length; j++) {
@ -3720,15 +3771,19 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
blockTime: txn["blocktime"] as int?,
);
satoshiBalanceTotal += utxo.value;
final utxoAmount = Amount(
rawValue: BigInt.from(utxo.value),
fractionDigits: coin.decimals,
);
satoshiBalanceTotal = satoshiBalanceTotal + utxoAmount;
if (utxo.isBlocked) {
satoshiBalanceBlocked += utxo.value;
satoshiBalanceBlocked = satoshiBalanceBlocked + utxoAmount;
} else {
if (utxo.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) {
satoshiBalanceSpendable += utxo.value;
satoshiBalanceSpendable = satoshiBalanceSpendable + utxoAmount;
} else {
satoshiBalancePending += utxo.value;
satoshiBalancePending = satoshiBalancePending + utxoAmount;
}
}
@ -4751,7 +4806,7 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
int spendAmount,
) async {
var lelantusEntry = await _getLelantusEntry();
final balance = availablePrivateBalance();
final balance = availablePrivateBalance().decimal;
int spendAmount = (balance * Decimal.fromInt(Constants.satsPerCoin(coin)))
.toBigInt()
.toInt();
@ -4808,27 +4863,34 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
// return fee;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
int fee = await estimateJoinSplitFee(satoshiAmount);
return fee;
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
int fee = await estimateJoinSplitFee(amount.raw.toInt());
return Amount(rawValue: BigInt.from(fee), fractionDigits: coin.decimals);
}
Future<int> estimateFeeForPublic(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeForPublic(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance = runningBalance +
Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -4837,19 +4899,24 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
final dustLimitAmount = Amount(
rawValue: BigInt.from(DUST_LIMIT),
fractionDigits: coin.decimals,
);
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + dustLimitAmount) {
final change = runningBalance - amount - twoOutPutFee;
if (change > dustLimitAmount &&
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
@ -4857,12 +4924,15 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
}
// TODO: correct formula for firo?
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -4876,7 +4946,11 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
Future<List<Map<String, dynamic>>> fastFetch(List<String> allTxHashes) async {
@ -4949,9 +5023,9 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
tx["address"] = tx["vout"][sendIndex]["scriptPubKey"]["addresses"][0];
tx["fees"] = tx["vin"][0]["nFees"];
final amount = Format.decimalAmountToSatoshis(
final Amount amount = Amount.fromDecimal(
Decimal.parse(tx["amount"].toString()),
coin,
fractionDigits: coin.decimals,
);
final txn = isar_models.Transaction(
@ -4961,15 +5035,12 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
type: isar_models.TransactionType.outgoing,
subType: isar_models.TransactionSubType.join,
amount: amount,
amountString: Amount(
rawValue: BigInt.from(amount),
fractionDigits: Coin.firo.decimals,
).toJsonString(),
fee: Format.decimalAmountToSatoshis(
amount: amount.raw.toInt(),
amountString: amount.toJsonString(),
fee: Amount.fromDecimal(
Decimal.parse(tx["fees"].toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
height: tx["height"] as int?,
isCancelled: false,
isLelantus: true,
@ -5037,12 +5108,12 @@ class FiroWallet extends CoinServiceAPI with WalletCache, WalletDB, FiroHive {
}
}
Decimal availablePrivateBalance() {
return balancePrivate.getSpendable();
Amount availablePrivateBalance() {
return balancePrivate.spendable;
}
Decimal availablePublicBalance() {
return balance.getSpendable();
Amount availablePublicBalance() {
return balance.spendable;
}
Future<int> get chainHeight async {

View file

@ -47,8 +47,14 @@ import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 1;
const int DUST_LIMIT = 294;
const int DUST_LIMIT_P2PKH = 546;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(294),
fractionDigits: Coin.particl.decimals,
);
final Amount DUST_LIMIT_P2PKH = Amount(
rawValue: BigInt.from(546),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2";
@ -1026,7 +1032,7 @@ class LitecoinWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -1056,14 +1062,14 @@ class LitecoinWallet extends CoinServiceAPI
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1423,9 +1429,18 @@ class LitecoinWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2354,8 +2369,11 @@ class LitecoinWallet extends CoinServiceAPI
feeRatePerKB: selectedTxFeeRate,
);
final int roughEstimate =
roughFeeEstimate(spendableOutputs.length, 1, selectedTxFeeRate);
final int roughEstimate = roughFeeEstimate(
spendableOutputs.length,
1,
selectedTxFeeRate,
).raw.toInt();
if (feeForOneOutput < roughEstimate) {
feeForOneOutput = roughEstimate;
}
@ -2412,7 +2430,7 @@ class LitecoinWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2420,7 +2438,7 @@ class LitecoinWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > DUST_LIMIT satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -3229,22 +3247,29 @@ class LitecoinWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance = runningBalance +
Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -3253,31 +3278,35 @@ class LitecoinWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
}
}
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(
((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -3291,7 +3320,11 @@ class LitecoinWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -12,6 +12,7 @@ import 'package:stackwallet/services/event_bus/events/global/updated_in_backgrou
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
@ -91,13 +92,13 @@ class Manager with ChangeNotifier {
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
final txInfo = await _currentWallet.prepareSend(
address: address,
satoshiAmount: satoshiAmount,
amount: amount,
args: args,
);
// notifyListeners();
@ -214,8 +215,8 @@ class Manager with ChangeNotifier {
bool get isConnected => _currentWallet.isConnected;
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
return _currentWallet.estimateFeeFor(satoshiAmount, feeRate);
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
return _currentWallet.estimateFeeFor(amount, feeRate);
}
Future<bool> generateNewAddress() async {

View file

@ -39,12 +39,10 @@ import 'package:stackwallet/services/mixins/wallet_cache.dart';
import 'package:stackwallet/services/mixins/wallet_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
@ -171,7 +169,7 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
(await _generateAddressForChain(0, 0)).value;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
MoneroTransactionPriority priority;
switch (feeRate) {
@ -193,9 +191,9 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
break;
}
final fee = walletBase!.calculateEstimatedFee(priority, satoshiAmount);
final fee = walletBase!.calculateEstimatedFee(priority, amount.raw.toInt());
return fee;
return Amount(rawValue: BigInt.from(fee), fractionDigits: coin.decimals);
}
@override
@ -432,7 +430,7 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
String toAddress = address;
@ -457,16 +455,13 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
// check for send all
bool isSendAll = false;
final balance = await _availableBalance;
if (satoshiAmount == balance) {
if (amount == balance) {
isSendAll = true;
}
Logging.instance
.log("$toAddress $satoshiAmount $args", level: LogLevel.Info);
String amountToSend =
Format.satoshisToAmount(satoshiAmount, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
Logging.instance
.log("$satoshiAmount $amountToSend", level: LogLevel.Info);
.log("$toAddress $amount $args", level: LogLevel.Info);
String amountToSend = amount.decimal.toString();
Logging.instance.log("$amount $amountToSend", level: LogLevel.Info);
monero_output.Output output = monero_output.Output(walletBase!);
output.address = toAddress;
@ -488,14 +483,16 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
PendingMoneroTransaction pendingMoneroTransaction =
await (awaitPendingTransaction!) as PendingMoneroTransaction;
int realfee = Format.decimalAmountToSatoshis(
Decimal.parse(pendingMoneroTransaction.feeFormatted), coin);
debugPrint("fee? $realfee");
final int realFee = Amount.fromDecimal(
Decimal.parse(pendingMoneroTransaction.feeFormatted),
fractionDigits: coin.decimals,
).raw.toInt();
Map<String, dynamic> txData = {
"pendingMoneroTransaction": pendingMoneroTransaction,
"fee": realfee,
"fee": realFee,
"addresss": toAddress,
"recipientAmt": satoshiAmount,
"recipientAmt": amount,
};
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -753,25 +750,34 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
coin: coin,
total: total,
spendable: available,
blockedTotal: 0,
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
pendingSpendable: total - available,
);
await updateCachedBalance(_balance!);
}
Future<int> get _availableBalance async {
Future<Amount> get _availableBalance async {
try {
int runningBalance = 0;
for (final entry in walletBase!.balance!.entries) {
runningBalance += entry.value.unlockedBalance;
}
return runningBalance;
return Amount(
rawValue: BigInt.from(runningBalance),
fractionDigits: coin.decimals,
);
} catch (_) {
return 0;
return Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
}
}
Future<int> get _totalBalance async {
Future<Amount> get _totalBalance async {
try {
final balanceEntries = walletBase?.balance?.entries;
if (balanceEntries != null) {
@ -780,7 +786,10 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
bal = bal + element.value.fullBalance;
}
await _updateCachedBalance(bal);
return bal;
return Amount(
rawValue: BigInt.from(bal),
fractionDigits: coin.decimals,
);
} else {
final transactions = walletBase!.transactionHistory!.transactions;
int transactionBalance = 0;
@ -793,10 +802,16 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await _updateCachedBalance(transactionBalance);
return transactionBalance;
return Amount(
rawValue: BigInt.from(transactionBalance),
fractionDigits: coin.decimals,
);
}
} catch (_) {
return _getCachedBalance();
return Amount(
rawValue: BigInt.from(_getCachedBalance()),
fractionDigits: coin.decimals,
);
}
}

View file

@ -48,7 +48,10 @@ import 'package:uuid/uuid.dart';
const int MINIMUM_CONFIRMATIONS = 2;
// Find real dust limit
const int DUST_LIMIT = 546;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(546),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770";
@ -1017,7 +1020,7 @@ class NamecoinWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -1047,14 +1050,14 @@ class NamecoinWallet extends CoinServiceAPI
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1413,9 +1416,18 @@ class NamecoinWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2347,8 +2359,11 @@ class NamecoinWallet extends CoinServiceAPI
feeRatePerKB: selectedTxFeeRate,
);
final int roughEstimate =
roughFeeEstimate(spendableOutputs.length, 1, selectedTxFeeRate);
final int roughEstimate = roughFeeEstimate(
spendableOutputs.length,
1,
selectedTxFeeRate,
).raw.toInt();
if (feeForOneOutput < roughEstimate) {
feeForOneOutput = roughEstimate;
}
@ -2405,7 +2420,7 @@ class NamecoinWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2413,7 +2428,7 @@ class NamecoinWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > DUST_LIMIT satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -3221,22 +3236,29 @@ class NamecoinWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance = runningBalance +
Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -3245,19 +3267,19 @@ class NamecoinWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
@ -3265,12 +3287,16 @@ class NamecoinWallet extends CoinServiceAPI
}
// TODO: Check if this is the correct formula for namecoin
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(
((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -3284,7 +3310,11 @@ class NamecoinWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -30,6 +30,7 @@ import 'package:stackwallet/services/mixins/wallet_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notifications_api.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/bip32_utils.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -44,10 +45,11 @@ import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
import '../../../utilities/amount.dart';
const int MINIMUM_CONFIRMATIONS = 1;
const int DUST_LIMIT = 294;
final Amount DUST_LIMIT = Amount(
rawValue: BigInt.from(294),
fractionDigits: Coin.particl.decimals,
);
const String GENESIS_HASH_MAINNET =
"0000ee0784c195317ac95623e22fddb8c7b8825dc3998e0bb924d66866eccf4c";
@ -945,7 +947,7 @@ class ParticlWallet extends CoinServiceAPI
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -975,14 +977,14 @@ class ParticlWallet extends CoinServiceAPI
// check for send all
bool isSendAll = false;
if (satoshiAmount == balance.spendable) {
if (amount == balance.spendable) {
isSendAll = true;
}
final bool coinControl = utxos != null;
final txData = await coinSelection(
satoshiAmountToSend: satoshiAmount,
satoshiAmountToSend: amount.raw.toInt(),
selectedTxFeeRate: rate,
recipientAddress: address,
isSendAll: isSendAll,
@ -1329,9 +1331,18 @@ class ParticlWallet extends CoinServiceAPI
numberOfBlocksFast: f,
numberOfBlocksAverage: m,
numberOfBlocksSlow: s,
fast: Format.decimalAmountToSatoshis(fast, coin),
medium: Format.decimalAmountToSatoshis(medium, coin),
slow: Format.decimalAmountToSatoshis(slow, coin),
fast: Amount.fromDecimal(
fast,
fractionDigits: coin.decimals,
).raw.toInt(),
medium: Amount.fromDecimal(
medium,
fractionDigits: coin.decimals,
).raw.toInt(),
slow: Amount.fromDecimal(
slow,
fractionDigits: coin.decimals,
).raw.toInt(),
);
Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info);
@ -2343,10 +2354,10 @@ class ParticlWallet extends CoinServiceAPI
json["scriptPubKey"]?["addresses"]?[0] as String? ??
json['scriptPubKey']?['type'] as String? ??
"",
value: Format.decimalAmountToSatoshis(
value: Amount.fromDecimal(
Decimal.parse((json["value"] ?? 0).toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
outputs.add(output);
}
@ -2514,8 +2525,11 @@ class ParticlWallet extends CoinServiceAPI
feeRatePerKB: selectedTxFeeRate,
);
final int roughEstimate =
roughFeeEstimate(spendableOutputs.length, 1, selectedTxFeeRate);
final int roughEstimate = roughFeeEstimate(
spendableOutputs.length,
1,
selectedTxFeeRate,
).raw.toInt();
if (feeForOneOutput < roughEstimate) {
feeForOneOutput = roughEstimate;
}
@ -2572,7 +2586,7 @@ class ParticlWallet extends CoinServiceAPI
if (satoshisBeingUsed - satoshiAmountToSend > feeForOneOutput) {
if (satoshisBeingUsed - satoshiAmountToSend >
feeForOneOutput + DUST_LIMIT) {
feeForOneOutput + DUST_LIMIT.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
@ -2580,7 +2594,7 @@ class ParticlWallet extends CoinServiceAPI
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > DUST_LIMIT satoshis, we perform the mechanics required to properly generate and use a new
// change address.
if (changeOutputSize > DUST_LIMIT &&
if (changeOutputSize > DUST_LIMIT.raw.toInt() &&
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize ==
feeForTwoOutputs) {
// generate new change address if current change address has been used
@ -3281,22 +3295,28 @@ class ParticlWallet extends CoinServiceAPI
(isActive) => this.isActive = isActive;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
final available = balance.spendable;
if (available == satoshiAmount) {
return satoshiAmount - (await sweepAllEstimate(feeRate));
} else if (satoshiAmount <= 0 || satoshiAmount > available) {
if (available == amount) {
return amount - (await sweepAllEstimate(feeRate));
} else if (amount <= Amount.zero || amount > available) {
return roughFeeEstimate(1, 2, feeRate);
}
int runningBalance = 0;
Amount runningBalance = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int inputCount = 0;
for (final output in (await utxos)) {
if (!output.isBlocked) {
runningBalance += output.value;
runningBalance += Amount(
rawValue: BigInt.from(output.value),
fractionDigits: coin.decimals,
);
inputCount++;
if (runningBalance > satoshiAmount) {
if (runningBalance > amount) {
break;
}
}
@ -3305,31 +3325,35 @@ class ParticlWallet extends CoinServiceAPI
final oneOutPutFee = roughFeeEstimate(inputCount, 1, feeRate);
final twoOutPutFee = roughFeeEstimate(inputCount, 2, feeRate);
if (runningBalance - satoshiAmount > oneOutPutFee) {
if (runningBalance - satoshiAmount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - satoshiAmount - twoOutPutFee;
if (runningBalance - amount > oneOutPutFee) {
if (runningBalance - amount > oneOutPutFee + DUST_LIMIT) {
final change = runningBalance - amount - twoOutPutFee;
if (change > DUST_LIMIT &&
runningBalance - satoshiAmount - change == twoOutPutFee) {
return runningBalance - satoshiAmount - change;
runningBalance - amount - change == twoOutPutFee) {
return runningBalance - amount - change;
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else {
return runningBalance - satoshiAmount;
return runningBalance - amount;
}
} else if (runningBalance - satoshiAmount == oneOutPutFee) {
} else if (runningBalance - amount == oneOutPutFee) {
return oneOutPutFee;
} else {
return twoOutPutFee;
}
}
int roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil();
Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) {
return Amount(
rawValue: BigInt.from(
((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() *
(feeRatePerKB / 1000).ceil()),
fractionDigits: coin.decimals,
);
}
Future<int> sweepAllEstimate(int feeRate) async {
Future<Amount> sweepAllEstimate(int feeRate) async {
int available = 0;
int inputCount = 0;
for (final output in (await utxos)) {
@ -3343,7 +3367,11 @@ class ParticlWallet extends CoinServiceAPI
// transaction will only have 1 output minus the fee
final estimatedFee = roughFeeEstimate(inputCount, 1, feeRate);
return available - estimatedFee;
return Amount(
rawValue: BigInt.from(available),
fractionDigits: coin.decimals,
) -
estimatedFee;
}
@override

View file

@ -41,12 +41,10 @@ import 'package:stackwallet/services/mixins/wallet_cache.dart';
import 'package:stackwallet/services/mixins/wallet_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
@ -173,7 +171,7 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
(await _generateAddressForChain(0, 0)).value;
@override
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
Future<Amount> estimateFeeFor(Amount amount, int feeRate) async {
MoneroTransactionPriority priority;
FeeRateType feeRateType = FeeRateType.slow;
switch (feeRate) {
@ -206,18 +204,28 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
aprox = (await prepareSend(
// This address is only used for getting an approximate fee, never for sending
address: "WW3iVcnoAY6K9zNdU4qmdvZELefx6xZz4PMpTwUifRkvMQckyadhSPYMVPJhBdYE8P9c27fg9RPmVaWNFx1cDaj61HnetqBiy",
satoshiAmount: satoshiAmount,
amount: amount,
args: {"feeRate": feeRateType}))['fee'];
await Future.delayed(const Duration(milliseconds: 500));
await Future<void>.delayed(const Duration(milliseconds: 500));
} catch (e, s) {
aprox = walletBase!.calculateEstimatedFee(priority, satoshiAmount);
aprox = walletBase!.calculateEstimatedFee(
priority,
amount.raw.toInt(),
);
}
}
});
print("this is the aprox fee $aprox for $satoshiAmount");
final fee = (aprox as int);
return fee;
print("this is the aprox fee $aprox for $amount");
if (aprox is Amount) {
return aprox as Amount;
} else {
return Amount(
rawValue: BigInt.from(aprox as int),
fractionDigits: coin.decimals,
);
}
}
@override
@ -451,7 +459,7 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
@override
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
try {
@ -475,16 +483,12 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
// check for send all
bool isSendAll = false;
final balance = await _availableBalance;
if (satoshiAmount == balance) {
if (amount == balance) {
isSendAll = true;
}
Logging.instance
.log("$address $satoshiAmount $args", level: LogLevel.Info);
String amountToSend =
Format.satoshisToAmount(satoshiAmount, coin: coin)
.toStringAsFixed(Constants.decimalPlacesForCoin(coin));
Logging.instance
.log("$satoshiAmount $amountToSend", level: LogLevel.Info);
Logging.instance.log("$address $amount $args", level: LogLevel.Info);
String amountToSend = amount.decimal.toString();
Logging.instance.log("$amount $amountToSend", level: LogLevel.Info);
wownero_output.Output output = wownero_output.Output(walletBase!);
output.address = address;
@ -507,15 +511,16 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
PendingWowneroTransaction pendingWowneroTransaction =
await (awaitPendingTransaction!) as PendingWowneroTransaction;
int realfee = Format.decimalAmountToSatoshis(
Decimal.parse(pendingWowneroTransaction.feeFormatted), coin);
//todo: check if print needed
// debugPrint("fee? $realfee");
final int realFee = Amount.fromDecimal(
Decimal.parse(pendingWowneroTransaction.feeFormatted),
fractionDigits: coin.decimals,
).raw.toInt();
Map<String, dynamic> txData = {
"pendingWowneroTransaction": pendingWowneroTransaction,
"fee": realfee,
"fee": realFee,
"addresss": address,
"recipientAmt": satoshiAmount,
"recipientAmt": amount,
};
Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -772,25 +777,34 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
coin: coin,
total: total,
spendable: available,
blockedTotal: 0,
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
),
pendingSpendable: total - available,
);
await updateCachedBalance(_balance!);
}
Future<int> get _availableBalance async {
Future<Amount> get _availableBalance async {
try {
int runningBalance = 0;
for (final entry in walletBase!.balance!.entries) {
runningBalance += entry.value.unlockedBalance;
}
return runningBalance;
return Amount(
rawValue: BigInt.from(runningBalance),
fractionDigits: coin.decimals,
);
} catch (_) {
return 0;
return Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
}
}
Future<int> get _totalBalance async {
Future<Amount> get _totalBalance async {
try {
final balanceEntries = walletBase?.balance?.entries;
if (balanceEntries != null) {
@ -799,7 +813,10 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
bal = bal + element.value.fullBalance;
}
await _updateCachedBalance(bal);
return bal;
return Amount(
rawValue: BigInt.from(bal),
fractionDigits: coin.decimals,
);
} else {
final transactions = walletBase!.transactionHistory!.transactions;
int transactionBalance = 0;
@ -812,10 +829,16 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
}
await _updateCachedBalance(transactionBalance);
return transactionBalance;
return Amount(
rawValue: BigInt.from(transactionBalance),
fractionDigits: coin.decimals,
);
}
} catch (_) {
return _getCachedBalance();
return Amount(
rawValue: BigInt.from(_getCachedBalance()),
fractionDigits: coin.decimals,
);
}
}

View file

@ -2,6 +2,7 @@ import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/services/ethereum/ethereum_api.dart';
import 'package:stackwallet/services/mixins/eth_token_cache.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/logger.dart';
class CachedEthTokenBalance with EthTokenCache {
@ -22,11 +23,22 @@ class CachedEthTokenBalance with EthTokenCache {
await updateCachedBalance(
TokenBalance(
contractAddress: token.address,
decimalPlaces: token.decimals,
total: response.value!,
spendable: response.value!,
blockedTotal: 0,
pendingSpendable: 0,
total: Amount(
rawValue: BigInt.from(response.value!),
fractionDigits: token.decimals,
),
spendable: Amount(
rawValue: BigInt.from(response.value!),
fractionDigits: token.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: token.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: token.decimals,
),
),
);
} else {

View file

@ -28,7 +28,6 @@ import 'package:stackwallet/utilities/eth_commons.dart';
import 'package:stackwallet/utilities/extensions/extensions.dart';
import 'package:stackwallet/utilities/extensions/impl/contract_abi.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:tuple/tuple.dart';
import 'package:web3dart/web3dart.dart' as web3dart;
@ -68,7 +67,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
Future<Map<String, dynamic>> prepareSend({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) async {
final feeRateType = args?["feeRate"];
@ -86,12 +85,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
break;
}
final feeEstimate = await estimateFeeFor(satoshiAmount, fee);
final decimalAmount =
Format.satoshisToAmount(satoshiAmount, coin: Coin.ethereum);
final bigIntAmount =
amountToBigInt(decimalAmount.toDouble(), tokenContract.decimals);
final feeEstimate = estimateFeeFor(fee);
final client = await getEthClient();
@ -101,8 +95,8 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final est = await client.estimateGas(
sender: myWeb3Address,
to: web3dart.EthereumAddress.fromHex(address),
data: _sendFunction.encodeCall(
[web3dart.EthereumAddress.fromHex(address), bigIntAmount]),
data: _sendFunction
.encodeCall([web3dart.EthereumAddress.fromHex(address), amount.raw]),
gasPrice: web3dart.EtherAmount.fromUnitAndValue(
web3dart.EtherUnit.wei,
fee,
@ -125,7 +119,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final tx = web3dart.Transaction.callContract(
contract: _deployedContract,
function: _sendFunction,
parameters: [web3dart.EthereumAddress.fromHex(address), bigIntAmount],
parameters: [web3dart.EthereumAddress.fromHex(address), amount.raw],
maxGas: _gasLimit,
gasPrice: web3dart.EtherAmount.fromUnitAndValue(
web3dart.EtherUnit.wei,
@ -138,7 +132,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
"fee": feeEstimate,
"feeInWei": fee,
"address": address,
"recipientAmt": satoshiAmount,
"recipientAmt": amount,
"ethTx": tx,
"chainId": (await client.getChainId()).toInt(),
"nonce": tx.nonce,
@ -238,9 +232,8 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
.sortByDerivationIndexDesc()
.findFirst();
Future<int> estimateFeeFor(int satoshiAmount, int feeRate) async {
final fee = estimateFee(feeRate, _gasLimit, coin.decimals);
return Format.decimalAmountToSatoshis(Decimal.parse(fee.toString()), coin);
Amount estimateFeeFor(int feeRate) {
return estimateFee(feeRate, _gasLimit, coin.decimals);
}
Future<FeeObject> get fees => EthereumAPI.getFees();
@ -424,11 +417,22 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final newBalance = TokenBalance(
contractAddress: tokenContract.address,
total: int.parse(_balance),
spendable: int.parse(_balance),
blockedTotal: 0,
pendingSpendable: 0,
decimalPlaces: tokenContract.decimals,
total: Amount.fromDecimal(
Decimal.parse(_balance),
fractionDigits: tokenContract.decimals,
),
spendable: Amount.fromDecimal(
Decimal.parse(_balance),
fractionDigits: tokenContract.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: tokenContract.decimals,
),
);
await updateCachedBalance(newBalance);
notifyListeners();

View file

@ -5,6 +5,7 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/services/event_bus/events/global/balance_refreshed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
mixin CoinControlInterface {
@ -35,24 +36,41 @@ mixin CoinControlInterface {
final utxos = await _db.getUTXOs(_walletId).findAll();
final currentChainHeight = await _getChainHeight();
int satoshiBalanceTotal = 0;
int satoshiBalancePending = 0;
int satoshiBalanceSpendable = 0;
int satoshiBalanceBlocked = 0;
Amount satoshiBalanceTotal = Amount(
rawValue: BigInt.zero,
fractionDigits: _coin.decimals,
);
Amount satoshiBalancePending = Amount(
rawValue: BigInt.zero,
fractionDigits: _coin.decimals,
);
Amount satoshiBalanceSpendable = Amount(
rawValue: BigInt.zero,
fractionDigits: _coin.decimals,
);
Amount satoshiBalanceBlocked = Amount(
rawValue: BigInt.zero,
fractionDigits: _coin.decimals,
);
for (final utxo in utxos) {
satoshiBalanceTotal += utxo.value;
final utxoAmount = Amount(
rawValue: BigInt.from(utxo.value),
fractionDigits: _coin.decimals,
);
satoshiBalanceTotal = satoshiBalanceTotal + utxoAmount;
if (utxo.isBlocked) {
satoshiBalanceBlocked += utxo.value;
satoshiBalanceBlocked = satoshiBalanceBlocked + utxoAmount;
} else {
if (utxo.isConfirmed(
currentChainHeight,
_coin.requiredConfirmations,
)) {
satoshiBalanceSpendable += utxo.value;
satoshiBalanceSpendable + satoshiBalanceSpendable + utxoAmount;
} else {
satoshiBalancePending += utxo.value;
satoshiBalancePending = satoshiBalancePending + utxoAmount;
}
}
}

View file

@ -6,7 +6,6 @@ import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:tuple/tuple.dart';
mixin ElectrumXParsing {
@ -33,12 +32,27 @@ mixin ElectrumXParsing {
Set<String> inputAddresses = {};
Set<String> outputAddresses = {};
int totalInputValue = 0;
int totalOutputValue = 0;
Amount totalInputValue = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount totalOutputValue = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
int amountSentFromWallet = 0;
int amountReceivedInWallet = 0;
int changeAmount = 0;
Amount amountSentFromWallet = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount amountReceivedInWallet = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
Amount changeAmount = Amount(
rawValue: BigInt.zero,
fractionDigits: coin.decimals,
);
// parse inputs
for (final input in txData["vin"] as List) {
@ -55,9 +69,9 @@ mixin ElectrumXParsing {
// check matching output
if (prevOut == output["n"]) {
// get value
final value = Format.decimalAmountToSatoshis(
final value = Amount.fromDecimal(
Decimal.parse(output["value"].toString()),
coin,
fractionDigits: coin.decimals,
);
// add value to total
@ -83,9 +97,9 @@ mixin ElectrumXParsing {
// parse outputs
for (final output in txData["vout"] as List) {
// get value
final value = Format.decimalAmountToSatoshis(
final value = Amount.fromDecimal(
Decimal.parse(output["value"].toString()),
coin,
fractionDigits: coin.decimals,
);
// add value to total
@ -121,7 +135,7 @@ mixin ElectrumXParsing {
Address transactionAddress = txData["address"] as Address;
TransactionType type;
int amount;
Amount amount;
if (mySentFromAddresses.isNotEmpty && myReceivedOnAddresses.isNotEmpty) {
// tx is sent to self
type = TransactionType.sentToSelf;
@ -189,10 +203,10 @@ mixin ElectrumXParsing {
json["scriptPubKey"]?["addresses"]?[0] as String? ??
json['scriptPubKey']?['type'] as String? ??
"",
value: Format.decimalAmountToSatoshis(
value: Amount.fromDecimal(
Decimal.parse(json["value"].toString()),
coin,
),
fractionDigits: coin.decimals,
).raw.toInt(),
);
outs.add(output);
}
@ -220,12 +234,10 @@ mixin ElectrumXParsing {
(DateTime.now().millisecondsSinceEpoch ~/ 1000),
type: type,
subType: txSubType,
amount: amount,
amountString: Amount(
rawValue: BigInt.from(amount),
fractionDigits: coin.decimals,
).toJsonString(),
fee: fee,
// amount may overflow. Deprecated. Use amountString
amount: amount.raw.toInt(),
amountString: amount.toJsonString(),
fee: fee.raw.toInt(),
height: txData["height"] as int?,
isCancelled: false,
isLelantus: false,

View file

@ -1,6 +1,7 @@
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
import 'package:stackwallet/models/token_balance.dart';
import 'package:stackwallet/utilities/amount.dart';
abstract class TokenCacheKeys {
static String tokenBalance(String contractAddress) {
@ -26,15 +27,27 @@ mixin EthTokenCache {
if (jsonString == null) {
return TokenBalance(
contractAddress: _token.address,
decimalPlaces: _token.decimals,
total: 0,
spendable: 0,
blockedTotal: 0,
pendingSpendable: 0,
total: Amount(
rawValue: BigInt.zero,
fractionDigits: _token.decimals,
),
spendable: Amount(
rawValue: BigInt.zero,
fractionDigits: _token.decimals,
),
blockedTotal: Amount(
rawValue: BigInt.zero,
fractionDigits: _token.decimals,
),
pendingSpendable: Amount(
rawValue: BigInt.zero,
fractionDigits: _token.decimals,
),
);
}
return TokenBalance.fromJson(
jsonString,
_token.decimals,
);
}

View file

@ -16,6 +16,7 @@ import 'package:stackwallet/exceptions/wallet/insufficient_balance_exception.dar
import 'package:stackwallet/exceptions/wallet/paynym_send_exception.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/signing_data.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/bip32_utils.dart';
import 'package:stackwallet/utilities/bip47_utils.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -52,7 +53,7 @@ mixin PaynymWalletInterface {
}) _estimateTxFee;
late final Future<Map<String, dynamic>> Function({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
}) _prepareSend;
late final Future<int> Function({
@ -93,7 +94,7 @@ mixin PaynymWalletInterface {
estimateTxFee,
required Future<Map<String, dynamic>> Function({
required String address,
required int satoshiAmount,
required Amount amount,
Map<String, dynamic>? args,
})
prepareSend,
@ -307,10 +308,11 @@ mixin PaynymWalletInterface {
return Format.uint8listToString(bytes);
}
Future<Map<String, dynamic>> preparePaymentCodeSend(
{required PaymentCode paymentCode,
required int satoshiAmount,
Map<String, dynamic>? args}) async {
Future<Map<String, dynamic>> preparePaymentCodeSend({
required PaymentCode paymentCode,
required Amount amount,
Map<String, dynamic>? args,
}) async {
if (!(await hasConnected(paymentCode.toString()))) {
throw PaynymSendException(
"No notification transaction sent to $paymentCode");
@ -326,7 +328,7 @@ mixin PaynymWalletInterface {
return _prepareSend(
address: sendToAddress.value,
satoshiAmount: satoshiAmount,
amount: amount,
args: args,
);
}

View file

@ -1,5 +1,6 @@
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
mixin WalletCache {
@ -70,10 +71,13 @@ mixin WalletCache {
if (jsonString == null) {
return Balance(
coin: _coin,
total: 0,
spendable: 0,
blockedTotal: 0,
pendingSpendable: 0,
total: Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
spendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
blockedTotal:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
pendingSpendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
);
}
return Balance.fromJson(jsonString, _coin);
@ -96,10 +100,13 @@ mixin WalletCache {
if (jsonString == null) {
return Balance(
coin: _coin,
total: 0,
spendable: 0,
blockedTotal: 0,
pendingSpendable: 0,
total: Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
spendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
blockedTotal:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
pendingSpendable:
Amount(rawValue: BigInt.zero, fractionDigits: _coin.decimals),
);
}
return Balance.fromJson(jsonString, _coin);

View file

@ -1,6 +1,8 @@
import 'dart:convert';
import 'package:decimal/decimal.dart';
import 'package:intl/number_symbols.dart';
import 'package:intl/number_symbols_data.dart';
final _ten = BigInt.from(10);
@ -60,6 +62,25 @@ class Amount {
return jsonEncode(toMap());
}
String localizedStringAsFixed({
required String locale,
int? decimalPlaces,
}) {
decimalPlaces ??= fractionDigits;
assert(decimalPlaces >= 0);
final String separator =
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
?.DECIMAL_SEP ??
".";
final intValue = decimal.shift(fractionDigits).toBigInt();
final fraction = (raw - intValue).toDecimal();
return "$intValue$separator${fraction.toStringAsFixed(decimalPlaces).substring(2)}";
}
// ===========================================================================
// ======= Deserialization ===================================================
@ -132,3 +153,34 @@ class Amount {
@override
int get hashCode => Object.hashAll([raw, fractionDigits]);
}
// =============================================================================
// =============================================================================
// ======= Extensions ==========================================================
extension DecimalAmountExt on Decimal {
Amount toAmount({required int fractionDigits}) {
return Amount.fromDecimal(
this,
fractionDigits: fractionDigits,
);
}
}
extension DoubleAmountExt on double {
Amount toAmount({required int fractionDigits}) {
return Amount.fromDouble(
this,
fractionDigits: fractionDigits,
);
}
}
extension IntAmountExtension on int {
Amount toAmount({required int fractionDigits}) {
return Amount(
rawValue: BigInt.from(this),
fractionDigits: fractionDigits,
);
}
}

View file

@ -7,6 +7,8 @@ import "package:hex/hex.dart";
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'amount.dart';
class GasTracker {
final Decimal average;
final Decimal fast;
@ -62,17 +64,12 @@ String getPrivateKey(String mnemonic, String mnemonicPassphrase) {
return HEX.encode(addressAtIndex.privateKey as List<int>);
}
double estimateFee(int feeRate, int gasLimit, int decimals) {
Amount estimateFee(int feeRate, int gasLimit, int decimals) {
final gweiAmount = feeRate / (pow(10, 9));
final fee = gasLimit * gweiAmount;
//Convert gwei to ETH
final feeInWei = fee * (pow(10, 9));
final ethAmount = feeInWei / (pow(10, decimals));
return ethAmount;
}
BigInt amountToBigInt(num amount, int decimal) {
final amountToSendinDecimal = amount * (pow(10, decimal));
return BigInt.from(amountToSendinDecimal);
return Amount.fromDouble(ethAmount, fractionDigits: decimals);
}

View file

@ -1,48 +1,43 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:decimal/decimal.dart';
import 'package:intl/number_symbols.dart';
import 'package:intl/number_symbols_data.dart' show numberFormatSymbols;
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
abstract class Format {
static String shorten(String value, int beginCount, int endCount) {
return "${value.substring(0, beginCount)}...${value.substring(value.length - endCount)}";
}
static Decimal satoshisToAmount(int sats, {required Coin coin}) {
return (Decimal.fromInt(sats) /
Decimal.fromInt(Constants.satsPerCoin(coin)))
.toDecimal(
scaleOnInfinitePrecision: Constants.decimalPlacesForCoin(coin));
}
static Decimal satoshisToEthTokenAmount(int sats, int decimalPlaces) {
return (Decimal.fromInt(sats) /
Decimal.fromInt(pow(10, decimalPlaces).toInt()))
.toDecimal(scaleOnInfinitePrecision: decimalPlaces);
}
///
static String satoshiAmountToPrettyString(
int sats, String locale, Coin coin) {
final amount = satoshisToAmount(sats, coin: coin);
return localizedStringAsFixed(
value: amount,
locale: locale,
decimalPlaces: Constants.decimalPlacesForCoin(coin),
);
}
static int decimalAmountToSatoshis(Decimal amount, Coin coin) {
final value = (Decimal.fromInt(Constants.satsPerCoin(coin)) * amount)
.floor()
.toBigInt();
return value.toInt();
}
// static Decimal satoshisToAmount(int sats, {required Coin coin}) {
// return (Decimal.fromInt(sats) /
// Decimal.fromInt(Constants.satsPerCoin(coin)))
// .toDecimal(
// scaleOnInfinitePrecision: Constants.decimalPlacesForCoin(coin));
// }
//
// static Decimal satoshisToEthTokenAmount(int sats, int decimalPlaces) {
// return (Decimal.fromInt(sats) /
// Decimal.fromInt(pow(10, decimalPlaces).toInt()))
// .toDecimal(scaleOnInfinitePrecision: decimalPlaces);
// }
//
// ///
// static String satoshiAmountToPrettyString(
// int sats, String locale, Coin coin) {
// final amount = satoshisToAmount(sats, coin: coin);
// return localizedStringAsFixed(
// value: amount,
// locale: locale,
// decimalPlaces: Constants.decimalPlacesForCoin(coin),
// );
// }
//
// static int decimalAmountToSatoshis(Decimal amount, Coin coin) {
// final value = (Decimal.fromInt(Constants.satsPerCoin(coin)) * amount)
// .floor()
// .toBigInt();
// return value.toInt();
// }
// format date string from unix timestamp
static String extractDateFrom(int timestamp, {bool localized = true}) {
@ -57,26 +52,26 @@ abstract class Format {
return "${date.day} ${Constants.monthMapShort[date.month]} ${date.year}, ${date.hour}:$minutes";
}
static String localizedStringAsFixed({
required Decimal value,
required String locale,
int decimalPlaces = 0,
}) {
assert(decimalPlaces >= 0);
final String separator =
(numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
(numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
?.DECIMAL_SEP ??
".";
final intValue = value.truncate();
final fraction = value - intValue;
return intValue.toStringAsFixed(0) +
separator +
fraction.toStringAsFixed(decimalPlaces).substring(2);
}
// static String localizedStringAsFixed({
// required Decimal value,
// required String locale,
// int decimalPlaces = 0,
// }) {
// assert(decimalPlaces >= 0);
//
// final String separator =
// (numberFormatSymbols[locale] as NumberSymbols?)?.DECIMAL_SEP ??
// (numberFormatSymbols[locale.substring(0, 2)] as NumberSymbols?)
// ?.DECIMAL_SEP ??
// ".";
//
// final intValue = value.truncate();
// final fraction = value - intValue;
//
// return intValue.toStringAsFixed(0) +
// separator +
// fraction.toStringAsFixed(decimalPlaces).substring(2);
// }
/// format date string as dd/mm/yy from DateTime object
static String formatDate(DateTime date) {

View file

@ -5,7 +5,6 @@ import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -104,12 +103,13 @@ class _ManagedFavoriteCardState extends ConsumerState<ManagedFavorite> {
),
Expanded(
child: Text(
"${Format.localizedStringAsFixed(
value: manager.balance.getTotal(),
"${manager.balance.total.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces: 8,
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
decimalPlaces: manager.coin.decimals,
)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
),
@ -146,11 +146,13 @@ class _ManagedFavoriteCardState extends ConsumerState<ManagedFavorite> {
height: 2,
),
Text(
"${Format.localizedStringAsFixed(
value: manager.balance.getTotal(),
locale: ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces: 8,
"${manager.balance.total.localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
),
decimalPlaces: manager.coin.decimals,
)} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle(context),
),

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
import 'package:stackwallet/providers/db/main_db_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
@ -239,10 +240,8 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
final amount = _transaction.realAmount;
return Text(
"$prefix${Format.localizedStringAsFixed(
value: amount.decimal,
"$prefix${amount.localizedStringAsFixed(
locale: locale,
decimalPlaces: amount.fractionDigits,
)} $unit",
style: STextStyles.itemSubtitle12(context),
);
@ -283,8 +282,10 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
final amount = _transaction.realAmount;
return Text(
"$prefix${Format.localizedStringAsFixed(
value: amount.decimal * price,
"$prefix${Amount.fromDecimal(
amount.decimal * price,
fractionDigits: 2,
).localizedStringAsFixed(
locale: locale,
decimalPlaces: 2,
)} $baseCurrency",

View file

@ -1,12 +1,11 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -33,27 +32,27 @@ class WalletInfoRowBalance extends ConsumerWidget {
),
);
Decimal balance;
Amount totalBalance;
int decimals;
String unit;
if (contractAddress == null) {
balance = manager.balance.getTotal();
totalBalance = manager.balance.total;
if (manager.coin == Coin.firo || manager.coin == Coin.firoTestNet) {
balance += (manager.wallet as FiroWallet).balancePrivate.getTotal();
totalBalance =
totalBalance + (manager.wallet as FiroWallet).balancePrivate.total;
}
unit = manager.coin.ticker;
decimals = manager.coin.decimals;
} else {
final ethWallet = manager.wallet as EthereumWallet;
final contract = MainDB.instance.getEthContractSync(contractAddress!)!;
balance = ethWallet.getCachedTokenBalance(contract).getTotal();
totalBalance = ethWallet.getCachedTokenBalance(contract).total;
unit = contract.symbol;
decimals = contract.decimals;
}
return Text(
"${Format.localizedStringAsFixed(
value: balance,
"${totalBalance.localizedStringAsFixed(
locale: locale,
decimalPlaces: decimals,
)} $unit",

View file

@ -1215,7 +1215,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1224,7 +1224,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -1913,7 +1913,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
@override
_i22.Future<Map<String, dynamic>> preparePaymentCodeSend({
required _i18.PaymentCode? paymentCode,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1922,7 +1922,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
[],
{
#paymentCode: paymentCode,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -2903,7 +2903,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -2912,7 +2912,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -3215,7 +3215,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -3224,7 +3224,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -417,7 +417,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
@override
_i8.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -426,7 +426,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -378,7 +378,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -387,7 +387,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -376,7 +376,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -385,7 +385,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -693,7 +693,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -702,7 +702,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -480,7 +480,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -489,7 +489,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -480,7 +480,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -489,7 +489,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -480,7 +480,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -489,7 +489,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -478,7 +478,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -487,7 +487,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -693,7 +693,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -702,7 +702,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -534,7 +534,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
@override
_i8.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -543,7 +543,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -462,7 +462,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
@override
_i8.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -471,7 +471,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -462,7 +462,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
@override
_i8.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -471,7 +471,7 @@ class MockManager extends _i1.Mock implements _i11.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -478,7 +478,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -487,7 +487,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -735,7 +735,7 @@ class MockManager extends _i1.Mock implements _i15.Manager {
@override
_i8.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -744,7 +744,7 @@ class MockManager extends _i1.Mock implements _i15.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -478,7 +478,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
@override
_i6.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -487,7 +487,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -249,7 +249,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -258,7 +258,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -248,7 +248,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -257,7 +257,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -247,7 +247,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -256,7 +256,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -289,7 +289,7 @@ class MockManager extends _i1.Mock implements _i8.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -298,7 +298,7 @@ class MockManager extends _i1.Mock implements _i8.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -249,7 +249,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
@override
_i7.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -258,7 +258,7 @@ class MockManager extends _i1.Mock implements _i5.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -103,7 +103,7 @@ class FakeCoinServiceAPI extends CoinServiceAPI {
@override
Future<Map<String, dynamic>> prepareSend(
{required String address,
required int satoshiAmount,
required int amount,
Map<String, dynamic>? args}) {
// TODO: implement prepareSend
throw UnimplementedError();

View file

@ -392,7 +392,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
@override
_i10.Future<Map<String, dynamic>> prepareSendPublic({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -401,7 +401,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -421,7 +421,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
@override
_i10.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -430,7 +430,7 @@ class MockFiroWallet extends _i1.Mock implements _i9.FiroWallet {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -1007,7 +1007,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1016,7 +1016,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -1704,7 +1704,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
@override
_i22.Future<Map<String, dynamic>> preparePaymentCodeSend({
required _i17.PaymentCode? paymentCode,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1713,7 +1713,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i25.BitcoinWallet {
[],
{
#paymentCode: paymentCode,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -2477,7 +2477,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -2486,7 +2486,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -2789,7 +2789,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
@override
_i22.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -2798,7 +2798,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

View file

@ -994,7 +994,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
@override
_i21.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1003,7 +1003,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -1691,7 +1691,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
@override
_i21.Future<Map<String, dynamic>> preparePaymentCodeSend({
required _i17.PaymentCode? paymentCode,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -1700,7 +1700,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
[],
{
#paymentCode: paymentCode,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -2202,7 +2202,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
@override
_i21.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -2211,7 +2211,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),
@ -2514,7 +2514,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i18.CoinServiceAPI {
@override
_i21.Future<Map<String, dynamic>> prepareSend({
required String? address,
required int? satoshiAmount,
required int? amount,
Map<String, dynamic>? args,
}) =>
(super.noSuchMethod(
@ -2523,7 +2523,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i18.CoinServiceAPI {
[],
{
#address: address,
#satoshiAmount: satoshiAmount,
#satoshiAmount: amount,
#args: args,
},
),

Some files were not shown because too many files have changed in this diff Show more