Merge branch 'mweb' of https://github.com/cake-tech/cake_wallet into mweb-bg-sync-2

This commit is contained in:
Matthew Fosse 2024-09-16 08:32:59 -07:00
commit e51d6ea8c0
39 changed files with 452 additions and 104 deletions

View file

@ -7,7 +7,14 @@ class ElectrumBalance extends Balance {
required this.confirmed,
required this.unconfirmed,
required this.frozen,
}) : super(confirmed, unconfirmed);
this.secondConfirmed = 0,
this.secondUnconfirmed = 0,
}) : super(
confirmed,
unconfirmed,
secondAvailable: secondConfirmed,
secondAdditional: secondUnconfirmed,
);
static ElectrumBalance? fromJSON(String? jsonSource) {
if (jsonSource == null) {
@ -25,6 +32,8 @@ class ElectrumBalance extends Balance {
int confirmed;
int unconfirmed;
final int frozen;
int secondConfirmed = 0;
int secondUnconfirmed = 0;
@override
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen);
@ -38,6 +47,22 @@ class ElectrumBalance extends Balance {
return frozenFormatted == '0.0' ? '' : frozenFormatted;
}
String toJSON() =>
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen});
@override
String get formattedSecondAvailableBalance => bitcoinAmountToString(amount: secondConfirmed ?? 0);
@override
String get formattedSecondAdditionalBalance =>
bitcoinAmountToString(amount: secondUnconfirmed ?? 0);
@override
String get formattedFullAvailableBalance =>
bitcoinAmountToString(amount: confirmed + (secondConfirmed ?? 0) - frozen);
String toJSON() => json.encode({
'confirmed': confirmed,
'unconfirmed': unconfirmed,
'frozen': frozen,
'secondConfirmed': secondConfirmed,
'secondUnconfirmed': secondUnconfirmed
});
}

View file

@ -884,7 +884,7 @@ abstract class ElectrumWalletBase
final totalAmount = amount + fee;
if (totalAmount > balance[currency]!.confirmed) {
if (totalAmount > (balance[currency]!.confirmed + balance[currency]!.secondConfirmed)) {
throw BitcoinTransactionWrongBalanceException();
}

View file

@ -244,9 +244,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
print("STARTING SYNC - MWEB ENABLED: $mwebEnabled");
syncStatus = SyncronizingSyncStatus();
await subscribeForUpdates();
await updateTransactions();
await updateFeeRates();
await updateFeeRates();
_feeRatesTimer?.cancel();
_feeRatesTimer =
Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates());
@ -265,14 +264,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
}
await waitForMwebAddresses();
await getStub();
await processMwebUtxos();
await updateTransactions();
await updateUnspent();
await updateBalance();
_syncTimer?.cancel();
// delay the timer by a second so we don't overrride the restoreheight if one is set
Timer(const Duration(seconds: 2), () async {
_syncTimer = Timer.periodic(const Duration(milliseconds: 1500), (timer) async {
if (syncStatus is FailedSyncStatus) return;
@ -319,7 +317,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
lastFewProgresses.add(syncStatus.progress());
if (lastFewProgresses.length < 4) return;
// limit list size to 4:
while(lastFewProgresses.length > 4) {
while (lastFewProgresses.length > 4) {
lastFewProgresses.removeAt(0);
}
// if the progress is the same over the last 40 seconds, restart the sync:
@ -330,9 +328,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
timer.cancel();
}
});
});
// this runs in the background and processes new utxos as they come in:
processMwebUtxos();
}
@action
@ -678,14 +673,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
int confirmed = balance.confirmed;
int unconfirmed = balance.unconfirmed;
int confirmedMweb = 0;
int unconfirmedMweb = 0;
try {
mwebUtxosBox.values.forEach((utxo) {
if (utxo.height > 0) {
confirmed += utxo.value.toInt();
confirmedMweb += utxo.value.toInt();
} else {
unconfirmed += utxo.value.toInt();
unconfirmedMweb += utxo.value.toInt();
}
});
if (/*confirmedMweb > 0 &&*/ unconfirmedMweb > 0) {
unconfirmedMweb = -1 * (confirmedMweb - unconfirmedMweb);
}
} catch (_) {}
// update unspent balances:
@ -735,7 +735,13 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
}
}
return ElectrumBalance(confirmed: confirmed, unconfirmed: unconfirmed, frozen: balance.frozen);
return ElectrumBalance(
confirmed: confirmed,
unconfirmed: unconfirmed,
frozen: balance.frozen,
secondConfirmed: confirmedMweb,
secondUnconfirmed: unconfirmedMweb,
);
}
@override
@ -869,7 +875,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final addresses = <String>{};
transaction.inputAddresses?.forEach((id) async {
final utxo = mwebUtxosBox.get(id);
await mwebUtxosBox.delete(id);
// await mwebUtxosBox.delete(id);
if (utxo == null) return;
final addressRecord = walletAddresses.allAddresses
.firstWhere((addressRecord) => addressRecord.address == utxo.address);

View file

@ -122,6 +122,10 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
Future<String> getChangeAddress({List<BitcoinOutput>? outputs, UtxoDetails? utxoDetails}) async {
// use regular change address on peg in, otherwise use mweb for change address:
if (!mwebEnabled) {
return super.getChangeAddress();
}
if (outputs != null && utxoDetails != null) {
// check if this is a PEGIN:
bool outputsToMweb = false;
@ -134,6 +138,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
outputsToMweb = true;
}
}
// TODO: this doesn't respect coin control because it doesn't know which available inputs are selected
utxoDetails.availableInputs.forEach((element) {
if (element.address.contains("mweb")) {
comesFromMweb = true;
@ -144,6 +149,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
if (isPegIn && mwebEnabled) {
return super.getChangeAddress();
}
// use regular change address if it's not an mweb tx:
if (!comesFromMweb && !outputsToMweb) {
return super.getChangeAddress();
}
}
if (mwebEnabled) {

View file

@ -1,13 +1,18 @@
abstract class Balance {
const Balance(this.available, this.additional);
const Balance(this.available, this.additional, {this.secondAvailable, this.secondAdditional});
final int available;
final int additional;
final int? secondAvailable;
final int? secondAdditional;
String get formattedAvailableBalance;
String get formattedAdditionalBalance;
String get formattedUnAvailableBalance => '';
String get formattedSecondAvailableBalance => '';
String get formattedSecondAdditionalBalance => '';
String get formattedFullAvailableBalance => '';
String get formattedFullUnAvailableBalance => '';
}

View file

@ -123,7 +123,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = spendKey != ""
var newWptr = (spendKey != "")
? monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
@ -149,6 +149,32 @@ void restoreWalletFromKeysSync(
message: monero.Wallet_errorString(newWptr));
}
// CW-712 - Try to restore deterministic wallet first, if the view key doesn't
// match the view key provided
if (spendKey != "") {
final viewKeyRestored = monero.Wallet_secretViewKey(newWptr);
if (viewKey != viewKeyRestored && viewKey != "") {
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
File(path).deleteSync();
File(path+".keys").deleteSync();
newWptr = monero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
restoreHeight: restoreHeight,
addressString: address,
viewKeyString: viewKey,
spendKeyString: spendKey,
nettype: 0,
);
final status = monero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(newWptr));
}
}
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;

View file

@ -140,7 +140,7 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = spendKey != ""
var newWptr = (spendKey != "")
? wownero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
path: path,
@ -165,7 +165,31 @@ void restoreWalletFromKeysSync(
throw WalletRestoreFromKeysException(
message: wownero.Wallet_errorString(newWptr));
}
// CW-712 - Try to restore deterministic wallet first, if the view key doesn't
// match the view key provided
if (spendKey != "") {
final viewKeyRestored = wownero.Wallet_secretViewKey(newWptr);
if (viewKey != viewKeyRestored && viewKey != "") {
wownero.WalletManager_closeWallet(wmPtr, newWptr, false);
File(path).deleteSync();
File(path+".keys").deleteSync();
newWptr = wownero.WalletManager_createWalletFromKeys(
wmPtr,
path: path,
password: password,
restoreHeight: restoreHeight,
addressString: address,
viewKeyString: viewKey,
spendKeyString: spendKey,
nettype: 0,
);
final status = wownero.Wallet_status(newWptr);
if (status != 0) {
throw WalletRestoreFromKeysException(
message: wownero.Wallet_errorString(newWptr));
}
}
}
wptr = newWptr;
openedWalletsByPath[path] = wptr!;

View file

@ -131,7 +131,7 @@ class CryptoBalanceWidget extends StatelessWidget {
builder: (_) {
if (dashboardViewModel.getMoneroError != null) {
return Padding(
padding: const EdgeInsets.fromLTRB(16,0,16,16),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: DashBoardRoundedCardWidget(
title: "Invalid monero bindings",
subTitle: dashboardViewModel.getMoneroError.toString(),
@ -146,13 +146,12 @@ class CryptoBalanceWidget extends StatelessWidget {
builder: (_) {
if (dashboardViewModel.getWowneroError != null) {
return Padding(
padding: const EdgeInsets.fromLTRB(16,0,16,16),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: DashBoardRoundedCardWidget(
title: "Invalid wownero bindings",
subTitle: dashboardViewModel.getWowneroError.toString(),
onTap: () {},
)
);
));
}
return Container();
},
@ -273,6 +272,18 @@ class CryptoBalanceWidget extends StatelessWidget {
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,
);
});
@ -290,12 +301,11 @@ class CryptoBalanceWidget extends StatelessWidget {
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.",
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),
@ -494,10 +504,18 @@ class BalanceRowWidget extends StatelessWidget {
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,
super.key,
});
@ -508,10 +526,18 @@ class BalanceRowWidget extends StatelessWidget {
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;
// void _showBalanceDescription(BuildContext context) {
@ -759,6 +785,94 @@ class BalanceRowWidget extends StatelessWidget {
),
],
),
if (hasSecondAvailableBalance)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 24),
Text(
'${secondAvailableBalanceLabel}',
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(
secondAvailableBalance,
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(
'${secondAvailableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).extension<BalancePageTheme>()!.textColor,
height: 1,
),
),
],
),
if (hasSecondAdditionalBalance)
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,
),
),
],
),
],
),
),

View file

@ -1,3 +1,6 @@
import 'dart:math';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
@ -35,7 +38,7 @@ class AddressList extends StatelessWidget {
separatorBuilder: (context, _) => const HorizontalSectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemCount: min(addressListViewModel.items.length, 100),// TODO: don't show all 1000 mweb addresses
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();

View file

@ -21,10 +21,14 @@ class BalanceRecord {
const BalanceRecord(
{required this.availableBalance,
required this.additionalBalance,
required this.secondAvailableBalance,
required this.secondAdditionalBalance,
required this.frozenBalance,
required this.fiatAvailableBalance,
required this.fiatAdditionalBalance,
required this.fiatFrozenBalance,
required this.fiatSecondAvailableBalance,
required this.fiatSecondAdditionalBalance,
required this.asset,
required this.formattedAssetTitle});
final String fiatAdditionalBalance;
@ -33,6 +37,10 @@ class BalanceRecord {
final String additionalBalance;
final String availableBalance;
final String frozenBalance;
final String secondAvailableBalance;
final String secondAdditionalBalance;
final String fiatSecondAdditionalBalance;
final String fiatSecondAvailableBalance;
final CryptoCurrency asset;
final String formattedAssetTitle;
}
@ -158,6 +166,26 @@ abstract class BalanceViewModelBase with Store {
}
}
@computed
String get secondAvailableBalanceLabel {
switch (wallet.type) {
case WalletType.litecoin:
return S.current.mweb_confirmed;
default:
return S.current.confirmed;
}
}
@computed
String get secondAdditionalBalanceLabel {
switch (wallet.type) {
case WalletType.litecoin:
return S.current.mweb_unconfirmed;
default:
return S.current.unconfirmed;
}
}
@computed
bool get hasMultiBalance => appStore.wallet!.type == WalletType.haven;
@ -207,6 +235,17 @@ abstract class BalanceViewModelBase with Store {
return walletBalance.formattedAdditionalBalance;
}
@computed
String get secondAdditionalBalance {
final walletBalance = _walletBalance;
if (displayMode == BalanceDisplayMode.hiddenBalance) {
return '---';
}
return walletBalance.formattedSecondAdditionalBalance;
}
@computed
String get availableFiatBalance {
final walletBalance = _walletBalance;
@ -243,9 +282,13 @@ abstract class BalanceViewModelBase with Store {
availableBalance: '---',
additionalBalance: '---',
frozenBalance: '---',
secondAvailableBalance: '---',
secondAdditionalBalance: '---',
fiatAdditionalBalance: isFiatDisabled ? '' : '---',
fiatAvailableBalance: isFiatDisabled ? '' : '---',
fiatFrozenBalance: isFiatDisabled ? '' : '---',
fiatSecondAvailableBalance: isFiatDisabled ? '' : '---',
fiatSecondAdditionalBalance: isFiatDisabled ? '' : '---',
asset: key,
formattedAssetTitle: _formatterAsset(key)));
}
@ -274,24 +317,46 @@ abstract class BalanceViewModelBase with Store {
' ' +
_getFiatBalance(price: price, cryptoAmount: getFormattedFrozenBalance(value)));
final secondAdditionalFiatBalance = isFiatDisabled
? ''
: (fiatCurrency.toString() +
' ' +
_getFiatBalance(price: price, cryptoAmount: value.formattedSecondAdditionalBalance));
final secondAvailableFiatBalance = isFiatDisabled
? ''
: (fiatCurrency.toString() +
' ' +
_getFiatBalance(price: price, cryptoAmount: value.formattedSecondAvailableBalance));
return MapEntry(
key,
BalanceRecord(
availableBalance: value.formattedAvailableBalance,
additionalBalance: value.formattedAdditionalBalance,
frozenBalance: getFormattedFrozenBalance(value),
secondAvailableBalance: value.formattedSecondAvailableBalance,
secondAdditionalBalance: value.formattedSecondAdditionalBalance,
fiatAdditionalBalance: additionalFiatBalance,
fiatAvailableBalance: availableFiatBalance,
fiatFrozenBalance: frozenFiatBalance,
fiatSecondAvailableBalance: secondAvailableFiatBalance,
fiatSecondAdditionalBalance: secondAdditionalFiatBalance,
asset: key,
formattedAssetTitle: _formatterAsset(key)));
});
}
@computed
bool get hasAdditionalBalance => _hasAdditionBalanceForWalletType(wallet.type);
bool get hasAdditionalBalance => _hasAdditionalBalanceForWalletType(wallet.type);
bool _hasAdditionBalanceForWalletType(WalletType type) {
@computed
bool get hasSecondAdditionalBalance => _hasSecondAdditionalBalanceForWalletType(wallet.type);
@computed
bool get hasSecondAvailableBalance => _hasSecondAvailableBalanceForWalletType(wallet.type);
bool _hasAdditionalBalanceForWalletType(WalletType type) {
switch (type) {
case WalletType.ethereum:
case WalletType.polygon:
@ -303,6 +368,20 @@ abstract class BalanceViewModelBase with Store {
}
}
bool _hasSecondAdditionalBalanceForWalletType(WalletType type) {
if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) {
return true;
}
return false;
}
bool _hasSecondAvailableBalanceForWalletType(WalletType type) {
if (wallet.type == WalletType.litecoin /*&& settingsStore.mwebEnabled*/) {
return true;
}
return false;
}
@computed
List<BalanceRecord> get formattedBalances {
final balance = balances.values.toList();

View file

@ -217,7 +217,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
PendingTransaction? pendingTransaction;
@computed
String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance;
String get balance => wallet.balance[selectedCryptoCurrency]!.formattedFullAvailableBalance;
@computed
bool get isFiatDisabled => balanceViewModel.isFiatDisabled;

View file

@ -396,6 +396,8 @@
"monero_light_theme": " ضوء مونيرو",
"moonpay_alert_text": "يجب أن تكون قيمة المبلغ أكبر من أو تساوي ${minAmount} ${fiatCurrency}",
"more_options": "المزيد من الخيارات",
"mweb_confirmed": "أكد MWEB",
"mweb_unconfirmed": "غير مؤكد MWEB",
"name": "ﻢﺳﺍ",
"nano_current_rep": "الممثل الحالي",
"nano_gpt_thanks_message": "شكرا لاستخدام nanogpt! تذكر أن تعود إلى المتصفح بعد اكتمال معاملتك!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Лека тема Monero",
"moonpay_alert_text": "Сумата трябва да бъде най-малко ${minAmount} ${fiatCurrency}",
"more_options": "Още настройки",
"mweb_confirmed": "Потвърден MWeb",
"mweb_unconfirmed": "Непотвърден mweb",
"name": "Име",
"nano_current_rep": "Настоящ представител",
"nano_gpt_thanks_message": "Благодаря, че използвахте Nanogpt! Не забравяйте да се върнете обратно към браузъра, след като транзакцията ви приключи!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Světlé téma Monero",
"moonpay_alert_text": "Částka musí být větší nebo rovna ${minAmount} ${fiatCurrency}",
"more_options": "Více možností",
"mweb_confirmed": "Potvrzený mweb",
"mweb_unconfirmed": "Nepotvrzené mWeb",
"name": "název",
"nano_current_rep": "Současný zástupce",
"nano_gpt_thanks_message": "Děkujeme za používání Nanogpt! Nezapomeňte se po dokončení transakce vydat zpět do prohlížeče!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Light-Thema",
"moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
"more_options": "Weitere Optionen",
"mweb_confirmed": "Bestätigt MWeb",
"mweb_unconfirmed": "Unbestätigter MWeb",
"name": "Name",
"nano_current_rep": "Aktueller Vertreter",
"nano_gpt_thanks_message": "Danke, dass du Nanogpt benutzt hast! Denken Sie daran, nach Abschluss Ihrer Transaktion zurück zum Browser zu gehen!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Light Theme",
"moonpay_alert_text": "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
"more_options": "More Options",
"mweb_confirmed": "Confirmed MWEB",
"mweb_unconfirmed": "Unconfirmed MWEB",
"name": "Name",
"nano_current_rep": "Current Representative",
"nano_gpt_thanks_message": "Thanks for using NanoGPT! Remember to head back to the browser after your transaction completes!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Tema ligero de Monero",
"moonpay_alert_text": "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
"more_options": "Más Opciones",
"mweb_confirmed": "Confirmado mweb",
"mweb_unconfirmed": "Mweb no confirmado",
"name": "Nombre",
"nano_current_rep": "Representante actual",
"nano_gpt_thanks_message": "¡Gracias por usar nanogpt! ¡Recuerde regresar al navegador después de que se complete su transacción!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Thème de lumière Monero",
"moonpay_alert_text": "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
"more_options": "Plus d'options",
"mweb_confirmed": "Confirmé MWEB",
"mweb_unconfirmed": "Mweb non confirmé",
"name": "Nom",
"nano_current_rep": "Représentant actuel",
"nano_gpt_thanks_message": "Merci d'avoir utilisé Nanogpt! N'oubliez pas de retourner au navigateur une fois votre transaction terminée!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Jigon Hasken Monero",
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
"more_options": "Ƙarin Zaɓuɓɓuka",
"mweb_confirmed": "Tabbatar da Mweb",
"mweb_unconfirmed": "Myconfired",
"name": "Suna",
"nano_current_rep": "Wakilin Yanzu",
"nano_gpt_thanks_message": "Na gode da amfani da Nanogpt! Ka tuna da komawa zuwa mai bincike bayan ma'amalar ka ta cika!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "मोनेरो लाइट थीम",
"moonpay_alert_text": "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
"more_options": "और विकल्प",
"mweb_confirmed": "MWEB की पुष्टि की",
"mweb_unconfirmed": "अपुष्ट MWEB",
"name": "नाम",
"nano_current_rep": "वर्तमान प्रतिनिधि",
"nano_gpt_thanks_message": "Nanogpt का उपयोग करने के लिए धन्यवाद! अपने लेन -देन के पूरा होने के बाद ब्राउज़र पर वापस जाना याद रखें!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero lagana tema",
"moonpay_alert_text": "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
"more_options": "Više opcija",
"mweb_confirmed": "Potvrđen MWeb",
"mweb_unconfirmed": "Nepotvrđeni mWeb",
"name": "Ime",
"nano_current_rep": "Trenutni predstavnik",
"nano_gpt_thanks_message": "Hvala što ste koristili nanogpt! Ne zaboravite da se vratite u preglednik nakon što vam se transakcija završi!",

View file

@ -387,6 +387,8 @@
"monero_light_theme": "Monero պայծառ տեսք",
"moonpay_alert_text": "Գումարի արժեքը պետք է լինի հավասար կամ ավելի քան ${minAmount} ${fiatCurrency}",
"more_options": "Այլ տարբերակներ",
"mweb_confirmed": "Հաստատված MWEB",
"mweb_unconfirmed": "Չկարգավորված Mweb",
"name": "Անուն",
"nano_current_rep": "Ընթացիկ ներկայացուցիչ",
"nano_gpt_thanks_message": "Շնորհակալություն NanoGPT-ն օգտագործելու համար: Հիշեք վերադառնալ դիտարկիչ ձեր փոխանցումն ավարտելուց հետո",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Tema Cahaya Monero",
"moonpay_alert_text": "Nilai jumlah harus lebih atau sama dengan ${minAmount} ${fiatCurrency}",
"more_options": "Opsi Lainnya",
"mweb_confirmed": "Mengkonfirmasi mWeb",
"mweb_unconfirmed": "MWEB yang belum dikonfirmasi",
"name": "Nama",
"nano_current_rep": "Perwakilan saat ini",
"nano_gpt_thanks_message": "Terima kasih telah menggunakan Nanogpt! Ingatlah untuk kembali ke browser setelah transaksi Anda selesai!",

View file

@ -397,6 +397,8 @@
"monero_light_theme": "Tema leggero Monero",
"moonpay_alert_text": "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
"more_options": "Altre opzioni",
"mweb_confirmed": "MWeb confermato",
"mweb_unconfirmed": "MWeb non confermato",
"name": "Nome",
"nano_current_rep": "Rappresentante attuale",
"nano_gpt_thanks_message": "Grazie per aver usato il nanogpt! Ricorda di tornare al browser dopo il completamento della transazione!",

View file

@ -397,6 +397,8 @@
"monero_light_theme": "モネロ ライト テーマ",
"moonpay_alert_text": "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
"more_options": "その他のオプション",
"mweb_confirmed": "確認されたMWEB",
"mweb_unconfirmed": "未確認のMWEB",
"name": "名前",
"nano_current_rep": "現在の代表",
"nano_gpt_thanks_message": "NanoGptを使用してくれてありがとうトランザクションが完了したら、ブラウザに戻ることを忘れないでください",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "모네로 라이트 테마",
"moonpay_alert_text": "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
"more_options": "추가 옵션",
"mweb_confirmed": "확인 mweb",
"mweb_unconfirmed": "확인되지 않은 mweb",
"name": "이름",
"nano_current_rep": "현재 대표",
"nano_gpt_thanks_message": "Nanogpt를 사용해 주셔서 감사합니다! 거래가 완료된 후 브라우저로 돌아가는 것을 잊지 마십시오!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Light အပြင်အဆင်",
"moonpay_alert_text": "ပမာဏ၏တန်ဖိုးသည် ${minAmount} ${fiatCurrency} နှင့် ပိုနေရမည်",
"more_options": "နောက်ထပ် ရွေးချယ်စရာများ",
"mweb_confirmed": "အတည်ပြုလိုက် mweb",
"mweb_unconfirmed": "အတည်မပြုနိုင်သော mweb",
"name": "နာမည်",
"nano_current_rep": "လက်ရှိကိုယ်စားလှယ်",
"nano_gpt_thanks_message": "nanogpt ကိုသုံးပြီးကျေးဇူးတင်ပါတယ် သင်၏ငွေပေးငွေယူပြီးနောက် browser သို့ပြန်သွားရန်သတိရပါ။",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Light-thema",
"moonpay_alert_text": "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
"more_options": "Meer opties",
"mweb_confirmed": "Bevestigde MWEB",
"mweb_unconfirmed": "Onbevestigde MWEB",
"name": "Naam",
"nano_current_rep": "Huidige vertegenwoordiger",
"nano_gpt_thanks_message": "Bedankt voor het gebruik van Nanogpt! Vergeet niet om terug te gaan naar de browser nadat uw transactie is voltooid!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Lekki motyw Monero",
"moonpay_alert_text": "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
"more_options": "Więcej opcji",
"mweb_confirmed": "Potwierdził MWEB",
"mweb_unconfirmed": "Niepotwierdzone MWEB",
"name": "Nazwa",
"nano_current_rep": "Obecny przedstawiciel",
"nano_gpt_thanks_message": "Dzięki za użycie Nanogpt! Pamiętaj, aby wrócić do przeglądarki po zakończeniu transakcji!",

View file

@ -397,6 +397,8 @@
"monero_light_theme": "Monero Light Theme",
"moonpay_alert_text": "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
"more_options": "Mais opções",
"mweb_confirmed": "MWEB confirmado",
"mweb_unconfirmed": "MWEB não confirmado",
"name": "Nome",
"nano_current_rep": "Representante atual",
"nano_gpt_thanks_message": "Obrigado por usar o Nanogpt! Lembre -se de voltar para o navegador após a conclusão da transação!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Светлая тема Monero",
"moonpay_alert_text": "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
"more_options": "Дополнительные параметры",
"mweb_confirmed": "Подтверждено MWEB",
"mweb_unconfirmed": "Неподтвержденная MWEB",
"name": "Имя",
"nano_current_rep": "Нынешний представитель",
"nano_gpt_thanks_message": "Спасибо за использование Nanogpt! Не забудьте вернуться в браузер после завершения транзакции!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "ธีมแสง Monero",
"moonpay_alert_text": "มูลค่าของจำนวนต้องมากกว่าหรือเท่ากับ ${minAmount} ${fiatCurrency}",
"more_options": "ตัวเลือกเพิ่มเติม",
"mweb_confirmed": "MWEB ยืนยันแล้ว",
"mweb_unconfirmed": "mweb ที่ไม่ได้รับการยืนยัน",
"name": "ชื่อ",
"nano_current_rep": "ตัวแทนปัจจุบัน",
"nano_gpt_thanks_message": "ขอบคุณที่ใช้ Nanogpt! อย่าลืมกลับไปที่เบราว์เซอร์หลังจากการทำธุรกรรมของคุณเสร็จสิ้น!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Light Theme",
"moonpay_alert_text": "Ang halaga ay dapat na higit pa o katumbas ng ${minAmount} ${fiatCurrency}",
"more_options": "Higit pang mga Pagpipilian",
"mweb_confirmed": "Nakumpirma na MWeb",
"mweb_unconfirmed": "Hindi nakumpirma si Mweb",
"name": "Pangalan",
"nano_current_rep": "Kasalukuyang Representative",
"nano_gpt_thanks_message": "Salamat sa paggamit ng NanoGPT! Tandaan na bumalik sa browser matapos makumpleto ang iyong transaksyon!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Monero Hafif Tema",
"moonpay_alert_text": "Tutar ${minAmount} ${fiatCurrency} miktarına eşit veya daha fazla olmalıdır",
"more_options": "Daha Fazla Seçenek",
"mweb_confirmed": "Onaylanmış mweb",
"mweb_unconfirmed": "Doğrulanmamış mweb",
"name": "İsim",
"nano_current_rep": "Mevcut temsilci",
"nano_gpt_thanks_message": "Nanogpt kullandığınız için teşekkürler! İşleminiz tamamlandıktan sonra tarayıcıya geri dönmeyi unutmayın!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "Легка тема Monero",
"moonpay_alert_text": "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
"more_options": "Більше параметрів",
"mweb_confirmed": "Підтвердив Mweb",
"mweb_unconfirmed": "Неперевірений MWEB",
"name": "Ім'я",
"nano_current_rep": "Поточний представник",
"nano_gpt_thanks_message": "Дякуємо за використання наногпта! Не забудьте повернутися до браузера після завершення транзакції!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "مونیرو لائٹ تھیم",
"moonpay_alert_text": "رقم کی قدر ${minAmount} ${fiatCurrency} کے برابر یا زیادہ ہونی چاہیے۔",
"more_options": "مزید زرائے",
"mweb_confirmed": "تصدیق شدہ MWEB",
"mweb_unconfirmed": "غیر مصدقہ MWEB",
"name": "ﻡﺎﻧ",
"nano_current_rep": "موجودہ نمائندہ",
"nano_gpt_thanks_message": "نانوگپٹ استعمال کرنے کا شکریہ! اپنے لین دین کی تکمیل کے بعد براؤزر کی طرف واپس جانا یاد رکھیں!",

View file

@ -140,8 +140,8 @@
"confirm": "Xác nhận",
"confirm_delete_template": "Thao tác này sẽ xóa mẫu này. Bạn có muốn tiếp tục không?",
"confirm_delete_wallet": "Thao tác này sẽ xóa ví này. Bạn có muốn tiếp tục không?",
"confirm_fee_deduction": "Xác nhận Khấu trừ Phí",
"confirm_fee_dedction_content": "Bạn có đồng ý trừ phí từ đầu ra không?",
"confirm_fee_deduction": "Xác nhận Khấu trừ Phí",
"confirm_sending": "Xác nhận gửi",
"confirm_silent_payments_switch_node": "Nút hiện tại của bạn không hỗ trợ thanh toán im lặng\\nCake Wallet sẽ chuyển sang một nút tương thích chỉ để quét",
"confirmations": "Xác nhận",
@ -386,6 +386,8 @@
"monero_light_theme": "Chủ đề sáng Monero",
"moonpay_alert_text": "Giá trị số tiền phải lớn hơn hoặc bằng ${minAmount} ${fiatCurrency}",
"more_options": "Thêm tùy chọn",
"mweb_confirmed": "Xác nhận MWEB",
"mweb_unconfirmed": "MWEB chưa được xác nhận",
"name": "Tên",
"nano_current_rep": "Đại diện hiện tại",
"nano_gpt_thanks_message": "Cảm ơn bạn đã sử dụng NanoGPT! Hãy nhớ quay lại trình duyệt sau khi giao dịch của bạn hoàn tất!",

View file

@ -397,6 +397,8 @@
"monero_light_theme": "Monero Light Akori",
"moonpay_alert_text": "Iye owó kò gbọ́dọ̀ kéré ju ${minAmount} ${fiatCurrency}",
"more_options": "Ìyàn àfikún",
"mweb_confirmed": "Jẹrisi Mweb",
"mweb_unconfirmed": "Ajopo Mweb",
"name": "Oruko",
"nano_current_rep": "Aṣoju lọwọlọwọ",
"nano_gpt_thanks_message": "O ṣeun fun lilo Nonnogt! Ranti lati tẹle pada si ẹrọ lilọ kiri ayelujara lẹhin iṣowo rẹ pari!",

View file

@ -396,6 +396,8 @@
"monero_light_theme": "门罗币浅色主题",
"moonpay_alert_text": "金额的价值必须大于或等于 ${minAmount} ${fiatCurrency}",
"more_options": "更多选项",
"mweb_confirmed": "确认的MWEB",
"mweb_unconfirmed": "未经证实的MWEB",
"name": "姓名",
"nano_current_rep": "当前代表",
"nano_gpt_thanks_message": "感谢您使用Nanogpt事务完成后请记住回到浏览器",