Cw 830 coin control getting cleared (#1825)

* init commit

* add select all button

* localisation all coins

* fix isSending and isFrozen state updates

* fix: clean up electrum UTXOs

* ui fixes

* address the review comments[skip ci]

* remove onPopInvoked[skip ci]

---------

Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Serhii 2024-11-28 17:53:03 +02:00 committed by GitHub
parent 4ca50b5e63
commit 9cd69c4ba3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 402 additions and 125 deletions

View file

@ -106,6 +106,15 @@ class BitcoinWalletService extends WalletService<
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
if (keysToDelete.isNotEmpty) {
await unspentCoinsInfoSource.deleteAll(keysToDelete);
}
}
@override

View file

@ -304,6 +304,7 @@ abstract class ElectrumWalletBase
Future<void> init() async {
await walletAddresses.init();
await transactionHistory.init();
await cleanUpDuplicateUnspentCoins();
await save();
_autoSaveTimer =
@ -1380,9 +1381,10 @@ abstract class ElectrumWalletBase
unspentCoins = updatedUnspentCoins;
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
if (currentWalletUnspentCoins.length != updatedUnspentCoins.length) {
unspentCoins.forEach((coin) => addCoinInfo(coin));
return;
}
await updateCoins(unspentCoins);
@ -1408,6 +1410,7 @@ abstract class ElectrumWalletBase
coin.isFrozen = coinInfo.isFrozen;
coin.isSending = coinInfo.isSending;
coin.note = coinInfo.note;
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
coin.bitcoinAddressRecord.balance += coinInfo.value;
} else {
@ -1445,20 +1448,27 @@ abstract class ElectrumWalletBase
@action
Future<void> addCoinInfo(BitcoinUnspent coin) async {
final newInfo = UnspentCoinsInfo(
walletId: id,
hash: coin.hash,
isFrozen: coin.isFrozen,
isSending: coin.isSending,
noteRaw: coin.note,
address: coin.bitcoinAddressRecord.address,
value: coin.value,
vout: coin.vout,
isChange: coin.isChange,
isSilentPayment: coin is BitcoinSilentPaymentsUnspent,
);
await unspentCoinsInfo.add(newInfo);
// Check if the coin is already in the unspentCoinsInfo for the wallet
final existingCoinInfo = unspentCoinsInfo.values.firstWhereOrNull(
(element) => element.walletId == walletInfo.id && element == coin);
if (existingCoinInfo == null) {
final newInfo = UnspentCoinsInfo(
walletId: id,
hash: coin.hash,
isFrozen: coin.isFrozen,
isSending: coin.isSending,
noteRaw: coin.note,
address: coin.bitcoinAddressRecord.address,
value: coin.value,
vout: coin.vout,
isChange: coin.isChange,
isSilentPayment: coin is BitcoinSilentPaymentsUnspent,
);
await unspentCoinsInfo.add(newInfo);
}
}
Future<void> _refreshUnspentCoinsInfo() async {
@ -1486,6 +1496,23 @@ abstract class ElectrumWalletBase
}
}
Future<void> cleanUpDuplicateUnspentCoins() async {
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
final Map<String, UnspentCoinsInfo> uniqueUnspentCoins = {};
final List<dynamic> duplicateKeys = [];
for (final unspentCoin in currentWalletUnspentCoins) {
final key = '${unspentCoin.hash}:${unspentCoin.vout}';
if (!uniqueUnspentCoins.containsKey(key)) {
uniqueUnspentCoins[key] = unspentCoin;
} else {
duplicateKeys.add(unspentCoin.key);
}
}
if (duplicateKeys.isNotEmpty) await unspentCoinsInfo.deleteAll(duplicateKeys);
}
int transactionVSize(String transactionHex) => BtcTransaction.fromRaw(transactionHex).getVSize();
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {

View file

@ -126,6 +126,15 @@ class LitecoinWalletService extends WalletService<
mwebdLogs.deleteSync();
}
}
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
if (keysToDelete.isNotEmpty) {
await unspentCoinsInfoSource.deleteAll(keysToDelete);
}
}
@override

View file

@ -85,6 +85,15 @@ class BitcoinCashWalletService extends WalletService<
final walletInfo = walletInfoSource.values
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
if (keysToDelete.isNotEmpty) {
await unspentCoinsInfoSource.deleteAll(keysToDelete);
}
}
@override

View file

@ -1,10 +1,11 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/unspent_comparable_mixin.dart';
import 'package:hive/hive.dart';
part 'unspent_coins_info.g.dart';
@HiveType(typeId: UnspentCoinsInfo.typeId)
class UnspentCoinsInfo extends HiveObject {
class UnspentCoinsInfo extends HiveObject with UnspentComparable {
UnspentCoinsInfo({
required this.walletId,
required this.hash,

View file

@ -0,0 +1,27 @@
mixin UnspentComparable {
String get address;
String get hash;
int get value;
int get vout;
String? get keyImage;
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is UnspentComparable &&
other.hash == hash &&
other.address == address &&
other.value == value &&
other.vout == vout &&
other.keyImage == keyImage;
}
@override
int get hashCode {
return Object.hash(address, hash, value, vout, keyImage);
}
}

View file

@ -1,4 +1,6 @@
class Unspent {
import 'package:cw_core/unspent_comparable_mixin.dart';
class Unspent with UnspentComparable {
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
: isSending = true,
isFrozen = false,

View file

@ -1,13 +1,14 @@
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_no_action.dart.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class UnspentCoinsListPage extends BasePage {
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
@ -15,16 +16,53 @@ class UnspentCoinsListPage extends BasePage {
@override
String get title => S.current.unspent_coins_title;
@override
Widget leading(BuildContext context) {
return MergeSemantics(
child: SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent),
),
onPressed: () async => await handleOnPopInvoked(context),
child: backButton(context),
),
),
),
),
);
}
final UnspentCoinsListViewModel unspentCoinsListViewModel;
Future<void> handleOnPopInvoked(BuildContext context) async {
final hasChanged = unspentCoinsListViewModel.hasAdjustableFieldChanged;
if (unspentCoinsListViewModel.items.isEmpty || !hasChanged) {
Navigator.of(context).pop();
} else {
unspentCoinsListViewModel.setIsDisposing(true);
await unspentCoinsListViewModel.dispose();
Navigator.of(context).pop();
Navigator.of(context).pop();
}
}
@override
Widget body(BuildContext context) => UnspentCoinsListForm(unspentCoinsListViewModel);
Widget body(BuildContext context) =>
UnspentCoinsListForm(unspentCoinsListViewModel, handleOnPopInvoked);
}
class UnspentCoinsListForm extends StatefulWidget {
UnspentCoinsListForm(this.unspentCoinsListViewModel);
UnspentCoinsListForm(this.unspentCoinsListViewModel, this.handleOnPopInvoked);
final UnspentCoinsListViewModel unspentCoinsListViewModel;
final Future<void> Function(BuildContext context) handleOnPopInvoked;
@override
UnspentCoinsListFormState createState() => UnspentCoinsListFormState(unspentCoinsListViewModel);
@ -35,36 +73,126 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
final UnspentCoinsListViewModel unspentCoinsListViewModel;
late Future<void> _initialization;
ReactionDisposer? _disposer;
@override
void initState() {
super.initState();
_initialization = unspentCoinsListViewModel.initialSetup();
_setupReactions();
}
void _setupReactions() {
_disposer = reaction<bool>(
(_) => unspentCoinsListViewModel.isDisposing,
(isDisposing) {
if (isDisposing) {
_showSavingDataAlert();
}
},
);
}
void _showSavingDataAlert() {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithNoAction(
alertContent: 'Updating, please wait…',
alertBarrierDismissible: false,
);
},
);
}
@override
void dispose() {
_disposer?.call();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(24, 12, 24, 24),
child: Observer(
builder: (_) => ListView.separated(
itemCount: unspentCoinsListViewModel.items.length,
separatorBuilder: (_, __) => SizedBox(height: 15),
itemBuilder: (_, int index) {
return Observer(builder: (_) {
final item = unspentCoinsListViewModel.items[index];
return PopScope(
canPop: false,
onPopInvokedWithResult: (bool didPop, Object? result) async {
if (didPop) return;
if(mounted)
await widget.handleOnPopInvoked(context);
},
child: FutureBuilder<void>(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel]),
child: UnspentCoinsListItem(
note: item.note,
amount: item.amount,
address: item.address,
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
isSilentPayment: item.isSilentPayment,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel.saveUnspentCoinInfo(item);
}));
});
})));
if (snapshot.hasError) return Center(child: Text('Failed to load unspent coins'));
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Observer(
builder: (_) => Column(
children: [
if (unspentCoinsListViewModel.items.isNotEmpty)
Row(
children: [
SizedBox(width: 12),
StandardCheckbox(
iconColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
value: unspentCoinsListViewModel.isAllSelected,
onChanged: (value) => unspentCoinsListViewModel.toggleSelectAll(value),
),
SizedBox(width: 12),
Text(
S.current.all_coins,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
SizedBox(height: 15),
Expanded(
child: unspentCoinsListViewModel.items.isEmpty
? Center(child: Text('No unspent coins available\ntry to reconnect',textAlign: TextAlign.center))
: ListView.separated(
itemCount: unspentCoinsListViewModel.items.length,
separatorBuilder: (_, __) => SizedBox(height: 15),
itemBuilder: (_, int index) {
final item = unspentCoinsListViewModel.items[index];
return Observer(
builder: (_) => GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
Routes.unspentCoinsDetails,
arguments: [item, unspentCoinsListViewModel],
),
child: UnspentCoinsListItem(
note: item.note,
amount: item.amount,
address: item.address,
isSending: item.isSending,
isFrozen: item.isFrozen,
isChange: item.isChange,
isSilentPayment: item.isSilentPayment,
onCheckBoxTap: item.isFrozen
? null
: () async {
item.isSending = !item.isSending;
await unspentCoinsListViewModel
.saveUnspentCoinInfo(item);
},
),
),
);
},
),
),
],
),
),
);
},
),
);
}
}

View file

@ -3,18 +3,18 @@ import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
class AlertWithNoAction extends BaseAlertDialog {
AlertWithNoAction({
required this.alertTitle,
this.alertTitle,
required this.alertContent,
this.alertBarrierDismissible = true,
Key? key,
});
final String alertTitle;
final String? alertTitle;
final String alertContent;
final bool alertBarrierDismissible;
@override
String get titleText => alertTitle;
String? get titleText => alertTitle;
@override
String get contentText => alertContent;
@ -26,5 +26,5 @@ class AlertWithNoAction extends BaseAlertDialog {
bool get isBottomDividerExists => false;
@override
Widget actionButtons(BuildContext context) => Container(height: 60);
Widget actionButtons(BuildContext context) => Container();
}

View file

@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
class BaseAlertDialog extends StatelessWidget {
String? get headerText => '';
String get titleText => '';
String? get titleText => '';
String get contentText => '';
@ -43,7 +43,7 @@ class BaseAlertDialog extends StatelessWidget {
Widget title(BuildContext context) {
return Text(
titleText,
titleText!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
@ -191,10 +191,11 @@ class BaseAlertDialog extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (headerText?.isNotEmpty ?? false) headerTitle(context),
titleText != null ?
Padding(
padding: EdgeInsets.fromLTRB(24, 20, 24, 0),
child: title(context),
),
) : SizedBox(height: 16),
isDividerExists
? Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),

View file

@ -1,10 +1,11 @@
import 'package:cw_core/unspent_comparable_mixin.dart';
import 'package:mobx/mobx.dart';
part 'unspent_coins_item.g.dart';
class UnspentCoinsItem = UnspentCoinsItemBase with _$UnspentCoinsItem;
abstract class UnspentCoinsItemBase with Store {
abstract class UnspentCoinsItemBase with Store, UnspentComparable {
UnspentCoinsItemBase({
required this.address,
required this.amount,
@ -13,7 +14,7 @@ abstract class UnspentCoinsItemBase with Store {
required this.note,
required this.isSending,
required this.isChange,
required this.amountRaw,
required this.value,
required this.vout,
required this.keyImage,
required this.isSilentPayment,
@ -41,7 +42,7 @@ abstract class UnspentCoinsItemBase with Store {
bool isChange;
@observable
int amountRaw;
int value;
@observable
int vout;

View file

@ -10,6 +10,7 @@ import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:hive/hive.dart';
import 'package:collection/collection.dart';
import 'package:mobx/mobx.dart';
part 'unspent_coins_list_view_model.g.dart';
@ -22,55 +23,66 @@ abstract class UnspentCoinsListViewModelBase with Store {
required Box<UnspentCoinsInfo> unspentCoinsInfo,
this.coinTypeToSpendFrom = UnspentCoinType.any,
}) : _unspentCoinsInfo = unspentCoinsInfo,
_items = ObservableList<UnspentCoinsItem>() {
_updateUnspentCoinsInfo();
_updateUnspents();
}
items = ObservableList<UnspentCoinsItem>(),
_originalState = {};
WalletBase wallet;
final WalletBase wallet;
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
final UnspentCoinType coinTypeToSpendFrom;
@observable
ObservableList<UnspentCoinsItem> _items;
ObservableList<UnspentCoinsItem> items;
final Map<String, Map<String, dynamic>> _originalState;
@observable
bool isDisposing = false;
@computed
ObservableList<UnspentCoinsItem> get items => _items;
bool get isAllSelected => items.every((element) => element.isFrozen || element.isSending);
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
final info =
getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
Future<void> initialSetup() async {
await _updateUnspents();
_storeOriginalState();
}
if (info == null) {
return;
}
info.isFrozen = item.isFrozen;
info.isSending = item.isSending;
info.note = item.note;
await info.save();
await _updateUnspents();
await wallet.updateBalance();
} catch (e) {
print(e.toString());
void _storeOriginalState() {
_originalState.clear();
for (final item in items) {
_originalState[item.hash] = {
'isFrozen': item.isFrozen,
'note': item.note,
'isSending': item.isSending,
};
}
}
UnspentCoinsInfo? getUnspentCoinInfo(
String hash, String address, int value, int vout, String? keyImage) {
bool _hasAdjustableFieldChanged(UnspentCoinsItem item) {
final original = _originalState[item.hash];
if (original == null) return false;
return original['isFrozen'] != item.isFrozen ||
original['note'] != item.note ||
original['isSending'] != item.isSending;
}
bool get hasAdjustableFieldChanged => items.any(_hasAdjustableFieldChanged);
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
return _unspentCoinsInfo.values.firstWhere((element) =>
element.walletId == wallet.id &&
element.hash == hash &&
element.address == address &&
element.value == value &&
element.vout == vout &&
element.keyImage == keyImage);
final existingInfo = _unspentCoinsInfo.values
.firstWhereOrNull((element) => element.walletId == wallet.id && element == item);
if (existingInfo == null) return;
existingInfo.isFrozen = item.isFrozen;
existingInfo.isSending = item.isSending;
existingInfo.note = item.note;
await existingInfo.save();
_updateUnspentCoinsInfo();
} catch (e) {
print("UnspentCoinsInfo not found for coin: $e");
return null;
print('Error saving coin info: $e');
}
}
@ -115,37 +127,60 @@ abstract class UnspentCoinsListViewModelBase with Store {
@action
void _updateUnspentCoinsInfo() {
_items.clear();
items.clear();
List<UnspentCoinsItem> unspents = [];
_getUnspents().forEach((Unspent elem) {
try {
final info =
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
if (info == null) {
return;
}
final unspents = _getUnspents()
.map((elem) {
try {
final existingItem = _unspentCoinsInfo.values
.firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);
unspents.add(UnspentCoinsItem(
address: elem.address,
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
hash: elem.hash,
isFrozen: info.isFrozen,
note: info.note,
isSending: info.isSending,
amountRaw: elem.value,
vout: elem.vout,
keyImage: elem.keyImage,
isChange: elem.isChange,
isSilentPayment: info.isSilentPayment ?? false,
));
} catch (e, s) {
print(s);
print(e.toString());
ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
}
});
if (existingItem == null) return null;
_items.addAll(unspents);
return UnspentCoinsItem(
address: elem.address,
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
hash: elem.hash,
isFrozen: existingItem.isFrozen,
note: existingItem.note,
isSending: existingItem.isSending,
value: elem.value,
vout: elem.vout,
keyImage: elem.keyImage,
isChange: elem.isChange,
isSilentPayment: existingItem.isSilentPayment ?? false,
);
} catch (e, s) {
print('Error: $e\nStack: $s');
ExceptionHandler.onError(
FlutterErrorDetails(exception: e, stack: s),
);
return null;
}
})
.whereType<UnspentCoinsItem>()
.toList();
unspents.sort((a, b) => b.value.compareTo(a.value));
items.addAll(unspents);
}
@action
void toggleSelectAll(bool value) {
for (final item in items) {
if (item.isFrozen || item.isSending == value) continue;
item.isSending = value;
saveUnspentCoinInfo(item);
}
}
@action
void setIsDisposing(bool value) => isDisposing = value;
@action
Future<void> dispose() async {
await _updateUnspents();
await wallet.updateBalance();
}
}

View file

@ -38,6 +38,7 @@
"agree_to": "من خلال إنشاء حساب فإنك توافق على",
"alert_notice": "يلاحظ",
"all": "الكل",
"all_coins": "كل العملات المعدنية",
"all_trades": "جميع عمليات التداول",
"all_transactions": "كل التحركات المالية",
"alphabetical": "مرتب حسب الحروف الأبجدية",

View file

@ -38,6 +38,7 @@
"agree_to": "Чрез създаването на акаунт вие се съгласявате с ",
"alert_notice": "Забележете",
"all": "ALL",
"all_coins": "Всички монети",
"all_trades": "Всички сделкки",
"all_transactions": "Всички транзакции",
"alphabetical": "Азбучен ред",

View file

@ -38,6 +38,7 @@
"agree_to": "Vytvořením účtu souhlasíte s ",
"alert_notice": "Oznámení",
"all": "VŠE",
"all_coins": "Všechny mince",
"all_trades": "Všechny obchody",
"all_transactions": "Všechny transakce",
"alphabetical": "Abecední",

View file

@ -38,6 +38,7 @@
"agree_to": "Indem Sie ein Konto erstellen, stimmen Sie den ",
"alert_notice": "Beachten",
"all": "ALLES",
"all_coins": "Alle Münzen",
"all_trades": "Alle Trades",
"all_transactions": "Alle Transaktionen",
"alphabetical": "Alphabetisch",

View file

@ -38,6 +38,7 @@
"agree_to": "By creating account you agree to the ",
"alert_notice": "Notice",
"all": "ALL",
"all_coins": "All Coins",
"all_trades": "All trades",
"all_transactions": "All transactions",
"alphabetical": "Alphabetical",

View file

@ -38,6 +38,7 @@
"agree_to": "Al crear una cuenta, aceptas ",
"alert_notice": "Aviso",
"all": "Todos",
"all_coins": "Todas las monedas",
"all_trades": "Todos los oficios",
"all_transactions": "Todas las transacciones",
"alphabetical": "Alfabético",

View file

@ -38,6 +38,7 @@
"agree_to": "En créant un compte, vous acceptez les ",
"alert_notice": "Avis",
"all": "TOUT",
"all_coins": "Toutes les pièces",
"all_trades": "Tous échanges",
"all_transactions": "Toutes transactions",
"alphabetical": "Alphabétique",

View file

@ -38,6 +38,7 @@
"agree_to": "Ta hanyar ƙirƙirar asusu kun yarda da",
"alert_notice": "Sanarwa",
"all": "DUK",
"all_coins": "Duk tsabar kudi",
"all_trades": "Duk ciniki",
"all_transactions": "Dukan Ma'amaloli",
"alphabetical": "Harafi",

View file

@ -38,6 +38,7 @@
"agree_to": "खाता बनाकर आप इससे सहमत होते हैं ",
"alert_notice": "सूचना",
"all": "सब",
"all_coins": "सभी सिक्के",
"all_trades": "सभी व्यापार",
"all_transactions": "सभी लेन - देन",
"alphabetical": "वर्णमाला",

View file

@ -38,6 +38,7 @@
"agree_to": "Stvaranjem računa pristajete na ",
"alert_notice": "Obavijest",
"all": "SVE",
"all_coins": "Sve kovanice",
"all_trades": "Svi obrti",
"all_transactions": "Sve transakcije",
"alphabetical": "Abecedno",

View file

@ -38,6 +38,7 @@
"agree_to": "Ստեղծելով հաշիվ դուք համաձայնում եք ",
"alert_notice": "Ծանուցում",
"all": "Բոլորը",
"all_coins": "Բոլոր մետաղադրամները",
"all_trades": "Բոլոր գործարքները",
"all_transactions": "Բոլոր գործառնությունները",
"alphabetical": "Այբբենական",

View file

@ -38,6 +38,7 @@
"agree_to": "Dengan membuat akun Anda setuju dengan ",
"alert_notice": "Melihat",
"all": "SEMUA",
"all_coins": "Semua koin",
"all_trades": "Semua perdagangan",
"all_transactions": "Semua transaksi",
"alphabetical": "Alfabetis",

View file

@ -38,6 +38,7 @@
"agree_to": "Creando un account accetti il ",
"alert_notice": "Avviso",
"all": "TUTTO",
"all_coins": "Tutte le monete",
"all_trades": "Svi obrti",
"all_transactions": "Sve transakcije",
"alphabetical": "Alfabetico",

View file

@ -38,6 +38,7 @@
"agree_to": "アカウントを作成することにより、",
"alert_notice": "知らせ",
"all": "すべて",
"all_coins": "すべてのコイン",
"all_trades": "すべての取引",
"all_transactions": "全取引",
"alphabetical": "アルファベット順",

View file

@ -38,6 +38,7 @@
"agree_to": "계정을 생성하면 ",
"alert_notice": "알아채다",
"all": "모든",
"all_coins": "모든 동전",
"all_trades": "A모든 거래",
"all_transactions": "모든 거래 창구",
"alphabetical": "알파벳순",
@ -495,8 +496,8 @@
"placeholder_transactions": "거래가 여기에 표시됩니다",
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
"please_select": "선택 해주세요:",
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",

View file

@ -38,6 +38,7 @@
"agree_to": "အကောင့်ဖန်တီးခြင်းဖြင့် သင်သည် ဤအရာကို သဘောတူပါသည်။",
"alert_notice": "မှတ်သား",
"all": "အားလုံး",
"all_coins": "အားလုံးဒင်္ဂါးများ",
"all_trades": "ကုန်သွယ်မှုအားလုံး",
"all_transactions": "အရောင်းအဝယ်အားလုံး",
"alphabetical": "အက္ခရာစဉ်",

View file

@ -38,6 +38,7 @@
"agree_to": "Door een account aan te maken gaat u akkoord met de ",
"alert_notice": "Kennisgeving",
"all": "ALLE",
"all_coins": "Alle munten",
"all_trades": "Alle transacties",
"all_transactions": "Alle transacties",
"alphabetical": "Alfabetisch",

View file

@ -38,6 +38,7 @@
"agree_to": "Tworząc konto wyrażasz zgodę na ",
"alert_notice": "Ogłoszenie",
"all": "WSZYSTKO",
"all_coins": "Wszystkie monety",
"all_trades": "Wszystkie operacje",
"all_transactions": "Wszystkie transakcje",
"alphabetical": "Alfabetyczny",

View file

@ -38,6 +38,7 @@
"agree_to": "Ao criar conta você concorda com ",
"alert_notice": "Perceber",
"all": "TUDO",
"all_coins": "Todas as moedas",
"all_trades": "Todas as negociações",
"all_transactions": "Todas as transacções",
"alphabetical": "alfabética",

View file

@ -38,6 +38,7 @@
"agree_to": "Создавая аккаунт, вы соглашаетесь с ",
"alert_notice": "Уведомление",
"all": "ВСЕ",
"all_coins": "Все монеты",
"all_trades": "Все сделки",
"all_transactions": "Все транзакции",
"alphabetical": "Алфавитный",

View file

@ -38,6 +38,7 @@
"agree_to": "การสร้างบัญชีของคุณยอมรับเงื่อนไขของ",
"alert_notice": "สังเกต",
"all": "ทั้งหมด",
"all_coins": "เหรียญทั้งหมด",
"all_trades": "การซื้อขายทั้งหมด",
"all_transactions": "การทำธุรกรรมทั้งหมด",
"alphabetical": "ตามตัวอักษร",

View file

@ -38,6 +38,7 @@
"agree_to": "Sa pamamagitan ng paggawa ng account sumasang-ayon ka sa ",
"alert_notice": "PAUNAWA",
"all": "LAHAT",
"all_coins": "Lahat ng mga barya",
"all_trades": "Lahat ng mga trade",
"all_transactions": "Lahat ng mga transaksyon",
"alphabetical": "Alpabeto",

View file

@ -38,6 +38,7 @@
"agree_to": "Hesap oluşturarak bunları kabul etmiş olursunuz ",
"alert_notice": "Fark etme",
"all": "HEPSİ",
"all_coins": "Tüm Paralar",
"all_trades": "Tüm takaslar",
"all_transactions": "Tüm transferler",
"alphabetical": "Alfabetik",

View file

@ -38,6 +38,7 @@
"agree_to": "Створюючи обліковий запис, ви погоджуєтеся з ",
"alert_notice": "Ув'язнення",
"all": "ВСЕ",
"all_coins": "Всі монети",
"all_trades": "Всі операції",
"all_transactions": "Всі транзакції",
"alphabetical": "Алфавітний",

View file

@ -38,6 +38,7 @@
"agree_to": "اکاؤنٹ بنا کر آپ اس سے اتفاق کرتے ہیں۔",
"alert_notice": "نوٹس",
"all": "تمام",
"all_coins": "تمام سکے",
"all_trades": "تمام تجارت",
"all_transactions": "تمام لین دین",
"alphabetical": "حروف تہجی کے مطابق",

View file

@ -38,6 +38,7 @@
"agree_to": "Bằng cách tạo tài khoản, bạn đồng ý với ",
"alert_notice": "Để ý",
"all": "TẤT CẢ",
"all_coins": "Tất cả các đồng tiền",
"all_trades": "Tất cả giao dịch",
"all_transactions": "Tất cả giao dịch",
"alphabetical": "Theo thứ tự chữ cái",

View file

@ -38,6 +38,7 @@
"agree_to": "Tẹ́ ẹ bá dá àkáǹtì ẹ jọ rò ",
"alert_notice": "Akiyesi",
"all": "Gbogbo",
"all_coins": "Gbogbo awọn owó",
"all_trades": "Gbogbo àwọn pàṣípààrọ̀",
"all_transactions": "Gbogbo àwọn àránṣẹ́",
"alphabetical": "Labidibi",

View file

@ -38,6 +38,7 @@
"agree_to": "创建账户即表示您同意 ",
"alert_notice": "注意",
"all": "全部",
"all_coins": "所有硬币",
"all_trades": "所有的变化",
"all_transactions": "所有交易",
"alphabetical": "按字母顺序",