Merge pull request #474 from cypherstack/ui-fixes

eth clean up
This commit is contained in:
Diego Salazar 2023-04-11 17:43:44 -06:00 committed by GitHub
commit 7c4840fb24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 306 additions and 622 deletions

View file

@ -359,10 +359,16 @@ class _ConfirmChangeNowSendViewState
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Text( Text(
"${(transactionInfo["fee"] as int).toAmountAsRaw( "${(transactionInfo["fee"] is Amount ? transactionInfo["fee"] as Amount : (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: ref.watch( fractionDigits: ref.watch(
managerProvider managerProvider
.select((value) => value.coin.decimals), .select((value) => value.coin.decimals),
),
)).localizedStringAsFixed(
locale: ref.watch(
localeServiceChangeNotifierProvider.select(
(value) => value.locale,
),
), ),
)} ${ref.watch( )} ${ref.watch(
managerProvider.select((value) => value.coin), managerProvider.select((value) => value.coin),
@ -401,10 +407,12 @@ class _ConfirmChangeNowSendViewState
final coin = ref.watch( final coin = ref.watch(
managerProvider.select((value) => value.coin), managerProvider.select((value) => value.coin),
); );
final fee = final fee = transactionInfo["fee"] is Amount
(transactionInfo["fee"] as int).toAmountAsRaw( ? transactionInfo["fee"] as Amount
fractionDigits: coin.decimals, : (transactionInfo["fee"] as int)
); .toAmountAsRaw(
fractionDigits: coin.decimals,
);
final amount = final amount =
transactionInfo["recipientAmt"] as Amount; transactionInfo["recipientAmt"] as Amount;
final total = amount + fee; final total = amount + fee;
@ -637,17 +645,17 @@ class _ConfirmChangeNowSendViewState
style: STextStyles.smallMed12(context), style: STextStyles.smallMed12(context),
), ),
Text( Text(
"${(transactionInfo["fee"] as int).toAmountAsRaw(fractionDigits: ref.watch( "${(transactionInfo["fee"] is Amount ? transactionInfo["fee"] as Amount : (transactionInfo["fee"] as int).toAmountAsRaw(fractionDigits: ref.watch(
managerProvider.select( managerProvider.select(
(value) => value.coin.decimals, (value) => value.coin.decimals,
), ),
)).localizedStringAsFixed( ))).localizedStringAsFixed(
locale: ref.watch( locale: ref.watch(
localeServiceChangeNotifierProvider.select( localeServiceChangeNotifierProvider.select(
(value) => value.locale, (value) => value.locale,
), ),
), ),
)} ${ref.watch( )} ${ref.watch(
managerProvider.select((value) => value.coin), managerProvider.select((value) => value.coin),
).ticker}", ).ticker}",
style: STextStyles.itemSubtitle12(context), style: STextStyles.itemSubtitle12(context),
@ -733,10 +741,11 @@ class _ConfirmChangeNowSendViewState
final coin = ref.watch( final coin = ref.watch(
managerProvider.select((value) => value.coin), managerProvider.select((value) => value.coin),
); );
final fee = final fee = transactionInfo["fee"] is Amount
(transactionInfo["fee"] as int).toAmountAsRaw( ? transactionInfo["fee"] as Amount
fractionDigits: coin.decimals, : (transactionInfo["fee"] as int).toAmountAsRaw(
); fractionDigits: coin.decimals,
);
final amount = final amount =
transactionInfo["recipientAmt"] as Amount; transactionInfo["recipientAmt"] as Amount;
final total = amount + fee; final total = amount + fee;

View file

@ -427,18 +427,18 @@ class _ConfirmTransactionViewState
style: STextStyles.smallMed12(context), style: STextStyles.smallMed12(context),
), ),
Text( Text(
"${(transactionInfo["fee"] as int).toAmountAsRaw( "${(transactionInfo["fee"] is Amount ? transactionInfo["fee"] as Amount : (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: ref.watch( fractionDigits: ref.watch(
managerProvider.select( managerProvider.select(
(value) => value.coin.decimals, (value) => value.coin.decimals,
),
), ),
).localizedStringAsFixed( ),
locale: ref.watch( )).localizedStringAsFixed(
localeServiceChangeNotifierProvider locale: ref.watch(
.select((value) => value.locale), localeServiceChangeNotifierProvider
), .select((value) => value.locale),
)} ${ref.watch( ),
)} ${ref.watch(
managerProvider.select((value) => value.coin), managerProvider.select((value) => value.coin),
).ticker}", ).ticker}",
style: STextStyles.itemSubtitle12(context), style: STextStyles.itemSubtitle12(context),
@ -552,10 +552,22 @@ class _ConfirmTransactionViewState
String fiatAmount = "N/A"; String fiatAmount = "N/A";
if (externalCalls) { if (externalCalls) {
final price = ref final price = widget.isTokenTx
.read(priceAnd24hChangeNotifierProvider) ? ref
.getPrice(coin) .read(
.item1; priceAnd24hChangeNotifierProvider)
.getTokenPrice(
ref
.read(tokenServiceProvider)!
.tokenContract
.address,
)
.item1
: ref
.read(
priceAnd24hChangeNotifierProvider)
.getPrice(coin)
.item1;
if (price > Decimal.zero) { if (price > Decimal.zero) {
fiatAmount = (amount.decimal * price) fiatAmount = (amount.decimal * price)
.toAmount(fractionDigits: 2) .toAmount(fractionDigits: 2)
@ -678,10 +690,12 @@ class _ConfirmTransactionViewState
value.getManager(walletId))) value.getManager(walletId)))
.coin; .coin;
final fee = (transactionInfo["fee"] as int) final fee = transactionInfo["fee"] is Amount
.toAmountAsRaw( ? transactionInfo["fee"] as Amount
fractionDigits: coin.decimals, : (transactionInfo["fee"] as int)
); .toAmountAsRaw(
fractionDigits: coin.decimals,
);
return Text( return Text(
"${fee.localizedStringAsFixed( "${fee.localizedStringAsFixed(
@ -857,9 +871,11 @@ class _ConfirmTransactionViewState
.select((value) => value.getManager(walletId))) .select((value) => value.getManager(walletId)))
.coin; .coin;
final fee = (transactionInfo["fee"] as int).toAmountAsRaw( final fee = transactionInfo["fee"] is Amount
fractionDigits: coin.decimals, ? transactionInfo["fee"] as Amount
); : (transactionInfo["fee"] as int).toAmountAsRaw(
fractionDigits: coin.decimals,
);
return Text( return Text(
"${fee.localizedStringAsFixed( "${fee.localizedStringAsFixed(
@ -879,56 +895,28 @@ class _ConfirmTransactionViewState
SizedBox( SizedBox(
height: isDesktop ? 23 : 12, height: isDesktop ? 23 : 12,
), ),
Padding( if (!widget.isTokenTx)
padding: isDesktop Padding(
? const EdgeInsets.symmetric(
horizontal: 32,
)
: const EdgeInsets.all(0),
child: RoundedContainer(
padding: isDesktop padding: isDesktop
? const EdgeInsets.symmetric( ? const EdgeInsets.symmetric(
horizontal: 16, horizontal: 32,
vertical: 18,
) )
: const EdgeInsets.all(12), : const EdgeInsets.all(0),
color: Theme.of(context) child: RoundedContainer(
.extension<StackColors>()! padding: isDesktop
.snackBarBackSuccess, ? const EdgeInsets.symmetric(
child: Row( horizontal: 16,
mainAxisAlignment: MainAxisAlignment.spaceBetween, vertical: 18,
children: [ )
Text( : const EdgeInsets.all(12),
isDesktop ? "Total amount to send" : "Total amount", color: Theme.of(context)
style: isDesktop .extension<StackColors>()!
? STextStyles.desktopTextExtraExtraSmall(context) .snackBarBackSuccess,
.copyWith( child: Row(
color: Theme.of(context) mainAxisAlignment: MainAxisAlignment.spaceBetween,
.extension<StackColors>()! children: [
.textConfirmTotalAmount, Text(
) isDesktop ? "Total amount to send" : "Total amount",
: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textConfirmTotalAmount,
),
),
Builder(builder: (context) {
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin));
final fee = (transactionInfo["fee"] as int)
.toAmountAsRaw(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 style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context) ? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith( .copyWith(
@ -936,18 +924,51 @@ class _ConfirmTransactionViewState
.extension<StackColors>()! .extension<StackColors>()!
.textConfirmTotalAmount, .textConfirmTotalAmount,
) )
: STextStyles.itemSubtitle12(context).copyWith( : STextStyles.titleBold12(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textConfirmTotalAmount, .textConfirmTotalAmount,
), ),
textAlign: TextAlign.right, ),
); Builder(builder: (context) {
}), final coin = ref.watch(
], walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).coin));
final fee = transactionInfo["fee"] is Amount
? transactionInfo["fee"] as Amount
: (transactionInfo["fee"] as int)
.toAmountAsRaw(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,
);
}),
],
),
), ),
), ),
),
SizedBox( SizedBox(
height: isDesktop ? 28 : 16, height: isDesktop ? 28 : 16,
), ),

View file

@ -2,6 +2,7 @@ import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart'; import 'package:stackwallet/pages/token_view/sub_widgets/token_summary.dart';
import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart'; import 'package:stackwallet/pages/token_view/sub_widgets/token_transaction_list_widget.dart';
import 'package:stackwallet/pages/token_view/token_view.dart'; import 'package:stackwallet/pages/token_view/token_view.dart';
@ -89,7 +90,10 @@ class _DesktopTokenViewState extends ConsumerState<DesktopTokenView> {
.extension<StackColors>()! .extension<StackColors>()!
.topNavIconPrimary, .topNavIconPrimary,
), ),
onPressed: Navigator.of(context).pop, onPressed: () {
ref.refresh(feeSheetSessionCacheProvider);
Navigator.of(context).pop();
},
), ),
const SizedBox( const SizedBox(
width: 15, width: 15,

View file

@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/models.dart'; import 'package:stackwallet/models/models.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/pages/token_view/token_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_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/providers/wallet/public_private_balance_state_provider.dart';
@ -22,9 +23,11 @@ class DesktopFeeDropDown extends ConsumerStatefulWidget {
const DesktopFeeDropDown({ const DesktopFeeDropDown({
Key? key, Key? key,
required this.walletId, required this.walletId,
this.isToken = false,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final bool isToken;
@override @override
ConsumerState<DesktopFeeDropDown> createState() => _DesktopFeeDropDownState(); ConsumerState<DesktopFeeDropDown> createState() => _DesktopFeeDropDownState();
@ -52,66 +55,84 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
switch (feeRateType) { switch (feeRateType) {
case FeeRateType.fast: case FeeRateType.fast:
if (ref.read(feeSheetSessionCacheProvider).fast[amount] == null) { if (ref.read(feeSheetSessionCacheProvider).fast[amount] == null) {
final manager = if (widget.isToken == false) {
ref.read(walletsChangeNotifierProvider).getManager(walletId); final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (coin == Coin.monero || coin == Coin.wownero) { if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor( final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.fast.raw!); amount, MoneroTransactionPriority.fast.raw!);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee; ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) && } else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state != ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") { "Private") {
ref.read(feeSheetSessionCacheProvider).fast[amount] = ref.read(feeSheetSessionCacheProvider).fast[amount] =
await (manager.wallet as FiroWallet) await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate); .estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).fast[amount] =
await manager.estimateFeeFor(amount, feeRate);
}
} else { } else {
ref.read(feeSheetSessionCacheProvider).fast[amount] = final tokenWallet = ref.read(tokenServiceProvider)!;
await manager.estimateFeeFor(amount, feeRate); final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
} }
} }
return ref.read(feeSheetSessionCacheProvider).fast[amount]!; return ref.read(feeSheetSessionCacheProvider).fast[amount]!;
case FeeRateType.average: case FeeRateType.average:
if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) { if (ref.read(feeSheetSessionCacheProvider).average[amount] == null) {
final manager = if (widget.isToken == false) {
ref.read(walletsChangeNotifierProvider).getManager(walletId); final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (coin == Coin.monero || coin == Coin.wownero) { if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor( final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.regular.raw!); amount, MoneroTransactionPriority.regular.raw!);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee; ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) && } else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state != ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") { "Private") {
ref.read(feeSheetSessionCacheProvider).average[amount] = ref.read(feeSheetSessionCacheProvider).average[amount] =
await (manager.wallet as FiroWallet) await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate); .estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).average[amount] =
await manager.estimateFeeFor(amount, feeRate);
}
} else { } else {
ref.read(feeSheetSessionCacheProvider).average[amount] = final tokenWallet = ref.read(tokenServiceProvider)!;
await manager.estimateFeeFor(amount, feeRate); final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
} }
} }
return ref.read(feeSheetSessionCacheProvider).average[amount]!; return ref.read(feeSheetSessionCacheProvider).average[amount]!;
case FeeRateType.slow: case FeeRateType.slow:
if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) { if (ref.read(feeSheetSessionCacheProvider).slow[amount] == null) {
final manager = if (widget.isToken == false) {
ref.read(walletsChangeNotifierProvider).getManager(walletId); final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId);
if (coin == Coin.monero || coin == Coin.wownero) { if (coin == Coin.monero || coin == Coin.wownero) {
final fee = await manager.estimateFeeFor( final fee = await manager.estimateFeeFor(
amount, MoneroTransactionPriority.slow.raw!); amount, MoneroTransactionPriority.slow.raw!);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee; ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) && } else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state != ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") { "Private") {
ref.read(feeSheetSessionCacheProvider).slow[amount] = ref.read(feeSheetSessionCacheProvider).slow[amount] =
await (manager.wallet as FiroWallet) await (manager.wallet as FiroWallet)
.estimateFeeForPublic(amount, feeRate); .estimateFeeForPublic(amount, feeRate);
} else {
ref.read(feeSheetSessionCacheProvider).slow[amount] =
await manager.estimateFeeFor(amount, feeRate);
}
} else { } else {
ref.read(feeSheetSessionCacheProvider).slow[amount] = final tokenWallet = ref.read(tokenServiceProvider)!;
await manager.estimateFeeFor(amount, feeRate); final fee = tokenWallet.estimateFeeFor(feeRate);
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
} }
} }
return ref.read(feeSheetSessionCacheProvider).slow[amount]!; return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
@ -321,14 +342,16 @@ class FeeDropDownChild extends ConsumerWidget {
), ),
if (feeObject != null) if (feeObject != null)
Text( Text(
estimatedTimeToBeIncludedInNextBlock( manager.coin == Coin.ethereum
Constants.targetBlockTimeInSeconds(manager.coin), ? ""
feeRateType == FeeRateType.fast : estimatedTimeToBeIncludedInNextBlock(
? feeObject!.numberOfBlocksFast Constants.targetBlockTimeInSeconds(manager.coin),
: feeRateType == FeeRateType.slow feeRateType == FeeRateType.fast
? feeObject!.numberOfBlocksSlow ? feeObject!.numberOfBlocksFast
: feeObject!.numberOfBlocksAverage, : feeRateType == FeeRateType.slow
), ? feeObject!.numberOfBlocksSlow
: feeObject!.numberOfBlocksAverage,
),
style: STextStyles.desktopTextExtraExtraSmall(context) style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith( .copyWith(
color: Theme.of(context) color: Theme.of(context)

View file

@ -1412,7 +1412,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
), ),
if (coin != Coin.epicCash) if (coin != Coin.epicCash)
Text( Text(
"Transaction fee (estimated)", "Transaction fee (${coin == Coin.ethereum ? "max" : "estimated"})",
style: STextStyles.desktopTextExtraSmall(context).copyWith( style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!

View file

@ -71,12 +71,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
late TextEditingController sendToController; late TextEditingController sendToController;
late TextEditingController cryptoAmountController; late TextEditingController cryptoAmountController;
late TextEditingController baseAmountController; late TextEditingController baseAmountController;
late TextEditingController nonceController;
late final SendViewAutoFillData? _data; late final SendViewAutoFillData? _data;
final _addressFocusNode = FocusNode(); final _addressFocusNode = FocusNode();
final _cryptoFocus = FocusNode(); final _cryptoFocus = FocusNode();
final _baseFocus = FocusNode(); final _baseFocus = FocusNode();
final _nonceFocusNode = FocusNode();
String? _note; String? _note;
@ -229,6 +231,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
amount: amount, amount: amount,
args: { args: {
"feeRate": ref.read(feeRateTypeStateProvider), "feeRate": ref.read(feeRateTypeStateProvider),
"nonce": int.tryParse(nonceController.text),
}, },
); );
@ -305,7 +308,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
right: 32, right: 32,
), ),
child: Text( child: SelectableText(
e.toString(), e.toString(),
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: STextStyles.desktopTextExtraExtraSmall(context) style: STextStyles.desktopTextExtraExtraSmall(context)
@ -370,8 +373,12 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
level: LogLevel.Info); level: LogLevel.Info);
_cachedAmountToSend = _amountToSend; _cachedAmountToSend = _amountToSend;
final price = final price = ref
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1; .read(priceAnd24hChangeNotifierProvider)
.getTokenPrice(
ref.read(tokenServiceProvider)!.tokenContract.address,
)
.item1;
if (price > Decimal.zero) { if (price > Decimal.zero) {
final String fiatAmountString = Amount.fromDecimal( final String fiatAmountString = Amount.fromDecimal(
@ -512,8 +519,12 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
.toAmount(fractionDigits: 2) .toAmount(fractionDigits: 2)
: Decimal.parse(baseAmountString).toAmount(fractionDigits: 2); : Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
final Decimal _price = final Decimal _price = ref
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1; .read(priceAnd24hChangeNotifierProvider)
.getTokenPrice(
ref.read(tokenServiceProvider)!.tokenContract.address,
)
.item1;
if (_price == Decimal.zero) { if (_price == Decimal.zero) {
_amountToSend = Decimal.zero.toAmount(fractionDigits: tokenDecimals); _amountToSend = Decimal.zero.toAmount(fractionDigits: tokenDecimals);
@ -577,6 +588,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
sendToController = TextEditingController(); sendToController = TextEditingController();
cryptoAmountController = TextEditingController(); cryptoAmountController = TextEditingController();
baseAmountController = TextEditingController(); baseAmountController = TextEditingController();
nonceController = TextEditingController();
// feeController = TextEditingController(); // feeController = TextEditingController();
onCryptoAmountChanged = _cryptoAmountChanged; onCryptoAmountChanged = _cryptoAmountChanged;
@ -621,11 +633,13 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
sendToController.dispose(); sendToController.dispose();
cryptoAmountController.dispose(); cryptoAmountController.dispose();
baseAmountController.dispose(); baseAmountController.dispose();
nonceController.dispose();
// feeController.dispose(); // feeController.dispose();
_addressFocusNode.dispose(); _addressFocusNode.dispose();
_cryptoFocus.dispose(); _cryptoFocus.dispose();
_baseFocus.dispose(); _baseFocus.dispose();
_nonceFocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -979,7 +993,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
height: 20, height: 20,
), ),
Text( Text(
"Transaction fee (estimated)", "Transaction fee (max)",
style: STextStyles.desktopTextExtraSmall(context).copyWith( style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
@ -990,9 +1004,59 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
// TODO mod this for token fees
DesktopFeeDropDown( DesktopFeeDropDown(
walletId: walletId, walletId: walletId,
isToken: true,
),
const SizedBox(
height: 20,
),
Text(
"Nonce",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
minLines: 1,
maxLines: 1,
key: const Key("sendViewNonceFieldKey"),
controller: nonceController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
keyboardType: const TextInputType.numberWithOptions(),
focusNode: _nonceFocusNode,
style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
),
decoration: standardInputDecoration(
"Leave empty to auto select nonce",
_nonceFocusNode,
context,
desktopMed: true,
).copyWith(
contentPadding: const EdgeInsets.only(
left: 16,
top: 11,
bottom: 12,
right: 5,
),
),
),
), ),
const SizedBox( const SizedBox(
height: 36, height: 36,

View file

@ -923,7 +923,7 @@ class EthereumWallet extends CoinServiceAPI with WalletCache, WalletDB {
// precision may be lost here hence the following amountString // precision may be lost here hence the following amountString
amount: (txData["recipientAmt"] as Amount).raw.toInt(), amount: (txData["recipientAmt"] as Amount).raw.toInt(),
amountString: (txData["recipientAmt"] as Amount).toJsonString(), amountString: (txData["recipientAmt"] as Amount).toJsonString(),
fee: txData["fee"] as int, fee: (txData["fee"] as Amount).raw.toInt(),
height: null, height: null,
isCancelled: false, isCancelled: false,
isLelantus: false, isLelantus: false,

View file

@ -449,7 +449,7 @@ abstract class EthereumAPI {
}) async { }) async {
try { try {
final uri = Uri.parse( final uri = Uri.parse(
"$stackBaseServer/state?addrs=$address&parts=nonce", "$stackBaseServer/state?addrs=$address&parts=all",
); );
final response = await get(uri); final response = await get(uri);
@ -469,7 +469,7 @@ abstract class EthereumAPI {
} }
} else { } else {
throw EthApiException( throw EthApiException(
"getWalletTokenBalance($address) failed with status code: " "getAddressNonce($address) failed with status code: "
"${response.statusCode}", "${response.statusCode}",
); );
} }
@ -480,7 +480,7 @@ abstract class EthereumAPI {
); );
} catch (e, s) { } catch (e, s) {
Logging.instance.log( Logging.instance.log(
"getWalletTokenBalance(): $e\n$s", "getAddressNonce(): $e\n$s",
level: LogLevel.Error, level: LogLevel.Error,
); );
return EthereumResponse( return EthereumResponse(
@ -501,12 +501,19 @@ abstract class EthereumAPI {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final json = jsonDecode(response.body) as Map; final json = jsonDecode(response.body) as Map;
if (json["success"] == true) { if (json["success"] == true) {
return EthereumResponse( try {
GasTracker.fromJson( return EthereumResponse(
Map<String, dynamic>.from(json["result"] as Map), GasTracker.fromJson(
), Map<String, dynamic>.from(json["result"]["result"] as Map),
null, ),
); null,
);
} catch (_) {
throw EthApiException(
"getGasOracle() failed with response: "
"${response.body}",
);
}
} else { } else {
throw EthApiException( throw EthApiException(
"getGasOracle() failed with response: " "getGasOracle() failed with response: "

View file

@ -90,30 +90,10 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
final myAddress = await currentReceivingAddress; final myAddress = await currentReceivingAddress;
final myWeb3Address = web3dart.EthereumAddress.fromHex(myAddress); final myWeb3Address = web3dart.EthereumAddress.fromHex(myAddress);
final est = await client.estimateGas(
sender: myWeb3Address,
to: web3dart.EthereumAddress.fromHex(address),
data: _sendFunction
.encodeCall([web3dart.EthereumAddress.fromHex(address), amount.raw]),
gasPrice: web3dart.EtherAmount.fromUnitAndValue(
web3dart.EtherUnit.wei,
fee,
),
amountOfGas: BigInt.from(_gasLimit),
);
final nonce = args?["nonce"] as int? ?? final nonce = args?["nonce"] as int? ??
await client.getTransactionCount(myWeb3Address, await client.getTransactionCount(myWeb3Address,
atBlock: const web3dart.BlockNum.pending()); atBlock: const web3dart.BlockNum.pending());
final nResponse = await EthereumAPI.getAddressNonce(address: myAddress);
print("==============================================================");
print("TOKEN client.estimateGas: $est");
print("TOKEN estimateFeeFor : $feeEstimate");
print("TOKEN nonce custom response: $nResponse");
print("TOKEN actual nonce : $nonce");
print("==============================================================");
final tx = web3dart.Transaction.callContract( final tx = web3dart.Transaction.callContract(
contract: _deployedContract, contract: _deployedContract,
function: _sendFunction, function: _sendFunction,
@ -179,7 +159,7 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
// precision may be lost here hence the following amountString // precision may be lost here hence the following amountString
amount: (txData["recipientAmt"] as Amount).raw.toInt(), amount: (txData["recipientAmt"] as Amount).raw.toInt(),
amountString: (txData["recipientAmt"] as Amount).toJsonString(), amountString: (txData["recipientAmt"] as Amount).toJsonString(),
fee: txData["fee"] as int, fee: (txData["fee"] as Amount).raw.toInt(),
height: null, height: null,
isCancelled: false, isCancelled: false,
isLelantus: false, isLelantus: false,

View file

@ -1,425 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
class TokenInfo {
final Coin coin;
final String walletId;
final String contractAddress;
const TokenInfo(
{required this.coin,
required this.walletId,
required this.contractAddress});
factory TokenInfo.fromJson(Map<String, dynamic> jsonObject) {
return TokenInfo(
coin: Coin.values.byName(jsonObject["coin"] as String),
walletId: jsonObject["id"] as String,
contractAddress: jsonObject["contractAddress"] as String,
);
}
Map<String, String> toMap() {
return {
"contractAddress": contractAddress,
"walletId": walletId,
"coin": coin.name,
};
}
String toJsonString() {
return jsonEncode(toMap());
}
@override
String toString() {
return "TokenInfo: ${toJsonString()}";
}
}
class TokensService extends ChangeNotifier {
late final SecureStorageInterface _secureStore;
// Future<Map<String, TokenInfo>>? _walletNames;
// Future<Map<String, TokenInfo>> get walletNames =>
// _walletNames ??= _fetchWalletNames();
TokensService({
required SecureStorageInterface secureStorageInterface,
}) {
_secureStore = secureStorageInterface;
}
// Future<Coin> getWalletCryptoCurrency({required String walletName}) async {
// final id = await getWalletId(walletName);
// final currency = DB.instance.get<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: "${id}_cryptoCurrency");
// return Coin.values.byName(currency as String);
// }
// Future<bool> renameWallet({
// required String from,
// required String to,
// required bool shouldNotifyListeners,
// }) async {
// if (from == to) {
// return true;
// }
//
// final walletInfo = DB.instance
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
//
// final info = walletInfo.values.firstWhere(
// (element) => element['name'] == from,
// orElse: () => <String, String>{}) as Map;
//
// if (info.isEmpty) {
// // tried to rename a non existing wallet
// Logging.instance
// .log("Tried to rename a non existing wallet!", level: LogLevel.Error);
// return false;
// }
//
// if (from != to &&
// (walletInfo.values.firstWhere((element) => element['name'] == to,
// orElse: () => <String, String>{}) as Map)
// .isNotEmpty) {
// // name already exists
// Logging.instance.log("wallet with name \"$to\" already exists!",
// level: LogLevel.Error);
// return false;
// }
//
// info["name"] = to;
// walletInfo[info['id']] = info;
//
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: 'names', value: walletInfo);
// await refreshWallets(shouldNotifyListeners);
// return true;
// }
// Future<Map<String, WalletInfo>> _fetchWalletNames() async {
// final names = DB.instance
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
// if (names == null) {
// Logging.instance.log(
// "Fetched wallet 'names' returned null. Setting initializing 'names'",
// level: LogLevel.Info);
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: 'names',
// value: <String, dynamic>{});
// return {};
// }
// Logging.instance.log("Fetched wallet names: $names", level: LogLevel.Info);
// final mapped = Map<String, dynamic>.from(names);
// mapped.removeWhere((name, dyn) {
// final jsonObject = Map<String, dynamic>.from(dyn as Map);
// try {
// Coin.values.byName(jsonObject["coin"] as String);
// return false;
// } catch (e, s) {
// Logging.instance.log("Error, ${jsonObject["coin"]} does not exist",
// level: LogLevel.Error);
// return true;
// }
// });
//
// return mapped.map((name, dyn) => MapEntry(
// name, WalletInfo.fromJson(Map<String, dynamic>.from(dyn as Map))));
// }
// Future<void> addExistingStackWallet({
// required String name,
// required String walletId,
// required Coin coin,
// required bool shouldNotifyListeners,
// }) async {
// final _names = DB.instance
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
//
// Map<String, dynamic> names;
// if (_names == null) {
// names = {};
// } else {
// names = Map<String, dynamic>.from(_names);
// }
//
// if (names.keys.contains(walletId)) {
// throw Exception("Wallet with walletId \"$walletId\" already exists!");
// }
// if (names.values.where((element) => element['name'] == name).isNotEmpty) {
// throw Exception("Wallet with name \"$name\" already exists!");
// }
//
// names[walletId] = {
// "id": walletId,
// "coin": coin.name,
// "name": name,
// };
//
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_cryptoCurrency",
// value: coin.name);
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_mnemonicHasBeenVerified",
// value: false);
// await DB.instance.addWalletBox(walletId: walletId);
// await refreshWallets(shouldNotifyListeners);
// }
// /// returns the new walletId if successful, otherwise null
// Future<String?> addNewWallet({
// required String name,
// required Coin coin,
// required bool shouldNotifyListeners,
// }) async {
// final _names = DB.instance
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
//
// Map<String, dynamic> names;
// if (_names == null) {
// names = {};
// } else {
// names = Map<String, dynamic>.from(_names);
// }
//
// // Prevent overwriting or storing empty names
// if (name.isEmpty ||
// names.values.where((element) => element['name'] == name).isNotEmpty) {
// return null;
// }
//
// final id = const Uuid().v1();
// names[id] = {
// "id": id,
// "coin": coin.name,
// "name": name,
// };
//
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${id}_cryptoCurrency",
// value: coin.name);
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${id}_mnemonicHasBeenVerified",
// value: false);
// await DB.instance.addWalletBox(walletId: id);
// await refreshWallets(shouldNotifyListeners);
// return id;
// }
// Future<List<String>> getFavoriteWalletIds() async {
// return DB.instance
// .values<String>(boxName: DB.boxNameFavoriteWallets)
// .toList();
// }
// Future<void> saveFavoriteWalletIds(List<String> walletIds) async {
// await DB.instance.deleteAll<String>(boxName: DB.boxNameFavoriteWallets);
// await DB.instance
// .addAll(boxName: DB.boxNameFavoriteWallets, values: walletIds);
// debugPrint("saveFavoriteWalletIds list: $walletIds");
// }
//
// Future<void> addFavorite(String walletId) async {
// final list = await getFavoriteWalletIds();
// if (!list.contains(walletId)) {
// list.add(walletId);
// }
// await saveFavoriteWalletIds(list);
// }
//
// Future<void> removeFavorite(String walletId) async {
// final list = await getFavoriteWalletIds();
// list.remove(walletId);
// await saveFavoriteWalletIds(list);
// }
//
// Future<void> moveFavorite({
// required int fromIndex,
// required int toIndex,
// }) async {
// final list = await getFavoriteWalletIds();
// if (fromIndex < toIndex) {
// toIndex -= 1;
// }
// final walletId = list.removeAt(fromIndex);
// list.insert(toIndex, walletId);
// await saveFavoriteWalletIds(list);
// }
//
// Future<bool> checkForDuplicate(String name) async {
// final names = DB.instance
// .get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map?;
// if (names == null) return false;
//
// return names.values.where((element) => element['name'] == name).isNotEmpty;
// }
Future<String?> getWalletId(String walletName) async {
final names = DB.instance
.get<dynamic>(boxName: DB.boxNameAllWalletsData, key: 'names') as Map;
final shells =
names.values.where((element) => element['name'] == walletName);
if (shells.isEmpty) {
return null;
}
return shells.first["id"] as String;
}
// Future<bool> isMnemonicVerified({required String walletId}) async {
// final isVerified = DB.instance.get<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
//
// if (isVerified == null) {
// Logging.instance.log(
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!",
// level: LogLevel.Error,
// );
// throw Exception(
// "isMnemonicVerified(walletId: $walletId) returned null which should never happen!");
// } else {
// return isVerified;
// }
// }
//
// Future<void> setMnemonicVerified({required String walletId}) async {
// final isVerified = DB.instance.get<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_mnemonicHasBeenVerified") as bool?;
//
// if (isVerified == null) {
// Logging.instance.log(
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!",
// level: LogLevel.Error,
// );
// throw Exception(
// "setMnemonicVerified(walletId: $walletId) tried running on non existent wallet!");
// } else if (isVerified) {
// Logging.instance.log(
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!",
// level: LogLevel.Error,
// );
// throw Exception(
// "setMnemonicVerified(walletId: $walletId) tried running on already verified wallet!");
// } else {
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_mnemonicHasBeenVerified",
// value: true);
// Logging.instance.log(
// "setMnemonicVerified(walletId: $walletId) successful",
// level: LogLevel.Error,
// );
// }
// }
//
// // pin + mnemonic as well as anything else in secureStore
// Future<int> deleteWallet(String name, bool shouldNotifyListeners) async {
// final names = DB.instance.get<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: 'names') as Map? ??
// {};
//
// final walletId = await getWalletId(name);
// if (walletId == null) {
// return 3;
// }
//
// Logging.instance.log(
// "deleteWallet called with name=$name and id=$walletId",
// level: LogLevel.Warning,
// );
//
// final shell = names.remove(walletId);
//
// if (shell == null) {
// return 0;
// }
//
// // TODO delete derivations!!!
// await _secureStore.delete(key: "${walletId}_pin");
// await _secureStore.delete(key: "${walletId}_mnemonic");
//
// await DB.instance.delete<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: "${walletId}_cryptoCurrency");
// await DB.instance.delete<dynamic>(
// boxName: DB.boxNameAllWalletsData,
// key: "${walletId}_mnemonicHasBeenVerified");
// if (coinFromPrettyName(shell['coin'] as String) == Coin.wownero) {
// final wowService =
// wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
// await wowService.remove(walletId);
// Logging.instance
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.monero) {
// final xmrService =
// monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
// await xmrService.remove(walletId);
// Logging.instance
// .log("monero wallet: $walletId deleted", level: LogLevel.Info);
// } else if (coinFromPrettyName(shell['coin'] as String) == Coin.epicCash) {
// final deleteResult =
// await deleteEpicWallet(walletId: walletId, secureStore: _secureStore);
// Logging.instance.log(
// "epic wallet: $walletId deleted with result: $deleteResult",
// level: LogLevel.Info);
// }
//
// // box data may currently still be read/written to if wallet was refreshing
// // when delete was requested so instead of deleting now we mark the wallet
// // as needs delete by adding it's id to a list which gets checked on app start
// await DB.instance.add<String>(
// boxName: DB.boxNameWalletsToDeleteOnStart, value: walletId);
//
// final lookupService = TradeSentFromStackService();
// for (final lookup in lookupService.all) {
// if (lookup.walletIds.contains(walletId)) {
// // update lookup data to reflect deleted wallet
// await lookupService.save(
// tradeWalletLookup: lookup.copyWith(
// walletIds: lookup.walletIds.where((id) => id != walletId).toList(),
// ),
// );
// }
// }
//
// // delete notifications tied to deleted wallet
// for (final notification in NotificationsService.instance.notifications) {
// if (notification.walletId == walletId) {
// await NotificationsService.instance.delete(notification, false);
// }
// }
//
// if (names.isEmpty) {
// await DB.instance.deleteAll<dynamic>(boxName: DB.boxNameAllWalletsData);
// _walletNames = Future(() => {});
// notifyListeners();
// return 2; // error code no wallets on device
// }
//
// await DB.instance.put<dynamic>(
// boxName: DB.boxNameAllWalletsData, key: 'names', value: names);
// await refreshWallets(shouldNotifyListeners);
// return 0;
// }
//
// Future<void> refreshWallets(bool shouldNotifyListeners) async {
// final newNames = await _fetchWalletNames();
// _walletNames = Future(() => newNames);
// if (shouldNotifyListeners) notifyListeners();
// }
}

View file

@ -15,7 +15,7 @@ class GasTracker {
final int numberOfBlocksAverage; final int numberOfBlocksAverage;
final int numberOfBlocksSlow; final int numberOfBlocksSlow;
final int timestamp; final String lastBlock;
const GasTracker({ const GasTracker({
required this.average, required this.average,
@ -24,19 +24,20 @@ class GasTracker {
required this.numberOfBlocksFast, required this.numberOfBlocksFast,
required this.numberOfBlocksAverage, required this.numberOfBlocksAverage,
required this.numberOfBlocksSlow, required this.numberOfBlocksSlow,
required this.timestamp, required this.lastBlock,
}); });
factory GasTracker.fromJson(Map<String, dynamic> json) { factory GasTracker.fromJson(Map<String, dynamic> json) {
final targetTime = Constants.targetBlockTimeInSeconds(Coin.ethereum); final targetTime = Constants.targetBlockTimeInSeconds(Coin.ethereum);
return GasTracker( return GasTracker(
average: Decimal.parse(json["average"]["price"].toString()), fast: Decimal.parse(json["FastGasPrice"].toString()),
fast: Decimal.parse(json["fast"]["price"].toString()), average: Decimal.parse(json["ProposeGasPrice"].toString()),
slow: Decimal.parse(json["slow"]["price"].toString()), slow: Decimal.parse(json["SafeGasPrice"].toString()),
numberOfBlocksAverage: (json["average"]["time"] as int) ~/ targetTime, // TODO fix hardcoded
numberOfBlocksFast: (json["fast"]["time"] as int) ~/ targetTime, numberOfBlocksFast: 30 ~/ targetTime,
numberOfBlocksSlow: (json["slow"]["time"] as int) ~/ targetTime, numberOfBlocksAverage: 180 ~/ targetTime,
timestamp: json["timestamp"] as int, numberOfBlocksSlow: 240 ~/ targetTime,
lastBlock: json["LastBlock"] as String,
); );
} }
} }