fix(cw_monero): prevent monero wallet from breaking during rename (#2214)

* fix(cw_monero): prevent monero wallet from breaking during rename

* update to cleaned up monero.dart

* fix: transaction screen not refreshing in monero

* fix: wallets not opening until app restart after rename.

* fix(cw_decred): wallet renaming throwing

* fix: transaction not being shown after sending until 1st confirmation

* fix(cw_monero): loop safeguard

* fix: don't await wallet.fetchTransactions
This commit is contained in:
cyan 2025-05-02 14:30:39 +02:00 committed by GitHub
parent dd8413bae2
commit a2294c4a06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 577 additions and 714 deletions

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

@ -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

@ -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