Merge remote-tracking branch 'origin/main'

This commit is contained in:
OmarHatem 2025-05-05 11:50:13 +03:00
commit cd0844dcc1
60 changed files with 1080 additions and 779 deletions

View file

@ -0,0 +1,22 @@
<svg width="158" height="158" viewBox="0 0 158 158" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3076_287)">
<path d="M59.8674 111.213C61.104 109.782 62.8874 109.123 64.6415 109.237L69.6321 103.461C69.1122 101.951 69.369 100.216 70.4903 98.9183C71.6101 97.6223 73.2894 97.1164 74.8608 97.4098L79.8316 91.6568L65.7827 79.5181L42.7631 106.16C41.9574 107.093 41.5571 108.305 41.6467 109.532L41.9828 114.163L48.3733 119.684L53.4019 119.732C54.767 119.746 56.0685 119.159 56.9607 118.127L58.6055 116.223C58.2383 114.504 58.6308 112.644 59.8674 111.213Z" fill="url(#paint0_linear_3076_287)"/>
<path d="M115.362 82.4834C126.257 69.8745 124.867 50.823 112.258 39.9285C99.6491 29.0339 80.5975 30.4236 69.703 43.0325C69.1902 43.6261 68.7318 44.2453 68.2752 44.866L68.1857 44.7886L56.0469 58.8375C52.6951 62.7168 53.1228 68.5796 57.002 71.9314L88.6121 99.2435C92.4913 102.595 98.3541 102.168 101.706 98.2884L113.845 84.2395L113.755 84.1621C114.303 83.6203 114.849 83.077 115.362 82.4834ZM104.671 48.709C108.551 52.0608 108.978 57.9236 105.626 61.8029C102.275 65.6821 96.4118 66.1098 92.5325 62.758C88.6532 59.4061 88.2256 53.5434 91.5774 49.6641C94.9292 45.7849 100.792 45.3572 104.671 48.709Z" fill="url(#paint1_linear_3076_287)"/>
</g>
<defs>
<linearGradient id="paint0_linear_3076_287" x1="72.8481" y1="85.54" x2="45.4677" y2="117.229" gradientUnits="userSpaceOnUse">
<stop stop-color="#E5A505"/>
<stop offset="0.01" stop-color="#E9A804"/>
<stop offset="0.06" stop-color="#F4B102"/>
<stop offset="0.129" stop-color="#FBB600"/>
<stop offset="0.323" stop-color="#FDB700"/>
</linearGradient>
<linearGradient id="paint1_linear_3076_287" x1="91.8949" y1="31.3914" x2="84.3571" y2="99.6065" gradientUnits="userSpaceOnUse">
<stop stop-color="#FEDE00"/>
<stop offset="1" stop-color="#FFD000"/>
</linearGradient>
<clipPath id="clip0_3076_287">
<rect width="111.4" height="111.4" fill="white" transform="translate(73.146) rotate(40.8281)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/utils/print_verbose.dart';
@ -602,7 +603,25 @@ abstract class DecredWalletBase
throw "wallet already exists at $newDirPath";
}
await Directory(currentDirPath).rename(newDirPath);
final sourceDir = Directory(currentDirPath);
final targetDir = Directory(newDirPath);
if (!targetDir.existsSync()) {
await targetDir.create(recursive: true);
}
await for (final entity in sourceDir.list(recursive: true)) {
final relativePath = entity.path.substring(sourceDir.path.length+1);
final targetPath = p.join(targetDir.path, relativePath);
if (entity is File) {
await entity.rename(targetPath);
} else if (entity is Directory) {
await Directory(targetPath).create(recursive: true);
}
}
await sourceDir.delete(recursive: true);
}
@override

View file

@ -118,6 +118,10 @@ class DecredWalletService extends WalletService<
currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
? testnet
: mainnet;
if (libwallet == null) {
libwallet = await Libwallet.spawn();
libwallet!.initLibdcrwallet("", "err");
}
final currentWallet = DecredWallet(
currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);

View file

@ -2,31 +2,31 @@ import 'dart:async';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/monero_account_list.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/wallet2.dart';
import 'package:monero/src/monero.dart';
monero.wallet? wptr = null;
bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
Wallet2Wallet? currentWallet = null;
bool get isViewOnly => int.tryParse(currentWallet!.secretSpendKey()) == 0;
int _wlptrForW = 0;
monero.WalletListener? _wlptr = null;
Wallet2WalletListener? _wlptr = null;
monero.WalletListener? getWlptr() {
if (wptr == null) return null;
if (wptr!.address == _wlptrForW) return _wlptr!;
_wlptrForW = wptr!.address;
_wlptr = monero.MONERO_cw_getWalletListener(wptr!);
Wallet2WalletListener? getWlptr() {
if (currentWallet == null) return null;
_wlptrForW = currentWallet!.ffiAddress();
_wlptr = currentWallet!.getWalletListener();
return _wlptr!;
}
monero.SubaddressAccount? subaddressAccount;
Wallet2SubaddressAccount? subaddressAccount;
bool isUpdating = false;
void refreshAccounts() {
try {
isUpdating = true;
subaddressAccount = monero.Wallet_subaddressAccount(wptr!);
monero.SubaddressAccount_refresh(subaddressAccount!);
subaddressAccount = currentWallet!.subaddressAccount();
subaddressAccount!.refresh();
isUpdating = false;
} catch (e) {
isUpdating = false;
@ -34,45 +34,28 @@ void refreshAccounts() {
}
}
List<monero.SubaddressAccountRow> getAllAccount() {
List<Wallet2SubaddressAccountRow> getAllAccount() {
// final size = monero.Wallet_numSubaddressAccounts(wptr!);
refreshAccounts();
int size = monero.SubaddressAccount_getAll_size(subaddressAccount!);
int size = subaddressAccount!.getAll_size();
if (size == 0) {
monero.Wallet_addSubaddressAccount(wptr!);
monero.Wallet_status(wptr!);
currentWallet!.addSubaddressAccount();
currentWallet!.status();
return [];
}
return List.generate(size, (index) {
return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index);
return subaddressAccount!.getAll_byIndex(index);
});
}
void addAccountSync({required String label}) {
monero.Wallet_addSubaddressAccount(wptr!, label: label);
}
void setLabelForAccountSync({required int accountIndex, required String label}) {
monero.SubaddressAccount_setLabel(subaddressAccount!, accountIndex: accountIndex, label: label);
MoneroAccountListBase.cachedAccounts[wptr!.address] = [];
refreshAccounts();
}
void _addAccount(String label) => addAccountSync(label: label);
void _setLabelForAccount(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
setLabelForAccountSync(label: label, accountIndex: accountIndex);
}
Future<void> addAccount({required String label}) async {
_addAccount(label);
void addAccount({required String label}) {
currentWallet!.addSubaddressAccount(label: label);
unawaited(store());
}
Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
_setLabelForAccount({'accountIndex': accountIndex, 'label': label});
unawaited(store());
}
void setLabelForAccount({required int accountIndex, required String label}) {
subaddressAccount!.setLabel(accountIndex: accountIndex, label: label);
MoneroAccountListBase.cachedAccounts[currentWallet!.ffiAddress()] = [];
refreshAccounts();
unawaited(store());
}

View file

@ -3,17 +3,18 @@ import 'dart:isolate';
import 'package:cw_monero/api/account_list.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/wallet2.dart';
import 'package:mutex/mutex.dart';
monero.Coins? coins = null;
Wallet2Coins? coins = null;
final coinsMutex = Mutex();
Future<void> refreshCoins(int accountIndex) async {
if (coinsMutex.isLocked) {
return;
}
coins = monero.Wallet_coins(wptr!);
final coinsPtr = coins!.address;
coins = currentWallet!.coins();
final coinsPtr = coins!.ffiAddress();
await coinsMutex.acquire();
await Isolate.run(() => monero.Coins_refresh(Pointer.fromAddress(coinsPtr)));
coinsMutex.release();
@ -21,14 +22,14 @@ Future<void> refreshCoins(int accountIndex) async {
Future<int> countOfCoins() async {
await coinsMutex.acquire();
final count = monero.Coins_count(coins!);
final count = coins!.count();
coinsMutex.release();
return count;
}
Future<monero.CoinsInfo> getCoin(int index) async {
Future<Wallet2CoinsInfo> getCoin(int index) async {
await coinsMutex.acquire();
final coin = monero.Coins_coin(coins!, index);
final coin = coins!.coin(index);
coinsMutex.release();
return coin;
}
@ -37,7 +38,7 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
final count = await countOfCoins();
for (int i = 0; i < count; i++) {
final coin = await getCoin(i);
final coinAddress = monero.CoinsInfo_keyImage(coin);
final coinAddress = coin.keyImage;
if (keyImage == coinAddress) {
return i;
}
@ -47,14 +48,14 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
Future<void> freezeCoin(int index) async {
await coinsMutex.acquire();
final coinsPtr = coins!.address;
final coinsPtr = coins!.ffiAddress();
await Isolate.run(() => monero.Coins_setFrozen(Pointer.fromAddress(coinsPtr), index: index));
coinsMutex.release();
}
Future<void> thawCoin(int index) async {
await coinsMutex.acquire();
final coinsPtr = coins!.address;
final coinsPtr = coins!.ffiAddress();
await Isolate.run(() => monero.Coins_thaw(Pointer.fromAddress(coinsPtr), index: index));
coinsMutex.release();
}

View file

@ -2,7 +2,8 @@
import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/monero.dart';
import 'package:monero/src/monero.dart';
bool isUpdating = false;
@ -16,7 +17,7 @@ class SubaddressInfoMetadata {
SubaddressInfoMetadata? subaddress = null;
String getRawLabel({required int accountIndex, required int addressIndex}) {
return monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
return currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex);
}
void refreshSubaddresses({required int accountIndex}) {
@ -46,7 +47,7 @@ class Subaddress {
final int received;
final int txCount;
String get label {
final localLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
final localLabel = currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex);
if (localLabel.startsWith("#$addressIndex")) return localLabel; // don't duplicate the ID if it was user-providen
return "#$addressIndex ${localLabel}".trim();
}
@ -66,26 +67,26 @@ int lastTxCount = 0;
List<TinyTransactionDetails> ttDetails = [];
List<Subaddress> getAllSubaddresses() {
txhistory = monero.Wallet_history(wptr!);
final txCount = monero.TransactionHistory_count(txhistory!);
if (lastTxCount != txCount && lastWptr != wptr!.address) {
txhistory = currentWallet!.history();
final txCount = txhistory!.count();
if (lastTxCount != txCount && lastWptr != currentWallet!.ffiAddress()) {
final List<TinyTransactionDetails> newttDetails = [];
lastTxCount = txCount;
lastWptr = wptr!.address;
lastWptr = currentWallet!.ffiAddress();
for (var i = 0; i < txCount; i++) {
final tx = monero.TransactionHistory_transaction(txhistory!, index: i);
if (monero.TransactionInfo_direction(tx) == monero.TransactionInfo_Direction.Out) continue;
final subaddrs = monero.TransactionInfo_subaddrIndex(tx).split(",");
final account = monero.TransactionInfo_subaddrAccount(tx);
final tx = txhistory!.transaction(i);
if (tx.direction() == TransactionInfo_Direction.Out.index) continue;
final subaddrs = tx.subaddrIndex().split(",");
final account = tx.subaddrAccount();
newttDetails.add(TinyTransactionDetails(
address: List.generate(subaddrs.length, (index) => getAddress(accountIndex: account, addressIndex: int.tryParse(subaddrs[index])??0)),
amount: monero.TransactionInfo_amount(tx),
amount: tx.amount(),
));
}
ttDetails.clear();
ttDetails.addAll(newttDetails);
}
final size = monero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex);
final size = currentWallet!.numSubaddresses(accountIndex: subaddress!.accountIndex);
final list = List.generate(size, (index) {
final ttDetailsLocal = ttDetails.where((element) {
final address = getAddress(
@ -119,46 +120,17 @@ List<Subaddress> getAllSubaddresses() {
}
int numSubaddresses(int subaccountIndex) {
return monero.Wallet_numSubaddresses(wptr!, accountIndex: subaccountIndex);
}
void addSubaddressSync({required int accountIndex, required String label}) {
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label);
refreshSubaddresses(accountIndex: accountIndex);
}
void setLabelForSubaddressSync(
{required int accountIndex, required int addressIndex, required String label}) {
monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
void _addSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
addSubaddressSync(accountIndex: accountIndex, label: label);
}
void _setLabelForSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
final addressIndex = args['addressIndex'] as int;
setLabelForSubaddressSync(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
return currentWallet!.numSubaddresses(accountIndex: subaccountIndex);
}
Future<void> addSubaddress({required int accountIndex, required String label}) async {
_addSubaddress({'accountIndex': accountIndex, 'label': label});
currentWallet!.addSubaddress(accountIndex: accountIndex, label: label);
refreshSubaddresses(accountIndex: accountIndex);
await store();
}
Future<void> setLabelForSubaddress(
{required int accountIndex, required int addressIndex, required String label}) async {
_setLabelForSubaddress({
'accountIndex': accountIndex,
'addressIndex': addressIndex,
'label': label
});
{required int accountIndex, required int addressIndex, required String label}) async {
currentWallet!.setSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
await store();
}
}

View file

@ -9,36 +9,38 @@ import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
import 'package:ffi/ffi.dart';
import 'package:monero/src/monero.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/wallet2.dart';
import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
import 'package:mutex/mutex.dart';
Map<int, Map<String, String>> txKeys = {};
String getTxKey(String txId) {
txKeys[wptr!.address] ??= {};
if (txKeys[wptr!.address]![txId] != null) {
return txKeys[wptr!.address]![txId]!;
txKeys[currentWallet!.ffiAddress()] ??= {};
if (txKeys[currentWallet!.ffiAddress()]![txId] != null) {
return txKeys[currentWallet!.ffiAddress()]![txId]!;
}
final txKey = monero.Wallet_getTxKey(wptr!, txid: txId);
final status = monero.Wallet_status(wptr!);
final txKey = currentWallet!.getTxKey(txid: txId);
final status = currentWallet!.status();
if (status != 0) {
monero.Wallet_errorString(wptr!);
txKeys[wptr!.address]![txId] = "";
currentWallet!.errorString();
txKeys[currentWallet!.ffiAddress()]![txId] = "";
return "";
}
txKeys[wptr!.address]![txId] = txKey;
txKeys[currentWallet!.ffiAddress()]![txId] = txKey;
return txKey;
}
final txHistoryMutex = Mutex();
monero.TransactionHistory? txhistory;
Wallet2TransactionHistory? txhistory;
bool isRefreshingTx = false;
Future<void> refreshTransactions() async {
if (isRefreshingTx == true) return;
isRefreshingTx = true;
txhistory ??= monero.Wallet_history(wptr!);
final ptr = txhistory!.address;
txhistory ??= currentWallet!.history();
final ptr = txhistory!.ffiAddress();
await txHistoryMutex.acquire();
await Isolate.run(() {
monero.TransactionHistory_refresh(Pointer.fromAddress(ptr));
@ -48,14 +50,14 @@ Future<void> refreshTransactions() async {
isRefreshingTx = false;
}
int countOfTransactions() => monero.TransactionHistory_count(txhistory!);
int countOfTransactions() => txhistory!.count();
Future<List<Transaction>> getAllTransactions() async {
List<Transaction> dummyTxs = [];
await txHistoryMutex.acquire();
txhistory ??= monero.Wallet_history(wptr!);
final startAddress = txhistory!.address * wptr!.address;
txhistory ??= currentWallet!.history();
final startAddress = txhistory!.ffiAddress() * currentWallet!.ffiAddress();
int size = countOfTransactions();
final list = <Transaction>[];
for (int index = 0; index < size; index++) {
@ -63,21 +65,21 @@ Future<List<Transaction>> getAllTransactions() async {
// Give main thread a chance to do other things.
await Future.delayed(Duration.zero);
}
if (txhistory!.address * wptr!.address != startAddress) {
if (txhistory!.ffiAddress() * currentWallet!.ffiAddress() != startAddress) {
printV("Loop broken because txhistory!.address * wptr!.address != startAddress");
break;
}
final txInfo = monero.TransactionHistory_transaction(txhistory!, index: index);
final txHash = monero.TransactionInfo_hash(txInfo);
txCache[wptr!.address] ??= {};
txCache[wptr!.address]![txHash] = Transaction(txInfo: txInfo);
list.add(txCache[wptr!.address]![txHash]!);
final txInfo = txhistory!.transaction(index);
final txHash = txInfo.hash();
txCache[currentWallet!.ffiAddress()] ??= {};
txCache[currentWallet!.ffiAddress()]![txHash] = Transaction(txInfo: txInfo);
list.add(txCache[currentWallet!.ffiAddress()]![txHash]!);
}
txHistoryMutex.release();
final accts = monero.Wallet_numSubaddressAccounts(wptr!);
final accts = currentWallet!.numSubaddressAccounts();
for (var i = 0; i < accts; i++) {
final fullBalance = monero.Wallet_balance(wptr!, accountIndex: i);
final availBalance = monero.Wallet_unlockedBalance(wptr!, accountIndex: i);
final fullBalance = currentWallet!.balance(accountIndex: i);
final availBalance = currentWallet!.unlockedBalance(accountIndex: i);
if (fullBalance > availBalance) {
if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isEmpty) {
dummyTxs.add(
@ -95,7 +97,7 @@ Future<List<Transaction>> getAllTransactions() async {
isSpend: false,
hash: "pending",
key: "",
txInfo: Pointer.fromAddress(0),
txInfo: DummyTransaction(),
)..timeStamp = DateTime.now()
);
}
@ -105,16 +107,21 @@ Future<List<Transaction>> getAllTransactions() async {
return list;
}
class DummyTransaction implements Wallet2TransactionInfo {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
Map<int, Map<String, Transaction>> txCache = {};
Future<Transaction> getTransaction(String txId) async {
if (txCache[wptr!.address] != null && txCache[wptr!.address]![txId] != null) {
return txCache[wptr!.address]![txId]!;
if (txCache[currentWallet!.ffiAddress()] != null && txCache[currentWallet!.ffiAddress()]![txId] != null) {
return txCache[currentWallet!.ffiAddress()]![txId]!;
}
await txHistoryMutex.acquire();
final tx = monero.TransactionHistory_transactionById(txhistory!, txid: txId);
final tx = txhistory!.transactionById(txId);
final txDart = Transaction(txInfo: tx);
txCache[wptr!.address] ??= {};
txCache[wptr!.address]![txId] = txDart;
txCache[currentWallet!.ffiAddress()] ??= {};
txCache[currentWallet!.ffiAddress()]![txId] = txDart;
txHistoryMutex.release();
return txDart;
}
@ -127,9 +134,9 @@ Future<PendingTransactionDescription> createTransactionSync(
int accountIndex = 0,
List<String> preferredInputs = const []}) async {
final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount);
final amt = amount == null ? 0 : currentWallet!.amountFromString(amount);
final waddr = wptr!.address;
final waddr = currentWallet!.ffiAddress();
// force reconnection in case the os killed the connection?
// fixes failed to get block height error.
@ -149,7 +156,7 @@ Future<PendingTransactionDescription> createTransactionSync(
final paymentIdAddr = paymentId_.address;
final preferredInputsAddr = preferredInputs_.address;
final spaddr = monero.defaultSeparator.address;
final pendingTx = Pointer<Void>.fromAddress(await Isolate.run(() {
final pendingTxPtr = Pointer<Void>.fromAddress(await Isolate.run(() {
final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction(
Pointer.fromAddress(waddr),
Pointer.fromAddress(addraddr).cast(),
@ -163,15 +170,16 @@ Future<PendingTransactionDescription> createTransactionSync(
);
return tx.address;
}));
final Wallet2PendingTransaction pendingTx = MoneroPendingTransaction(pendingTxPtr);
calloc.free(address_);
calloc.free(paymentId_);
calloc.free(preferredInputs_);
final String? error = (() {
final status = monero.PendingTransaction_status(pendingTx);
final status = pendingTx.status();
if (status == 0) {
return null;
}
return monero.PendingTransaction_errorString(pendingTx);
return pendingTx.errorString();
})();
if (error != null) {
@ -182,10 +190,10 @@ Future<PendingTransactionDescription> createTransactionSync(
throw CreationTransactionException(message: message);
}
final rAmt = monero.PendingTransaction_amount(pendingTx);
final rFee = monero.PendingTransaction_fee(pendingTx);
final rHash = monero.PendingTransaction_txid(pendingTx, '');
final rHex = monero.PendingTransaction_hex(pendingTx, '');
final rAmt = pendingTx.amount();
final rFee = pendingTx.fee();
final rHash = pendingTx.txid('');
final rHex = pendingTx.hex('');
final rTxKey = rHash;
return PendingTransactionDescription(
@ -194,7 +202,7 @@ Future<PendingTransactionDescription> createTransactionSync(
hash: rHash,
hex: rHex,
txKey: rTxKey,
pointerAddress: pendingTx.address,
pointerAddress: pendingTx.ffiAddress(),
);
}
@ -206,9 +214,9 @@ Future<PendingTransactionDescription> createTransactionMultDest(
List<String> preferredInputs = const []}) async {
final dstAddrs = outputs.map((e) => e.address).toList();
final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList();
final amounts = outputs.map((e) => currentWallet!.amountFromString(e.amount)).toList();
final waddr = wptr!.address;
final waddr = currentWallet!.ffiAddress();
// force reconnection in case the os killed the connection
Isolate.run(() async {
@ -227,49 +235,50 @@ Future<PendingTransactionDescription> createTransactionMultDest(
).address;
}));
if (monero.PendingTransaction_status(txptr) != 0) {
throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr));
final Wallet2PendingTransaction tx = MoneroPendingTransaction(txptr);
if (tx.status() != 0) {
throw CreationTransactionException(message: tx.errorString());
}
return PendingTransactionDescription(
amount: monero.PendingTransaction_amount(txptr),
fee: monero.PendingTransaction_fee(txptr),
hash: monero.PendingTransaction_txid(txptr, ''),
hex: monero.PendingTransaction_hex(txptr, ''),
txKey: monero.PendingTransaction_txid(txptr, ''),
pointerAddress: txptr.address,
amount: tx.amount(),
fee: tx.fee(),
hash: tx.txid(''),
hex: tx.hex(''),
txKey: tx.txid(''),
pointerAddress: tx.ffiAddress(),
);
}
String? commitTransactionFromPointerAddress({required int address, required bool useUR}) =>
commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR);
commitTransaction(tx: MoneroPendingTransaction(Pointer.fromAddress(address)), useUR: useUR);
String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) {
final transactionPointerAddress = transactionPointer.address;
String? commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) {
final txCommit = useUR
? monero.PendingTransaction_commitUR(transactionPointer, 120)
? tx.commitUR(120)
: Isolate.run(() {
monero.PendingTransaction_commit(
Pointer.fromAddress(transactionPointerAddress),
Pointer.fromAddress(tx.ffiAddress()),
filename: '',
overwrite: false,
);
});
String? error = (() {
final status = monero.PendingTransaction_status(transactionPointer.cast());
final status = tx.status();
if (status == 0) {
return null;
}
return monero.PendingTransaction_errorString(transactionPointer.cast());
return tx.errorString();
})();
if (error == null) {
error = (() {
final status = monero.Wallet_status(wptr!);
final status = currentWallet!.status();
if (status == 0) {
return null;
}
return monero.Wallet_errorString(wptr!);
return currentWallet!.errorString();
})();
}
@ -283,43 +292,9 @@ String? commitTransaction({required monero.PendingTransaction transactionPointer
}
}
Future<PendingTransactionDescription> _createTransactionSync(Map args) async {
final address = args['address'] as String;
final paymentId = args['paymentId'] as String;
final amount = args['amount'] as String?;
final priorityRaw = args['priorityRaw'] as int;
final accountIndex = args['accountIndex'] as int;
final preferredInputs = args['preferredInputs'] as List<String>;
return createTransactionSync(
address: address,
paymentId: paymentId,
amount: amount,
priorityRaw: priorityRaw,
accountIndex: accountIndex,
preferredInputs: preferredInputs);
}
Future<PendingTransactionDescription> createTransaction(
{required String address,
required int priorityRaw,
String? amount,
String paymentId = '',
int accountIndex = 0,
List<String> preferredInputs = const []}) async =>
_createTransactionSync({
'address': address,
'paymentId': paymentId,
'amount': amount,
'priorityRaw': priorityRaw,
'accountIndex': accountIndex,
'preferredInputs': preferredInputs
});
class Transaction {
final String displayLabel;
late final String subaddressLabel = monero.Wallet_getSubaddressLabel(
wptr!,
late final String subaddressLabel = currentWallet!.getSubaddressLabel(
accountIndex: accountIndex,
addressIndex: addressIndex,
);
@ -372,26 +347,26 @@ class Transaction {
// final SubAddress? subAddress;
// List<Transfer> transfers = [];
// final int txIndex;
final monero.TransactionInfo txInfo;
final Wallet2TransactionInfo txInfo;
Transaction({
required this.txInfo,
}) : displayLabel = monero.TransactionInfo_label(txInfo),
hash = monero.TransactionInfo_hash(txInfo),
}) : displayLabel = txInfo.label(),
hash = txInfo.hash(),
timeStamp = DateTime.fromMillisecondsSinceEpoch(
monero.TransactionInfo_timestamp(txInfo) * 1000,
txInfo.timestamp() * 1000,
),
isSpend = monero.TransactionInfo_direction(txInfo) ==
monero.TransactionInfo_Direction.Out,
amount = monero.TransactionInfo_amount(txInfo),
paymentId = monero.TransactionInfo_paymentId(txInfo),
accountIndex = monero.TransactionInfo_subaddrAccount(txInfo),
addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0,
addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
blockheight = monero.TransactionInfo_blockHeight(txInfo),
confirmations = monero.TransactionInfo_confirmations(txInfo),
fee = monero.TransactionInfo_fee(txInfo),
description = monero.TransactionInfo_description(txInfo),
key = getTxKey(monero.TransactionInfo_hash(txInfo));
isSpend = txInfo.direction() ==
monero.TransactionInfo_Direction.Out.index,
amount = txInfo.amount(),
paymentId = txInfo.paymentId(),
accountIndex = txInfo.subaddrAccount(),
addressIndex = int.tryParse(txInfo.subaddrIndex().split(", ")[0]) ?? 0,
addressIndexList = txInfo.subaddrIndex().split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
blockheight = txInfo.blockHeight(),
confirmations = txInfo.confirmations(),
fee = txInfo.fee(),
description = txInfo.description(),
key = getTxKey(txInfo.hash());
Transaction.dummy({
required this.displayLabel,

View file

@ -5,8 +5,6 @@ import 'dart:isolate';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
import 'package:cw_monero/api/wallet_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:monero/monero.dart' as monero;
import 'package:mutex/mutex.dart';
import 'package:polyseed/polyseed.dart';
@ -15,36 +13,37 @@ bool debugMonero = false;
int getSyncingHeight() {
// final height = monero.MONERO_cw_WalletListener_height(getWlptr());
final h2 = monero.Wallet_blockChainHeight(wptr!);
if (currentWallet == null) return 0;
final h2 = currentWallet!.blockChainHeight();
// printV("height: $height / $h2");
return h2;
}
bool isNeededToRefresh() {
final wlptr = getWlptr();
if (wlptr == null) return false;
final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(wlptr);
monero.MONERO_cw_WalletListener_resetNeedToRefresh(wlptr);
final wl = getWlptr();
if (wl == null) return false;
final ret = wl.isNeedToRefresh();
wl.resetNeedToRefresh();
return ret;
}
bool isNewTransactionExist() {
final wlptr = getWlptr();
if (wlptr == null) return false;
final ret = monero.MONERO_cw_WalletListener_isNewTransactionExist(wlptr);
monero.MONERO_cw_WalletListener_resetIsNewTransactionExist(wlptr);
final ret = wlptr.isNewTransactionExist();
wlptr.resetIsNewTransactionExist();
return ret;
}
String getFilename() => monero.Wallet_filename(wptr!);
String getFilename() => currentWallet!.filename();
String getSeed() {
// monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
final cakepolyseed =
monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed");
currentWallet!.getCacheAttribute(key: "cakewallet.seed");
final cakepassphrase = getPassphrase();
final weirdPolyseed = monero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase);
final weirdPolyseed = currentWallet!.getPolyseed(passphrase: cakepassphrase);
if (weirdPolyseed != "") return weirdPolyseed;
if (cakepolyseed != "") {
@ -63,7 +62,7 @@ String getSeed() {
return cakepolyseed;
}
final bip39 = monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed.bip39");
final bip39 = currentWallet!.getCacheAttribute(key: "cakewallet.seed.bip39");
if(bip39.isNotEmpty) return bip39;
@ -85,29 +84,29 @@ String? getSeedLanguage(String? language) {
String getSeedLegacy(String? language) {
final cakepassphrase = getPassphrase();
language = getSeedLanguage(language);
var legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
if (monero.Wallet_status(wptr!) != 0) {
if (monero.Wallet_errorString(wptr!).contains("seed_language")) {
monero.Wallet_setSeedLanguage(wptr!, language: "English");
legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
var legacy = currentWallet!.seed(seedOffset: cakepassphrase);
if (currentWallet!.status() != 0) {
if (currentWallet!.errorString().contains("seed_language")) {
currentWallet!.setSeedLanguage(language: "English");
legacy = currentWallet!.seed(seedOffset: cakepassphrase);
}
}
if (language != null) {
monero.Wallet_setSeedLanguage(wptr!, language: language);
final status = monero.Wallet_status(wptr!);
currentWallet!.setSeedLanguage(language: language);
final status = currentWallet!.status();
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = currentWallet!.errorString();
if (legacy.isNotEmpty) {
return "$err\n\n$legacy";
}
return err;
}
legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
legacy = currentWallet!.seed(seedOffset: cakepassphrase);
}
if (monero.Wallet_status(wptr!) != 0) {
final err = monero.Wallet_errorString(wptr!);
if (currentWallet!.status() != 0) {
final err = currentWallet!.errorString();
if (legacy.isNotEmpty) {
return "$err\n\n$legacy";
}
@ -117,7 +116,7 @@ String getSeedLegacy(String? language) {
}
String getPassphrase() {
return monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase");
return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
}
Map<int, Map<int, Map<int, String>>> addressCache = {};
@ -125,31 +124,31 @@ Map<int, Map<int, Map<int, String>>> addressCache = {};
String getAddress({int accountIndex = 0, int addressIndex = 0}) {
// printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}");
// this could be a while loop, but I'm in favor of making it if to not cause freezes
if (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)-1 < addressIndex) {
if (monero.Wallet_numSubaddressAccounts(wptr!) < accountIndex) {
monero.Wallet_addSubaddressAccount(wptr!);
if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
if (currentWallet!.numSubaddressAccounts() < accountIndex) {
currentWallet!.addSubaddressAccount();
} else {
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex);
currentWallet!.addSubaddress(accountIndex: accountIndex);
}
}
addressCache[wptr!.address] ??= {};
addressCache[wptr!.address]![accountIndex] ??= {};
addressCache[wptr!.address]![accountIndex]![addressIndex] ??= monero.Wallet_address(wptr!,
addressCache[currentWallet!.ffiAddress()] ??= {};
addressCache[currentWallet!.ffiAddress()]![accountIndex] ??= {};
addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex] ??= currentWallet!.address(
accountIndex: accountIndex, addressIndex: addressIndex);
return addressCache[wptr!.address]![accountIndex]![addressIndex]!;
return addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex]!;
}
int getFullBalance({int accountIndex = 0}) =>
monero.Wallet_balance(wptr!, accountIndex: accountIndex);
currentWallet!.balance(accountIndex: accountIndex);
int getUnlockedBalance({int accountIndex = 0}) =>
monero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
currentWallet!.unlockedBalance(accountIndex: accountIndex);
int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!);
int getCurrentHeight() => currentWallet!.blockChainHeight();
int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!);
int getNodeHeightSync() => currentWallet!.daemonBlockChainHeight();
bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0;
bool isConnectedSync() => currentWallet!.connected() != 0;
Future<bool> setupNodeSync(
{required String address,
@ -168,7 +167,7 @@ Future<bool> setupNodeSync(
daemonPassword: $password ?? ''
}
''');
final addr = wptr!.address;
final addr = currentWallet!.ffiAddress();
printV("init: start");
await Isolate.run(() {
monero.Wallet_init(Pointer.fromAddress(addr),
@ -180,10 +179,10 @@ Future<bool> setupNodeSync(
});
printV("init: end");
final status = monero.Wallet_status(wptr!);
final status = currentWallet!.status();
if (status != 0) {
final error = monero.Wallet_errorString(wptr!);
final error = currentWallet!.errorString();
if (error != "no tx keys found for this txid") {
printV("error: $error");
throw SetupWalletException(message: error);
@ -191,8 +190,8 @@ Future<bool> setupNodeSync(
}
if (true) {
monero.Wallet_init3(
wptr!, argv0: '',
currentWallet!.init3(
argv0: '',
defaultLogBaseName: 'moneroc',
console: true,
logPath: '',
@ -203,19 +202,19 @@ Future<bool> setupNodeSync(
}
void startRefreshSync() {
monero.Wallet_refreshAsync(wptr!);
monero.Wallet_startRefresh(wptr!);
currentWallet!.refreshAsync();
currentWallet!.startRefresh();
}
void setRefreshFromBlockHeight({required int height}) {
monero.Wallet_setRefreshFromBlockHeight(wptr!,
currentWallet!.setRefreshFromBlockHeight(
refresh_from_block_height: height);
}
void setRecoveringFromSeed({required bool isRecovery}) {
monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery);
monero.Wallet_store(wptr!);
currentWallet!.setRecoveringFromSeed(recoveringFromSeed: isRecovery);
currentWallet!.store();
}
final storeMutex = Mutex();
@ -224,18 +223,18 @@ final storeMutex = Mutex();
int lastStorePointer = 0;
int lastStoreHeight = 0;
void storeSync({bool force = false}) async {
final addr = wptr!.address;
final addr = currentWallet!.ffiAddress();
final synchronized = await Isolate.run(() {
return monero.Wallet_synchronized(Pointer.fromAddress(addr));
});
if (lastStorePointer == wptr!.address &&
lastStoreHeight + 5000 > monero.Wallet_blockChainHeight(wptr!) &&
if (lastStorePointer == addr &&
lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
!synchronized &&
!force) {
return;
}
lastStorePointer = wptr!.address;
lastStoreHeight = monero.Wallet_blockChainHeight(wptr!);
lastStorePointer = currentWallet!.ffiAddress();
lastStoreHeight = currentWallet!.blockChainHeight();
await storeMutex.acquire();
await Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr));
@ -244,25 +243,25 @@ void storeSync({bool force = false}) async {
}
void setPasswordSync(String password) {
monero.Wallet_setPassword(wptr!, password: password);
currentWallet!.setPassword(password: password);
final status = monero.Wallet_status(wptr!);
final status = currentWallet!.status();
if (status != 0) {
throw Exception(monero.Wallet_errorString(wptr!));
throw Exception(currentWallet!.errorString());
}
}
void closeCurrentWallet() {
monero.Wallet_stop(wptr!);
currentWallet!.stop();
}
String getSecretViewKey() => monero.Wallet_secretViewKey(wptr!);
String getSecretViewKey() => currentWallet!.secretViewKey();
String getPublicViewKey() => monero.Wallet_publicViewKey(wptr!);
String getPublicViewKey() => currentWallet!.publicViewKey();
String getSecretSpendKey() => monero.Wallet_secretSpendKey(wptr!);
String getSecretSpendKey() => currentWallet!.secretSpendKey();
String getPublicSpendKey() => monero.Wallet_publicSpendKey(wptr!);
String getPublicSpendKey() => currentWallet!.publicSpendKey();
class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction)
@ -360,52 +359,32 @@ Future<bool> _setupNodeSync(Map<String, Object?> args) async {
socksProxyAddress: socksProxyAddress);
}
bool _isConnected(Object _) => isConnectedSync();
int _getNodeHeight(Object _) => getNodeHeightSync();
void startRefresh() => startRefreshSync();
Future<void> setupNode(
{required String address,
String? login,
String? password,
bool useSSL = false,
String? socksProxyAddress,
bool isLightWallet = false}) async =>
_setupNodeSync({
'address': address,
'login': login,
'password': password,
'useSSL': useSSL,
'isLightWallet': isLightWallet,
'socksProxyAddress': socksProxyAddress
});
Future<void> store() async => _storeSync(0);
Future<bool> isConnected() async => _isConnected(0);
Future<bool> isConnected() async => isConnectedSync();
Future<int> getNodeHeight() async => _getNodeHeight(0);
Future<int> getNodeHeight() async => getNodeHeightSync();
void rescanBlockchainAsync() => monero.Wallet_rescanBlockchainAsync(wptr!);
void rescanBlockchainAsync() => currentWallet!.rescanBlockchainAsync();
String getSubaddressLabel(int accountIndex, int addressIndex) {
return monero.Wallet_getSubaddressLabel(wptr!,
return currentWallet!.getSubaddressLabel(
accountIndex: accountIndex, addressIndex: addressIndex);
}
Future setTrustedDaemon(bool trusted) async =>
monero.Wallet_setTrustedDaemon(wptr!, arg: trusted);
currentWallet!.setTrustedDaemon(arg: trusted);
Future<bool> trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!);
Future<bool> trustedDaemon() async => currentWallet!.trustedDaemon();
String signMessage(String message, {String address = ""}) {
return monero.Wallet_signMessage(wptr!, message: message, address: address);
return currentWallet!.signMessage(message: message, address: address);
}
bool verifyMessage(String message, String address, String signature) {
return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
return currentWallet!.verifySignedMessage(message: message, address: address, signature: signature);
}
Map<String, List<int>> debugCallLength() => monero.debugCallLength;

View file

@ -12,6 +12,8 @@ import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.dart';
import 'package:cw_monero/ledger.dart';
import 'package:flutter/foundation.dart';
import 'package:monero/src/monero.dart';
import 'package:monero/src/wallet2.dart';
import 'package:monero/monero.dart' as monero;
class MoneroCException implements Exception {
@ -24,9 +26,10 @@ class MoneroCException implements Exception {
}
void checkIfMoneroCIsFine() {
final cppCsCpp = monero.MONERO_checksum_wallet2_api_c_cpp();
final cppCsH = monero.MONERO_checksum_wallet2_api_c_h();
final cppCsExp = monero.MONERO_checksum_wallet2_api_c_exp();
final checksum = MoneroWalletChecksum();
final cppCsCpp = checksum.checksum_wallet2_api_c_cpp();
final cppCsH = checksum.checksum_wallet2_api_c_h();
final cppCsExp = checksum.checksum_wallet2_api_c_exp();
final dartCsCpp = monero.wallet2_api_c_cpp_sha256;
final dartCsH = monero.wallet2_api_c_h_sha256;
@ -44,36 +47,35 @@ void checkIfMoneroCIsFine() {
throw MoneroCException("monero_c and monero.dart wrapper export list mismatch.\nLogic errors can occur.\nRefusing to run in release mode.\ncpp: '$cppCsExp'\ndart: '$dartCsExp'");
}
}
monero.WalletManager? _wmPtr;
final monero.WalletManager wmPtr = Pointer.fromAddress((() {
Wallet2WalletManager? _wmPtr;
Wallet2WalletManager wmPtr = (() {
try {
// Problems with the wallet? Crashes? Lags? this will print all calls to xmr
// codebase, so it will be easier to debug what happens. At least easier
// than plugging gdb in. Especially on windows/android.
monero.printStarts = false;
if (kDebugMode && debugMonero) {
monero.WalletManagerFactory_setLogLevel(4);
MoneroWalletManagerFactory().setLogLevel(4);
}
_wmPtr ??= monero.WalletManagerFactory_getWalletManager();
_wmPtr ??= MoneroWalletManagerFactory().getWalletManager();
if (kDebugMode && debugMonero) {
monero.WalletManagerFactory_setLogLevel(4);
MoneroWalletManagerFactory().setLogLevel(4);
}
printV("ptr: $_wmPtr");
} catch (e) {
printV(e);
rethrow;
}
return _wmPtr!.address;
})());
return _wmPtr!;
})();
void createWalletPointer() {
final newWptr = monero.WalletManager_createWallet(wmPtr,
Wallet2Wallet createWalletPointer() {
final newWptr = wmPtr.createWallet(
path: "", password: "", language: "", networkType: 0);
wptr = newWptr;
return newWptr;
}
void createWalletSync(
void createWallet(
{required String path,
required String password,
required String language,
@ -81,28 +83,25 @@ void createWalletSync(
int nettype = 0}) {
txhistory = null;
language = getSeedLanguage(language)!;
final newWptr = monero.WalletManager_createWallet(wmPtr,
final newW = wmPtr.createWallet(
path: path, password: password, language: language, networkType: 0);
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
throw WalletCreationException(message: monero.Wallet_errorString(newWptr));
throw WalletCreationException(message: newW.errorString());
}
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, newW);
wptr = newWptr;
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
monero.Wallet_store(wptr!, path: path);
openedWalletsByPath[path] = wptr!;
currentWallet = newW;
currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase);
currentWallet!.store(path: path);
openedWalletsByPath[path] = currentWallet!;
_lastOpenedWallet = path;
// is the line below needed?
// setupNodeSync(address: "node.moneroworld.com:18089");
}
bool isWalletExistSync({required String path}) {
return monero.WalletManager_walletExists(wmPtr, path);
bool isWalletExist({required String path}) {
return wmPtr.walletExists(path);
}
void restoreWalletFromSeedSync(
@ -113,8 +112,7 @@ void restoreWalletFromSeedSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
final newWptr = monero.WalletManager_recoveryWallet(
wmPtr,
final newW = wmPtr.recoveryWallet(
path: path,
password: password,
mnemonic: seed,
@ -123,10 +121,10 @@ void restoreWalletFromSeedSync(
networkType: 0,
);
final status = monero.Wallet_status(newWptr);
final status = newW.status();
if (status != 0) {
final error = monero.Wallet_errorString(newWptr);
final error = newW.errorString();
if (error.contains('word list failed verification')) {
throw WalletRestoreFromSeedException(
message: "Seed verification failed, please make sure you entered the correct seed with the correct words order",
@ -134,20 +132,20 @@ void restoreWalletFromSeedSync(
}
throw WalletRestoreFromSeedException(message: error);
}
wptr = newWptr;
currentWallet = newW;
setRefreshFromBlockHeight(height: restoreHeight);
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, newW);
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase);
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
monero.Wallet_store(wptr!);
currentWallet!.store(path: path);
_lastOpenedWallet = path;
}
void restoreWalletFromKeysSync(
void restoreWalletFromKeys(
{required String path,
required String password,
required String language,
@ -157,8 +155,8 @@ void restoreWalletFromKeysSync(
int nettype = 0,
int restoreHeight = 0}) {
txhistory = null;
var newWptr = (spendKey != "")
? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr,
var newW = (spendKey != "")
? wmPtr.createDeterministicWalletFromSpendKey(
path: path,
password: password,
language: language,
@ -166,8 +164,7 @@ void restoreWalletFromKeysSync(
newWallet: true,
// TODO(mrcyjanek): safe to remove
restoreHeight: restoreHeight)
: monero.WalletManager_createWalletFromKeys(
wmPtr,
: wmPtr.createWalletFromKeys(
path: path,
password: password,
restoreHeight: restoreHeight,
@ -177,22 +174,21 @@ void restoreWalletFromKeysSync(
nettype: 0,
);
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(newWptr));
message: newW.errorString());
}
// 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);
final viewKeyRestored = newW.secretViewKey();
if (viewKey != viewKeyRestored && viewKey != "") {
monero.WalletManager_closeWallet(wmPtr, newWptr, false);
wmPtr.closeWallet(newW, false);
File(path).deleteSync();
File(path + ".keys").deleteSync();
newWptr = monero.WalletManager_createWalletFromKeys(
wmPtr,
newW = wmPtr.createWalletFromKeys(
path: path,
password: password,
restoreHeight: restoreHeight,
@ -201,19 +197,19 @@ void restoreWalletFromKeysSync(
spendKeyString: spendKey,
nettype: 0,
);
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(newWptr));
message: newW.errorString());
}
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, newW);
}
}
wptr = newWptr;
currentWallet = newW;
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
_lastOpenedWallet = path;
}
@ -228,8 +224,7 @@ void restoreWalletFromPolyseedWithOffset(
int nettype = 0}) {
txhistory = null;
final newWptr = monero.WalletManager_createWalletFromPolyseed(
wmPtr,
final newW = wmPtr.createWalletFromPolyseed(
path: path,
password: password,
networkType: nettype,
@ -240,24 +235,24 @@ void restoreWalletFromPolyseedWithOffset(
kdfRounds: 1,
);
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
final err = monero.Wallet_errorString(newWptr);
final err = newW.errorString();
printV("err: $err");
throw WalletRestoreFromKeysException(message: err);
}
wptr = newWptr;
currentWallet = newW;
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset);
monero.Wallet_store(wptr!);
currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: seedOffset);
currentWallet!.store(path: path);
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, currentWallet!);
storeSync();
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
}
@ -282,8 +277,7 @@ void restoreWalletFromSpendKeySync(
// );
txhistory = null;
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
wmPtr,
final newW = wmPtr.createDeterministicWalletFromSpendKey(
path: path,
password: password,
language: language,
@ -292,23 +286,23 @@ void restoreWalletFromSpendKeySync(
restoreHeight: restoreHeight,
);
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
final err = monero.Wallet_errorString(newWptr);
final err = newW.errorString();
printV("err: $err");
throw WalletRestoreFromKeysException(message: err);
}
wptr = newWptr;
currentWallet = newW;
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
storeSync();
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, currentWallet!);
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
_lastOpenedWallet = path;
}
@ -321,41 +315,42 @@ Future<void> restoreWalletFromHardwareWallet(
int nettype = 0,
int restoreHeight = 0}) async {
txhistory = null;
final wmPtr = MoneroWalletManagerFactory().getWalletManager().ffiAddress();
final newWptrAddr = await Isolate.run(() {
return monero.WalletManager_createWalletFromDevice(wmPtr,
return monero.WalletManager_createWalletFromDevice(Pointer.fromAddress(wmPtr),
path: path,
password: password,
restoreHeight: restoreHeight,
deviceName: deviceName)
.address;
});
final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr));
final status = monero.Wallet_status(newWptr);
final status = newW.status();
if (status != 0) {
final error = monero.Wallet_errorString(newWptr);
final error = newW.errorString();
throw WalletRestoreFromSeedException(message: error);
}
wptr = newWptr;
currentWallet = newW;
currentWallet!.store(path: path);
_lastOpenedWallet = path;
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
}
Map<String, monero.wallet> openedWalletsByPath = {};
Map<String, Wallet2Wallet> openedWalletsByPath = {};
Future<void> loadWallet(
{required String path, required String password, int nettype = 0}) async {
if (openedWalletsByPath[path] != null) {
txhistory = null;
wptr = openedWalletsByPath[path]!;
currentWallet = openedWalletsByPath[path]!;
return;
}
if (wptr == null || path != _lastOpenedWallet) {
if (wptr != null) {
final addr = wptr!.address;
if (currentWallet == null || path != _lastOpenedWallet) {
if (currentWallet != null) {
final addr = currentWallet!.ffiAddress();
Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr));
});
@ -366,19 +361,24 @@ Future<void> loadWallet(
/// 0: Software Wallet
/// 1: Ledger
/// 2: Trezor
late final deviceType;
var deviceType = 0;
if (Platform.isAndroid || Platform.isIOS) {
deviceType = monero.WalletManager_queryWalletDevice(
wmPtr,
deviceType = wmPtr.queryWalletDevice(
keysFileName: "$path.keys",
password: password,
kdfRounds: 1,
);
final status = monero.WalletManager_errorString(wmPtr);
final status = wmPtr.errorString();
if (status != "") {
printV("loadWallet:"+status);
throw WalletOpeningException(message: status);
// This is most likely closeWallet call leaking error. This is fine.
if (status.contains("failed to save file")) {
printV("loadWallet: error leaked: $status");
deviceType = 0;
} else {
throw WalletOpeningException(message: status);
}
}
} else {
deviceType = 0;
@ -388,107 +388,47 @@ Future<void> loadWallet(
if (gLedger == null) {
throw Exception("Tried to open a ledger wallet with no ledger connected");
}
final dummyWPtr = wptr ??
monero.WalletManager_openWallet(wmPtr, path: '', password: '');
final dummyWPtr = (currentWallet ??
wmPtr.openWallet(path: '', password: ''));
enableLedgerExchange(dummyWPtr, gLedger!);
}
final addr = wmPtr.address;
final addr = wmPtr.ffiAddress();
final newWptrAddr = await Isolate.run(() {
return monero.WalletManager_openWallet(Pointer.fromAddress(addr),
path: path, password: password)
.address;
});
final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr));
int status = monero.Wallet_status(newWptr);
int status = newW.status();
if (status != 0) {
final err = monero.Wallet_errorString(newWptr);
final err = newW.errorString();
printV("loadWallet:"+err);
throw WalletOpeningException(message: err);
}
if (deviceType == 0) {
setupBackgroundSync(password, newWptr);
setupBackgroundSync(password, newW);
}
wptr = newWptr;
currentWallet = newW;
_lastOpenedWallet = path;
openedWalletsByPath[path] = wptr!;
openedWalletsByPath[path] = currentWallet!;
}
}
void setupBackgroundSync(String password, Pointer<Void>? wptrOverride) {
if (isViewOnlyBySpendKey(wptrOverride)) {
void setupBackgroundSync(String password, Wallet2Wallet wallet) {
if (isViewOnlyBySpendKey(wallet)) {
return;
}
monero.Wallet_setupBackgroundSync(wptrOverride ?? wptr!, backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: '');
if (monero.Wallet_status(wptrOverride ?? wptr!) != 0) {
wallet.setupBackgroundSync(backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: '');
if (wallet.status() != 0) {
// We simply ignore the error.
printV("setupBackgroundSync: ${monero.Wallet_errorString(wptrOverride ?? wptr!)}");
printV("setupBackgroundSync: ${wallet.errorString()}");
}
}
void _createWallet(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
final passphrase = args['passphrase'] as String;
createWalletSync(path: path, password: password, language: language, passphrase: passphrase);
}
void _restoreFromSeed(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final passphrase = args['passphrase'] as String;
final seed = args['seed'] as String;
final restoreHeight = args['restoreHeight'] as int;
return restoreWalletFromSeedSync(
path: path, password: password, passphrase: passphrase, seed: seed, restoreHeight: restoreHeight);
}
void _restoreFromKeys(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final language = args['language'] as String;
final restoreHeight = args['restoreHeight'] as int;
final address = args['address'] as String;
final viewKey = args['viewKey'] as String;
final spendKey = args['spendKey'] as String;
restoreWalletFromKeysSync(
path: path,
password: password,
language: language,
restoreHeight: restoreHeight,
address: address,
viewKey: viewKey,
spendKey: spendKey);
}
void _restoreFromSpendKey(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final seed = args['seed'] as String;
final language = args['language'] as String;
final spendKey = args['spendKey'] as String;
final restoreHeight = args['restoreHeight'] as int;
restoreWalletFromSpendKeySync(
path: path,
password: password,
seed: seed,
language: language,
restoreHeight: restoreHeight,
spendKey: spendKey);
}
Future<void> _openWallet(Map<String, String> args) async => loadWallet(
path: args['path'] as String, password: args['password'] as String);
bool _isWalletExist(String path) => isWalletExistSync(path: path);
Future<void> openWallet(
{required String path,
@ -496,77 +436,4 @@ Future<void> openWallet(
int nettype = 0}) async =>
loadWallet(path: path, password: password, nettype: nettype);
Future<void> openWalletAsync(Map<String, String> args) async =>
_openWallet(args);
Future<void> createWallet(
{required String path,
required String password,
required String language,
required String passphrase,
int nettype = 0}) async =>
_createWallet({
'path': path,
'password': password,
'language': language,
'passphrase': passphrase,
'nettype': nettype
});
void restoreFromSeed(
{required String path,
required String password,
required String passphrase,
required String seed,
int nettype = 0,
int restoreHeight = 0}) =>
_restoreFromSeed({
'path': path,
'password': password,
'passphrase': passphrase,
'seed': seed,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future<void> restoreFromKeys(
{required String path,
required String password,
required String language,
required String address,
required String viewKey,
required String spendKey,
int nettype = 0,
int restoreHeight = 0}) async =>
_restoreFromKeys({
'path': path,
'password': password,
'language': language,
'address': address,
'viewKey': viewKey,
'spendKey': spendKey,
'nettype': nettype,
'restoreHeight': restoreHeight
});
Future<void> restoreFromSpendKey(
{required String path,
required String password,
required String seed,
required String language,
required String spendKey,
int nettype = 0,
int restoreHeight = 0}) async =>
_restoreFromSpendKey({
'path': path,
'password': password,
'seed': seed,
'language': language,
'spendKey': spendKey,
'nettype': nettype,
'restoreHeight': restoreHeight
});
bool isWalletExist({required String path}) => _isWalletExist(path);
bool isViewOnlyBySpendKey(Pointer<Void>? wptrOverride) => int.tryParse(monero.Wallet_secretSpendKey(wptrOverride ?? wptr!)) == 0;
bool isViewOnlyBySpendKey(Wallet2Wallet? wallet) => int.tryParse((wallet??currentWallet!).secretSpendKey()) == 0;

View file

@ -7,26 +7,26 @@ import 'package:cw_core/utils/print_verbose.dart';
import 'package:ffi/ffi.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/wallet2.dart';
LedgerConnection? gLedger;
Timer? _ledgerExchangeTimer;
Timer? _ledgerKeepAlive;
void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
void enableLedgerExchange(Wallet2Wallet wallet, LedgerConnection connection) {
_ledgerExchangeTimer?.cancel();
_ledgerExchangeTimer = Timer.periodic(Duration(milliseconds: 1), (_) async {
final ledgerRequestLength = monero.Wallet_getSendToDeviceLength(ptr);
final ledgerRequest = monero.Wallet_getSendToDevice(ptr)
final ledgerRequestLength = wallet.getSendToDeviceLength();
final ledgerRequest = wallet.getSendToDevice()
.cast<Uint8>()
.asTypedList(ledgerRequestLength);
if (ledgerRequestLength > 0) {
_ledgerKeepAlive?.cancel();
final Pointer<Uint8> emptyPointer = malloc<Uint8>(0);
monero.Wallet_setDeviceSendData(
ptr, emptyPointer.cast<UnsignedChar>(), 0);
wallet.setDeviceSendData(
emptyPointer.cast<UnsignedChar>(), 0);
malloc.free(emptyPointer);
_logLedgerCommand(ledgerRequest, false);
@ -45,8 +45,8 @@ void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
result.asTypedList(response.length)[i] = response[i];
}
monero.Wallet_setDeviceReceivedData(
ptr, result.cast<UnsignedChar>(), response.length);
wallet.setDeviceReceivedData(
result.cast<UnsignedChar>(), response.length);
malloc.free(result);
keepAlive(connection);
}

View file

@ -4,7 +4,7 @@ import 'package:cw_monero/api/wallet_manager.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/account.dart';
import 'package:cw_monero/api/account_list.dart' as account_list;
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/monero.dart';
part 'monero_account_list.g.dart';
@ -50,32 +50,32 @@ abstract class MoneroAccountListBase with Store {
List<Account> getAll() {
final allAccounts = account_list.getAllAccount();
final currentCount = allAccounts.length;
cachedAccounts[account_list.wptr!.address] ??= [];
cachedAccounts[account_list.currentWallet!.ffiAddress()] ??= [];
if (cachedAccounts[account_list.wptr!.address]!.length == currentCount) {
return cachedAccounts[account_list.wptr!.address]!;
if (cachedAccounts[account_list.currentWallet!.ffiAddress()]!.length == currentCount) {
return cachedAccounts[account_list.currentWallet!.ffiAddress()]!;
}
cachedAccounts[account_list.wptr!.address] = allAccounts.map((accountRow) {
final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow);
cachedAccounts[account_list.currentWallet!.ffiAddress()] = allAccounts.map((accountRow) {
final balance = accountRow.getUnlockedBalance();
return Account(
id: monero.SubaddressAccountRow_getRowId(accountRow),
label: monero.SubaddressAccountRow_getLabel(accountRow),
balance: moneroAmountToString(amount: monero.Wallet_amountFromString(balance)),
id: accountRow.getRowId(),
label: accountRow.getLabel(),
balance: moneroAmountToString(amount: account_list.currentWallet!.amountFromString(balance)),
);
}).toList();
return cachedAccounts[account_list.wptr!.address]!;
return cachedAccounts[account_list.currentWallet!.ffiAddress()]!;
}
Future<void> addAccount({required String label}) async {
await account_list.addAccount(label: label);
void addAccount({required String label}) {
account_list.addAccount(label: label);
update();
}
Future<void> setLabelAccount({required int accountIndex, required String label}) async {
await account_list.setLabelForAccount(accountIndex: accountIndex, label: label);
void setLabelAccount({required int accountIndex, required String label}) {
account_list.setLabelForAccount(accountIndex: accountIndex, label: label);
update();
}

View file

@ -1,7 +1,7 @@
import 'package:cw_core/unspent_transaction_output.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_monero/api/coins_info.dart';
import 'package:monero/monero.dart' as monero;
import 'package:monero/src/monero.dart';
class MoneroUnspent extends Unspent {
static Future<MoneroUnspent> fromUnspent(String address, String hash, String keyImage, int value, bool isFrozen, bool isUnlocked) async {

View file

@ -39,6 +39,7 @@ import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:mobx/mobx.dart';
import 'package:monero/src/monero.dart' as m;
import 'package:monero/monero.dart' as monero;
part 'monero_wallet.g.dart';
@ -84,7 +85,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
monero_wallet.getUnlockedBalance(accountIndex: account.id))
});
_updateSubAddress(isEnabledAutoGenerateSubaddress, account: account);
_askForUpdateTransactionHistory();
unawaited(updateTransactions());
});
reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
@ -139,7 +140,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
passphrase: monero_wallet.getPassphrase());
int? get restoreHeight =>
transactionHistory.transactions.values.firstOrNull?.height ?? monero.Wallet_getRefreshFromBlockHeight(wptr!);
transactionHistory.transactions.values.firstOrNull?.height ?? currentWallet?.getRefreshFromBlockHeight();
monero_wallet.SyncListener? _listener;
ReactionDisposer? _onAccountChangeReaction;
@ -169,7 +170,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (monero_wallet.getCurrentHeight() <= 1) {
monero_wallet.setRefreshFromBlockHeight(
height: walletInfo.restoreHeight);
setupBackgroundSync(password, wptr!);
setupBackgroundSync(password, currentWallet!);
}
}
@ -189,14 +190,23 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) {
printV("closing wallet");
final wmaddr = wmPtr.address;
final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address;
await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
});
final wmaddr = wmPtr.ffiAddress();
final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.ffiAddress();
openedWalletsByPath.remove("$currentWalletDirPath/$name");
wptr = null;
if (Platform.isWindows) {
await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
});
} else {
unawaited(Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
}));
}
currentWallet = null;
printV("wallet closed");
}
}
@ -211,7 +221,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Future<void> connectToNode({required Node node}) async {
try {
syncStatus = ConnectingSyncStatus();
await monero_wallet.setupNode(
await monero_wallet.setupNodeSync(
address: node.uri.toString(),
login: node.login,
password: node.password,
@ -237,10 +247,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
isBackgroundSyncRunning = true;
await save();
monero.Wallet_startBackgroundSync(wptr!);
final status = monero.Wallet_status(wptr!);
currentWallet!.startBackgroundSync();
final status = currentWallet!.status();
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = currentWallet!.errorString();
isBackgroundSyncRunning = false;
printV("startBackgroundSync: $err");
}
@ -256,9 +266,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Future<void> stopSync() async {
if (isBackgroundSyncRunning) {
printV("Stopping background sync");
monero.Wallet_store(wptr!);
monero.Wallet_stopBackgroundSync(wptr!, '');
monero_wallet.store();
currentWallet!.store();
currentWallet!.stopBackgroundSync('');
currentWallet!.store();
isBackgroundSyncRunning = false;
}
await save();
@ -269,9 +279,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Future<void> stopBackgroundSync(String password) async {
if (isBackgroundSyncRunning) {
printV("Stopping background sync");
monero.Wallet_store(wptr!);
monero.Wallet_stopBackgroundSync(wptr!, password);
monero.Wallet_store(wptr!);
currentWallet!.store();
currentWallet!.stopBackgroundSync(password);
currentWallet!.store();
isBackgroundSyncRunning = false;
}
}
@ -308,44 +318,44 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
Future<bool> submitTransactionUR(String ur) async {
final retStatus = monero.Wallet_submitTransactionUR(wptr!, ur);
final status = monero.Wallet_status(wptr!);
final retStatus = currentWallet!.submitTransactionUR(ur);
final status = currentWallet!.status();
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = currentWallet!.errorString();
throw MoneroTransactionCreationException("unable to broadcast signed transaction: $err");
}
return retStatus;
}
bool importKeyImagesUR(String ur) {
final retStatus = monero.Wallet_importKeyImagesUR(wptr!, ur);
final status = monero.Wallet_status(wptr!);
final retStatus = currentWallet!.importKeyImagesUR(ur);
final status = currentWallet!.status();
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = currentWallet!.errorString();
throw Exception("unable to import key images: $err");
}
return retStatus;
}
String exportOutputsUR(bool all) {
final str = monero.Wallet_exportOutputsUR(wptr!, all: all);
final status = monero.Wallet_status(wptr!);
final str = currentWallet!.exportOutputsUR(all: all);
final status = currentWallet!.status();
if (status != 0) {
final err = monero.Wallet_errorString(wptr!);
final err = currentWallet!.errorString();
throw MoneroTransactionCreationException("unable to export UR: $err");
}
return str;
}
bool needExportOutputs(int amount) {
if (int.tryParse(monero.Wallet_secretSpendKey(wptr!)) != 0) {
if (int.tryParse(currentWallet!.secretSpendKey()) != 0) {
return false;
}
// viewOnlyBalance - balance that we can spend
// TODO(mrcyjanek): remove hasUnknownKeyImages when we cleanup coin control
return (monero.Wallet_viewOnlyBalance(wptr!,
return (currentWallet!.viewOnlyBalance(
accountIndex: walletAddresses.account!.id) < amount) ||
monero.Wallet_hasUnknownKeyImages(wptr!);
currentWallet!.hasUnknownKeyImages();
}
@override
@ -425,12 +435,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
if (inputs.isEmpty) MoneroTransactionCreationException(
'No inputs selected');
pendingTransactionDescription =
await transaction_history.createTransaction(
await transaction_history.createTransactionSync(
address: address!,
amount: amount,
priorityRaw: _credentials.priority.serialize(),
accountIndex: walletAddresses.account!.id,
preferredInputs: inputs);
preferredInputs: inputs,
paymentId: '');
}
// final status = monero.PendingTransaction_status(pendingTransactionDescription);
@ -485,14 +496,25 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) {
// NOTE: this is realistically only required on windows.
// That's why we await it only on that platform - other platforms actually understand
// the concept of a file properly...
printV("closing wallet");
final wmaddr = wmPtr.address;
final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address;
await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
});
final wmaddr = wmPtr.ffiAddress();
final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.ffiAddress();
openedWalletsByPath.remove("$currentWalletDirPath/$name");
if (Platform.isWindows) {
await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
});
} else {
unawaited(Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
}));
}
printV("wallet closed");
}
try {
@ -501,32 +523,33 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Directory(await pathForWalletDir(name: name, type: type));
final newWalletDirPath =
await pathForWalletDir(name: newWalletName, type: type);
await currentWalletDir.rename(newWalletDirPath);
// -- use new waller folder to rename files with old names still --
final renamedWalletPath = newWalletDirPath + '/$name';
final currentCacheFile = File(renamedWalletPath);
final currentKeysFile = File('$renamedWalletPath.keys');
final currentAddressListFile = File('$renamedWalletPath.address.txt');
final backgroundSyncFile = File('$renamedWalletPath.background');
final newWalletPath =
await pathForWallet(name: newWalletName, type: type);
// Create new directory if it doesn't exist
await Directory(newWalletDirPath).create(recursive: true);
// -- use new waller folder to copy files with old names still --
final currentWalletPath = currentWalletDir.path + '/$name';
final currentCacheFile = File(currentWalletPath);
final currentKeysFile = File('$currentWalletPath.keys');
final currentAddressListFile = File('$currentWalletPath.address.txt');
final backgroundSyncFile = File('$currentWalletPath.background');
if (currentCacheFile.existsSync()) {
await currentCacheFile.rename(newWalletPath);
await currentCacheFile.copy("${newWalletDirPath}/$newWalletName");
}
if (currentKeysFile.existsSync()) {
await currentKeysFile.rename('$newWalletPath.keys');
await currentKeysFile.copy("${newWalletDirPath}/$newWalletName.keys");
}
if (currentAddressListFile.existsSync()) {
await currentAddressListFile.rename('$newWalletPath.address.txt');
await currentAddressListFile.copy("${newWalletDirPath}/$newWalletName.address.txt");
}
if (backgroundSyncFile.existsSync()) {
await backgroundSyncFile.rename('$newWalletPath.background');
await backgroundSyncFile.copy("${newWalletDirPath}/$newWalletName.background");
}
await currentWalletDir.delete(recursive: true);
await backupWalletFiles(newWalletName);
} catch (e) {
final currentWalletPath = await pathForWallet(name: name, type: type);
@ -572,12 +595,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
walletInfo.restoreHeight = height;
walletInfo.isRecovery = true;
monero_wallet.setRefreshFromBlockHeight(height: height);
setupBackgroundSync(password, wptr!);
setupBackgroundSync(password, currentWallet!);
monero_wallet.rescanBlockchainAsync();
await startSync();
_askForUpdateBalance();
walletAddresses.accountList.update();
await _askForUpdateTransactionHistory();
await updateTransactions();
await save();
await walletInfo.save();
}
@ -591,15 +614,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final coinCount = await countOfCoins();
for (var i = 0; i < coinCount; i++) {
final coin = await getCoin(i);
final coinSpent = monero.CoinsInfo_spent(coin);
if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
final coinSpent = coin.spent();
if (coinSpent == false && coin.subaddrAccount() == walletAddresses.account!.id) {
final unspent = await MoneroUnspent.fromUnspent(
monero.CoinsInfo_address(coin),
monero.CoinsInfo_hash(coin),
monero.CoinsInfo_keyImage(coin),
monero.CoinsInfo_amount(coin),
monero.CoinsInfo_frozen(coin),
monero.CoinsInfo_unlocked(coin),
coin.address(),
coin.hash(),
coin.keyImage(),
coin.amount(),
coin.frozen(),
coin.unlocked(),
);
// TODO: double-check the logic here
if (unspent.hash.isNotEmpty) {
@ -704,6 +727,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
acc[tx.id] = tx;
return acc;
});
// This is needed to update the transaction history when new transaction is made.
unawaited(updateTransactions());
return resp;
}
@ -792,7 +817,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
monero_wallet.setRecoveringFromSeed(isRecovery: true);
monero_wallet.setRefreshFromBlockHeight(height: height);
setupBackgroundSync(password, wptr!);
setupBackgroundSync(password, currentWallet!);
}
int _getHeightDistance(DateTime date) {
@ -831,9 +856,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
}
Future<void> _askForUpdateTransactionHistory() async =>
await updateTransactions();
int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(
accountIndex: walletAddresses.account!.id);
@ -852,13 +874,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
printV("onNewBlock: $height, $blocksLeft, $ptc");
try {
if (walletInfo.isRecovery) {
await _askForUpdateTransactionHistory();
await updateTransactions();
_askForUpdateBalance();
walletAddresses.accountList.update();
}
if (blocksLeft < 100) {
await _askForUpdateTransactionHistory();
await updateTransactions();
_askForUpdateBalance();
walletAddresses.accountList.update();
syncStatus = SyncedSyncStatus();
@ -881,7 +903,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
void _onNewTransaction() async {
try {
await _askForUpdateTransactionHistory();
await updateTransactions();
_askForUpdateBalance();
await Future<void>.delayed(Duration(seconds: 1));
} catch (e) {
@ -917,8 +939,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
void setLedgerConnection(LedgerConnection connection) {
final dummyWPtr = wptr ??
monero.WalletManager_openWallet(wmPtr, path: '', password: '');
final dummyWPtr = createWalletPointer();
enableLedgerExchange(dummyWPtr, connection);
}

View file

@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'package:collection/collection.dart';
import 'package:cw_core/get_height_by_date.dart';
@ -20,6 +22,7 @@ import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:monero/src/monero.dart' as m;
import 'package:monero/monero.dart' as monero;
import 'package:polyseed/polyseed.dart';
@ -139,7 +142,7 @@ class MoneroWalletService extends WalletService<
overrideHeight: heightOverride, passphrase: credentials.passphrase);
}
await monero_wallet_manager.createWallet(
monero_wallet_manager.createWallet(
path: path,
password: credentials.password!,
language: credentials.language,
@ -179,7 +182,7 @@ class MoneroWalletService extends WalletService<
if (walletFilesExist(path)) await repairOldAndroidWallet(name);
await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
.openWallet(path: path, password: password);
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(
@ -217,13 +220,23 @@ class MoneroWalletService extends WalletService<
if (openedWalletsByPath["$path/$wallet"] != null) {
// NOTE: this is realistically only required on windows.
printV("closing wallet");
final wmaddr = wmPtr.address;
final waddr = openedWalletsByPath["$path/$wallet"]!.address;
// await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), false);
// });
final w = openedWalletsByPath["$path/$wallet"]!;
final wmaddr = wmPtr.ffiAddress();
final waddr = w.ffiAddress();
openedWalletsByPath.remove("$path/$wallet");
if (Platform.isWindows) {
await Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
});
} else {
unawaited(Isolate.run(() {
monero.WalletManager_closeWallet(
Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
}));
}
printV("wallet closed");
}
@ -263,7 +276,7 @@ class MoneroWalletService extends WalletService<
{bool? isTestnet}) async {
try {
final path = await pathForWallet(name: credentials.name, type: getType());
await monero_wallet_manager.restoreFromKeys(
monero_wallet_manager.restoreWalletFromKeys(
path: path,
password: credentials.password!,
language: credentials.language,
@ -293,9 +306,13 @@ class MoneroWalletService extends WalletService<
final password = credentials.password;
final height = credentials.height;
if (wptr == null) monero_wallet_manager.createWalletPointer();
if (currentWallet == null) {
final tmpWptr = monero_wallet_manager.createWalletPointer();
enableLedgerExchange(tmpWptr, credentials.ledgerConnection);
} else {
enableLedgerExchange(currentWallet!, credentials.ledgerConnection);
}
enableLedgerExchange(wptr!, credentials.ledgerConnection);
await monero_wallet_manager.restoreWalletFromHardwareWallet(
path: path,
password: password!,
@ -352,7 +369,7 @@ class MoneroWalletService extends WalletService<
try {
final path = await pathForWallet(name: credentials.name, type: getType());
monero_wallet_manager.restoreFromSeed(
monero_wallet_manager.restoreWalletFromSeedSync(
path: path,
password: credentials.password!,
passphrase: credentials.passphrase,
@ -393,7 +410,7 @@ class MoneroWalletService extends WalletService<
walletInfo.isRecovery = true;
walletInfo.restoreHeight = height;
monero_wallet_manager.restoreFromSeed(
monero_wallet_manager.restoreWalletFromSeedSync(
path: path,
password: password,
passphrase: '',
@ -401,12 +418,12 @@ class MoneroWalletService extends WalletService<
restoreHeight: height,
);
monero.Wallet_setCacheAttribute(wptr!,
currentWallet!.setCacheAttribute(
key: "cakewallet.seed.bip39", value: mnemonic);
monero.Wallet_setCacheAttribute(wptr!,
currentWallet!.setCacheAttribute(
key: "cakewallet.passphrase", value: passphrase ?? '');
monero.Wallet_store(wptr!);
currentWallet!.store();
final wallet = MoneroWallet(
walletInfo: walletInfo,
@ -472,7 +489,7 @@ class MoneroWalletService extends WalletService<
walletInfo.isRecovery = true;
walletInfo.restoreHeight = height;
await monero_wallet_manager.restoreFromSpendKey(
monero_wallet_manager.restoreWalletFromSpendKeySync(
path: path,
password: password,
seed: seed,
@ -481,8 +498,8 @@ class MoneroWalletService extends WalletService<
spendKey: spendKey);
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase??'');
final wallet = MoneroWallet(
walletInfo: walletInfo,
@ -529,7 +546,7 @@ class MoneroWalletService extends WalletService<
if (walletFilesExist(path)) await repairOldAndroidWallet(name);
await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
.openWallet(path: path, password: password);
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(

View file

@ -573,8 +573,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
ref: "84e52393e395d75f449bcd81e23028889538118f"
resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"

View file

@ -27,7 +27,7 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
ref: 84e52393e395d75f449bcd81e23028889538118f
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
path: impls/monero.dart
mutex: ^3.1.0
ledger_flutter_plus: ^1.4.1

View file

@ -480,8 +480,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
ref: "84e52393e395d75f449bcd81e23028889538118f"
resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"

View file

@ -25,7 +25,7 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash
path: impls/monero.dart
mutex: ^3.1.0

View file

@ -485,8 +485,8 @@ packages:
dependency: "direct main"
description:
path: "impls/monero.dart"
ref: "84e52393e395d75f449bcd81e23028889538118f"
resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
url: "https://github.com/mrcyjanek/monero_c"
source: git
version: "0.0.0"

View file

@ -26,7 +26,7 @@ dependencies:
monero:
git:
url: https://github.com/mrcyjanek/monero_c
ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash
ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash
path: impls/monero.dart
dev_dependencies:
flutter_test:

View file

@ -208,42 +208,40 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a
cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3
device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926
cw_decred: a02cf30175a46971c1e2fa22c48407534541edc6
cw_mweb: 3aea2fb35b2bd04d8b2d21b83216f3b8fb768d85
device_display_brightness: 04374ebd653619292c1d996f00f42877ea19f17f
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
devicelocale: bd64aa714485a8afdaded0892c1e7d5b7f680cf8
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
sensitive_clipboard: 161e9abc3d56b3131309d8a321eb4690a803c16b
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
sp_scanner: b1bc9321690980bdb44bba7ec85d5543e716d1b5
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152
universal_ble: ff19787898040d721109c6324472e5dd4bc86adc
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a

View file

@ -32,23 +32,28 @@ class WalletLoadingService {
Future<void> renameWallet(WalletType type, String name, String newName,
{String? password}) async {
final walletService = walletServiceFactory.call(type);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
try {
final walletService = walletServiceFactory.call(type);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
// Save the current wallet's password to the new wallet name's key
await keyService.saveWalletPassword(walletName: newName, password: walletPassword);
// Delete previous wallet name from keyService to keep only new wallet's name
// otherwise keeps duplicate (old and new names)
await keyService.deleteWalletPassword(walletName: name);
// Save the current wallet's password to the new wallet name's key
await keyService.saveWalletPassword(walletName: newName, password: walletPassword);
await walletService.rename(name, walletPassword, newName);
await walletService.rename(name, walletPassword, newName);
// Delete previous wallet name from keyService to keep only new wallet's name
// otherwise keeps duplicate (old and new names)
await keyService.deleteWalletPassword(walletName: name);
// set shared preferences flag based on previous wallet name
if (type == WalletType.monero) {
final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name);
final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false;
final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName);
await sharedPreferences.setBool(newNameKey, isPasswordUpdated);
// set shared preferences flag based on previous wallet name
if (type == WalletType.monero) {
final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name);
final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false;
final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName);
await sharedPreferences.setBool(newNameKey, isPasswordUpdated);
}
} catch (error, stack) {
await ExceptionHandler.resetLastPopupDate();
await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
}
}

View file

@ -39,14 +39,14 @@ class CWMoneroAccountList extends MoneroAccountList {
@override
Future<void> addAccount(Object wallet, {required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList.addAccount(label: label);
moneroWallet.walletAddresses.accountList.addAccount(label: label);
}
@override
Future<void> setLabelAccount(Object wallet,
{required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList
moneroWallet.walletAddresses.accountList
.setLabelAccount(accountIndex: accountIndex, label: label);
}
}

View file

@ -204,7 +204,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
);
return Container();
}),
if (widget.privacySettingsViewModel.hasPassphraseOption)
if (widget.privacySettingsViewModel.hasPassphraseOption && !widget.isFromRestore)
Padding(
padding: EdgeInsets.all(24),
child: Form(

View file

@ -5,10 +5,14 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_mode.dart';
@ -52,7 +56,6 @@ class WalletRestorePage extends BasePage {
// String? derivationPath = null;
DerivationInfo? derivationInfo;
@override
Function(BuildContext)? get popWidget => (context) => seedSettingsViewModel.setPassphrase(null);
@ -102,37 +105,81 @@ class WalletRestorePage extends BasePage {
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: Column(
children: [
Observer(
builder: (context) {
return walletRestoreViewModel.mode == WalletRestoreMode.seed
? StandardCheckbox(
value: walletRestoreViewModel.hasPassphrase,
caption: S.of(context).wallet_has_passphrase,
onChanged: (value) {
walletRestoreViewModel.hasPassphrase = value;
},
)
: SizedBox.shrink();
},
),
SizedBox(height: 16),
PrimaryButton(
key: ValueKey('wallet_restore_advanced_settings_button_key'),
onPressed: () {
Navigator.of(context).pushNamed(
Routes.advancedPrivacySettings,
arguments: {
'isFromRestore': true,
'type': walletRestoreViewModel.type,
'useTestnet': walletRestoreViewModel.useTestnet,
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
},
);
},
text: S.of(context).advanced_settings,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
),
SizedBox(height: 8),
Observer(
builder: (context) {
return LoadingPrimaryButton(
key: ValueKey('wallet_restore_seed_or_key_restore_button_key'),
onPressed: () async => await _confirmForm(context),
text: S.of(context).restore_recover,
color: Theme.of(context)
.extension<WalletListTheme>()!
.createNewWalletButtonBackgroundColor,
textColor: Theme.of(context)
.extension<WalletListTheme>()!
.restoreWalletButtonTextColor,
onPressed: () async {
if (walletRestoreViewModel.hasPassphrase) {
await showModalBottomSheet<void>(
context: context,
isDismissible: false,
isScrollControlled: true,
builder: (BuildContext bottomSheetContext) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom,
),
child: AddPassphraseBottomSheet(
currentTheme: currentTheme,
titleText: S.of(context).add_passphrase,
onRestoreButtonPressed: (passphrase) async {
await _onPassphraseBottomSheetRestoreButtonPressed(
passphrase,
context,
);
},
),
);
},
);
} else {
await _confirmForm(context);
}
},
text: walletRestoreViewModel.hasPassphrase
? S.of(context).add_passphrase
: S.of(context).restore_recover,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
isLoading: walletRestoreViewModel.state is IsExecutingState,
isDisabled: !walletRestoreViewModel.isButtonEnabled,
);
},
),
const SizedBox(height: 25),
GestureDetector(
key: ValueKey('wallet_restore_advanced_settings_button_key'),
onTap: () {
Navigator.of(context)
.pushNamed(Routes.advancedPrivacySettings, arguments: {
'isFromRestore': true,
'type': walletRestoreViewModel.type,
'useTestnet': walletRestoreViewModel.useTestnet,
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
});
},
child: Text(S.of(context).advanced_settings),
),
const SizedBox(height: 24),
],
),
)
@ -144,6 +191,14 @@ class WalletRestorePage extends BasePage {
);
}
Future<void> _onPassphraseBottomSheetRestoreButtonPressed(
String passphrase,
BuildContext context,
) async {
walletRestoreViewModel.seedSettingsViewModel.setPassphrase(passphrase);
await _confirmForm(context);
}
Map<String, dynamic> _credentials() {
final credentials = <String, dynamic>{};
@ -172,9 +227,10 @@ class WalletRestorePage extends BasePage {
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
if (walletRestoreViewModel.type != WalletType.decred) {
credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['address'] =
walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['spendKey'] =
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
credentials['height'] =
walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
}
@ -335,8 +391,8 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
final initialIndex = walletRestoreViewModel.mode == WalletRestoreMode.seed
? 0
: _hasKeysTab
? 1
: 0;
? 1
: 0;
_tabController = TabController(length: tabCount, vsync: this, initialIndex: initialIndex);
@ -429,7 +485,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
padding: EdgeInsets.zero,
tabs: [
Tab(text: S.of(context).widgets_seed),
if (_hasKeysTab) Tab(text: S.of(context).keys),
if (_hasKeysTab) Tab(text: S.of(context).keys),
],
),
),
@ -527,42 +583,30 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
}
bool _isValidSeed() {
final seedPhrase = walletRestoreFromSeedFormKey
.currentState!.seedWidgetStateKey.currentState!.text;
final seedPhrase =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true;
final seedWords = seedPhrase.split(' ');
if (seedWords.length == 14 &&
walletRestoreViewModel.type == WalletType.wownero) return true;
if (seedWords.length == 26 &&
walletRestoreViewModel.type == WalletType.zano) return true;
if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
if (seedWords.length == 12 &&
walletRestoreViewModel.type == WalletType.monero) {
return walletRestoreFromSeedFormKey
.currentState
?.blockchainHeightKey
.currentState
?.restoreHeightController
.text
.isNotEmpty == true;
if (seedWords.length == 12 && walletRestoreViewModel.type == WalletType.monero) {
return walletRestoreFromSeedFormKey.currentState?.blockchainHeightKey.currentState
?.restoreHeightController.text.isNotEmpty ==
true;
}
if ([WalletType.monero, WalletType.wownero, WalletType.haven]
.contains(walletRestoreViewModel.type) &&
seedWords.length ==
WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
seedWords.length == WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
return true;
}
// bip39:
final validBip39SeedLengths = [12, 18, 24];
final nonBip39WalletTypes = [
WalletType.wownero,
WalletType.haven,
WalletType.decred
];
final nonBip39WalletTypes = [WalletType.wownero, WalletType.haven, WalletType.decred];
// if it's a bip39 wallet and the length is not valid return false
if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
!(validBip39SeedLengths.contains(seedWords.length))) {
@ -570,14 +614,12 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
}
if ((walletRestoreViewModel.type == WalletType.decred) &&
seedWords.length !=
WalletRestoreViewModelBase.decredSeedMnemonicLength) {
seedWords.length != WalletRestoreViewModelBase.decredSeedMnemonicLength) {
return false;
}
final words = walletRestoreFromSeedFormKey
.currentState!.seedWidgetStateKey.currentState!.words
.toSet();
final words =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
return seedWords.toSet().difference(words).toSet().isEmpty;
}

View file

@ -15,6 +15,8 @@ class BaseTextFormField extends StatelessWidget {
this.textColor,
this.hintColor,
this.borderColor,
this.fillColor,
this.filled,
this.prefix,
this.prefixIcon,
this.suffix,
@ -44,6 +46,8 @@ class BaseTextFormField extends StatelessWidget {
final Color? textColor;
final Color? hintColor;
final Color? borderColor;
final Color? fillColor;
bool? filled;
final Widget? prefix;
final Widget? prefixIcon;
final Widget? suffix;
@ -89,6 +93,8 @@ class BaseTextFormField extends StatelessWidget {
prefixIcon: prefixIcon,
suffix: suffix,
suffixIcon: suffixIcon,
fillColor: fillColor,
filled: filled,
hintStyle: placeholderTextStyle ??
TextStyle(
color: hintColor ?? Theme.of(context).hintColor,

View file

@ -0,0 +1,220 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class AddPassphraseBottomSheet extends StatefulWidget {
AddPassphraseBottomSheet({
required String titleText,
required this.currentTheme,
required this.onRestoreButtonPressed,
});
final void Function(String) onRestoreButtonPressed;
final ThemeBase currentTheme;
@override
State<AddPassphraseBottomSheet> createState() => _AddPassphraseBottomSheetState();
}
class _AddPassphraseBottomSheetState extends State<AddPassphraseBottomSheet> {
late final TextEditingController passphraseController;
late final TextEditingController confirmPassphraseController;
@override
void initState() {
super.initState();
passphraseController = TextEditingController();
confirmPassphraseController = TextEditingController();
}
@override
void dispose() {
passphraseController.dispose();
confirmPassphraseController.dispose();
super.dispose();
}
bool obscurePassphrase = true;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.vertical(top: Radius.circular(30.0)),
color: Theme.of(context).dialogBackgroundColor,
),
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
children: [
Row(
children: [
const Spacer(flex: 4),
Expanded(
flex: 2,
child: Container(
height: 6,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.6),
),
),
),
const Spacer(flex: 4),
],
),
SizedBox(height: 16),
Text(
S.of(context).add_passphrase,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
decoration: TextDecoration.none,
),
),
SvgPicture.asset('assets/images/passphrase_key.svg'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${S.of(context).warning.toUpperCase()}: ',
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w700,
color: Palette.red,
decoration: TextDecoration.none,
),
),
TextSpan(
text: S.of(context).add_passphrase_warning_text,
),
],
),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor.withOpacity(0.7),
decoration: TextDecoration.none,
),
),
),
SizedBox(height: 24),
TextFormField(
key: ValueKey('add_passphrase_bottom_sheet_widget_passphrase_textfield_key'),
controller: passphraseController,
obscureText: obscurePassphrase,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 12),
filled: true,
fillColor: Theme.of(context).cardColor,
hintText: S.of(context).required_passphrase,
suffixIcon: GestureDetector(
onTap: () {
setState(() {
obscurePassphrase = !obscurePassphrase;
});
},
child: Icon(
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
size: 24,
color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.6),
),
),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(16),
),
),
),
SizedBox(height: 8),
TextFormField(
key: ValueKey('add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key'),
controller: confirmPassphraseController,
obscureText: obscurePassphrase,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 12),
filled: true,
fillColor: Theme.of(context).cardColor,
hintText: S.of(context).confirm_passphrase,
suffixIcon: GestureDetector(
onTap: () {
setState(() {
obscurePassphrase = !obscurePassphrase;
});
},
child: Icon(
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
size: 24,
color: Theme.of(context).textTheme.bodyLarge?.color?.withOpacity(0.6),
),
),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(16),
),
),
validator: (text) {
if (text == passphraseController.text) {
return null;
}
return S.of(context).passphrases_doesnt_match;
},
),
SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Container(
padding: const EdgeInsets.only(right: 8.0, top: 8.0),
child: PrimaryButton(
key: ValueKey('add_passphrase_bottom_sheet_widget_cancel_button_key'),
onPressed: () {
Navigator.pop(context);
},
text: S.of(context).cancel,
color: Theme.of(context).cardColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
),
),
),
Flexible(
child: Container(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: PrimaryButton(
key: ValueKey('add_passphrase_bottom_sheet_widget_restore_button_key'),
onPressed: () {
Navigator.pop(context);
widget.onRestoreButtonPressed(passphraseController.text);
},
text: S.of(context).restore,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
),
),
),
],
),
),
SizedBox(height: 24),
],
),
),
);
}
}

View file

@ -57,7 +57,11 @@ abstract class ContactViewModelBase with Store {
state = IsExecutingState();
final now = DateTime.now();
if (doesContactNameExist(name)) {
final nameExists = _contact == null
? doesContactNameExist(name)
: doesContactNameExist(name) && _contact.original.name != name;
if (nameExists) {
state = FailureState(S.current.contact_name_exists);
return;
}

View file

@ -271,10 +271,19 @@ abstract class DashboardViewModelBase with Store {
});
_transactionDisposer?.reaction.dispose();
_transactionDisposer = reaction(
(_) => appStore.wallet!.transactionHistory.transactions.length,
_transactionDisposerCallback,
);
_transactionDisposer = reaction((_) {
final length = appStore.wallet!.transactionHistory.transactions.length;
if (length == 0) {
return 0;
}
int confirmations = 1;
if (![WalletType.solana, WalletType.tron].contains(wallet.type)) {
try {
confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1;
} catch (_) {}
}
return length * confirmations;
}, _transactionDisposerCallback);
if (hasSilentPayments) {
silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
@ -891,8 +900,19 @@ abstract class DashboardViewModelBase with Store {
_transactionDisposer?.reaction.dispose();
_transactionDisposer = reaction((_) => appStore.wallet!.transactionHistory.transactions.length,
_transactionDisposerCallback);
_transactionDisposer = reaction((_) {
final length = appStore.wallet!.transactionHistory.transactions.length;
if (length == 0) {
return 0;
}
int confirmations = 1;
if (![WalletType.solana, WalletType.tron].contains(wallet.type)) {
try {
confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1;
} catch (_) {}
}
return length * confirmations;
}, _transactionDisposerCallback);
}
@action

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart';
@ -591,7 +593,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
}
final sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String());
unawaited(wallet.fetchTransactions());
state = TransactionCommitted();
} catch (e) {
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));

View file

@ -43,6 +43,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
type == WalletType.solana ||
type == WalletType.tron,
isButtonEnabled = false,
hasPassphrase = false,
mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
type: type, isRecovery: true) {
@ -89,6 +90,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
@observable
WalletRestoreMode mode;
@observable
bool hasPassphrase;
@observable
bool isButtonEnabled;

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "أضف أموالاً مدفوعة مسبقًا إلى البطاقات (حتى ${value})",
"add_new_node": "أضافة عقدة جديدة",
"add_new_word": "أضف كلمة جديدة",
"add_passphrase": "أضف عبارة المرور",
"add_passphrase_warning_text": "أدخل عبارة المرور فقط إذا كنت قد استخدمت واحدة لهذه المحفظة في الماضي. إذا أدخلت عبارة الممر الخاطئ أو لم تستخدم عبارة تمريرة من قبل على هذه المحفظة ، فلن ترى أيًا من الأموال أو التاريخ الحالي.",
"add_receiver": "أضف مستقبل آخر (اختياري)",
"add_secret_code": " ﺔﻗﺩﺎﺼﻤﻟﺍ ﻖﻴﺒﻄﺗ ﻰﻟﺇ ﻱﺮﺴﻟﺍ ﺰﻣﺮﻟﺍ ﺍﺬﻫ ﻒﺿﺃ ﻭﺃ",
"add_tip": "أضف بقشيش",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "تتطلب عمليات الإرسال إلى المحافظ الداخلية",
"require_for_sends_to_non_contacts": "تتطلب لارسال لغير جهات الاتصال",
"require_pin_after": "طلب PIN بعد",
"required_passphrase": "عبارة المرور",
"rescan": "إعادة الفحص",
"resend_code": "الرجاء إعادة إرسالها",
"reset": "إعادة",
"reset_password": "إعادة تعيين كلمة المرور",
"restore": "يعيد",
"restore_active_seed": "السييد النشطة",
"restore_address": "العنوان",
"restore_bitcoin_description_from_keys": "قم باستعادة محفظتك من سلسلة WIF التي تم إنشاؤها من مفاتيحك الخاصة",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "يمكنك دائمًا عرض هذه البذرة مرة أخرى تحت",
"wallet_group_empty_state_text_one": "يبدو أنه ليس لديك أي مجموعات محفظة متوافقة !\n\n انقر",
"wallet_group_empty_state_text_two": "أدناه لجعل واحدة جديدة.",
"wallet_has_passphrase": "هذه المحفظة تحتوي على عبارة ممر",
"wallet_keys": "سييد المحفظة / المفاتيح",
"wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
"wallet_list_edit_group_name": "تحرير اسم المجموعة",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Добавете предплатени средства в картите (до ${value})",
"add_new_node": "Добави нов node",
"add_new_word": "Добавяне на нова дума",
"add_passphrase": "Добавете парола",
"add_passphrase_warning_text": "Въведете парола само ако сте използвали такава за този портфейл в миналото. Ако въведете грешна парола или не сте използвали парола преди в този портфейл, няма да видите нито един от съществуващите средства или история.",
"add_receiver": "Добавяне на друг получател (не е задължително)",
"add_secret_code": "Или добавете този таен код към приложение за удостоверяване",
"add_tip": "Add Tip",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Изискване за изпращане до вътрешни портфейли",
"require_for_sends_to_non_contacts": "Изискване за изпращане до лица без контакт",
"require_pin_after": "Въведете PIN след",
"required_passphrase": "Парола",
"rescan": "Сканирай отново",
"resend_code": "Повторно изпращане",
"reset": "Нулиране",
"reset_password": "Нулиране на парола",
"restore": "Възстановяване",
"restore_active_seed": "Активиране на seed",
"restore_address": "Адреси",
"restore_bitcoin_description_from_keys": "Възстановяване на портфейл чрез WIF, изведен от Вашите private keys",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Винаги можете да видите това семе отново под",
"wallet_group_empty_state_text_one": "Изглежда, че нямате съвместими групи портфейли !\n\n tap",
"wallet_group_empty_state_text_two": "по -долу, за да се направи нов.",
"wallet_has_passphrase": "Този портфейл има парола",
"wallet_keys": "Seed/keys на портфейла",
"wallet_list_create_new_wallet": "Създаване на нов портфейл",
"wallet_list_edit_group_name": "Редактиране на име на групата",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Všechny předplacené prostředky na kartě (až ${value})",
"add_new_node": "Přidat nový uzel",
"add_new_word": "Přidat nové slovo",
"add_passphrase": "Přidejte přístupovou frázi",
"add_passphrase_warning_text": "Zadejte přístupovou frázi pouze tehdy, pokud jste ji v minulosti použili pro tuto peněženku. Pokud zadáte nesprávnou přístupovou frázi nebo jste na této peněžence nepoužili přístupovou frázi, neuvidíte žádnou existující fondy nebo historii.",
"add_receiver": "Přidat dalšího příjemce (nepovinné)",
"add_secret_code": "Nebo přidejte tento tajný kód do ověřovací aplikace",
"add_tip": "Přidat spropitné",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Vyžadovat pro odesílání do interních peněženek",
"require_for_sends_to_non_contacts": "Vyžadovat pro odesílání nekontaktním osobám",
"require_pin_after": "Vyžadovat PIN po",
"required_passphrase": "Passphrase",
"rescan": "Znovu prohledat",
"resend_code": "Prosím poslat znovu",
"reset": "Vymazat",
"reset_password": "Resetovat heslo",
"restore": "Obnovit",
"restore_active_seed": "Aktivní seed",
"restore_address": "Adresa",
"restore_bitcoin_description_from_keys": "Obnovte svou peněženku pomocí vygenerovaného WIF řetězce z vašich soukromých klíčů",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Toto semeno si můžete vždy znovu prohlédnout",
"wallet_group_empty_state_text_one": "Vypadá to, že nemáte žádné kompatibilní skupiny peněženky !\n\n",
"wallet_group_empty_state_text_two": "Níže vytvořit nový.",
"wallet_has_passphrase": "Tato peněženka má přístupovou frázi",
"wallet_keys": "Seed/klíče peněženky",
"wallet_list_create_new_wallet": "Vytvořit novou peněženku",
"wallet_list_edit_group_name": "Upravit název skupiny",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Prepaid-Guthaben zu den Karten hinzufügen (bis zu ${value})",
"add_new_node": "Neuen Knoten hinzufügen",
"add_new_word": "Neues Wort hinzufügen",
"add_passphrase": "Fügen Sie Passphrase hinzu",
"add_passphrase_warning_text": "Geben Sie nur eine Passphrase ein, wenn Sie in der Vergangenheit eine für diese Brieftasche verwendet haben. Wenn Sie die falsche Passphrase eingeben oder in dieser Brieftasche noch keine Passphrase verwendet haben, werden Sie keine vorhandenen Gelder oder Geschichte sehen.",
"add_receiver": "Fügen Sie einen weiteren Empfänger hinzu (optional)",
"add_secret_code": "Oder fügen Sie diesen Geheimcode einer Authentifizierungs-App hinzu",
"add_tip": "Tipp hinzufügen",
@ -73,10 +75,10 @@
"awaiting_payment_confirmation": "Warten auf Zahlungsbestätigung",
"background_sync": "Hintergrundsynchronisation",
"background_sync_mode": "Hintergrundsynchronisierungsmodus",
"background_sync_on_battery_low": "Synchronisieren Sie einen niedrigen Akku",
"background_sync_on_battery_low": "Bei niedrigem Akkustand synchronisieren",
"background_sync_on_charging": "Nur beim Laden synchronisieren",
"background_sync_on_device_idle": "Nur dann synchronisieren, wenn das Gerät nicht verwendet wird",
"background_sync_on_unmetered_network": "Erfordern ein nicht modisches Netzwerk",
"background_sync_on_unmetered_network": "Erfordere ein ungetaktetes Netzwerk",
"backup": "Sicherung",
"backup_file": "Sicherungsdatei",
"backup_password": "Passwort sichern",
@ -276,7 +278,7 @@
"enable_mempool_api": "Mempool-API für genaue Gebühren und Daten",
"enable_replace_by_fee": "Aktivieren Sie Ersatz für Fee",
"enable_silent_payments_scanning": "Scannen Sie nach Silent Payments ihrer Adresse",
"enabled": "Ermöglicht",
"enabled": "Aktiviert",
"enter_amount": "Betrag eingeben",
"enter_backup_password": "Sicherungskennwort hier eingeben",
"enter_code": "Code eingeben",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Erforderlich für Sendungen an interne Wallets",
"require_for_sends_to_non_contacts": "Erforderlich für Versendungen an Nichtkontakte",
"require_pin_after": "PIN anfordern nach",
"required_passphrase": "Passphrase",
"rescan": "Erneut scannen",
"resend_code": "Bitte erneut senden",
"reset": "Zurücksetzen",
"reset_password": "Passwort zurücksetzen",
"restore": "Wiederherstellen",
"restore_active_seed": "Aktiver Seed",
"restore_address": "Adresse",
"restore_bitcoin_description_from_keys": "Stellen Sie Ihre Wallet aus der generierten WIF-Zeichenfolge aus Ihren privaten Schlüsseln wieder her",
@ -1002,6 +1006,7 @@
"wallet_group_description_view_seed": "Sie können diesen Seed immer wieder untersuchen",
"wallet_group_empty_state_text_one": "Sieht so aus, als hätten Sie keine kompatiblen Walletgruppen !\n\n TAP",
"wallet_group_empty_state_text_two": "unten, um einen neuen zu machen.",
"wallet_has_passphrase": "Diese Brieftasche hat eine Passphrase",
"wallet_keys": "Wallet-Seed/-Schlüssel",
"wallet_list_create_new_wallet": "Neue Wallet erstellen",
"wallet_list_edit_group_name": "Gruppenname bearbeiten",
@ -1061,4 +1066,4 @@
"you_will_send": "Konvertieren von",
"youCanGoBackToYourDapp": "Sie können jetzt zu Ihrem Dapp zurückkehren",
"yy": "YY"
}
}

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Add prepaid funds to the cards (up to ${value})",
"add_new_node": "Add new node",
"add_new_word": "Add new word",
"add_passphrase": "Add Passphrase",
"add_passphrase_warning_text": "Only enter a passphrase if you have used one for this wallet in the past. If you enter the wrong passphrase or have not used a passphrase before on this wallet, you won't see any of existing funds or history.",
"add_receiver": "Add another receiver (optional)",
"add_secret_code": "Or, add this secret code to an authenticator app",
"add_tip": "Add Tip",
@ -76,7 +78,7 @@
"background_sync_on_battery_low": "Synchronize on low battery",
"background_sync_on_charging": "Synchronize only when charging",
"background_sync_on_device_idle": "Synchronize only when device is not being used",
"background_sync_on_unmetered_network": "Require unmetred network",
"background_sync_on_unmetered_network": "Require unmetered network",
"backup": "Backup",
"backup_file": "Backup file",
"backup_password": "Backup password",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Require for sends to internal wallets",
"require_for_sends_to_non_contacts": "Require for sends to non-contacts",
"require_pin_after": "Require PIN after",
"required_passphrase": "Passphrase",
"rescan": "Rescan",
"resend_code": "Please resend it",
"reset": "Reset",
"reset_password": "Reset Password",
"restore": "Restore",
"restore_active_seed": "Active seed",
"restore_address": "Address",
"restore_bitcoin_description_from_keys": "Restore your wallet from generated WIF string from your private keys",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "You can always view this seed again under",
"wallet_group_empty_state_text_one": "Looks like you don't have any compatible wallet groups!\n\nTap",
"wallet_group_empty_state_text_two": "below to make a new one.",
"wallet_has_passphrase": "This wallet has a passphrase",
"wallet_keys": "Wallet seed/keys",
"wallet_list_create_new_wallet": "Create New Wallet",
"wallet_list_edit_group_name": "Edit Group Name",
@ -1059,4 +1064,4 @@
"you_will_send": "Convert from",
"youCanGoBackToYourDapp": "You can go back to your dApp now",
"yy": "YY"
}
}

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Agregar fondos prepagos a las tarjetas (hasta ${value})",
"add_new_node": "Agregar nuevo nodo",
"add_new_word": "Agregar palabra nueva",
"add_passphrase": "Agregar frase de pases",
"add_passphrase_warning_text": "Solo ingrese una frase de pases si ha usado una para esta billetera en el pasado. Si ingresa a la frase de pases incorrecta o no ha utilizado una frase de pases antes en esta billetera, no verá ninguno de los fondos o historial existentes.",
"add_receiver": "Agregar otro receptor (opcional)",
"add_secret_code": "O agregue este código secreto a una aplicación de autenticación",
"add_tip": "Agregar sugerencia",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Requerido para envíos a billeteras internas",
"require_for_sends_to_non_contacts": "Requerido para envíos a no contactos",
"require_pin_after": "Requerir PIN después de",
"required_passphrase": "Frase",
"rescan": "Reescanear",
"resend_code": "Por favor reenvíalo",
"reset": "Reiniciar",
"reset_password": "Restablecer contraseña",
"restore": "Restaurar",
"restore_active_seed": "Semilla activa",
"restore_address": "Dirección",
"restore_bitcoin_description_from_keys": "Restaure su billetera a partir de una cadena WIF generada a partir de sus claves privadas",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "Siempre puedes ver esta semilla nuevamente debajo",
"wallet_group_empty_state_text_one": "Parece que no tienes ningún grupo de billetera compatible !\n\n toque",
"wallet_group_empty_state_text_two": "a continuación para hacer uno nuevo.",
"wallet_has_passphrase": "Esta billetera tiene una frase de pases",
"wallet_keys": "Billetera semilla/claves",
"wallet_list_create_new_wallet": "Crear nueva billetera",
"wallet_list_edit_group_name": "Editar nombre de grupo",
@ -1059,4 +1064,4 @@
"you_will_send": "Convertir de",
"youCanGoBackToYourDapp": "Puedes volver a tu dapp ahora",
"yy": "YY"
}
}

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Ajouter des fonds prépayés aux cartes (jusqu'à ${value})",
"add_new_node": "Ajouter un nouveau nœud",
"add_new_word": "Ajouter un nouveau mot",
"add_passphrase": "Ajouter la phrase secrète",
"add_passphrase_warning_text": "Entrez une phrase secrète si vous en avez utilisé un pour ce portefeuille dans le passé. Si vous entrez dans la mauvaise phrase de passe ou si vous n'avez pas utilisé de phrase de passe auparavant sur ce portefeuille, vous ne verrez aucun fonds ou historique existant.",
"add_receiver": "Ajouter un autre bénéficiaire (optionnel)",
"add_secret_code": "Ou ajoutez ce code secret à une application d'authentification",
"add_tip": "Ajouter un pourboire",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Exiger pour les envois vers des portefeuilles (wallets) internes",
"require_for_sends_to_non_contacts": "Exiger pour les envois hors contacts",
"require_pin_after": "Code PIN requis après",
"required_passphrase": "Phrase secrète",
"rescan": "Analyser la blockchain",
"resend_code": "Veuillez le renvoyer",
"reset": "Réinitialiser",
"reset_password": "Réinitialiser le mot de passe",
"restore": "Restaurer",
"restore_active_seed": "Phrase secrète (seed) active",
"restore_address": "Adresse",
"restore_bitcoin_description_from_keys": "Restaurer votre portefeuille (wallet) d'après la chaîne WIF générée d'après vos clefs privées",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Vous pouvez toujours revoir cette graine sous",
"wallet_group_empty_state_text_one": "On dirait que vous n'avez pas de groupes de portefeuilles compatibles !\n\n Tap",
"wallet_group_empty_state_text_two": "Ci-dessous pour en faire un nouveau.",
"wallet_has_passphrase": "Ce portefeuille a une phrase secrète",
"wallet_keys": "Phrase secrète (seed)/Clefs du portefeuille (wallet)",
"wallet_list_create_new_wallet": "Créer un Nouveau Portefeuille (Wallet)",
"wallet_list_edit_group_name": "Modifier le nom du groupe",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Ƙara kuɗin da aka riga aka biya a katunan (har zuwa ${value})",
"add_new_node": "Ƙara sabon node",
"add_new_word": "Ƙara kalma sabuwa",
"add_passphrase": "Addara fasphrase",
"add_passphrase_warning_text": "Kawai shigar da kalmar wucewa idan kun yi amfani da ɗaya don wannan walat a baya. Idan ka shigar da kalmar wucewa ko ba a yi amfani da kalmar wucewa ba kafin a wannan waljin, ba za ka ga wani kudaden da ake da su ba ko tarihi.",
"add_receiver": "Ƙara wani mai karɓa (na zaɓi)",
"add_secret_code": "Ko, ƙara wannan lambar sirrin zuwa ƙa'idar mai tabbatarwa",
"add_tip": "Ƙara Tukwici",
@ -623,10 +625,12 @@
"require_for_sends_to_internal_wallets": "Bukatar aika zuwa wallet na ciki",
"require_for_sends_to_non_contacts": "Bukatar aika zuwa waɗanda ba lambobin sadarwa ba",
"require_pin_after": "Bukatar PIN bayan",
"required_passphrase": "Mashiganya",
"rescan": "Rescan",
"resend_code": "Da fatan za a sake aika shi",
"reset": "Sake saiti",
"reset_password": "Sake saita kalmar wucewa",
"restore": "Sabunta",
"restore_active_seed": "iri mai aiki",
"restore_address": "Address",
"restore_bitcoin_description_from_keys": "Dawo da kwalinku daga WIF string dake generate daga maɓallan sirri",
@ -1001,6 +1005,7 @@
"wallet_group_description_view_seed": "Koyaushe zaka iya duba wannan zuriya",
"wallet_group_empty_state_text_one": "Kamar dai ba ku da wata ƙungiya matattara !\n\n Taɓa",
"wallet_group_empty_state_text_two": "da ke ƙasa don yin sabo.",
"wallet_has_passphrase": "Wannan walat ɗin yana da kalmar wucewa",
"wallet_keys": "Iri/maɓalli na walat",
"wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet",
"wallet_list_edit_group_name": "Shirya sunan rukuni",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "कार्ड में प्रीपेड धनराशि जोड़ें (${value} तक)",
"add_new_node": "नया नोड जोड़ें",
"add_new_word": "नया शब्द जोड़ें",
"add_passphrase": "पासफ़्रेज़ जोड़ें",
"add_passphrase_warning_text": "यदि आपने अतीत में इस बटुए के लिए एक का उपयोग किया है, तो केवल एक पासफ्रेज़ दर्ज करें। यदि आप गलत पासफ्रेज़ में प्रवेश करते हैं या इस वॉलेट पर पहले पासफ्रेज़ का उपयोग नहीं करते हैं, तो आप मौजूदा फंड या इतिहास में से कोई भी नहीं देखेंगे।",
"add_receiver": "एक और रिसीवर जोड़ें (वैकल्पिक)",
"add_secret_code": "या, इस गुप्त कोड को प्रमाणक ऐप में जोड़ें",
"add_tip": "टिप जोड़ें",
@ -623,10 +625,12 @@
"require_for_sends_to_internal_wallets": "आंतरिक वॉलेट में भेजने की आवश्यकता है",
"require_for_sends_to_non_contacts": "गैर-संपर्कों को भेजने की आवश्यकता",
"require_pin_after": "इसके बाद पिन आवश्यक है",
"required_passphrase": "पदबंध",
"rescan": "पुन: स्कैन",
"resend_code": "कृपया इसे फिर से भेजें",
"reset": "रीसेट",
"reset_password": "पासवर्ड रीसेट करें",
"restore": "पुनर्स्थापित करना",
"restore_active_seed": "सक्रिय बीज",
"restore_address": "पता",
"restore_bitcoin_description_from_keys": "अपने निजी कुंजी से उत्पन्न WIF स्ट्रिंग से अपने वॉलेट को पुनर्स्थापित करें",
@ -1001,6 +1005,7 @@
"wallet_group_description_view_seed": "आप हमेशा इस बीज को फिर से देख सकते हैं",
"wallet_group_empty_state_text_one": "लगता है कि आपके पास कोई संगत बटुआ समूह नहीं है!\n\nनल",
"wallet_group_empty_state_text_two": "नीचे एक नया बनाने के लिए।",
"wallet_has_passphrase": "इस बटुए में एक पासफ़्रेज़ है",
"wallet_keys": "बटुआ बीज / चाबियाँ",
"wallet_list_create_new_wallet": "नया बटुआ बनाएँ",
"wallet_list_edit_group_name": "समूह का नाम संपादित करें",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Dodajte unaprijed uplaćena sredstva na kartice (do ${value})",
"add_new_node": "Dodaj novi node",
"add_new_word": "Dodaj novu riječ",
"add_passphrase": "Dodajte prolaznu frazu",
"add_passphrase_warning_text": "U prošlosti unesite samo prolaznu frazu ako ste je koristili za ovaj novčanik. Ako uđete u pogrešnu lozu ili niste prije koristili prolaznu frazu na ovom novčaniku, nećete vidjeti nijedno postojeće fondove ili povijest.",
"add_receiver": "Dodajte drugi prijemnik (izborno)",
"add_secret_code": "Ili dodajte ovaj tajni kod u aplikaciju za autentifikaciju",
"add_tip": "Dodaj savjet",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Zahtijeva za slanje u interne novčanike",
"require_for_sends_to_non_contacts": "Zahtijeva za slanje nekontaktima",
"require_pin_after": "Zahtijevaj PIN nakon",
"required_passphrase": "Prolazna fraza",
"rescan": "Ponovno skeniranje",
"resend_code": "Molimo da ga ponovno pošaljete",
"reset": "Resetiraj",
"reset_password": "Poništi lozinku",
"restore": "Vratiti",
"restore_active_seed": "Aktivan pristupni izraz",
"restore_address": "Adresa",
"restore_bitcoin_description_from_keys": "Oporavi novčanik pomoću WIF niza generiranog iz vlastitih privatnih ključeva (keys)",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Uvijek možete ponovo pogledati ovo sjeme ispod",
"wallet_group_empty_state_text_one": "Izgleda da nemate nikakve kompatibilne grupe novčanika !\n\n",
"wallet_group_empty_state_text_two": "Ispod da napravite novi.",
"wallet_has_passphrase": "Ovaj novčanik ima prolaznu frazu",
"wallet_keys": "Pristupni izraz/ključ novčanika",
"wallet_list_create_new_wallet": "Izradi novi novčanik",
"wallet_list_edit_group_name": "Uredi naziv grupe",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Ավելացնել նախավճար քարտերի վրա (մինչև ${value})",
"add_new_node": "Ավելացնել նոր հանգույց",
"add_new_word": "Ավելացնել նոր բառ",
"add_passphrase": "Ավելացնել գաղտնաբառ",
"add_passphrase_warning_text": "Մուտքագրեք միայն գաղտնաբառ, եթե նախկինում այս դրամապանակի համար օգտագործեք մեկը: Եթե ​​մուտքագրեք սխալ գաղտնաբառ կամ այս դրամապանակում նախկինում չեք օգտագործել գաղտնաբառ, ապա առկա միջոցներից կամ պատմություն չեք տեսնի:",
"add_receiver": "Ավելացնել ևս մեկ ստացող (ընտրովի)",
"add_secret_code": "Կամ ավելացրեք այս գաղտնի կոդը վավերացնող հավելվածում",
"add_tip": "Ավելացնել Թեյավճար",
@ -620,10 +622,12 @@
"require_for_sends_to_internal_wallets": "Պահանջվում է ներքին դրամապանակներ ուղարկելու համար",
"require_for_sends_to_non_contacts": "Պահանջվում է ոչ կոնտակտ անձանց ուղարկելու համար",
"require_pin_after": "Պահանջվում է PIN-ը հետո",
"required_passphrase": "Փոշի",
"rescan": "Վերասկանավորել",
"resend_code": "Խնդրում ենք կրկին ուղարկել",
"reset": "Վերասահմանել",
"reset_password": "Վերասահմանել գաղտնաբառը",
"restore": "Վերականգնել",
"restore_active_seed": "Ակտիվ սերմ",
"restore_address": "Հասցե",
"restore_bitcoin_description_from_keys": "Վերականգնեք ձեր դրամապանակը ձեր գախտնի բանալիներից ստացված WIF տողից",
@ -997,6 +1001,7 @@
"wallet_group_description_view_seed": "Միշտ կարող եք կրկին դիտել այս սերմը ներքեւում",
"wallet_group_empty_state_text_one": "Կարծես թե որեւէ համատեղելի դրամապանակի խմբեր չունեք:\n\nԹակել",
"wallet_group_empty_state_text_two": "ներքեւում `նորը կազմելու համար:",
"wallet_has_passphrase": "Այս դրամապանակն ունի գաղտնաբառ",
"wallet_keys": "Դրամապանակի սերմ/բանալիներ",
"wallet_list_create_new_wallet": "Ստեղծել Նոր Դրամապանակ",
"wallet_list_edit_group_name": "Խմբագրել խմբի անվանումը",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Tambahkan dana pra-bayar ke kartu (hingga ${value})",
"add_new_node": "Tambah node baru",
"add_new_word": "Tambahkan kata baru",
"add_passphrase": "Tambahkan frasa sandi",
"add_passphrase_warning_text": "Hanya masukkan frasa sandi jika Anda telah menggunakan satu untuk dompet ini di masa lalu. Jika Anda memasukkan frasa sandi yang salah atau belum menggunakan frasa sandi sebelumnya di dompet ini, Anda tidak akan melihat dana atau sejarah yang ada.",
"add_receiver": "Tambahkan penerima lain (opsional)",
"add_secret_code": "Atau, tambahkan kode rahasia ini ke aplikasi autentikator",
"add_tip": "Tambahkan Tip",
@ -623,10 +625,12 @@
"require_for_sends_to_internal_wallets": "Diperlukan untuk mengirim ke dompet internal",
"require_for_sends_to_non_contacts": "Wajibkan untuk mengirim ke non-kontak",
"require_pin_after": "Meminta PIN setelah",
"required_passphrase": "Frasa sandi",
"rescan": "Pindai ulang",
"resend_code": "Silakan kirim ulang",
"reset": "Reset",
"reset_password": "Atur Ulang Kata Sandi",
"restore": "Memulihkan",
"restore_active_seed": "Seed aktif",
"restore_address": "Alamat",
"restore_bitcoin_description_from_keys": "Pulihkan dompet Anda dari string WIF yang dihasilkan dari private keys Anda",
@ -1002,6 +1006,7 @@
"wallet_group_description_view_seed": "Anda selalu dapat melihat benih ini lagi di bawah",
"wallet_group_empty_state_text_one": "Sepertinya Anda tidak memiliki grup dompet yang kompatibel !\n\n tap",
"wallet_group_empty_state_text_two": "di bawah ini untuk membuat yang baru.",
"wallet_has_passphrase": "Dompet ini memiliki frasa sandi",
"wallet_keys": "Seed/kunci dompet",
"wallet_list_create_new_wallet": "Buat Dompet Baru",
"wallet_list_edit_group_name": "Edit Nama Grup",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Aggiungi fondi prepagati alle carte (fino a ${value})",
"add_new_node": "Aggiungi nuovo nodo",
"add_new_word": "Aggiungi nuova parola",
"add_passphrase": "Aggiungi passphrase",
"add_passphrase_warning_text": "Inserisci una passphrase solo se ne hai usato uno per questo portafoglio in passato. Se si inserisce la passphrase sbagliata o non hai prima utilizzato una passphrase su questo portafoglio, non vedrai nessuno dei fondi o della storia esistenti.",
"add_receiver": "Aggiungi un altro ricevitore (opzionale)",
"add_secret_code": "Oppure aggiungi questo codice segreto a un'app di autenticazione",
"add_tip": "Aggiungi suggerimento",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Richiedi per invii a portafogli interni",
"require_for_sends_to_non_contacts": "Richiedi per invii a non contatti",
"require_pin_after": "Richiedi PIN dopo",
"required_passphrase": "Passphrase",
"rescan": "Scansiona di nuovo",
"resend_code": "Per favore, invialo nuovamente",
"reset": "Ripristina",
"reset_password": "Reimposta password",
"restore": "Ripristinare",
"restore_active_seed": "Seme attivo",
"restore_address": "Indirizzo",
"restore_bitcoin_description_from_keys": "Recupera il tuo portafoglio da una stringa WIF generata dalle tue chiavi private",
@ -1001,6 +1005,7 @@
"wallet_group_description_view_seed": "Puoi sempre visualizzare di nuovo questo seme sotto",
"wallet_group_empty_state_text_one": "Sembra che tu non abbia alcun gruppo di portafoglio compatibile!\n\nPremi",
"wallet_group_empty_state_text_two": "Di seguito per crearne uno nuovo.",
"wallet_has_passphrase": "Questo portafoglio ha una passphrase",
"wallet_keys": "Seme Portafoglio /chiavi",
"wallet_list_create_new_wallet": "Crea Nuovo Portafoglio",
"wallet_list_edit_group_name": "Modifica nome del gruppo",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "プリペイド資金をカードに追加します(最大 ${value}",
"add_new_node": "新しいノードを追加",
"add_new_word": "新しい単語を追加",
"add_passphrase": "パスフレーズを追加します",
"add_passphrase_warning_text": "過去にこのウォレットに使用した場合にのみ、パスフレーズを入力してください。間違ったパスフレーズに入ったり、このウォレットでパスフレーズを使用したことがない場合、既存の資金や歴史はありません。",
"add_receiver": "別のレシーバーを追加します(オプション)",
"add_secret_code": "または、このシークレット コードを認証アプリに追加します",
"add_tip": "ヒントを追加",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "内部ウォレットへの送信に必須",
"require_for_sends_to_non_contacts": "非連絡先への送信に必須",
"require_pin_after": "後に PIN が必要",
"required_passphrase": "パスフレーズ",
"rescan": "再スキャン",
"resend_code": "再送してください",
"reset": "リセットする",
"reset_password": "パスワードのリセット",
"restore": "復元する",
"restore_active_seed": "アクティブシード",
"restore_address": "住所",
"restore_bitcoin_description_from_keys": "秘密鍵から生成されたWIF文字列からウォレットを復元します",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "いつでもこの種を再び見ることができます",
"wallet_group_empty_state_text_one": "互換性のあるウォレットグループがないようです!\n\nタップ",
"wallet_group_empty_state_text_two": "以下に新しいものを作るために。",
"wallet_has_passphrase": "このウォレットにはパスフレーズがあります",
"wallet_keys": "ウォレットシード/キー",
"wallet_list_create_new_wallet": "新しいウォレットを作成",
"wallet_list_edit_group_name": "グループ名を編集します",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "카드에 선불 금액 추가(최대 ${value})",
"add_new_node": "새 노드 추가",
"add_new_word": "새로운 단어 추가",
"add_passphrase": "암호를 추가하십시오",
"add_passphrase_warning_text": "과거 에이 지갑에 사용한 경우에만 암호를 입력하십시오. 이 지갑에서 잘못된 암호를 입력하거나 암호를 사용하지 않은 경우 기존 자금이나 이력이 보이지 않을 것입니다.",
"add_receiver": "다른 수신기 추가(선택 사항)",
"add_secret_code": "또는 이 비밀 코드를 인증 앱에 추가하세요.",
"add_tip": "팁 추가",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "내부 지갑으로 보내는 데 필요",
"require_for_sends_to_non_contacts": "비접촉자에게 보내는 데 필요",
"require_pin_after": "다음 이후에 PIN 필요",
"required_passphrase": "암호",
"rescan": "재검색",
"resend_code": "다시 보내주세요",
"reset": "다시 놓기",
"reset_password": "비밀번호 재설정",
"restore": "복원하다",
"restore_active_seed": "활성 종자",
"restore_address": "주소",
"restore_bitcoin_description_from_keys": "개인 키에서 생성 된 WIF 문자열에서 지갑 복원",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "이 씨앗을 언제든지 다시 볼 수 있습니다",
"wallet_group_empty_state_text_one": "호환 지갑 그룹이없는 것 같습니다 !\n\n TAP",
"wallet_group_empty_state_text_two": "아래에서 새로운 것을 만들기 위해.",
"wallet_has_passphrase": "이 지갑에는 암호가 있습니다",
"wallet_keys": "지갑 시드 / 키",
"wallet_list_create_new_wallet": "새 월렛 만들기",
"wallet_list_edit_group_name": "그룹 이름 편집",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "ကတ်များသို့ ကြိုတင်ငွေပေးငွေများ ထည့်ပါ (${value} အထိ)",
"add_new_node": "နှာခေါင်း အသစ်ထည့်ပါ။",
"add_new_word": "စကားလုံးအသစ်ထည့်ပါ။",
"add_passphrase": "passphrase ထည့်ပါ",
"add_passphrase_warning_text": "အကယ်. သင်သည်ယခင်ကဤပိုက်ဆံအိတ်အတွက်တစ်ခုသုံးခဲ့လျှင် passphrase တစ်ခုသာရိုက်ထည့်ပါ။ အကယ်. သင်သည်မှားယွင်းသော passphrase ကို 0 င်ရောက်ခြင်းသို့မဟုတ်ဤပိုက်ဆံအိတ်ပေါ်တွင် passphrase မသုံးပါကလက်ရှိရန်ပုံငွေများသို့မဟုတ်သမိုင်းကိုသင်မတွေ့ရပါ။",
"add_receiver": "အခြားလက်ခံသူ ထည့်ပါ (ချန်လှပ်ထားနိုင်သည်)",
"add_secret_code": "သို့မဟုတ် ဤလျှို့ဝှက်ကုဒ်ကို အထောက်အထားစိစစ်ခြင်းအက်ပ်တစ်ခုသို့ ထည့်ပါ။",
"add_tip": "အကြံပြုချက်ထည့်ပါ။",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "အတွင်းပိုင်း ပိုက်ဆံအိတ်များသို့ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_sends_to_non_contacts": "အဆက်အသွယ်မရှိသူများထံ ပေးပို့ရန် လိုအပ်သည်။",
"require_pin_after": "ပြီးနောက် PIN လိုအပ်ပါသည်။",
"required_passphrase": "စကားှက်PPRase",
"rescan": "ပြန်စကင်န်လုပ်ပါ။",
"resend_code": "ကျေးဇူးပြု၍ ပြန်ပို့ပါ။",
"reset": "ပြန်လည်သတ်မှတ်ပါ။",
"reset_password": "လျှို့ဝှတ်နံပါတ်အားမူလအတိုင်းပြန်လုပ်သည်",
"restore": "ပြန်လည်တည်ထောင်",
"restore_active_seed": "တက်ကြွသောအစေ့",
"restore_address": "လိပ်စာ",
"restore_bitcoin_description_from_keys": "သင့်ကိုယ်ပိုင်သော့များမှ ထုတ်လုပ်ထားသော WIF စာကြောင်းမှ သင့်ပိုက်ဆံအိတ်ကို ပြန်လည်ရယူပါ။",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "သင်သည်ဤမျိုးစေ့ကိုနောက်တဖန်ရှုမြင်နိုင်သည်",
"wallet_group_empty_state_text_one": "သင့်တွင်သဟဇာတဖြစ်သောပိုက်ဆံအိတ်အုပ်စုများမရှိပါ။ !\n\n ကိုအသာပုတ်ပါ",
"wallet_group_empty_state_text_two": "အသစ်တစ်ခုကိုတစ်ခုလုပ်ဖို့အောက်တွင်ဖော်ပြထားသော။",
"wallet_has_passphrase": "ဒီပိုက်ဆံအိတ်က passphrase ရှိတယ်",
"wallet_keys": "ပိုက်ဆံအိတ် အစေ့/သော့များ",
"wallet_list_create_new_wallet": "Wallet အသစ်ဖန်တီးပါ။",
"wallet_list_edit_group_name": "အုပ်စုအမည်ကိုတည်းဖြတ်ပါ",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Voeg prepaid tegoed toe aan de kaarten (tot ${value})",
"add_new_node": "Voeg een nieuw knooppunt toe",
"add_new_word": "Nieuw woord toevoegen",
"add_passphrase": "Voeg wachtwoordzin toe",
"add_passphrase_warning_text": "Voer alleen een wachtwoordzin in als u er in het verleden een voor deze portemonnee hebt gebruikt. Als u de verkeerde wachtwoordzin invoert of nog niet eerder op deze portemonnee een wachtwoordzin hebt gebruikt, ziet u geen bestaande fondsen of geschiedenis.",
"add_receiver": "Nog een ontvanger toevoegen (optioneel)",
"add_secret_code": "Of voeg deze geheime code toe aan een authenticator-app",
"add_tip": "Tip toevoegen",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Vereist voor verzendingen naar interne portefeuilles",
"require_for_sends_to_non_contacts": "Vereist voor verzendingen naar niet-contacten",
"require_pin_after": "Pincode vereist na",
"required_passphrase": "Wachtwoordzin",
"rescan": "Opnieuw scannen",
"resend_code": "Stuur het alstublieft opnieuw",
"reset": "Reset",
"reset_password": "Wachtwoord resetten",
"restore": "Herstellen",
"restore_active_seed": "Actief zaad",
"restore_address": "Adres",
"restore_bitcoin_description_from_keys": "Herstel uw portemonnee van de gegenereerde WIF-string van uw privésleutels",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "Je kunt dit zaad altijd opnieuw bekijken",
"wallet_group_empty_state_text_one": "Het lijkt erop dat je geen compatibele portemonnee -groepen hebt !\n\n TAP",
"wallet_group_empty_state_text_two": "hieronder om een nieuwe te maken.",
"wallet_has_passphrase": "Deze portemonnee heeft een wachtwoordzin",
"wallet_keys": "Portemonnee zaad/sleutels",
"wallet_list_create_new_wallet": "Maak een nieuwe portemonnee",
"wallet_list_edit_group_name": "Groepsnaam bewerken",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Dodaj przedpłacone środki do kart (do ${value})",
"add_new_node": "Dodaj nowy węzeł",
"add_new_word": "Dodaj nowe słowo",
"add_passphrase": "Dodaj hasło",
"add_passphrase_warning_text": "Wprowadź hasło tylko wtedy, gdy w przeszłości używałeś jednego do tego portfela. Jeśli wejdziesz do niewłaściwej hasła lub nie użyjesz wcześniejszego pensjonatu na tym portfelu, nie zobaczysz żadnego z istniejących funduszy ani historii.",
"add_receiver": "Dodaj kolejnego odbiorcę (opcjonalnie)",
"add_secret_code": "Możesz też dodać ten tajny kod do aplikacji uwierzytelniającej",
"add_tip": "Dodaj wskazówkę",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Wymagaj wysyłania do portfeli wewnętrznych",
"require_for_sends_to_non_contacts": "Wymagaj wysyłania do osób niekontaktowych",
"require_pin_after": "Wymagaj kodu PIN po",
"required_passphrase": "Fraza",
"rescan": "Skanuj ponownie",
"resend_code": "Wyślij go ponownie",
"reset": "Wyczyść",
"reset_password": "Zresetuj hasło",
"restore": "Przywrócić",
"restore_active_seed": "Aktywne seedy",
"restore_address": "Adres",
"restore_bitcoin_description_from_keys": "Przywróć swój portfel z klucza prywatnego",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Zawsze możesz ponownie zobaczyć to ziarno pod",
"wallet_group_empty_state_text_one": "Wygląda na to, że nie masz żadnych kompatybilnych grup portfeli !\n\n Tap",
"wallet_group_empty_state_text_two": "poniżej, aby zrobić nowy.",
"wallet_has_passphrase": "Ten portfel ma panie",
"wallet_keys": "Klucze portfela",
"wallet_list_create_new_wallet": "Utwórz nowy portfel",
"wallet_list_edit_group_name": "Edytuj nazwę grupy",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Adicionar fundos pré-pagos aos cartões (até ${value})",
"add_new_node": "Adicionar novo nó",
"add_new_word": "Adicionar nova palavra",
"add_passphrase": "Adicione a senha",
"add_passphrase_warning_text": "Digite apenas uma senha se você usou uma para esta carteira no passado. Se você inserir a senha errada ou não já usou uma senha antes nesta carteira, não verá nenhum dos fundos ou histórico existentes.",
"add_receiver": "Adicione outro receptor (opcional)",
"add_secret_code": "Ou adicione este código secreto a um aplicativo autenticador",
"add_tip": "Adicionar Dica",
@ -623,10 +625,12 @@
"require_for_sends_to_internal_wallets": "Exigir envios para carteiras internas",
"require_for_sends_to_non_contacts": "Exigir para envios para não-contatos",
"require_pin_after": "Exigir PIN após",
"required_passphrase": "Senha",
"rescan": "Reescanear",
"resend_code": "Por favor, reenvie",
"reset": "Limpar",
"reset_password": "Redefinir senha",
"restore": "Restaurar",
"restore_active_seed": "Semente ativa",
"restore_address": "Endereço",
"restore_bitcoin_description_from_keys": "Restaure sua carteira a partir da string WIF gerada de suas chaves privadas",
@ -1002,6 +1006,7 @@
"wallet_group_description_view_seed": "Você sempre pode ver esta semente novamente em",
"wallet_group_empty_state_text_one": "Parece que você não tem nenhum grupo de carteira compatível !\n\n Toque",
"wallet_group_empty_state_text_two": "abaixo para fazer um novo.",
"wallet_has_passphrase": "Esta carteira tem uma senha",
"wallet_keys": "Semente/chaves da carteira",
"wallet_list_create_new_wallet": "Criar nova carteira",
"wallet_list_edit_group_name": "Editar o nome do grupo",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Добавить предоплаченные средства на карты (до ${value})",
"add_new_node": "Добавить новую ноду",
"add_new_word": "Добавить новое слово",
"add_passphrase": "Добавить пасфраз",
"add_passphrase_warning_text": "Введите фразу только в том случае, если вы использовали один для этого кошелька в прошлом. Если вы введете неверную пассисную фразу или не использовали пасфразу до этого кошелька, вы не увидите ни одного существующего фонда или истории.",
"add_receiver": "Добавить получателя (необязательно)",
"add_secret_code": "Или добавьте этот секретный код в приложение для аутентификации.",
"add_tip": "Добавить подсказку",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Требовать отправки на внутренние кошельки",
"require_for_sends_to_non_contacts": "Требовать для отправки не контактам",
"require_pin_after": "Требовать ПИН после",
"required_passphrase": "Пасфраза",
"rescan": "Пересканировать",
"resend_code": "Пожалуйста, отправьте еще раз",
"reset": "Сброс",
"reset_password": "Сбросить пароль",
"restore": "Восстановить",
"restore_active_seed": "Активная мнемоническая фраза",
"restore_address": "Адрес",
"restore_bitcoin_description_from_keys": "Вы можете восстановить кошелёк с помощью WIF",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "Вы всегда можете просматривать это семя снова под",
"wallet_group_empty_state_text_one": "Похоже, у вас нет никаких совместимых групп кошелька !\n\n tap",
"wallet_group_empty_state_text_two": "ниже, чтобы сделать новый.",
"wallet_has_passphrase": "Этот кошелек имеет фразу",
"wallet_keys": "Мнемоническая фраза/ключи кошелька",
"wallet_list_create_new_wallet": "Создать новый кошелёк",
"wallet_list_edit_group_name": "Редактировать название группы",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "เพิ่มเงินสำรองไว้บนบัตร (ถึง ${value})",
"add_new_node": "เพิ่มโหนดใหม่",
"add_new_word": "เพิ่มคำใหม่",
"add_passphrase": "เพิ่มวลีรหัสผ่าน",
"add_passphrase_warning_text": "ป้อนวลีรหัสผ่านเท่านั้นหากคุณเคยใช้สำหรับกระเป๋าเงินนี้ในอดีต หากคุณป้อนวลีรหัสผ่านที่ไม่ถูกต้องหรือไม่เคยใช้ข้อความรหัสผ่านมาก่อนในกระเป๋าเงินนี้คุณจะไม่เห็นเงินทุนหรือประวัติใด ๆ ที่มีอยู่",
"add_receiver": "เพิ่มผู้รับอื่น ๆ (ตัวเลือก)",
"add_secret_code": "หรือเพิ่มรหัสลับนี้ลงในแอปตรวจสอบความถูกต้อง",
"add_tip": "เพิ่มคำแนะนำ",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "จำเป็นต้องส่งไปยังกระเป๋าเงินภายใน",
"require_for_sends_to_non_contacts": "จำเป็นต้องส่งไปยังผู้ที่ไม่ได้ติดต่อ",
"require_pin_after": "ต้องการ PIN หลังจาก",
"required_passphrase": "วรรณะ",
"rescan": "สแกนใหม่",
"resend_code": "โปรดส่งอีกครั้ง",
"reset": "รีเซ็ต",
"reset_password": "รีเซ็ตรหัสผ่าน",
"restore": "คืนค่า",
"restore_active_seed": "ซีดที่ใช้งานอยู่",
"restore_address": "ที่อยู่",
"restore_bitcoin_description_from_keys": "กู้กระเป๋าของคุณจากสตริง WIF ที่สร้างขึ้นจากคีย์ส่วนตัวของคุณ",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "คุณสามารถดูเมล็ดพันธุ์นี้ได้อีกครั้งภายใต้",
"wallet_group_empty_state_text_one": "ดูเหมือนว่าคุณจะไม่มีกลุ่มกระเป๋าเงินที่เข้ากันได้ !\n\n แตะ",
"wallet_group_empty_state_text_two": "ด้านล่างเพื่อสร้างใหม่",
"wallet_has_passphrase": "กระเป๋าเงินนี้มีข้อความรหัสผ่าน",
"wallet_keys": "ซีดของกระเป๋า/คีย์",
"wallet_list_create_new_wallet": "สร้างกระเป๋าใหม่",
"wallet_list_edit_group_name": "แก้ไขชื่อกลุ่ม",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Magdagdag ng mga prepaid na pondo sa card (hanggang sa ${value})",
"add_new_node": "Magdagdag ng bagong node",
"add_new_word": "Magdagdag ng bagong salita",
"add_passphrase": "Magdagdag ng passphrase",
"add_passphrase_warning_text": "Magpasok lamang ng isang passphrase kung ginamit mo ang isa para sa pitaka na ito sa nakaraan. Kung nagpasok ka ng maling passphrase o hindi pa gumamit ng isang passphrase bago sa pitaka na ito, hindi mo makikita ang alinman sa umiiral na pondo o kasaysayan.",
"add_receiver": "Magdagdag ng isa pang tatanggap (opsyonal)",
"add_secret_code": "O, idagdag ang sikretong code na ito sa isang authenticator app",
"add_tip": "Magdagdag ng Tip",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Nangangailangan para sa pagpapadala sa mga panloob na wallet",
"require_for_sends_to_non_contacts": "Nangangailangan para sa pagpapadala sa mga hindi contact",
"require_pin_after": "Nangangailangan ng PIN pagkatapos",
"required_passphrase": "Passphrase",
"rescan": "Muling i-scan",
"resend_code": "Mangyaring ipadala ito muli",
"reset": "I-reset",
"reset_password": "I-reset ang password",
"restore": "Ibalik",
"restore_active_seed": "Aktibong seed",
"restore_address": "Address",
"restore_bitcoin_description_from_keys": "Ibalik ang iyong wallet mula sa nabuong WIF string mula sa iyong mga private key",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Maaari mong palaging tingnan ang binhi na ito sa ilalim",
"wallet_group_empty_state_text_one": "Mukhang wala kang anumang mga katugmang pangkat ng pitaka!\n\ntap",
"wallet_group_empty_state_text_two": "sa ibaba upang gumawa ng bago.",
"wallet_has_passphrase": "Ang pitaka na ito ay may isang passphrase",
"wallet_keys": "Wallet seed/keys",
"wallet_list_create_new_wallet": "Lumikha ng bagong wallet",
"wallet_list_edit_group_name": "I -edit ang Pangalan ng Grupo",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Ön ödemeli kartlara para ekle (En fazla yüklenebilir tutar: ${value})",
"add_new_node": "Yeni düğüm ekle",
"add_new_word": "Yeni kelime ekle",
"add_passphrase": "Parola ekle",
"add_passphrase_warning_text": "Sadece geçmişte bu cüzdan için bir tane kullandıysanız bir parola girin. Yanlış parola girerseniz veya daha önce bu cüzdanda bir parola kullanmadıysanız, mevcut fonlardan veya geçmişi görmezsiniz.",
"add_receiver": "Başka bir alıcı ekle (isteğe bağlı)",
"add_secret_code": "Veya bu gizli kodu bir kimlik doğrulama uygulamasına ekleyin",
"add_tip": "Bahşiş Ekle",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "Dahili cüzdanlara yapılan gönderimler için gereklilik",
"require_for_sends_to_non_contacts": "Kişi olmayan kişilere göndermeler için gerekli kıl",
"require_pin_after": "Şu kadar süre sonra PIN iste",
"required_passphrase": "Parola",
"rescan": "Yeniden Tara",
"resend_code": "Lütfen tekrar gönder",
"reset": "Sıfırla",
"reset_password": "Parolamı sıfırla",
"restore": "Eski haline getirmek",
"restore_active_seed": "Tohumu aktifleştir",
"restore_address": "Adres",
"restore_bitcoin_description_from_keys": "Cüzdanını, oluşturulan WIF dizesinden veya özel anahtarlarından geri yükle",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "Bu tohumu her zaman tekrar görebilirsiniz",
"wallet_group_empty_state_text_one": "Herhangi bir uyumlu cüzdan grubunuz yok gibi görünüyor !\n\n TAP",
"wallet_group_empty_state_text_two": "Yeni bir tane yapmak için aşağıda.",
"wallet_has_passphrase": "Bu cüzdanın bir parola var",
"wallet_keys": "Cüzdan tohumu/anahtarları",
"wallet_list_create_new_wallet": "Yeni Cüzdan Oluştur",
"wallet_list_edit_group_name": "Grup Adını Düzenle",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Додайте передплачені кошти на картки (до ${value})",
"add_new_node": "Додати новий вузол",
"add_new_word": "Добавити нове слово",
"add_passphrase": "Додати фразу",
"add_passphrase_warning_text": "Введіть парольну фразу, якщо ви використовували її для цього гаманця в минулому. Якщо ви вводите неправильну пасфразу або раніше не використовували парольну фразу на цьому гаманці, ви не побачите жодного з існуючих коштів чи історії.",
"add_receiver": "Додати одержувача (необов'язково)",
"add_secret_code": "Або додайте цей секретний код до програми автентифікації",
"add_tip": "Додати підказку",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Вимагати надсилання на внутрішні гаманці",
"require_for_sends_to_non_contacts": "Вимагати для надсилання неконтактним особам",
"require_pin_after": "Вимагати PIN після",
"required_passphrase": "Пропуск",
"rescan": "Пересканувати",
"resend_code": "Будь ласка, надішліть його повторно",
"reset": "Скинути",
"reset_password": "Скинути пароль",
"restore": "Відновити",
"restore_active_seed": "Активна мнемонічна фраза",
"restore_address": "Адреса",
"restore_bitcoin_description_from_keys": "Ви можете відновити гаманець за допомогою WIF",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "Ви завжди можете переглянути це насіння ще раз під",
"wallet_group_empty_state_text_one": "Схоже, у вас немає сумісних груп гаманця !\n\n Торкніться",
"wallet_group_empty_state_text_two": "нижче, щоб зробити новий.",
"wallet_has_passphrase": "Цей гаманець має фразу",
"wallet_keys": "Мнемонічна фраза/ключі гаманця",
"wallet_list_create_new_wallet": "Створити новий гаманець",
"wallet_list_edit_group_name": "Назва групи редагування",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "کارڈز میں پری پیڈ فنڈز شامل کریں (${value} تک)",
"add_new_node": "نیا نوڈ شامل کریں۔",
"add_new_word": "نیا لفظ شامل کریں۔",
"add_passphrase": "پاسفریز شامل کریں",
"add_passphrase_warning_text": "صرف ایک پاسفریز درج کریں اگر آپ نے ماضی میں اس پرس کے لئے ایک استعمال کیا ہو۔ اگر آپ غلط پاسفریس میں داخل ہوتے ہیں یا اس پرس سے پہلے پاس فیز کا استعمال نہیں کرتے ہیں تو ، آپ کو موجودہ فنڈز یا تاریخ میں سے کوئی بھی نظر نہیں آئے گا۔",
"add_receiver": "دوسرا وصول کنندہ شامل کریں (اختیاری)",
"add_secret_code": " ۔ﮟﯾﺮﮐ ﻞﻣﺎﺷ ﮟﯿﻣ ﭗﯾﺍ ﮦﺪﻨﻨﮐ ﻖﯾﺪﺼﺗ ﻮﮐ ﮈﻮﮐ ﮧﯿﻔﺧ ﺱﺍ ،ﺎﯾ",
"add_tip": "ٹپ شامل کریں۔",
@ -623,10 +625,12 @@
"require_for_sends_to_internal_wallets": "اندرونی بٹوے پر بھیجنے کے لیے درکار ہے۔",
"require_for_sends_to_non_contacts": "غیر رابطوں کو بھیجنے کی ضرورت ہے۔",
"require_pin_after": "اس کے بعد PIN کی ضرورت ہے۔",
"required_passphrase": "پاسفریز",
"rescan": "دوبارہ اسکین کریں۔",
"resend_code": "براہ کرم اسے دوبارہ بھیجیں۔",
"reset": "دوبارہ ترتیب دیں۔",
"reset_password": "پاس ورڈ ری سیٹ",
"restore": "بحال کریں",
"restore_active_seed": "فعال بیج",
"restore_address": "پتہ",
"restore_bitcoin_description_from_keys": "اپنی نجی کلیدوں سے تیار کردہ WIF سٹرنگ سے اپنے بٹوے کو بحال کریں۔",
@ -1001,6 +1005,7 @@
"wallet_group_description_view_seed": "آپ ہمیشہ اس بیج کو دوبارہ دیکھ سکتے ہیں",
"wallet_group_empty_state_text_one": "ایسا لگتا ہے کہ آپ کے پاس کوئی مطابقت پذیر والیٹ گروپس نہیں ہیں !\n\n نل",
"wallet_group_empty_state_text_two": "ایک نیا بنانے کے لئے ذیل میں.",
"wallet_has_passphrase": "اس پرس میں پاسفریس ہے",
"wallet_keys": "بٹوے کے بیج / چابیاں",
"wallet_list_create_new_wallet": "نیا والیٹ بنائیں",
"wallet_list_edit_group_name": "گروپ کے نام میں ترمیم کریں",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Thêm tiền trả trước vào thẻ (tối đa ${value})",
"add_new_node": "Thêm nút mới",
"add_new_word": "Thêm từ mới",
"add_passphrase": "Thêm cụm mật khẩu",
"add_passphrase_warning_text": "Chỉ nhập một cụm mật khẩu nếu bạn đã sử dụng một cái cho ví này trong quá khứ. Nếu bạn nhập sai cụm từ hoặc chưa sử dụng cụm mật khẩu trước đây trên ví này, bạn sẽ không thấy bất kỳ quỹ hoặc lịch sử hiện có nào.",
"add_receiver": "Thêm người nhận khác (tùy chọn)",
"add_secret_code": "Hoặc, thêm mã bí mật này vào ứng dụng xác thực",
"add_tip": "Thêm tiền boa",
@ -619,10 +621,12 @@
"require_for_sends_to_internal_wallets": "Yêu cầu khi gửi đến ví nội bộ",
"require_for_sends_to_non_contacts": "Yêu cầu khi gửi đến người không phải danh bạ",
"require_pin_after": "Yêu cầu PIN sau",
"required_passphrase": "Cụm cụm",
"rescan": "Quét lại",
"resend_code": "Vui lòng gửi lại",
"reset": "Đặt lại",
"reset_password": "Đặt lại mật khẩu",
"restore": "Khôi phục",
"restore_active_seed": "Hạt giống hoạt động",
"restore_address": "Địa chỉ",
"restore_bitcoin_description_from_keys": "Khôi phục ví của bạn từ chuỗi WIF được tạo từ khóa riêng của bạn",
@ -996,6 +1000,7 @@
"wallet_group_description_view_seed": "Bạn luôn có thể xem lại hạt giống này dưới",
"wallet_group_empty_state_text_one": "Có vẻ như bạn không có bất kỳ nhóm ví tương thích nào !\n\n Tap",
"wallet_group_empty_state_text_two": "Dưới đây để làm một cái mới.",
"wallet_has_passphrase": "Ví này có một cụm từ",
"wallet_keys": "Hạt giống/khóa ví",
"wallet_list_create_new_wallet": "Tạo ví mới",
"wallet_list_edit_group_name": "Chỉnh sửa tên nhóm",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "Ẹ fikún owó sí àwọn káàdì (kò tóbi ju ${value})",
"add_new_node": "Fi apẹka kún",
"add_new_word": "Fikún ọ̀rọ̀ títun",
"add_passphrase": "Ṣafikun Kọwe",
"add_passphrase_warning_text": "Tẹ ni iwe ikawe nikan ti o ba ti lo ọkan fun apamọwọ yii ni igba atijọ. Ti o ba tẹ iwe ọrọ kukuru ti ko tọ tabi ko lo iwe kukuru ṣaaju lori apamọwọ yii, iwọ kii yoo wo eyikeyi awọn owo tabi itan-akọọlẹ.",
"add_receiver": "Fikún àdírẹ́sì mìíràn (ìyàn nìyí)",
"add_secret_code": "Tabi, ṣafikun koodu aṣiri yii si ohun elo onijeri kan",
"add_tip": "Fún owó àfikún",
@ -622,10 +624,12 @@
"require_for_sends_to_internal_wallets": "Beere fun fifiranṣẹ si awọn apamọwọ inu",
"require_for_sends_to_non_contacts": "Beere fun fifiranṣẹ si awọn ti kii ṣe awọn olubasọrọ",
"require_pin_after": "Ẹ nílò òǹkà ìdánimọ̀ àdáni láàárín",
"required_passphrase": "Kukurukọni",
"rescan": "Tún Wá",
"resend_code": "Ẹ jọ̀wọ́ tún un ránṣé",
"reset": "Tún ṣe",
"reset_password": "Tún ọ̀rọ̀ aṣínà ṣe",
"restore": "Mu pada",
"restore_active_seed": "Hóró lọ́wọ́",
"restore_address": "Àdírẹ́sì",
"restore_bitcoin_description_from_keys": "Mú àpamọ́wọ́ yín padà láti ọ̀rọ̀ WIF t'á ti dá láti kọ́kọ́rọ́ àdáni yín",
@ -1000,6 +1004,7 @@
"wallet_group_description_view_seed": "O le nigbagbogbo wo irugbin yii lẹẹkansi labẹ",
"wallet_group_empty_state_text_one": "O dabi pe o ko ni eyikeyi awọn ẹgbẹ ti o ni ibamu!\n\ntẹ ni kia kia",
"wallet_group_empty_state_text_two": "ni isalẹ lati ṣe ọkan titun.",
"wallet_has_passphrase": "Apamọwọ yii ni iwe kukuru kan",
"wallet_keys": "Hóró/kọ́kọ́rọ́ àpamọ́wọ́",
"wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun",
"wallet_list_edit_group_name": "Ṣatunṣe Orukọ Ẹgbẹ",

View file

@ -15,6 +15,8 @@
"add_fund_to_card": "向卡中添加预付资金(最多 ${value}",
"add_new_node": "添加新节点",
"add_new_word": "添加新词",
"add_passphrase": "添加密码",
"add_passphrase_warning_text": "仅当您过去曾经为此钱包使用一个时,才输入密码。如果您在此钱包上输入错误的密码短语或在此钱包上之前没有使用过密码,则不会看到任何现有的资金或历史记录。",
"add_receiver": "添加另一個接收器(可選)",
"add_secret_code": "或者,将此密码添加到身份验证器应用程序中",
"add_tip": "添加提示",
@ -621,10 +623,12 @@
"require_for_sends_to_internal_wallets": "需要发送到内部钱包",
"require_for_sends_to_non_contacts": "需要发送给非联系人",
"require_pin_after": "之后需要 PIN",
"required_passphrase": "密码",
"rescan": "重新扫描",
"resend_code": "请重新发送",
"reset": "重置",
"reset_password": "重置密码",
"restore": "恢复",
"restore_active_seed": "活动种子",
"restore_address": "地址",
"restore_bitcoin_description_from_keys": "从私钥中生成的WIF字符串恢复您钱包",
@ -999,6 +1003,7 @@
"wallet_group_description_view_seed": "您可以随时再次在下面查看此种子",
"wallet_group_empty_state_text_one": "看起来您没有任何兼容的钱包组!\n\n tap",
"wallet_group_empty_state_text_two": "下面是一个新的。",
"wallet_has_passphrase": "这个钱包有一个密码",
"wallet_keys": "钱包种子/密钥",
"wallet_list_create_new_wallet": "创建新钱包",
"wallet_list_edit_group_name": "编辑组名称",

View file

@ -8,7 +8,7 @@ if [[ ! -d "monero_c/.git" ]];
then
git clone https://github.com/mrcyjanek/monero_c --branch master monero_c
cd monero_c
git checkout 84e52393e395d75f449bcd81e23028889538118f
git checkout b335585a7fb94b315eb52bd88f2da6d3489fa508
git reset --hard
git submodule update --init --force --recursive
./apply_patches.sh monero