refactor amount formatter to handle unified eth/sol contracts
What changed, and why it matters
This commit is a code cleanup that merges two separate ways of handling Ethereum and Solana token amounts into one shared path. It does not appear to fix an active security bug, but it removes duplicated logic that could previously have led to inconsistent token display or parsing. The change is mostly internal refactoring across wallet balance and transaction screens.
Treat as a routine refactor. Review the unified AmountUnit decimal handling to confirm Solana SPL tokens use the correct decimals in all edge cases, and verify that legacy transaction views now correctly label SPL token transactions. No urgent security action is indicated.
Security signals we found
Unification of token contract handling reduces duplicated decimal/unit logic, lowering the chance of inconsistent token amount formatting between Ethereum and Solana tokens
Addition of splToken subtype recognition in legacy transaction views may prevent Solana token transactions from being misclassified as non-token transactions
No explicit bounds, validation, or cryptographic changes are present in the diff
Evidence from the diff
The patch refactors AmountFormatter and AmountUnit to accept a single Contract? tokenContract parameter instead of separate EthContract? and SolContract? parameters. Call sites across send, token, transaction, and desktop wallet views are updated to pass the unified contract. It also expands isTokenTx checks to include TransactionSubType.splToken in a couple of legacy transaction views. The diff is large but largely formatting and mechanical renaming; no explicit vulnerability is patched.
Changed components
lib/utilities/amount/amount_formatter.dartlib/utilities/amount/amount_unit.dartlib/pages/send_view/confirm_transaction_view.dartlib/pages/send_view/token_send_view.dartlib/pages/token_view/sub_widgets/my_token_select_item.dartlib/pages/token_view/sub_widgets/token_summary.dartlib/pages/wallet_view/transaction_views/transaction_details_view.dartlib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dartlib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dartlib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dartlib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dartlib/widgets/transaction_card.dartlib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dartInspect captured patch +436 / −511
diff --git a/lib/pages/send_view/confirm_transaction_view.dart b/lib/pages/send_view/confirm_transaction_view.dart
index fe6525a..9a7640a 100644
--- a/lib/pages/send_view/confirm_transaction_view.dart
+++ b/lib/pages/send_view/confirm_transaction_view.dart
@@ -723,14 +723,12 @@ class _ConfirmTransactionViewState
.watch(pAmountFormatter(coin))
.format(
amountWithoutChange,
- ethContract:
+ tokenContract:
widget.isTokenTx && wallet is! SolanaWallet
? ref
.watch(pCurrentTokenWallet)!
.tokenContract
- : null,
- solContract:
- widget.isTokenTx && wallet is SolanaWallet
+ : widget.isTokenTx && wallet is SolanaWallet
? ref
.watch(pCurrentSolanaTokenWallet)!
.solContract
@@ -975,7 +973,7 @@ class _ConfirmTransactionViewState
.watch(pAmountFormatter(coin))
.format(
amountWithoutChange,
- ethContract:
+ tokenContract:
widget.isTokenTx &&
wallet is! SolanaWallet
? ref
@@ -983,10 +981,8 @@ class _ConfirmTransactionViewState
pCurrentTokenWallet,
)!
.tokenContract
- : null,
- solContract:
- widget.isTokenTx &&
- wallet is SolanaWallet
+ : widget.isTokenTx &&
+ wallet is SolanaWallet
? ref
.watch(
pCurrentSolanaTokenWallet,
diff --git a/lib/pages/send_view/token_send_view.dart b/lib/pages/send_view/token_send_view.dart
index 8294209..3d30fc5 100644
--- a/lib/pages/send_view/token_send_view.dart
+++ b/lib/pages/send_view/token_send_view.dart
@@ -295,7 +295,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
if (!_cryptoAmountChangeLock) {
final cryptoAmount = ref
.read(pAmountFormatter(coin))
- .tryParse(cryptoAmountController.text, ethContract: tokenContract);
+ .tryParse(cryptoAmountController.text, tokenContract: tokenContract);
if (cryptoAmount != null) {
_amountToSend = cryptoAmount;
if (_cachedAmountToSend != null &&
@@ -746,7 +746,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
)),
)
.spendable,
- ethContract: tokenContract,
+ tokenContract: tokenContract,
withUnitName: false,
indicatePrecisionLoss: true,
);
@@ -772,7 +772,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
)),
)
.spendable,
- ethContract: tokenContract,
+ tokenContract:
+ tokenContract,
),
style: STextStyles.titleBold12(
context,
diff --git a/lib/pages/token_view/sub_widgets/my_token_select_item.dart b/lib/pages/token_view/sub_widgets/my_token_select_item.dart
index 745f2a0..a00813e 100644
--- a/lib/pages/token_view/sub_widgets/my_token_select_item.dart
+++ b/lib/pages/token_view/sub_widgets/my_token_select_item.dart
@@ -15,7 +15,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../models/isar/models/ethereum/eth_contract.dart';
import '../../../pages_desktop_specific/my_stack_view/wallet_view/desktop_token_view.dart';
-import '../../../providers/db/main_db_provider.dart';
import '../../../providers/providers.dart';
import '../../../services/ethereum/cached_eth_token_balance.dart';
import '../../../themes/stack_colors.dart';
@@ -64,21 +63,20 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
await showDialog<void>(
barrierDismissible: false,
context: context,
- builder:
- (context) => BasicDialog(
- title: "Failed to load token data",
- desktopHeight: double.infinity,
- desktopWidth: 450,
- rightButton: PrimaryButton(
- label: "OK",
- onPressed: () {
- Navigator.of(context).pop();
- if (!isDesktop) {
- Navigator.of(context).pop();
- }
- },
- ),
- ),
+ builder: (context) => BasicDialog(
+ title: "Failed to load token data",
+ desktopHeight: double.infinity,
+ desktopWidth: 450,
+ rightButton: PrimaryButton(
+ label: "OK",
+ onPressed: () {
+ Navigator.of(context).pop();
+ if (!isDesktop) {
+ Navigator.of(context).pop();
+ }
+ },
+ ),
+ ),
);
return false;
}
@@ -153,10 +151,9 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
padding: const EdgeInsets.all(0),
child: MaterialButton(
key: Key("walletListItemButtonKey_${widget.token.symbol}"),
- padding:
- isDesktop
- ? const EdgeInsets.symmetric(horizontal: 28, vertical: 24)
- : const EdgeInsets.symmetric(horizontal: 12, vertical: 13),
+ padding: isDesktop
+ ? const EdgeInsets.symmetric(horizontal: 28, vertical: 24)
+ : const EdgeInsets.symmetric(horizontal: 12, vertical: 13),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
@@ -181,17 +178,15 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
children: [
Text(
widget.token.name,
- style:
- isDesktop
- ? STextStyles.desktopTextExtraSmall(
+ style: isDesktop
+ ? STextStyles.desktopTextExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textDark,
- )
- : STextStyles.titleBold12(context),
+ ).extension<StackColors>()!.textDark,
+ )
+ : STextStyles.titleBold12(context),
),
const Spacer(),
Text(
@@ -210,19 +205,17 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
)),
)
.total,
- ethContract: widget.token,
+ tokenContract: widget.token,
),
- style:
- isDesktop
- ? STextStyles.desktopTextExtraSmall(
+ style: isDesktop
+ ? STextStyles.desktopTextExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textDark,
- )
- : STextStyles.itemSubtitle(context),
+ ).extension<StackColors>()!.textDark,
+ )
+ : STextStyles.itemSubtitle(context),
),
],
),
@@ -231,24 +224,22 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
children: [
Text(
widget.token.symbol,
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- )
- : STextStyles.itemSubtitle(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ )
+ : STextStyles.itemSubtitle(context),
),
const Spacer(),
if (priceString != null)
Text(
"$priceString "
"${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- )
- : STextStyles.itemSubtitle(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ )
+ : STextStyles.itemSubtitle(context),
),
],
),
diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart
index 2c09077..0f1bd17 100644
--- a/lib/pages/token_view/sub_widgets/token_summary.dart
+++ b/lib/pages/token_view/sub_widgets/token_summary.dart
@@ -83,10 +83,9 @@ class TokenSummary extends ConsumerWidget {
children: [
SvgPicture.asset(
Assets.svg.walletDesktop,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryTextSecondary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryTextSecondary,
width: 12,
height: 12,
),
@@ -94,10 +93,9 @@ class TokenSummary extends ConsumerWidget {
Text(
ref.watch(pWalletName(walletId)),
style: STextStyles.w500_12(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryTextSecondary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryTextSecondary,
),
),
],
@@ -113,12 +111,11 @@ class TokenSummary extends ConsumerWidget {
Ethereum(CryptoCurrencyNetwork.main),
),
)
- .format(balance.total, ethContract: token),
+ .format(balance.total, tokenContract: token),
style: STextStyles.pageTitleH1(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryTextPrimary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryTextPrimary,
),
),
const SizedBox(width: 10),
@@ -134,10 +131,9 @@ class TokenSummary extends ConsumerWidget {
Text(
"${(balance.total.decimal * price).toAmount(fractionDigits: 2).fiatString(locale: ref.watch(localeServiceChangeNotifierProvider.select((value) => value.locale)))} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
style: STextStyles.subtitle500(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryTextPrimary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryTextPrimary,
),
),
const SizedBox(height: 20),
@@ -156,8 +152,9 @@ class TokenSummary extends ConsumerWidget {
(value) => value!.tokenContract.address,
),
),
- overrideIconColor:
- Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
+ overrideIconColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.topNavIconPrimary,
),
),
],
@@ -266,8 +263,9 @@ class TokenOptionsButton extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RawMaterialButton(
- fillColor:
- Theme.of(context).extension<StackColors>()!.tokenSummaryButtonBG,
+ fillColor: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryButtonBG,
elevation: 0,
focusElevation: 0,
hoverElevation: 0,
@@ -283,36 +281,31 @@ class TokenOptionsButton extends StatelessWidget {
padding: const EdgeInsets.all(10),
child: ConditionalParent(
condition: iconSize < 24,
- builder:
- (child) => RoundedContainer(
- padding: const EdgeInsets.all(6),
- color: Theme.of(context)
- .extension<StackColors>()!
- .tokenSummaryIcon
- .withOpacity(0.4),
- radiusMultiplier: 10,
- child: Center(child: child),
- ),
- child:
- iconAssetPathSVG.startsWith("assets/")
- ? SvgPicture.asset(
- iconAssetPathSVG,
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryIcon,
- width: iconSize,
- height: iconSize,
- )
- : SvgPicture.file(
- File(iconAssetPathSVG),
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryIcon,
- width: iconSize,
- height: iconSize,
- ),
+ builder: (child) => RoundedContainer(
+ padding: const EdgeInsets.all(6),
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryIcon.withOpacity(0.4),
+ radiusMultiplier: 10,
+ child: Center(child: child),
+ ),
+ child: iconAssetPathSVG.startsWith("assets/")
+ ? SvgPicture.asset(
+ iconAssetPathSVG,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryIcon,
+ width: iconSize,
+ height: iconSize,
+ )
+ : SvgPicture.file(
+ File(iconAssetPathSVG),
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryIcon,
+ width: iconSize,
+ height: iconSize,
+ ),
),
),
),
@@ -320,10 +313,9 @@ class TokenOptionsButton extends StatelessWidget {
Text(
subLabel,
style: STextStyles.w500_12(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.tokenSummaryTextPrimary,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.tokenSummaryTextPrimary,
),
),
],
diff --git a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
index 1e3aa7c..b38935d 100644
--- a/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
+++ b/lib/pages/wallet_view/transaction_views/transaction_details_view.dart
@@ -97,7 +97,9 @@ class _TransactionDetailsViewState
void initState() {
isDesktop = Util.isDesktop;
_transaction = widget.transaction;
- isTokenTx = _transaction.subType == TransactionSubType.ethToken;
+ isTokenTx =
+ _transaction.subType == TransactionSubType.ethToken ||
+ _transaction.subType == TransactionSubType.splToken;
walletId = widget.walletId;
minConfirms = ref
@@ -518,7 +520,7 @@ class _TransactionDetailsViewState
: CrossAxisAlignment.start,
children: [
SelectableText(
- "$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}",
+ "$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: ethContract)}",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context,
diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart
index fd678c0..2d38c8d 100644
--- a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart
+++ b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart
@@ -1046,15 +1046,7 @@ class _DesktopTransactionCardRowState
builder: (context) {
final formattedAmount = ref
.watch(pAmountFormatter(coin))
- .format(
- amount,
- ethContract: contract is EthContract
- ? contract as EthContract
- : null,
- solContract: contract is SolContract
- ? contract as SolContract
- : null,
- );
+ .format(amount, tokenContract: contract);
return Text(
"$prefix$formattedAmount",
diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart
index 3ee5cd7..a55940e 100644
--- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart
+++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart
@@ -274,14 +274,7 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
.watch(pAmountFormatter(coin))
.format(
amount,
- ethContract:
- tokenContract is EthContract
- ? tokenContract as EthContract
- : null,
- solContract:
- tokenContract is SolContract
- ? tokenContract as SolContract
- : null,
+ tokenContract: tokenContract,
);
return Text(
diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
index 46dc95b..8e7e392 100644
--- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
+++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart
@@ -23,8 +23,6 @@ import 'package:url_launcher/url_launcher.dart';
import '../../../../models/isar/models/blockchain_data/transaction.dart';
import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
import '../../../../models/isar/models/contract.dart';
-import '../../../../models/isar/models/ethereum/eth_contract.dart';
-import '../../../../models/isar/models/solana/sol_contract.dart';
import '../../../../notifications/show_flush_bar.dart';
import '../../../../providers/global/address_book_service_provider.dart';
import '../../../../providers/providers.dart';
@@ -201,7 +199,13 @@ class _TransactionV2DetailsViewState
coin = widget.coin;
- if (_transaction.subType == TransactionSubType.ethToken) {
+ if (_transaction.subType == TransactionSubType.splToken) {
+ tokenContract = ref
+ .read(mainDBProvider)
+ .getSolContractSync(_transaction.contractAddress!);
+
+ unit = tokenContract!.symbol;
+ } else if (_transaction.subType == TransactionSubType.ethToken) {
tokenContract = ref
.read(mainDBProvider)
.getEthContractSync(_transaction.contractAddress!);
@@ -2202,15 +2206,7 @@ class _TxDetailsAmountHeader extends ConsumerWidget {
builder: (context) {
final formattedAmount = ref
.watch(pAmountFormatter(coin))
- .format(
- amount,
- ethContract: tokenContract is EthContract
- ? tokenContract as EthContract
- : null,
- solContract: tokenContract is SolContract
- ? tokenContract as SolContract
- : null,
- );
+ .format(amount, tokenContract: tokenContract);
return SelectableText(
"$amountPrefix$formattedAmount",
style: detailStyle,
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart
index bf57331..f01cdd2 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_token_send.dart
@@ -108,15 +108,14 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
final tokenWallet = ref.read(pCurrentTokenWallet)!;
final Amount amount = _amountToSend!;
- final Amount availableBalance =
- ref
- .read(
- pTokenBalance((
- walletId: walletId,
- contractAddress: tokenWallet.tokenContract.address,
- )),
- )
- .spendable;
+ final Amount availableBalance = ref
+ .read(
+ pTokenBalance((
+ walletId: walletId,
+ contractAddress: tokenWallet.tokenContract.address,
+ )),
+ )
+ .spendable;
// confirm send all
if (amount == availableBalance) {
@@ -237,8 +236,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
address: _address!,
amount: amount,
isChange: false,
- addressType:
- tokenWallet.cryptoCurrency.getAddressType(_address!)!,
+ addressType: tokenWallet.cryptoCurrency.getAddressType(
+ _address!,
+ )!,
),
],
feeRateType: ref.read(feeRateTypeDesktopStateProvider),
@@ -260,18 +260,17 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
unawaited(
showDialog(
context: context,
- builder:
- (context) => DesktopDialog(
- maxHeight: MediaQuery.of(context).size.height - 64,
- maxWidth: 580,
- child: ConfirmTransactionView(
- txData: txData,
- walletId: walletId,
- onSuccess: clearSendForm,
- isTokenTx: true,
- routeOnSuccessName: DesktopHomeView.routeName,
- ),
- ),
+ builder: (context) => DesktopDialog(
+ maxHeight: MediaQuery.of(context).size.height - 64,
+ maxWidth: 580,
+ child: ConfirmTransactionView(
+ txData: txData,
+ walletId: walletId,
+ onSuccess: clearSendForm,
+ isTokenTx: true,
+ routeOnSuccessName: DesktopHomeView.routeName,
+ ),
+ ),
),
);
}
@@ -360,7 +359,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
.read(pAmountFormatter(coin))
.tryParse(
cryptoAmountController.text,
- ethContract: ref.read(pCurrentTokenWallet)!.tokenContract,
+ tokenContract: ref.read(pCurrentTokenWallet)!.tokenContract,
);
if (cryptoAmount != null) {
@@ -371,21 +370,19 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
}
_cachedAmountToSend = _amountToSend;
- final price =
- ref
- .read(priceAnd24hChangeNotifierProvider)
- .getTokenPrice(
- ref.read(pCurrentTokenWallet)!.tokenContract.address,
- )
- ?.value;
+ final price = ref
+ .read(priceAnd24hChangeNotifierProvider)
+ .getTokenPrice(ref.read(pCurrentTokenWallet)!.tokenContract.address)
+ ?.value;
if (price != null && price > Decimal.zero) {
- final String fiatAmountString = Amount.fromDecimal(
- _amountToSend!.decimal * price,
- fractionDigits: 2,
- ).fiatString(
- locale: ref.read(localeServiceChangeNotifierProvider).locale,
- );
+ final String fiatAmountString =
+ Amount.fromDecimal(
+ _amountToSend!.decimal * price,
+ fractionDigits: 2,
+ ).fiatString(
+ locale: ref.read(localeServiceChangeNotifierProvider).locale,
+ );
baseAmountController.text = fiatAmountString;
}
@@ -464,8 +461,10 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
// autofill amount field
if (paymentData.amount != null) {
final Amount amount = Decimal.parse(paymentData.amount!).toAmount(
- fractionDigits:
- ref.read(pCurrentTokenWallet)!.tokenContract.decimals,
+ fractionDigits: ref
+ .read(pCurrentTokenWallet)!
+ .tokenContract
+ .decimals,
);
cryptoAmountController.text = ref
.read(pAmountFormatter(coin))
@@ -519,36 +518,33 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
}
void fiatTextFieldOnChanged(String baseAmountString) {
- final int tokenDecimals =
- ref.read(pCurrentTokenWallet)!.tokenContract.decimals;
+ final int tokenDecimals = ref
+ .read(pCurrentTokenWallet)!
+ .tokenContract
+ .decimals;
if (baseAmountString.isNotEmpty &&
baseAmountString != "." &&
baseAmountString != ",") {
- final baseAmount =
- baseAmountString.contains(",")
- ? Decimal.parse(
- baseAmountString.replaceFirst(",", "."),
- ).toAmount(fractionDigits: 2)
- : Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
-
- final Decimal? _price =
- ref
- .read(priceAnd24hChangeNotifierProvider)
- .getTokenPrice(
- ref.read(pCurrentTokenWallet)!.tokenContract.address,
- )
- ?.value;
+ final baseAmount = baseAmountString.contains(",")
+ ? Decimal.parse(
+ baseAmountString.replaceFirst(",", "."),
+ ).toAmount(fractionDigits: 2)
+ : Decimal.parse(baseAmountString).toAmount(fractionDigits: 2);
+
+ final Decimal? _price = ref
+ .read(priceAnd24hChangeNotifierProvider)
+ .getTokenPrice(ref.read(pCurrentTokenWallet)!.tokenContract.address)
+ ?.value;
if (_price == null || _price == Decimal.zero) {
_amountToSend = Decimal.zero.toAmount(fractionDigits: tokenDecimals);
} else {
- _amountToSend =
- baseAmount <= Amount.zero
- ? Decimal.zero.toAmount(fractionDigits: tokenDecimals)
- : (baseAmount.decimal / _price)
- .toDecimal(scaleOnInfinitePrecision: tokenDecimals)
- .toAmount(fractionDigits: tokenDecimals);
+ _amountToSend = baseAmount <= Amount.zero
+ ? Decimal.zero.toAmount(fractionDigits: tokenDecimals)
+ : (baseAmount.decimal / _price)
+ .toDecimal(scaleOnInfinitePrecision: tokenDecimals)
+ .toAmount(fractionDigits: tokenDecimals);
}
if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) {
return;
@@ -560,7 +556,7 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
.format(
_amountToSend!,
withUnitName: false,
- ethContract: ref.read(pCurrentTokenWallet)!.tokenContract,
+ tokenContract: ref.read(pCurrentTokenWallet)!.tokenContract,
);
_cryptoAmountChangeLock = true;
@@ -581,8 +577,10 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
.read(
pTokenBalance((
walletId: walletId,
- contractAddress:
- ref.read(pCurrentTokenWallet)!.tokenContract.address,
+ contractAddress: ref
+ .read(pCurrentTokenWallet)!
+ .tokenContract
+ .address,
)),
)
.spendable
@@ -679,10 +677,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Text(
"Send from",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
@@ -692,10 +689,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Text(
"Amount",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
@@ -715,13 +711,12 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
key: const Key("amountInputFieldCryptoTextFieldKey"),
controller: cryptoAmountController,
focusNode: _cryptoFocus,
- keyboardType:
- Util.isDesktop
- ? null
- : const TextInputType.numberWithOptions(
- signed: false,
- decimal: true,
- ),
+ keyboardType: Util.isDesktop
+ ? null
+ : const TextInputType.numberWithOptions(
+ signed: false,
+ decimal: true,
+ ),
textAlign: TextAlign.right,
inputFormatters: [
AmountInputFormatter(
@@ -752,10 +747,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
),
hintText: "0",
hintStyle: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultText,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultText,
),
prefixIcon: FittedBox(
fit: BoxFit.scaleDown,
@@ -764,10 +758,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
child: Text(
ref.watch(pAmountUnit(coin)).unitForContract(tokenContract),
style: STextStyles.smallMed14(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
),
@@ -790,13 +783,12 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
key: const Key("amountInputFieldFiatTextFieldKey"),
controller: baseAmountController,
focusNode: _baseFocus,
- keyboardType:
- Util.isDesktop
- ? null
- : const TextInputType.numberWithOptions(
- signed: false,
- decimal: true,
- ),
+ keyboardType: Util.isDesktop
+ ? null
+ : const TextInputType.numberWithOptions(
+ signed: false,
+ decimal: true,
+ ),
textAlign: TextAlign.right,
inputFormatters: [
AmountInputFormatter(
@@ -823,10 +815,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
),
hintText: "0",
hintStyle: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldDefaultText,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultText,
),
prefixIcon: FittedBox(
fit: BoxFit.scaleDown,
@@ -839,10 +830,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
),
),
style: STextStyles.smallMed14(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.accentColorDark,
),
),
),
@@ -853,10 +843,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Text(
"Send to",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
@@ -893,127 +882,128 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
},
focusNode: _addressFocusNode,
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveText,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveText,
height: 1.8,
),
- decoration: standardInputDecoration(
- "Enter ${tokenContract.symbol} address",
- _addressFocusNode,
- context,
- desktopMed: true,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 11,
- bottom: 12,
- right: 5,
- ),
- suffixIcon: Padding(
- padding:
- sendToController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter ${tokenContract.symbol} address",
+ _addressFocusNode,
+ context,
+ desktopMed: true,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 11,
+ bottom: 12,
+ right: 5,
+ ),
+ suffixIcon: Padding(
+ padding: sendToController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- _addressToggleFlag
- ? TextFieldIconButton(
- key: const Key(
- "sendTokenViewClearAddressFieldButtonKey",
- ),
- onTap: () {
- sendToController.text = "";
- _address = "";
- _updatePreviewButtonState(
- _address,
- _amountToSend,
- );
- setState(() {
- _addressToggleFlag = false;
- });
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "sendTokenViewPasteAddressFieldButtonKey",
- ),
- onTap: pasteAddress,
- child:
- sendToController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (sendToController.text.isEmpty)
- TextFieldIconButton(
- key: const Key("sendTokenViewAddressBookButtonKey"),
- onTap: () async {
- final entry = await showDialog<
- ContactAddressEntry?
- >(
- context: context,
- builder:
- (context) => DesktopDialog(
- maxWidth: 696,
- maxHeight: 600,
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ _addressToggleFlag
+ ? TextFieldIconButton(
+ key: const Key(
+ "sendTokenViewClearAddressFieldButtonKey",
+ ),
+ onTap: () {
+ sendToController.text = "";
+ _address = "";
+ _updatePreviewButtonState(
+ _address,
+ _amountToSend,
+ );
+ setState(() {
+ _addressToggleFlag = false;
+ });
+ },
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ key: const Key(
+ "sendTokenViewPasteAddressFieldButtonKey",
+ ),
+ onTap: pasteAddress,
+ child: sendToController.text.isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (sendToController.text.isEmpty)
+ TextFieldIconButton(
+ key: const Key(
+ "sendTokenViewAddressBookButtonKey",
+ ),
+ onTap: () async {
+ final entry =
+ await showDialog<ContactAddressEntry?>(
+ context: context,
+ builder: (context) => DesktopDialog(
+ maxWidth: 696,
+ maxHeight: 600,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
children: [
- Padding(
- padding: const EdgeInsets.only(
- left: 32,
- ),
- child: Text(
- "Address book",
- style: STextStyles.desktopH3(
- context,
+ Row(
+ mainAxisAlignment:
+ MainAxisAlignment
+ .spaceBetween,
+ children: [
+ Padding(
+ padding:
+ const EdgeInsets.only(
+ left: 32,
+ ),
+ child: Text(
+ "Address book",
+ style:
+ STextStyles.desktopH3(
+ context,
+ ),
+ ),
),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: AddressBookAddressChooser(
+ coin: coin,
),
),
- const DesktopDialogCloseButton(),
],
),
- Expanded(
- child: AddressBookAddressChooser(
- coin: coin,
- ),
- ),
- ],
- ),
- ),
- );
+ ),
+ );
- if (entry != null) {
- sendToController.text =
- entry.other ?? entry.label;
+ if (entry != null) {
+ sendToController.text =
+ entry.other ?? entry.label;
- _address = entry.address;
+ _address = entry.address;
- _updatePreviewButtonState(
- _address,
- _amountToSend,
- );
+ _updatePreviewButtonState(
+ _address,
+ _amountToSend,
+ );
- setState(() {
- _addressToggleFlag = true;
- });
- }
- },
- child: const AddressBookIcon(),
- ),
- ],
+ setState(() {
+ _addressToggleFlag = true;
+ });
+ }
+ },
+ child: const AddressBookIcon(),
+ ),
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
Builder(
@@ -1031,8 +1021,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
error,
textAlign: TextAlign.left,
style: STextStyles.label(context).copyWith(
- color:
- Theme.of(context).extension<StackColors>()!.textError,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textError,
),
),
),
@@ -1054,10 +1045,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
Text(
"Nonce",
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
@@ -1077,25 +1067,25 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
keyboardType: const TextInputType.numberWithOptions(),
focusNode: _nonceFocusNode,
style: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension<StackColors>()!.textFieldActiveText,
+ 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,
- ),
- ),
+ 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(height: 36),
@@ -1103,10 +1093,9 @@ class _DesktopTokenSendState extends ConsumerState<DesktopTokenSend> {
buttonHeight: ButtonHeight.l,
label: "Preview send",
enabled: ref.watch(previewTokenTxButtonStateProvider.state).state,
- onPressed:
- ref.watch(previewTokenTxButtonStateProvider.state).state
- ? previewSend
- : null,
+ onPressed: ref.watch(previewTokenTxButtonStateProvider.state).state
+ ? previewSend
+ : null,
),
],
);
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart
index c89a57d..ac01104 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_wallet_summary.dart
@@ -12,7 +12,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../models/balance.dart';
-import '../../../../models/isar/models/ethereum/eth_contract.dart';
+import '../../../../models/isar/models/contract.dart';
import '../../../../pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import '../../../../providers/providers.dart';
import '../../../../providers/wallet/public_private_balance_state_provider.dart';
@@ -33,7 +33,6 @@ import '../../../../wallets/isar/providers/eth/token_balance_provider.dart';
import '../../../../wallets/isar/providers/solana/current_sol_token_wallet_provider.dart';
import '../../../../wallets/isar/providers/solana/sol_token_balance_provider.dart';
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
-import '../../../../wallets/wallet/impl/sub_wallets/solana_token_wallet.dart';
import 'desktop_balance_toggle_button.dart';
class DesktopWalletSummary extends ConsumerStatefulWidget {
@@ -83,46 +82,36 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
prefsChangeNotifierProvider.select((value) => value.currency),
);
- // For Ethereum tokens, get the token contract; for Solana tokens, get the token wallet.
- final EthContract? tokenContract;
- final SolanaTokenWallet? solanaTokenWallet;
+ final coin = ref.watch(pWalletCoin(walletId));
+ final Contract? tokenContract;
+
if (widget.isToken) {
- switch (ref.watch(pWalletCoin(walletId))) {
+ switch (coin) {
case Ethereum():
tokenContract = ref.watch(
pCurrentTokenWallet.select((value) => value!.tokenContract),
);
- solanaTokenWallet = null;
break;
case Solana():
- tokenContract = null;
- // this cannot be null if coin is sol and isToken is true.
- // if it is null, then there is a bug somewhere else.
- solanaTokenWallet = ref.watch(pCurrentSolanaTokenWallet);
+ tokenContract = ref.watch(
+ pCurrentSolanaTokenWallet.select((value) => value!.solContract),
+ );
break;
default:
tokenContract = null;
- solanaTokenWallet = null;
}
} else {
tokenContract = null;
- solanaTokenWallet = null;
}
- final price = widget.isToken && tokenContract != null
+ final price = tokenContract != null
? ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getTokenPrice(tokenContract!.address),
),
)
- : widget.isToken && solanaTokenWallet != null
- ? ref.watch(
- priceAnd24hChangeNotifierProvider.select(
- (value) => value.getTokenPrice(solanaTokenWallet!.tokenMint),
- ),
- )
: ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getPrice(coin),
@@ -148,7 +137,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
}
} else {
final Balance balance;
- if (widget.isToken && tokenContract != null) {
+ if (tokenContract != null && coin is Ethereum) {
// Ethereum token balance
balance = ref.watch(
pTokenBalance((
@@ -156,12 +145,12 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
contractAddress: tokenContract.address,
)),
);
- } else if (widget.isToken && solanaTokenWallet != null) {
+ } else if (tokenContract != null && coin is Solana) {
// Watch Solana token balance from db.
balance = ref.watch(
pSolanaTokenBalance((
walletId: walletId,
- tokenMint: solanaTokenWallet.tokenMint,
+ tokenMint: tokenContract.address,
)),
);
} else {
@@ -185,11 +174,7 @@ class _WDesktopWalletSummaryState extends ConsumerState<DesktopWalletSummary> {
child: SelectableText(
ref
.watch(pAmountFormatter(coin))
- .format(
- balanceToShow,
- ethContract: tokenContract,
- solContract: solanaTokenWallet?.solContract,
- ),
+ .format(balanceToShow, tokenContract: tokenContract),
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart
index d1a51ba..e193ab3 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/firo_desktop_wallet_summary.dart
@@ -195,7 +195,7 @@ class _Balance extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SelectableText(
- ref.watch(pAmountFormatter(coin)).format(amount, ethContract: null),
+ ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null),
style: STextStyles.desktopH3(context),
textAlign: TextAlign.end,
);
diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart
index 6ce1d19..6b025f0 100644
--- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart
+++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/mweb_desktop_wallet_summary.dart
@@ -62,14 +62,13 @@ class _WMwebDesktopWalletSummaryState
if (ref.watch(
prefsChangeNotifierProvider.select((value) => value.externalCalls),
)) {
- price =
- ref
- .watch(
- priceAnd24hChangeNotifierProvider.select(
- (value) => value.getPrice(coin),
- ),
- )
- ?.value;
+ price = ref
+ .watch(
+ priceAnd24hChangeNotifierProvider.select(
+ (value) => value.getPrice(coin),
+ ),
+ )
+ ?.value;
}
final _showAvailable =
@@ -77,12 +76,14 @@ class _WMwebDesktopWalletSummaryState
WalletBalanceToggleState.available;
final balance0 = ref.watch(pWalletBalanceSecondary(walletId));
- final balanceToShowSpark =
- _showAvailable ? balance0.spendable : balance0.total;
+ final balanceToShowSpark = _showAvailable
+ ? balance0.spendable
+ : balance0.total;
final balance2 = ref.watch(pWalletBalance(walletId));
- final balanceToShowPublic =
- _showAvailable ? balance2.spendable : balance2.total;
+ final balanceToShowPublic = _showAvailable
+ ? balance2.spendable
+ : balance2.total;
return Consumer(
builder: (context, ref, __) {
@@ -169,7 +170,7 @@ class _Balance extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SelectableText(
- ref.watch(pAmountFormatter(coin)).format(amount, ethContract: null),
+ ref.watch(pAmountFormatter(coin)).format(amount, tokenContract: null),
style: STextStyles.desktopH3(context),
textAlign: TextAlign.end,
);
diff --git a/lib/utilities/amount/amount_formatter.dart b/lib/utilities/amount/amount_formatter.dart
index f5e047f..6a6f01f 100644
--- a/lib/utilities/amount/amount_formatter.dart
+++ b/lib/utilities/amount/amount_formatter.dart
@@ -1,29 +1,27 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
-import '../../models/isar/models/ethereum/eth_contract.dart';
-import '../../models/isar/models/solana/sol_contract.dart';
+
+import '../../models/isar/models/contract.dart';
import '../../providers/global/locale_provider.dart';
import '../../providers/global/prefs_provider.dart';
+import '../../wallets/crypto_currency/crypto_currency.dart';
import 'amount.dart';
import 'amount_unit.dart';
-import '../../wallets/crypto_currency/crypto_currency.dart';
final pAmountUnit = Provider.family<AmountUnit, CryptoCurrency>(
(ref, coin) => ref.watch(
- prefsChangeNotifierProvider.select(
- (value) => value.amountUnit(coin),
- ),
+ prefsChangeNotifierProvider.select((value) => value.amountUnit(coin)),
),
);
final pMaxDecimals = Provider.family<int, CryptoCurrency>(
(ref, coin) => ref.watch(
- prefsChangeNotifierProvider.select(
- (value) => value.maxDecimals(coin),
- ),
+ prefsChangeNotifierProvider.select((value) => value.maxDecimals(coin)),
),
);
-final pAmountFormatter =
- Provider.family<AmountFormatter, CryptoCurrency>((ref, coin) {
+final pAmountFormatter = Provider.family<AmountFormatter, CryptoCurrency>((
+ ref,
+ coin,
+) {
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale),
);
@@ -52,8 +50,7 @@ class AmountFormatter {
String format(
Amount amount, {
String? overrideUnit,
- EthContract? ethContract,
- SolContract? solContract,
+ Contract? tokenContract,
bool withUnitName = true,
bool indicatePrecisionLoss = true,
}) {
@@ -65,20 +62,16 @@ class AmountFormatter {
withUnitName: withUnitName,
indicatePrecisionLoss: indicatePrecisionLoss,
overrideUnit: overrideUnit,
- tokenContract: ethContract,
- splToken: solContract,
+ tokenContract: tokenContract,
);
}
- Amount? tryParse(
- String string, {
- EthContract? ethContract,
- }) {
+ Amount? tryParse(String string, {Contract? tokenContract}) {
return unit.tryParse(
string,
locale: locale,
coin: coin,
- tokenContract: ethContract,
+ tokenContract: tokenContract,
);
}
}
diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart
index 7763a3e..0d96fbd 100644
--- a/lib/utilities/amount/amount_unit.dart
+++ b/lib/utilities/amount/amount_unit.dart
@@ -11,12 +11,14 @@
import 'dart:math' as math;
import 'package:decimal/decimal.dart';
+
+import '../../models/isar/models/contract.dart';
import '../../models/isar/models/ethereum/eth_contract.dart';
import '../../models/isar/models/solana/sol_contract.dart';
-import 'amount.dart';
-import '../util.dart';
import '../../wallets/crypto_currency/crypto_currency.dart';
import '../../wallets/crypto_currency/intermediate/nano_currency.dart';
+import '../util.dart';
+import 'amount.dart';
// preserve index order as index is used to store value in preferences
enum AmountUnit {
@@ -30,8 +32,7 @@ enum AmountUnit {
zepto(21),
yocto(24),
ronto(27),
- quecto(30),
- ;
+ quecto(30);
const AmountUnit(this.shift);
final int shift;
@@ -170,9 +171,7 @@ extension AmountUnitExt on AmountUnit {
case AmountUnit.atto:
return "wei";
default:
- throw ArgumentError(
- "Does eth even allow more than 18 decimal places?",
- );
+ throw ArgumentError("Does eth even allow more than 18 decimal places?");
}
}
@@ -201,7 +200,7 @@ extension AmountUnitExt on AmountUnit {
String value, {
required String locale,
required CryptoCurrency coin,
- EthContract? tokenContract,
+ Contract? tokenContract,
bool overrideWithDecimalPlacesFromString = false,
}) {
final precisionLost = value.startsWith("~");
@@ -252,8 +251,7 @@ extension AmountUnitExt on AmountUnit {
bool withUnitName = true,
bool indicatePrecisionLoss = true,
String? overrideUnit,
- EthContract? tokenContract,
- SolContract? splToken,
+ Contract? tokenContract,
}) {
assert(maxDecimalPlaces >= 0);
@@ -297,10 +295,6 @@ extension AmountUnitExt on AmountUnit {
updatedMax = maxDecimalPlaces > tokenContract.decimals
? tokenContract.decimals
: maxDecimalPlaces;
- } else if (splToken != null) {
- updatedMax = maxDecimalPlaces > splToken.decimals
- ? splToken.decimals
- : maxDecimalPlaces;
} else {
updatedMax = maxDecimalPlaces > coin.fractionDigits
? coin.fractionDigits
@@ -319,8 +313,9 @@ extension AmountUnitExt on AmountUnit {
if (remainder.length > actualDecimalPlaces) {
// check for loss of precision
- final remainingRemainder =
- BigInt.tryParse(remainder.substring(actualDecimalPlaces));
+ final remainingRemainder = BigInt.tryParse(
+ remainder.substring(actualDecimalPlaces),
+ );
if (remainingRemainder != null) {
didLosePrecision = remainingRemainder > BigInt.zero;
}
@@ -354,10 +349,10 @@ extension AmountUnitExt on AmountUnit {
}
// return the value with the proper unit symbol
- if (tokenContract != null) {
+ if (tokenContract is EthContract) {
overrideUnit = unitForContract(tokenContract);
- } else if (splToken != null) {
- overrideUnit = unitForSplToken(splToken);
+ } else if (tokenContract is SolContract) {
+ overrideUnit = unitForSplToken(tokenContract);
}
return "$returnValue ${overrideUnit ?? unitForCoin(coin)}";
diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart
index 211776f..c8d23af 100644
--- a/lib/widgets/transaction_card.dart
+++ b/lib/widgets/transaction_card.dart
@@ -18,7 +18,6 @@ import '../models/isar/models/isar_models.dart';
import '../notifications/show_flush_bar.dart';
import '../pages/wallet_view/sub_widgets/tx_icon.dart';
import '../pages/wallet_view/transaction_views/transaction_details_view.dart';
-import '../providers/db/main_db_provider.dart';
import '../providers/providers.dart';
import '../themes/stack_colors.dart';
import '../utilities/amount/amount.dart';
@@ -27,7 +26,6 @@ import '../utilities/constants.dart';
import '../utilities/format.dart';
import '../utilities/text_styles.dart';
import '../utilities/util.dart';
-import '../wallets/crypto_currency/coins/mimblewimblecoin.dart';
import '../wallets/crypto_currency/crypto_currency.dart';
import 'desktop/desktop_dialog.dart';
@@ -117,10 +115,15 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
@override
void initState() {
walletId = widget.walletId;
- minConfirms =
- ref.read(pWallets).getWallet(walletId).cryptoCurrency.minConfirms;
+ minConfirms = ref
+ .read(pWallets)
+ .getWallet(walletId)
+ .cryptoCurrency
+ .minConfirms;
_transaction = widget.transaction;
- isTokenTx = _transaction.subType == TransactionSubType.ethToken;
+ isTokenTx =
+ _transaction.subType == TransactionSubType.ethToken ||
+ _transaction.subType == TransactionSubType.splToken;
if (Util.isDesktop) {
if (_transaction.type == TransactionType.outgoing) {
prefix = "-";
@@ -152,17 +155,15 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
prefsChangeNotifierProvider.select((value) => value.currency),
);
- final price =
- ref
- .watch(
- priceAnd24hChangeNotifierProvider.select(
- (value) =>
- isTokenTx
- ? value.getTokenPrice(_transaction.otherData!)
- : value.getPrice(coin),
- ),
- )
- ?.value;
+ final price = ref
+ .watch(
+ priceAnd24hChangeNotifierProvider.select(
+ (value) => isTokenTx
+ ? value.getTokenPrice(_transaction.otherData!)
+ : value.getPrice(coin),
+ ),
+ )
+ ?.value;
final currentHeight = ref.watch(
pWallets.select(
@@ -215,16 +216,15 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
if (Util.isDesktop) {
await showDialog<void>(
context: context,
- builder:
- (context) => DesktopDialog(
- maxHeight: MediaQuery.of(context).size.height - 64,
- maxWidth: 580,
- child: TransactionDetailsView(
- transaction: _transaction,
- coin: coin,
- walletId: walletId,
- ),
- ),
+ builder: (context) => DesktopDialog(
+ maxHeight: MediaQuery.of(context).size.height - 64,
+ maxWidth: 580,
+ child: TransactionDetailsView(
+ transaction: _transaction,
+ coin: coin,
+ walletId: walletId,
+ ),
+ ),
);
} else {
unawaited(
@@ -259,13 +259,13 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
child: Text(
_transaction.isCancelled
? coin is Ethereum
- ? "Failed"
- : "Cancelled"
+ ? "Failed"
+ : "Cancelled"
: whatIsIt(
- _transaction.type,
- coin,
- currentHeight,
- ),
+ _transaction.type,
+ coin,
+ currentHeight,
+ ),
style: STextStyles.itemSubtitle12(context),
),
),
@@ -276,10 +276,15 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
fit: BoxFit.scaleDown,
child: Builder(
builder: (_) {
- final amount = _transaction.realAmount;
+ final formattedAmount = ref
+ .watch(pAmountFormatter(coin))
+ .format(
+ _transaction.realAmount,
+ tokenContract: tokenContract,
+ );
return Text(
- "$prefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: tokenContract)}",
+ "$prefix$formattedAmount",
style: STextStyles.itemSubtitle12(context),
);
},
diff --git a/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart b/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart
index 298304c..ef81c10 100644
--- a/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart
+++ b/lib/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart
@@ -13,8 +13,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../db/isar/main_db.dart';
import '../../../models/isar/models/contract.dart';
-import '../../../models/isar/models/ethereum/eth_contract.dart';
-import '../../../models/isar/models/solana/sol_contract.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/amount/amount.dart';
import '../../../utilities/amount/amount_formatter.dart';
@@ -87,11 +85,7 @@ class WalletInfoRowBalance extends ConsumerWidget {
return Text(
ref
.watch(pAmountFormatter(info.coin))
- .format(
- totalBalance,
- ethContract: contract is EthContract ? contract : null,
- solContract: contract is SolContract ? contract : null,
- ),
+ .format(totalBalance, tokenContract: contract),
style: Util.isDesktop
? STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.