Full balance (#1457)

* fix: Confirm widget is still mounted

* feat: Modify balance display to include full balance

* fix: Modifying balance

* chore: Feature cleanup

* fix: Add frozen balance into consideration when taking available balance  and add field to make full balance display only on bitcoin and litecoin wallets

* fix: Adjust balance card to display correct available and unavailable balance, unavailable balance should only be displayed when there is one WIP

* fix: Cleanup balance page and balance page view_model

* chore: Revert formatting

* fix: Remove full balance

* fix: Remove full balance

* fix: Remove full balance

* chore: Rever formating [skip ci]

* feat: Finalize display only available and unavailable balance

* fix: Modify the way balance is displayed, activate frozen balance with label, remove unavailable/additional balance for bitcoin wallet type

* fix: Issues coming from syncing with main

* fix: Modify additional balance label

* fix: Monero and Wownero balances display bug

* fix: Resolve merge conflicts

* feat: Activate CPFP for BTC, LTC and BCH, also fix issues with frozen balance display

* - minor fix
- remove unused functions

* Fix conflicts

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
Co-authored-by: tuxsudo <tuxsudo@tux.pizza>
This commit is contained in:
David Adegoke 2024-12-27 04:54:47 +01:00 committed by GitHub
parent b1c9be637f
commit c9a6abeea4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 1257 additions and 1232 deletions

View file

@ -39,7 +39,7 @@ class ElectrumBalance extends Balance {
int secondUnconfirmed = 0; int secondUnconfirmed = 0;
@override @override
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen); String get formattedAvailableBalance => bitcoinAmountToString(amount: ((confirmed + unconfirmed) - frozen) );
@override @override
String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed); String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed);
@ -58,7 +58,7 @@ class ElectrumBalance extends Balance {
@override @override
String get formattedFullAvailableBalance => String get formattedFullAvailableBalance =>
bitcoinAmountToString(amount: confirmed + secondConfirmed - frozen); bitcoinAmountToString(amount: (confirmed + unconfirmed) + secondConfirmed - frozen);
String toJSON() => json.encode({ String toJSON() => json.encode({
'confirmed': confirmed, 'confirmed': confirmed,

View file

@ -2213,18 +2213,6 @@ abstract class ElectrumWalletBase
var totalConfirmed = 0; var totalConfirmed = 0;
var totalUnconfirmed = 0; var totalUnconfirmed = 0;
unspentCoinsInfo.values.forEach((info) {
unspentCoins.forEach((element) {
if (element.hash == info.hash &&
element.vout == info.vout &&
info.isFrozen &&
element.bitcoinAddressRecord.address == info.address &&
element.value == info.value) {
totalFrozen += element.value;
}
});
});
if (hasSilentPaymentsScanning) { if (hasSilentPaymentsScanning) {
// Add values from unspent coins that are not fetched by the address list // Add values from unspent coins that are not fetched by the address list
// i.e. scanned silent payments // i.e. scanned silent payments
@ -2240,6 +2228,20 @@ abstract class ElectrumWalletBase
}); });
} }
unspentCoinsInfo.values.forEach((info) {
unspentCoins.forEach((element) {
if (element.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) return;
if (element.hash == info.hash &&
element.vout == info.vout &&
info.isFrozen &&
element.bitcoinAddressRecord.address == info.address &&
element.value == info.value) {
totalFrozen += element.value;
}
});
});
final balances = await Future.wait(balanceFutures); final balances = await Future.wait(balanceFutures);
if (balances.isNotEmpty && balances.first['confirmed'] == null) { if (balances.isNotEmpty && balances.first['confirmed'] == null) {

View file

@ -3,36 +3,25 @@ import 'package:cw_core/monero_amount_format.dart';
class MoneroBalance extends Balance { class MoneroBalance extends Balance {
MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0}) MoneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
: formattedFullBalance = moneroAmountToString(amount: frozenBalance + fullBalance), : formattedUnconfirmedBalance = moneroAmountToString(amount: fullBalance - unlockedBalance),
formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance), formattedUnlockedBalance = moneroAmountToString(amount: unlockedBalance - frozenBalance),
formattedLockedBalance = formattedFrozenBalance = moneroAmountToString(amount: frozenBalance),
moneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance),
super(unlockedBalance, fullBalance); super(unlockedBalance, fullBalance);
MoneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance,
this.formattedLockedBalance = '0.0'})
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = moneroParseAmount(amount: formattedLockedBalance),
super(moneroParseAmount(amount: formattedUnlockedBalance),
moneroParseAmount(amount: formattedFullBalance));
final int fullBalance; final int fullBalance;
final int unlockedBalance; final int unlockedBalance;
final int frozenBalance; final int frozenBalance;
final String formattedFullBalance; final String formattedUnconfirmedBalance;
final String formattedUnlockedBalance; final String formattedUnlockedBalance;
final String formattedLockedBalance; final String formattedFrozenBalance;
@override @override
String get formattedUnAvailableBalance => String get formattedUnAvailableBalance =>
formattedLockedBalance == '0.0' ? '' : formattedLockedBalance; formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
@override @override
String get formattedAvailableBalance => formattedUnlockedBalance; String get formattedAvailableBalance => formattedUnlockedBalance;
@override @override
String get formattedAdditionalBalance => formattedFullBalance; String get formattedAdditionalBalance => formattedUnconfirmedBalance;
} }

View file

@ -3,36 +3,26 @@ import 'package:cw_core/wownero_amount_format.dart';
class WowneroBalance extends Balance { class WowneroBalance extends Balance {
WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0}) WowneroBalance({required this.fullBalance, required this.unlockedBalance, this.frozenBalance = 0})
: formattedFullBalance = wowneroAmountToString(amount: fullBalance), : formattedUnconfirmedBalance = wowneroAmountToString(amount: fullBalance - unlockedBalance),
formattedUnlockedBalance = wowneroAmountToString(amount: unlockedBalance - frozenBalance), formattedUnlockedBalance = wowneroAmountToString(amount: unlockedBalance - frozenBalance),
formattedLockedBalance = formattedFrozenBalance =
wowneroAmountToString(amount: frozenBalance + fullBalance - unlockedBalance), wowneroAmountToString(amount: frozenBalance),
super(unlockedBalance, fullBalance); super(unlockedBalance, fullBalance);
WowneroBalance.fromString(
{required this.formattedFullBalance,
required this.formattedUnlockedBalance,
this.formattedLockedBalance = '0.0'})
: fullBalance = wowneroParseAmount(amount: formattedFullBalance),
unlockedBalance = wowneroParseAmount(amount: formattedUnlockedBalance),
frozenBalance = wowneroParseAmount(amount: formattedLockedBalance),
super(wowneroParseAmount(amount: formattedUnlockedBalance),
wowneroParseAmount(amount: formattedFullBalance));
final int fullBalance; final int fullBalance;
final int unlockedBalance; final int unlockedBalance;
final int frozenBalance; final int frozenBalance;
final String formattedFullBalance; final String formattedUnconfirmedBalance;
final String formattedUnlockedBalance; final String formattedUnlockedBalance;
final String formattedLockedBalance; final String formattedFrozenBalance;
@override @override
String get formattedUnAvailableBalance => String get formattedUnAvailableBalance =>
formattedLockedBalance == '0.0' ? '' : formattedLockedBalance; formattedFrozenBalance == '0.0' ? '' : formattedFrozenBalance;
@override @override
String get formattedAvailableBalance => formattedUnlockedBalance; String get formattedAvailableBalance => formattedUnlockedBalance;
@override @override
String get formattedAdditionalBalance => formattedFullBalance; String get formattedAdditionalBalance => formattedUnconfirmedBalance;
} }

View file

@ -11,13 +11,12 @@ class EVMChainERC20Balance extends Balance {
final int exponent; final int exponent;
@override @override
String get formattedAdditionalBalance { String get formattedAdditionalBalance => _balance();
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
return formattedBalance.substring(0, min(12, formattedBalance.length));
}
@override @override
String get formattedAvailableBalance { String get formattedAvailableBalance => _balance();
String _balance() {
final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString(); final String formattedBalance = (balance / BigInt.from(10).pow(exponent)).toString();
return formattedBalance.substring(0, min(12, formattedBalance.length)); return formattedBalance.substring(0, min(12, formattedBalance.length));
} }

View file

@ -751,11 +751,18 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
int _getFrozenBalance() { int _getFrozenBalance() {
var frozenBalance = 0; var frozenBalance = 0;
for (var coin in unspentCoinsInfo.values.where((element) => unspentCoinsInfo.values.forEach((info) {
element.walletId == id && unspentCoins.forEach((element) {
element.accountIndex == walletAddresses.account!.id)) { if (element.hash == info.hash &&
if (coin.isFrozen && !coin.isSending) frozenBalance += coin.value; element.vout == info.vout &&
} info.isFrozen &&
element.value == info.value && info.walletId == id &&
info.accountIndex == walletAddresses.account!.id) {
if (element.isFrozen && !element.isSending) frozenBalance+= element.value;
}
});
});
return frozenBalance; return frozenBalance;
} }

View file

@ -1,6 +1,6 @@
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart'; import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/balance/crypto_balance_widget.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';

View file

@ -79,7 +79,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet
import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';

View file

@ -24,7 +24,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/action_button.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/action_button.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_page.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';

View file

@ -8,7 +8,7 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_page.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/router.dart' as Router; import 'package:cake_wallet/router.dart' as Router;

View file

@ -0,0 +1,88 @@
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance/crypto_balance_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class BalancePage extends StatelessWidget {
BalancePage({
required this.dashboardViewModel,
required this.settingsStore,
required this.nftViewModel,
});
final DashboardViewModel dashboardViewModel;
final NFTViewModel nftViewModel;
final SettingsStore settingsStore;
@override
Widget build(BuildContext context) {
return Observer(
builder: (context) {
final isEVMCompatible = isEVMCompatibleChain(dashboardViewModel.type);
return DefaultTabController(
length: isEVMCompatible ? 2 : 1,
child: Column(
children: [
if (isEVMCompatible)
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: TabBar(
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
physics: NeverScrollableScrollPhysics(),
labelStyle: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
height: 1,
),
unselectedLabelStyle: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
height: 1,
),
labelColor:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
dividerColor: Colors.transparent,
indicatorColor:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
unselectedLabelColor: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor
.withOpacity(0.5),
tabAlignment: TabAlignment.start,
tabs: [
Tab(text: 'My Crypto'),
Tab(text: 'My NFTs'),
],
),
),
),
Expanded(
child: TabBarView(
physics: NeverScrollableScrollPhysics(),
children: [
CryptoBalanceWidget(dashboardViewModel: dashboardViewModel),
if (isEVMCompatible) NFTListingPage(nftViewModel: nftViewModel)
],
),
),
],
),
);
},
);
}
}

View file

@ -0,0 +1,654 @@
import 'dart:math';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/unspent_coin_type.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class BalanceRowWidget extends StatelessWidget {
BalanceRowWidget({
required this.availableBalanceLabel,
required this.availableBalance,
required this.availableFiatBalance,
required this.additionalBalanceLabel,
required this.additionalBalance,
required this.additionalFiatBalance,
required this.secondAvailableBalanceLabel,
required this.secondAvailableBalance,
required this.secondAvailableFiatBalance,
required this.secondAdditionalBalanceLabel,
required this.secondAdditionalBalance,
required this.secondAdditionalFiatBalance,
required this.frozenBalance,
required this.frozenFiatBalance,
required this.currency,
required this.hasAdditionalBalance,
required this.hasSecondAvailableBalance,
required this.hasSecondAdditionalBalance,
required this.isTestnet,
required this.dashboardViewModel,
super.key,
});
final String availableBalanceLabel;
final String availableBalance;
final String availableFiatBalance;
final String additionalBalanceLabel;
final String additionalBalance;
final String additionalFiatBalance;
final String secondAvailableBalanceLabel;
final String secondAvailableBalance;
final String secondAvailableFiatBalance;
final String secondAdditionalBalanceLabel;
final String secondAdditionalBalance;
final String secondAdditionalFiatBalance;
final String frozenBalance;
final String frozenFiatBalance;
final CryptoCurrency currency;
final bool hasAdditionalBalance;
final bool hasSecondAvailableBalance;
final bool hasSecondAdditionalBalance;
final bool isTestnet;
final DashboardViewModel dashboardViewModel;
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
border: Border.all(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
width: 1,
),
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
),
child: Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 8, bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => dashboardViewModel.balanceViewModel.switchBalanceValue(),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: hasAdditionalBalance
? () => _showBalanceDescription(
context, S.of(context).available_balance_description)
: null,
child: Row(
children: [
Semantics(
hint: 'Double tap to see more information',
container: true,
child: Text('${availableBalanceLabel}',
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor,
height: 1)),
),
if (hasAdditionalBalance)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
),
],
),
),
SizedBox(height: 6),
AutoSizeText(availableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.balanceAmountColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.start),
SizedBox(height: 6),
if (isTestnet)
Text(S.of(context).testnet_coins_no_value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1)),
if (!isTestnet)
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1)),
],
),
SizedBox(
width: min(MediaQuery.of(context).size.width * 0.2, 100),
child: Center(
child: Column(
children: [
CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
displayOnError: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title.substring(0, min(currency.title.length, 2)),
style: TextStyle(fontSize: 11),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey.shade400,
),
),
),
const SizedBox(height: 10),
Text(
currency.title,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.assetTitleColor,
height: 1,
),
),
],
),
),
),
],
),
),
if (frozenBalance.isNotEmpty)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: hasAdditionalBalance
? () => _showBalanceDescription(
context, S.of(context).unavailable_balance_description)
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 26),
Row(
children: [
Text(
S.of(context).unavailable_balance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
height: 1,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
),
],
),
SizedBox(height: 8),
AutoSizeText(
frozenBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).extension<BalancePageTheme>()!.balanceAmountColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
SizedBox(height: 4),
if (!isTestnet)
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
),
),
],
),
),
if (hasAdditionalBalance)
GestureDetector(
onTap: () => dashboardViewModel.balanceViewModel.switchBalanceValue(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24),
Text(
'${additionalBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
height: 1,
),
),
SizedBox(height: 8),
AutoSizeText(
additionalBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.assetTitleColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
SizedBox(height: 4),
if (!isTestnet)
Text(
'${additionalFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
),
),
],
),
),
],
),
),
),
if (hasSecondAdditionalBalance || hasSecondAvailableBalance) ...[
SizedBox(height: 10),
Container(
margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
border: Border.all(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
width: 1,
),
color: Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor,
),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 8, bottom: 16),
child: Stack(
children: [
if (currency == CryptoCurrency.ltc)
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
padding: EdgeInsets.only(right: 16, top: 0),
child: Column(
children: [
Container(
child: ImageIcon(
AssetImage('assets/images/mweb_logo.png'),
color: Theme.of(context)
.extension<BalancePageTheme>()!
.assetTitleColor,
size: 40,
),
),
const SizedBox(height: 10),
Text(
'MWEB',
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.assetTitleColor,
height: 1,
),
),
],
),
),
],
),
if (hasSecondAvailableBalance)
GestureDetector(
onTap: () => dashboardViewModel.balanceViewModel.switchBalanceValue(),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => launchUrl(
Uri.parse(
"https://docs.cakewallet.com/cryptos/litecoin.html#mweb"),
mode: LaunchMode.externalApplication,
),
child: Row(
children: [
Text(
'${secondAvailableBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor,
height: 1,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
)
],
),
),
SizedBox(height: 8),
AutoSizeText(
secondAvailableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.assetTitleColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
SizedBox(height: 6),
if (!isTestnet)
Text(
'${secondAvailableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.textColor,
height: 1,
),
),
],
),
],
),
),
],
),
),
Container(
margin: const EdgeInsets.only(top: 0, left: 24, right: 8, bottom: 16),
child: Stack(
children: [
if (hasSecondAdditionalBalance)
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24),
Text(
'${secondAdditionalBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor,
height: 1,
),
),
SizedBox(height: 8),
AutoSizeText(
secondAdditionalBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.assetTitleColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
SizedBox(height: 4),
if (!isTestnet)
Text(
'${secondAdditionalFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.textColor,
height: 1,
),
),
],
),
],
),
],
),
),
IntrinsicHeight(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Semantics(
label: S.of(context).litecoin_mweb_pegin,
child: OutlinedButton(
onPressed: () {
final mwebAddress =
bitcoin!.getUnusedMwebAddress(dashboardViewModel.wallet);
PaymentRequest? paymentRequest = null;
if ((mwebAddress?.isNotEmpty ?? false)) {
paymentRequest = PaymentRequest.fromUri(
Uri.parse("litecoin:${mwebAddress}"));
}
Navigator.pushNamed(
context,
Routes.send,
arguments: {
'paymentRequest': paymentRequest,
'coinTypeToSpendFrom': UnspentCoinType.nonMweb,
},
);
},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.grey.shade400.withAlpha(50),
side: BorderSide(
color: Colors.grey.shade400.withAlpha(50), width: 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
height: 30,
width: 30,
'assets/images/received.png',
color: Theme.of(context)
.extension<BalancePageTheme>()!
.balanceAmountColor,
),
const SizedBox(width: 8),
Text(
S.of(context).litecoin_mweb_pegin,
style: TextStyle(
color: Theme.of(context)
.extension<BalancePageTheme>()!
.textColor,
),
),
],
),
),
),
),
),
SizedBox(width: 24),
Expanded(
child: Semantics(
label: S.of(context).litecoin_mweb_pegout,
child: OutlinedButton(
onPressed: () {
final litecoinAddress =
bitcoin!.getUnusedSegwitAddress(dashboardViewModel.wallet);
PaymentRequest? paymentRequest = null;
if ((litecoinAddress?.isNotEmpty ?? false)) {
paymentRequest = PaymentRequest.fromUri(
Uri.parse("litecoin:${litecoinAddress}"));
}
Navigator.pushNamed(
context,
Routes.send,
arguments: {
'paymentRequest': paymentRequest,
'coinTypeToSpendFrom': UnspentCoinType.mweb,
},
);
},
style: OutlinedButton.styleFrom(
backgroundColor: Colors.grey.shade400.withAlpha(50),
side: BorderSide(
color: Colors.grey.shade400.withAlpha(50), width: 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Container(
padding: EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
height: 30,
width: 30,
'assets/images/upload.png',
color: Theme.of(context)
.extension<BalancePageTheme>()!
.balanceAmountColor,
),
const SizedBox(width: 8),
Text(
S.of(context).litecoin_mweb_pegout,
style: TextStyle(
color: Theme.of(context)
.extension<BalancePageTheme>()!
.textColor,
),
),
],
),
),
),
),
),
],
),
),
),
SizedBox(height: 16),
],
),
),
),
],
],
);
}
void _showBalanceDescription(BuildContext context, String content) {
showPopUp<void>(context: context, builder: (_) => InformationPage(information: content));
}
}

View file

@ -0,0 +1,424 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/dashboard/pages/balance/balance_row_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/src/widgets/introducing_card.dart';
import 'package:cake_wallet/src/widgets/standard_switch.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:url_launcher/url_launcher.dart';
class CryptoBalanceWidget extends StatelessWidget {
const CryptoBalanceWidget({
super.key,
required this.dashboardViewModel,
});
final DashboardViewModel dashboardViewModel;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Observer(
builder: (_) {
if (dashboardViewModel.getMoneroError != null) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: DashBoardRoundedCardWidget(
title: "Invalid monero bindings",
subTitle: dashboardViewModel.getMoneroError.toString(),
onTap: () {},
),
);
}
return Container();
},
),
Observer(
builder: (_) {
if (dashboardViewModel.getWowneroError != null) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: DashBoardRoundedCardWidget(
title: "Invalid wownero bindings",
subTitle: dashboardViewModel.getWowneroError.toString(),
onTap: () {},
));
}
return Container();
},
),
Observer(
builder: (_) => dashboardViewModel.balanceViewModel.hasAccounts
? HomeScreenAccountWidget(
walletName: dashboardViewModel.name, accountName: dashboardViewModel.subname)
: Column(
children: [
SizedBox(height: 16),
Container(
margin: const EdgeInsets.only(left: 24, bottom: 16),
child: Observer(
builder: (_) {
return Row(
children: [
Text(
dashboardViewModel.balanceViewModel.asset,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.center,
),
if (dashboardViewModel.wallet.isHardwareWallet)
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
'assets/images/hardware_wallet/ledger_nano_x.png',
width: 24,
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor,
),
),
if (dashboardViewModel
.balanceViewModel.isHomeScreenSettingsEnabled)
InkWell(
onTap: () => Navigator.pushNamed(context, Routes.homeSettings,
arguments: dashboardViewModel.balanceViewModel),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
'assets/images/home_screen_settings_icon.png',
color: Theme.of(context)
.extension<DashboardPageTheme>()!
.pageTitleTextColor,
),
),
),
],
);
},
),
),
],
)),
Observer(
builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard && FeatureFlag.isCakePayEnabled) {
return IntroducingCard(
title: S.of(context).introducing_cake_pay,
subTitle: S.of(context).cake_pay_learn_more,
borderColor: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,
closeCard: dashboardViewModel.balanceViewModel.disableIntroCakePayCard);
}
return Container();
},
),
Observer(builder: (_) {
if (!dashboardViewModel.showRepWarning) {
return const SizedBox();
}
return Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
title: S.of(context).rep_warning,
subTitle: S.of(context).rep_warning_sub,
onTap: () => Navigator.of(context).pushNamed(Routes.changeRep),
onClose: () {
dashboardViewModel.settingsStore.shouldShowRepWarning = false;
},
),
);
}),
Observer(
builder: (_) {
return ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (_, __) => Container(padding: EdgeInsets.only(bottom: 8)),
itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length,
itemBuilder: (__, index) {
final balance =
dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
return Observer(builder: (_) {
return BalanceRowWidget(
dashboardViewModel: dashboardViewModel,
availableBalanceLabel:
'${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
availableBalance: balance.availableBalance,
availableFiatBalance: balance.fiatAvailableBalance,
additionalBalanceLabel:
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
additionalBalance: balance.additionalBalance,
additionalFiatBalance: balance.fiatAdditionalBalance,
frozenBalance: balance.frozenBalance,
frozenFiatBalance: balance.fiatFrozenBalance,
currency: balance.asset,
hasAdditionalBalance:
dashboardViewModel.balanceViewModel.hasAdditionalBalance,
hasSecondAdditionalBalance:
dashboardViewModel.balanceViewModel.hasSecondAdditionalBalance,
hasSecondAvailableBalance:
dashboardViewModel.balanceViewModel.hasSecondAvailableBalance,
secondAdditionalBalance: balance.secondAdditionalBalance,
secondAdditionalFiatBalance: balance.fiatSecondAdditionalBalance,
secondAvailableBalance: balance.secondAvailableBalance,
secondAvailableFiatBalance: balance.fiatSecondAvailableBalance,
secondAdditionalBalanceLabel:
'${dashboardViewModel.balanceViewModel.secondAdditionalBalanceLabel}',
secondAvailableBalanceLabel:
'${dashboardViewModel.balanceViewModel.secondAvailableBalanceLabel}',
isTestnet: dashboardViewModel.isTestnet,
);
});
},
);
},
),
Observer(builder: (context) {
return Column(
children: [
if (dashboardViewModel.isMoneroWalletBrokenReasons.isNotEmpty) ...[
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
customBorder: 30,
title: "This wallet has encountered an issue",
subTitle: "Here are the things that you should note:\n - " +
dashboardViewModel.isMoneroWalletBrokenReasons.join("\n - ") +
"\n\nPlease restart your wallet and if it doesn't help contact our support.",
onTap: () {},
))
],
if (dashboardViewModel.showSilentPaymentsCard) ...[
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
customBorder: 30,
title: S.of(context).silent_payments,
subTitle: S.of(context).enable_silent_payments_scanning,
hint: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => launchUrl(
Uri.parse(
"https://docs.cakewallet.com/cryptos/bitcoin#silent-payments"),
mode: LaunchMode.externalApplication,
),
child: Row(
children: [
Text(
S.of(context).what_is_silent_payments,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor,
height: 1,
),
softWrap: true,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.extension<BalancePageTheme>()!
.labelTextColor),
)
],
),
),
Observer(
builder: (_) => StandardSwitch(
value: dashboardViewModel.silentPaymentsScanningActive,
onTaped: () => _toggleSilentPaymentsScanning(context),
),
)
],
),
],
),
onTap: () => _toggleSilentPaymentsScanning(context),
icon: Icon(
Icons.lock,
color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
size: 50,
),
),
),
],
if (dashboardViewModel.showMwebCard) ...[
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget(
customBorder: 30,
title: S.of(context).litecoin_mweb,
subTitle: S.of(context).litecoin_mweb_description,
hint: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => launchUrl(
Uri.parse("https://docs.cakewallet.com/cryptos/litecoin/#mweb"),
mode: LaunchMode.externalApplication,
),
child: Text(
S.of(context).learn_more,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color:
Theme.of(context).extension<BalancePageTheme>()!.labelTextColor,
height: 1,
),
softWrap: true,
),
),
SizedBox(height: 8),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => _dismissMweb(context),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
),
child: Text(
S.of(context).litecoin_mweb_dismiss,
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: () => _enableMweb(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
),
child: Text(
S.of(context).enable,
maxLines: 1,
),
),
),
],
),
],
),
onTap: () => {},
icon: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: ImageIcon(
AssetImage('assets/images/mweb_logo.png'),
color: Color.fromARGB(255, 11, 70, 129),
size: 40,
),
),
),
),
],
],
);
}),
],
),
);
}
Future<void> _toggleSilentPaymentsScanning(BuildContext context) async {
final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive;
final newValue = !isSilentPaymentsScanningActive;
dashboardViewModel.silentPaymentsScanningActive = newValue;
final needsToSwitch = !isSilentPaymentsScanningActive &&
await bitcoin!.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) == false;
if (needsToSwitch) {
return showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: S.of(context).confirm_silent_payments_switch_node,
rightButtonText: S.of(context).confirm,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
dashboardViewModel.setSilentPaymentsScanning(newValue);
Navigator.of(context).pop();
},
actionLeftButton: () {
dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive;
Navigator.of(context).pop();
},
));
}
return dashboardViewModel.setSilentPaymentsScanning(newValue);
}
Future<void> _enableMweb(BuildContext context) async {
if (!dashboardViewModel.hasEnabledMwebBefore) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithOneAction(
alertTitle: S.of(context).alert_notice,
alertContent: S.of(context).litecoin_mweb_warning,
buttonText: S.of(context).understand,
buttonAction: () {
Navigator.of(context).pop();
},
));
}
dashboardViewModel.setMwebEnabled();
}
Future<void> _dismissMweb(BuildContext context) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithOneAction(
alertTitle: S.of(context).alert_notice,
alertContent: S.of(context).litecoin_mweb_enable_later,
buttonText: S.of(context).understand,
buttonAction: () {
Navigator.of(context).pop();
},
));
dashboardViewModel.dismissMweb();
}
}

File diff suppressed because it is too large Load diff

View file

@ -158,17 +158,17 @@ abstract class BalanceViewModelBase with Store {
case WalletType.banano: case WalletType.banano:
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.bitcoinCash:
case WalletType.none:
return S.current.xmr_available_balance; return S.current.xmr_available_balance;
default:
return S.current.confirmed;
} }
} }
@computed @computed
String get additionalBalanceLabel { String get additionalBalanceLabel {
switch (wallet.type) { switch (wallet.type) {
case WalletType.monero:
case WalletType.wownero:
case WalletType.haven: case WalletType.haven:
case WalletType.ethereum: case WalletType.ethereum:
case WalletType.polygon: case WalletType.polygon:
@ -357,7 +357,12 @@ abstract class BalanceViewModelBase with Store {
bool mwebEnabled = false; bool mwebEnabled = false;
@computed @computed
bool get hasAdditionalBalance => _hasAdditionalBalanceForWalletType(wallet.type); bool get hasAdditionalBalance {
bool isWalletTypeActivated = _hasAdditionalBalanceForWalletType(wallet.type);
bool isNotZeroAmount = additionalBalance != "0.0";
return isWalletTypeActivated && isNotZeroAmount;
}
@computed @computed
bool get hasSecondAdditionalBalance => bool get hasSecondAdditionalBalance =>
@ -373,6 +378,9 @@ abstract class BalanceViewModelBase with Store {
case WalletType.polygon: case WalletType.polygon:
case WalletType.solana: case WalletType.solana:
case WalletType.tron: case WalletType.tron:
case WalletType.bitcoin:
case WalletType.bitcoinCash:
case WalletType.litecoin:
return false; return false;
default: default:
return true; return true;

View file

@ -330,6 +330,7 @@
"freeze": "تجميد", "freeze": "تجميد",
"frequently_asked_questions": "الأسئلة الشائعة", "frequently_asked_questions": "الأسئلة الشائعة",
"frozen": "مجمدة", "frozen": "مجمدة",
"frozen_balance": "التوازن المجمد",
"full_balance": "الرصيد الكامل", "full_balance": "الرصيد الكامل",
"gas_exceeds_allowance": "الغاز المطلوب بالمعاملة يتجاوز البدل.", "gas_exceeds_allowance": "الغاز المطلوب بالمعاملة يتجاوز البدل.",
"generate_name": "توليد الاسم", "generate_name": "توليد الاسم",

View file

@ -330,6 +330,7 @@
"freeze": "Замразяване", "freeze": "Замразяване",
"frequently_asked_questions": "Често задавани въпроси", "frequently_asked_questions": "Често задавани въпроси",
"frozen": "Замразени", "frozen": "Замразени",
"frozen_balance": "Замразен баланс",
"full_balance": "Пълен баланс", "full_balance": "Пълен баланс",
"gas_exceeds_allowance": "Газът, изискван от транзакцията, надвишава надбавката.", "gas_exceeds_allowance": "Газът, изискван от транзакцията, надвишава надбавката.",
"generate_name": "Генериране на име", "generate_name": "Генериране на име",

View file

@ -330,6 +330,7 @@
"freeze": "Zmrazit", "freeze": "Zmrazit",
"frequently_asked_questions": "Často kladené otázky", "frequently_asked_questions": "Často kladené otázky",
"frozen": "Zmraženo", "frozen": "Zmraženo",
"frozen_balance": "Zmrazená rovnováha",
"full_balance": "Celkový zůstatek", "full_balance": "Celkový zůstatek",
"gas_exceeds_allowance": "Plyn vyžadovaný transakcí přesahuje příspěvek.", "gas_exceeds_allowance": "Plyn vyžadovaný transakcí přesahuje příspěvek.",
"generate_name": "Generovat jméno", "generate_name": "Generovat jméno",

View file

@ -330,6 +330,7 @@
"freeze": "Einfrieren", "freeze": "Einfrieren",
"frequently_asked_questions": "Häufig gestellte Fragen", "frequently_asked_questions": "Häufig gestellte Fragen",
"frozen": "Gefroren", "frozen": "Gefroren",
"frozen_balance": "Gefrorenes Gleichgewicht",
"full_balance": "Gesamtguthaben", "full_balance": "Gesamtguthaben",
"gas_exceeds_allowance": "Die durch Transaktion erforderliche Gas übertrifft die Zulage.", "gas_exceeds_allowance": "Die durch Transaktion erforderliche Gas übertrifft die Zulage.",
"generate_name": "Namen generieren", "generate_name": "Namen generieren",

View file

@ -330,6 +330,7 @@
"freeze": "Freeze", "freeze": "Freeze",
"frequently_asked_questions": "Frequently asked questions", "frequently_asked_questions": "Frequently asked questions",
"frozen": "Frozen", "frozen": "Frozen",
"frozen_balance": "Frozen Balance",
"full_balance": "Full Balance", "full_balance": "Full Balance",
"gas_exceeds_allowance": "Gas required by transaction exceeds allowance.", "gas_exceeds_allowance": "Gas required by transaction exceeds allowance.",
"generate_name": "Generate Name", "generate_name": "Generate Name",

View file

@ -330,6 +330,7 @@
"freeze": "Congelar", "freeze": "Congelar",
"frequently_asked_questions": "Preguntas frecuentes", "frequently_asked_questions": "Preguntas frecuentes",
"frozen": "Congelada", "frozen": "Congelada",
"frozen_balance": "Equilibrio congelado",
"full_balance": "Balance completo", "full_balance": "Balance completo",
"gas_exceeds_allowance": "El gas requerido por la transacción excede la asignación.", "gas_exceeds_allowance": "El gas requerido por la transacción excede la asignación.",
"generate_name": "Generar nombre", "generate_name": "Generar nombre",

View file

@ -330,6 +330,7 @@
"freeze": "Geler", "freeze": "Geler",
"frequently_asked_questions": "Foire aux questions", "frequently_asked_questions": "Foire aux questions",
"frozen": "Gelées", "frozen": "Gelées",
"frozen_balance": "Équilibre gelé",
"full_balance": "Solde Complet", "full_balance": "Solde Complet",
"gas_exceeds_allowance": "Le gaz requis par la transaction dépasse l'allocation.", "gas_exceeds_allowance": "Le gaz requis par la transaction dépasse l'allocation.",
"generate_name": "Générer un nom", "generate_name": "Générer un nom",

View file

@ -330,6 +330,7 @@
"freeze": "Daskare", "freeze": "Daskare",
"frequently_asked_questions": "Tambayoyin da ake yawan yi", "frequently_asked_questions": "Tambayoyin da ake yawan yi",
"frozen": "Daskararre", "frozen": "Daskararre",
"frozen_balance": "Daidaituwa mai sanyi",
"full_balance": "DUKAN KUDI", "full_balance": "DUKAN KUDI",
"gas_exceeds_allowance": "Gas da ake buƙata ta hanyar ma'amala ya wuce izini.", "gas_exceeds_allowance": "Gas da ake buƙata ta hanyar ma'amala ya wuce izini.",
"generate_name": "Ƙirƙirar Suna", "generate_name": "Ƙirƙirar Suna",

View file

@ -330,6 +330,7 @@
"freeze": "फ्रीज", "freeze": "फ्रीज",
"frequently_asked_questions": "अक्सर पूछे जाने वाले प्रश्न", "frequently_asked_questions": "अक्सर पूछे जाने वाले प्रश्न",
"frozen": "जमा हुआ", "frozen": "जमा हुआ",
"frozen_balance": "जमे हुए संतुलन",
"full_balance": "पूर्ण संतुलन", "full_balance": "पूर्ण संतुलन",
"gas_exceeds_allowance": "लेनदेन द्वारा आवश्यक गैस भत्ता से अधिक है।", "gas_exceeds_allowance": "लेनदेन द्वारा आवश्यक गैस भत्ता से अधिक है।",
"generate_name": "नाम जनरेट करें", "generate_name": "नाम जनरेट करें",

View file

@ -330,6 +330,7 @@
"freeze": "Zamrznuti", "freeze": "Zamrznuti",
"frequently_asked_questions": "Često postavljana pitanja", "frequently_asked_questions": "Često postavljana pitanja",
"frozen": "Smrznuto", "frozen": "Smrznuto",
"frozen_balance": "Smrznuta ravnoteža",
"full_balance": "Pun iznos", "full_balance": "Pun iznos",
"gas_exceeds_allowance": "Plin potreban transakcijom premašuje dodatak.", "gas_exceeds_allowance": "Plin potreban transakcijom premašuje dodatak.",
"generate_name": "Generiraj ime", "generate_name": "Generiraj ime",

View file

@ -330,6 +330,7 @@
"freeze": "Կասեցնել", "freeze": "Կասեցնել",
"frequently_asked_questions": "Հաճախ տրվող հարցեր", "frequently_asked_questions": "Հաճախ տրվող հարցեր",
"frozen": "Կասեցված", "frozen": "Կասեցված",
"frozen_balance": "Սառեցված հավասարակշռություն",
"full_balance": "Լրիվ մնացորդ", "full_balance": "Լրիվ մնացորդ",
"gas_exceeds_allowance": "Գործարքով պահանջվող գազը գերազանցում է նպաստը:", "gas_exceeds_allowance": "Գործարքով պահանջվող գազը գերազանցում է նպաստը:",
"generate_name": "Գեներացնել անուն", "generate_name": "Գեներացնել անուն",

View file

@ -330,6 +330,7 @@
"freeze": "Freeze", "freeze": "Freeze",
"frequently_asked_questions": "Pertanyaan yang sering diajukan", "frequently_asked_questions": "Pertanyaan yang sering diajukan",
"frozen": "Dibekukan", "frozen": "Dibekukan",
"frozen_balance": "Keseimbangan beku",
"full_balance": "Saldo Penuh", "full_balance": "Saldo Penuh",
"gas_exceeds_allowance": "Gas yang dibutuhkan oleh transaksi melebihi tunjangan.", "gas_exceeds_allowance": "Gas yang dibutuhkan oleh transaksi melebihi tunjangan.",
"generate_name": "Hasilkan Nama", "generate_name": "Hasilkan Nama",

View file

@ -331,6 +331,7 @@
"freeze": "Congelare", "freeze": "Congelare",
"frequently_asked_questions": "Domande frequenti", "frequently_asked_questions": "Domande frequenti",
"frozen": "Congelato", "frozen": "Congelato",
"frozen_balance": "Equilibrio congelato",
"full_balance": "Saldo Completo", "full_balance": "Saldo Completo",
"gas_exceeds_allowance": "Il gas richiesto dalla transazione supera l'indennità.", "gas_exceeds_allowance": "Il gas richiesto dalla transazione supera l'indennità.",
"generate_name": "Genera nome", "generate_name": "Genera nome",

View file

@ -330,6 +330,7 @@
"freeze": "氷結", "freeze": "氷結",
"frequently_asked_questions": "よくある質問", "frequently_asked_questions": "よくある質問",
"frozen": "凍った", "frozen": "凍った",
"frozen_balance": "凍結バランス",
"full_balance": "フルバランス", "full_balance": "フルバランス",
"gas_exceeds_allowance": "取引に必要なガスは、手当を超えています。", "gas_exceeds_allowance": "取引に必要なガスは、手当を超えています。",
"generate_name": "名前の生成", "generate_name": "名前の生成",

View file

@ -330,6 +330,7 @@
"freeze": "얼다", "freeze": "얼다",
"frequently_asked_questions": "자주 묻는 질문", "frequently_asked_questions": "자주 묻는 질문",
"frozen": "겨울 왕국", "frozen": "겨울 왕국",
"frozen_balance": "냉동 균형",
"full_balance": "풀 밸런스", "full_balance": "풀 밸런스",
"gas_exceeds_allowance": "거래에 필요한 가스는 수당을 초과합니다.", "gas_exceeds_allowance": "거래에 필요한 가스는 수당을 초과합니다.",
"generate_name": "이름 생성", "generate_name": "이름 생성",

View file

@ -330,6 +330,7 @@
"freeze": "အေးခဲ", "freeze": "အေးခဲ",
"frequently_asked_questions": "မေးလေ့ရှိသောမေးခွန်းများ", "frequently_asked_questions": "မေးလေ့ရှိသောမေးခွန်းများ",
"frozen": "ဖြူဖြူ", "frozen": "ဖြူဖြူ",
"frozen_balance": "လက်ကျန်ငွေ",
"full_balance": "Balance အပြည့်", "full_balance": "Balance အပြည့်",
"gas_exceeds_allowance": "ငွေပေးငွေယူမှလိုအပ်သောဓာတ်ငွေ့ထောက်ပံ့ကြေးကျော်လွန်။", "gas_exceeds_allowance": "ငွေပေးငွေယူမှလိုအပ်သောဓာတ်ငွေ့ထောက်ပံ့ကြေးကျော်လွန်။",
"generate_name": "အမည်ဖန်တီးပါ။", "generate_name": "အမည်ဖန်တီးပါ။",

View file

@ -330,6 +330,7 @@
"freeze": "Bevriezen", "freeze": "Bevriezen",
"frequently_asked_questions": "Veelgestelde vragen", "frequently_asked_questions": "Veelgestelde vragen",
"frozen": "Bevroren", "frozen": "Bevroren",
"frozen_balance": "Bevroren balans",
"full_balance": "Volledig saldo", "full_balance": "Volledig saldo",
"gas_exceeds_allowance": "Gas vereist door transactie overschrijdt de vergoeding.", "gas_exceeds_allowance": "Gas vereist door transactie overschrijdt de vergoeding.",
"generate_name": "Naam genereren", "generate_name": "Naam genereren",

View file

@ -330,6 +330,7 @@
"freeze": "Zamróź", "freeze": "Zamróź",
"frequently_asked_questions": "Często zadawane pytania", "frequently_asked_questions": "Często zadawane pytania",
"frozen": "Zamrożone", "frozen": "Zamrożone",
"frozen_balance": "Mrożona równowaga",
"full_balance": "Pełne saldo", "full_balance": "Pełne saldo",
"gas_exceeds_allowance": "Gaz wymagany przez transakcję przekracza dodatek.", "gas_exceeds_allowance": "Gaz wymagany przez transakcję przekracza dodatek.",
"generate_name": "Wygeneruj nazwę", "generate_name": "Wygeneruj nazwę",

View file

@ -330,6 +330,7 @@
"freeze": "Congelar", "freeze": "Congelar",
"frequently_asked_questions": "Perguntas frequentes", "frequently_asked_questions": "Perguntas frequentes",
"frozen": "Congeladas", "frozen": "Congeladas",
"frozen_balance": "Equilíbrio congelado",
"full_balance": "Saldo total", "full_balance": "Saldo total",
"gas_exceeds_allowance": "O gás exigido pela transação excede o subsídio.", "gas_exceeds_allowance": "O gás exigido pela transação excede o subsídio.",
"generate_name": "Gerar nome", "generate_name": "Gerar nome",

View file

@ -330,6 +330,7 @@
"freeze": "Заморозить", "freeze": "Заморозить",
"frequently_asked_questions": "Часто задаваемые вопросы", "frequently_asked_questions": "Часто задаваемые вопросы",
"frozen": "Заморожено", "frozen": "Заморожено",
"frozen_balance": "Замороженный баланс",
"full_balance": "Весь баланс", "full_balance": "Весь баланс",
"gas_exceeds_allowance": "Газ, требуемый в результате транзакции, превышает пособие.", "gas_exceeds_allowance": "Газ, требуемый в результате транзакции, превышает пособие.",
"generate_name": "Создать имя", "generate_name": "Создать имя",

View file

@ -330,6 +330,7 @@
"freeze": "ดักจับ", "freeze": "ดักจับ",
"frequently_asked_questions": "คำถามที่พบบ่อย", "frequently_asked_questions": "คำถามที่พบบ่อย",
"frozen": "ถูกดักจับ", "frozen": "ถูกดักจับ",
"frozen_balance": "สมดุลแช่แข็ง",
"full_balance": "ยอดคงเหลือทั้งหมด", "full_balance": "ยอดคงเหลือทั้งหมด",
"gas_exceeds_allowance": "ก๊าซที่ต้องการโดยการทำธุรกรรมเกินค่าเผื่อ", "gas_exceeds_allowance": "ก๊าซที่ต้องการโดยการทำธุรกรรมเกินค่าเผื่อ",
"generate_name": "สร้างชื่อ", "generate_name": "สร้างชื่อ",

View file

@ -330,6 +330,7 @@
"freeze": "I-freeze", "freeze": "I-freeze",
"frequently_asked_questions": "Mga madalas itanong", "frequently_asked_questions": "Mga madalas itanong",
"frozen": "Frozen", "frozen": "Frozen",
"frozen_balance": "Frozen na balanse",
"full_balance": "Buong Balanse", "full_balance": "Buong Balanse",
"gas_exceeds_allowance": "Ang gas na kinakailangan ng transaksyon ay lumampas sa allowance.", "gas_exceeds_allowance": "Ang gas na kinakailangan ng transaksyon ay lumampas sa allowance.",
"generate_name": "Bumuo ng pangalan", "generate_name": "Bumuo ng pangalan",

View file

@ -330,6 +330,7 @@
"freeze": "Dondur", "freeze": "Dondur",
"frequently_asked_questions": "Sıkça sorulan sorular", "frequently_asked_questions": "Sıkça sorulan sorular",
"frozen": "Dondurulmuş", "frozen": "Dondurulmuş",
"frozen_balance": "Dondurulmuş denge",
"full_balance": "Tüm bakiye", "full_balance": "Tüm bakiye",
"gas_exceeds_allowance": "İşlemin gerektirdiği gaz ödeneği aşar.", "gas_exceeds_allowance": "İşlemin gerektirdiği gaz ödeneği aşar.",
"generate_name": "İsim Oluştur", "generate_name": "İsim Oluştur",

View file

@ -330,6 +330,7 @@
"freeze": "Заморозити", "freeze": "Заморозити",
"frequently_asked_questions": "Часті запитання", "frequently_asked_questions": "Часті запитання",
"frozen": "Заморожено", "frozen": "Заморожено",
"frozen_balance": "Заморожений баланс",
"full_balance": "Весь баланс", "full_balance": "Весь баланс",
"gas_exceeds_allowance": "Газ, необхідний транзакціям, перевищує надбавку.", "gas_exceeds_allowance": "Газ, необхідний транзакціям, перевищує надбавку.",
"generate_name": "Згенерувати назву", "generate_name": "Згенерувати назву",

View file

@ -330,6 +330,7 @@
"freeze": "منجمد", "freeze": "منجمد",
"frequently_asked_questions": "اکثر پوچھے گئے سوالات", "frequently_asked_questions": "اکثر پوچھے گئے سوالات",
"frozen": "منجمد", "frozen": "منجمد",
"frozen_balance": "منجمد توازن",
"full_balance": "مکمل بیلنس", "full_balance": "مکمل بیلنس",
"gas_exceeds_allowance": "لین دین کے ذریعہ درکار گیس الاؤنس سے زیادہ ہے۔", "gas_exceeds_allowance": "لین دین کے ذریعہ درکار گیس الاؤنس سے زیادہ ہے۔",
"generate_name": "نام پیدا کریں۔", "generate_name": "نام پیدا کریں۔",

View file

@ -329,6 +329,7 @@
"freeze": "Đóng băng", "freeze": "Đóng băng",
"frequently_asked_questions": "Các câu hỏi thường gặp", "frequently_asked_questions": "Các câu hỏi thường gặp",
"frozen": "Đã đóng băng", "frozen": "Đã đóng băng",
"frozen_balance": "Cân bằng đông lạnh",
"full_balance": "Số dư đầy đủ", "full_balance": "Số dư đầy đủ",
"gas_exceeds_allowance": "Gas theo yêu cầu của giao dịch vượt quá trợ cấp.", "gas_exceeds_allowance": "Gas theo yêu cầu của giao dịch vượt quá trợ cấp.",
"generate_name": "Tạo tên", "generate_name": "Tạo tên",

View file

@ -331,6 +331,7 @@
"freeze": "Tì pa", "freeze": "Tì pa",
"frequently_asked_questions": "Àwọn ìbéèrè la máa ń béèrè", "frequently_asked_questions": "Àwọn ìbéèrè la máa ń béèrè",
"frozen": "Ó l'a tì pa", "frozen": "Ó l'a tì pa",
"frozen_balance": "Iwontunwonsi ti o tutu",
"full_balance": "Ìyókù owó kíkún", "full_balance": "Ìyókù owó kíkún",
"gas_exceeds_allowance": "Gaasi ti a beere nipasẹ idunadura ju lọ.", "gas_exceeds_allowance": "Gaasi ti a beere nipasẹ idunadura ju lọ.",
"generate_name": "Ṣẹda Orukọ", "generate_name": "Ṣẹda Orukọ",

View file

@ -330,6 +330,7 @@
"freeze": "凍結", "freeze": "凍結",
"frequently_asked_questions": "常见问题", "frequently_asked_questions": "常见问题",
"frozen": "凍結的", "frozen": "凍結的",
"frozen_balance": "冷冻平衡",
"full_balance": "全部余额", "full_balance": "全部余额",
"gas_exceeds_allowance": "交易要求的气体超出了津贴。", "gas_exceeds_allowance": "交易要求的气体超出了津贴。",
"generate_name": "生成名称", "generate_name": "生成名称",