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:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:cw_core/exceptions.dart'; import 'package:cw_core/exceptions.dart';
import 'package:cw_core/transaction_direction.dart'; import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/utils/print_verbose.dart';
@ -602,7 +603,25 @@ abstract class DecredWalletBase
throw "wallet already exists at $newDirPath"; 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 @override

View file

@ -118,6 +118,10 @@ class DecredWalletService extends WalletService<
currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
? testnet ? testnet
: mainnet; : mainnet;
if (libwallet == null) {
libwallet = await Libwallet.spawn();
libwallet!.initLibdcrwallet("", "err");
}
final currentWallet = DecredWallet( final currentWallet = DecredWallet(
currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet); 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/api/wallet.dart';
import 'package:cw_monero/monero_account_list.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; Wallet2Wallet? currentWallet = null;
bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0; bool get isViewOnly => int.tryParse(currentWallet!.secretSpendKey()) == 0;
int _wlptrForW = 0; int _wlptrForW = 0;
monero.WalletListener? _wlptr = null; Wallet2WalletListener? _wlptr = null;
monero.WalletListener? getWlptr() { Wallet2WalletListener? getWlptr() {
if (wptr == null) return null; if (currentWallet == null) return null;
if (wptr!.address == _wlptrForW) return _wlptr!; _wlptrForW = currentWallet!.ffiAddress();
_wlptrForW = wptr!.address; _wlptr = currentWallet!.getWalletListener();
_wlptr = monero.MONERO_cw_getWalletListener(wptr!);
return _wlptr!; return _wlptr!;
} }
monero.SubaddressAccount? subaddressAccount; Wallet2SubaddressAccount? subaddressAccount;
bool isUpdating = false; bool isUpdating = false;
void refreshAccounts() { void refreshAccounts() {
try { try {
isUpdating = true; isUpdating = true;
subaddressAccount = monero.Wallet_subaddressAccount(wptr!); subaddressAccount = currentWallet!.subaddressAccount();
monero.SubaddressAccount_refresh(subaddressAccount!); subaddressAccount!.refresh();
isUpdating = false; isUpdating = false;
} catch (e) { } catch (e) {
isUpdating = false; isUpdating = false;
@ -34,45 +34,28 @@ void refreshAccounts() {
} }
} }
List<monero.SubaddressAccountRow> getAllAccount() { List<Wallet2SubaddressAccountRow> getAllAccount() {
// final size = monero.Wallet_numSubaddressAccounts(wptr!); // final size = monero.Wallet_numSubaddressAccounts(wptr!);
refreshAccounts(); refreshAccounts();
int size = monero.SubaddressAccount_getAll_size(subaddressAccount!); int size = subaddressAccount!.getAll_size();
if (size == 0) { if (size == 0) {
monero.Wallet_addSubaddressAccount(wptr!); currentWallet!.addSubaddressAccount();
monero.Wallet_status(wptr!); currentWallet!.status();
return []; return [];
} }
return List.generate(size, (index) { return List.generate(size, (index) {
return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index); return subaddressAccount!.getAll_byIndex(index);
}); });
} }
void addAccountSync({required String label}) { void addAccount({required String label}) {
monero.Wallet_addSubaddressAccount(wptr!, label: label); currentWallet!.addSubaddressAccount(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);
unawaited(store()); unawaited(store());
} }
Future<void> setLabelForAccount({required int accountIndex, required String label}) async { void setLabelForAccount({required int accountIndex, required String label}) {
_setLabelForAccount({'accountIndex': accountIndex, 'label': label}); subaddressAccount!.setLabel(accountIndex: accountIndex, label: label);
unawaited(store()); 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:cw_monero/api/account_list.dart';
import 'package:monero/monero.dart' as monero; import 'package:monero/monero.dart' as monero;
import 'package:monero/src/wallet2.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
monero.Coins? coins = null; Wallet2Coins? coins = null;
final coinsMutex = Mutex(); final coinsMutex = Mutex();
Future<void> refreshCoins(int accountIndex) async { Future<void> refreshCoins(int accountIndex) async {
if (coinsMutex.isLocked) { if (coinsMutex.isLocked) {
return; return;
} }
coins = monero.Wallet_coins(wptr!); coins = currentWallet!.coins();
final coinsPtr = coins!.address; final coinsPtr = coins!.ffiAddress();
await coinsMutex.acquire(); await coinsMutex.acquire();
await Isolate.run(() => monero.Coins_refresh(Pointer.fromAddress(coinsPtr))); await Isolate.run(() => monero.Coins_refresh(Pointer.fromAddress(coinsPtr)));
coinsMutex.release(); coinsMutex.release();
@ -21,14 +22,14 @@ Future<void> refreshCoins(int accountIndex) async {
Future<int> countOfCoins() async { Future<int> countOfCoins() async {
await coinsMutex.acquire(); await coinsMutex.acquire();
final count = monero.Coins_count(coins!); final count = coins!.count();
coinsMutex.release(); coinsMutex.release();
return count; return count;
} }
Future<monero.CoinsInfo> getCoin(int index) async { Future<Wallet2CoinsInfo> getCoin(int index) async {
await coinsMutex.acquire(); await coinsMutex.acquire();
final coin = monero.Coins_coin(coins!, index); final coin = coins!.coin(index);
coinsMutex.release(); coinsMutex.release();
return coin; return coin;
} }
@ -37,7 +38,7 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
final count = await countOfCoins(); final count = await countOfCoins();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
final coin = await getCoin(i); final coin = await getCoin(i);
final coinAddress = monero.CoinsInfo_keyImage(coin); final coinAddress = coin.keyImage;
if (keyImage == coinAddress) { if (keyImage == coinAddress) {
return i; return i;
} }
@ -47,14 +48,14 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
Future<void> freezeCoin(int index) async { Future<void> freezeCoin(int index) async {
await coinsMutex.acquire(); await coinsMutex.acquire();
final coinsPtr = coins!.address; final coinsPtr = coins!.ffiAddress();
await Isolate.run(() => monero.Coins_setFrozen(Pointer.fromAddress(coinsPtr), index: index)); await Isolate.run(() => monero.Coins_setFrozen(Pointer.fromAddress(coinsPtr), index: index));
coinsMutex.release(); coinsMutex.release();
} }
Future<void> thawCoin(int index) async { Future<void> thawCoin(int index) async {
await coinsMutex.acquire(); await coinsMutex.acquire();
final coinsPtr = coins!.address; final coinsPtr = coins!.ffiAddress();
await Isolate.run(() => monero.Coins_thaw(Pointer.fromAddress(coinsPtr), index: index)); await Isolate.run(() => monero.Coins_thaw(Pointer.fromAddress(coinsPtr), index: index));
coinsMutex.release(); coinsMutex.release();
} }

View file

@ -2,7 +2,8 @@
import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_monero/api/transaction_history.dart';
import 'package:cw_monero/api/wallet.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; bool isUpdating = false;
@ -16,7 +17,7 @@ class SubaddressInfoMetadata {
SubaddressInfoMetadata? subaddress = null; SubaddressInfoMetadata? subaddress = null;
String getRawLabel({required int accountIndex, required int addressIndex}) { 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}) { void refreshSubaddresses({required int accountIndex}) {
@ -46,7 +47,7 @@ class Subaddress {
final int received; final int received;
final int txCount; final int txCount;
String get label { 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 if (localLabel.startsWith("#$addressIndex")) return localLabel; // don't duplicate the ID if it was user-providen
return "#$addressIndex ${localLabel}".trim(); return "#$addressIndex ${localLabel}".trim();
} }
@ -66,26 +67,26 @@ int lastTxCount = 0;
List<TinyTransactionDetails> ttDetails = []; List<TinyTransactionDetails> ttDetails = [];
List<Subaddress> getAllSubaddresses() { List<Subaddress> getAllSubaddresses() {
txhistory = monero.Wallet_history(wptr!); txhistory = currentWallet!.history();
final txCount = monero.TransactionHistory_count(txhistory!); final txCount = txhistory!.count();
if (lastTxCount != txCount && lastWptr != wptr!.address) { if (lastTxCount != txCount && lastWptr != currentWallet!.ffiAddress()) {
final List<TinyTransactionDetails> newttDetails = []; final List<TinyTransactionDetails> newttDetails = [];
lastTxCount = txCount; lastTxCount = txCount;
lastWptr = wptr!.address; lastWptr = currentWallet!.ffiAddress();
for (var i = 0; i < txCount; i++) { for (var i = 0; i < txCount; i++) {
final tx = monero.TransactionHistory_transaction(txhistory!, index: i); final tx = txhistory!.transaction(i);
if (monero.TransactionInfo_direction(tx) == monero.TransactionInfo_Direction.Out) continue; if (tx.direction() == TransactionInfo_Direction.Out.index) continue;
final subaddrs = monero.TransactionInfo_subaddrIndex(tx).split(","); final subaddrs = tx.subaddrIndex().split(",");
final account = monero.TransactionInfo_subaddrAccount(tx); final account = tx.subaddrAccount();
newttDetails.add(TinyTransactionDetails( newttDetails.add(TinyTransactionDetails(
address: List.generate(subaddrs.length, (index) => getAddress(accountIndex: account, addressIndex: int.tryParse(subaddrs[index])??0)), 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.clear();
ttDetails.addAll(newttDetails); 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 list = List.generate(size, (index) {
final ttDetailsLocal = ttDetails.where((element) { final ttDetailsLocal = ttDetails.where((element) {
final address = getAddress( final address = getAddress(
@ -119,46 +120,17 @@ List<Subaddress> getAllSubaddresses() {
} }
int numSubaddresses(int subaccountIndex) { int numSubaddresses(int subaccountIndex) {
return monero.Wallet_numSubaddresses(wptr!, accountIndex: subaccountIndex); return currentWallet!.numSubaddresses(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);
} }
Future<void> addSubaddress({required int accountIndex, required String label}) async { 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(); await store();
} }
Future<void> setLabelForSubaddress( Future<void> setLabelForSubaddress(
{required int accountIndex, required int addressIndex, required String label}) async { {required int accountIndex, required int addressIndex, required String label}) async {
_setLabelForSubaddress({ currentWallet!.setSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
'accountIndex': accountIndex,
'addressIndex': addressIndex,
'label': label
});
await store(); 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/api/wallet.dart';
import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart'; import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:monero/src/monero.dart';
import 'package:monero/monero.dart' as monero; 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:monero/src/generated_bindings_monero.g.dart' as monero_gen;
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
Map<int, Map<String, String>> txKeys = {}; Map<int, Map<String, String>> txKeys = {};
String getTxKey(String txId) { String getTxKey(String txId) {
txKeys[wptr!.address] ??= {}; txKeys[currentWallet!.ffiAddress()] ??= {};
if (txKeys[wptr!.address]![txId] != null) { if (txKeys[currentWallet!.ffiAddress()]![txId] != null) {
return txKeys[wptr!.address]![txId]!; return txKeys[currentWallet!.ffiAddress()]![txId]!;
} }
final txKey = monero.Wallet_getTxKey(wptr!, txid: txId); final txKey = currentWallet!.getTxKey(txid: txId);
final status = monero.Wallet_status(wptr!); final status = currentWallet!.status();
if (status != 0) { if (status != 0) {
monero.Wallet_errorString(wptr!); currentWallet!.errorString();
txKeys[wptr!.address]![txId] = ""; txKeys[currentWallet!.ffiAddress()]![txId] = "";
return ""; return "";
} }
txKeys[wptr!.address]![txId] = txKey; txKeys[currentWallet!.ffiAddress()]![txId] = txKey;
return txKey; return txKey;
} }
final txHistoryMutex = Mutex(); final txHistoryMutex = Mutex();
monero.TransactionHistory? txhistory; Wallet2TransactionHistory? txhistory;
bool isRefreshingTx = false; bool isRefreshingTx = false;
Future<void> refreshTransactions() async { Future<void> refreshTransactions() async {
if (isRefreshingTx == true) return; if (isRefreshingTx == true) return;
isRefreshingTx = true; isRefreshingTx = true;
txhistory ??= monero.Wallet_history(wptr!); txhistory ??= currentWallet!.history();
final ptr = txhistory!.address; final ptr = txhistory!.ffiAddress();
await txHistoryMutex.acquire(); await txHistoryMutex.acquire();
await Isolate.run(() { await Isolate.run(() {
monero.TransactionHistory_refresh(Pointer.fromAddress(ptr)); monero.TransactionHistory_refresh(Pointer.fromAddress(ptr));
@ -48,14 +50,14 @@ Future<void> refreshTransactions() async {
isRefreshingTx = false; isRefreshingTx = false;
} }
int countOfTransactions() => monero.TransactionHistory_count(txhistory!); int countOfTransactions() => txhistory!.count();
Future<List<Transaction>> getAllTransactions() async { Future<List<Transaction>> getAllTransactions() async {
List<Transaction> dummyTxs = []; List<Transaction> dummyTxs = [];
await txHistoryMutex.acquire(); await txHistoryMutex.acquire();
txhistory ??= monero.Wallet_history(wptr!); txhistory ??= currentWallet!.history();
final startAddress = txhistory!.address * wptr!.address; final startAddress = txhistory!.ffiAddress() * currentWallet!.ffiAddress();
int size = countOfTransactions(); int size = countOfTransactions();
final list = <Transaction>[]; final list = <Transaction>[];
for (int index = 0; index < size; index++) { 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. // Give main thread a chance to do other things.
await Future.delayed(Duration.zero); 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"); printV("Loop broken because txhistory!.address * wptr!.address != startAddress");
break; break;
} }
final txInfo = monero.TransactionHistory_transaction(txhistory!, index: index); final txInfo = txhistory!.transaction(index);
final txHash = monero.TransactionInfo_hash(txInfo); final txHash = txInfo.hash();
txCache[wptr!.address] ??= {}; txCache[currentWallet!.ffiAddress()] ??= {};
txCache[wptr!.address]![txHash] = Transaction(txInfo: txInfo); txCache[currentWallet!.ffiAddress()]![txHash] = Transaction(txInfo: txInfo);
list.add(txCache[wptr!.address]![txHash]!); list.add(txCache[currentWallet!.ffiAddress()]![txHash]!);
} }
txHistoryMutex.release(); txHistoryMutex.release();
final accts = monero.Wallet_numSubaddressAccounts(wptr!); final accts = currentWallet!.numSubaddressAccounts();
for (var i = 0; i < accts; i++) { for (var i = 0; i < accts; i++) {
final fullBalance = monero.Wallet_balance(wptr!, accountIndex: i); final fullBalance = currentWallet!.balance(accountIndex: i);
final availBalance = monero.Wallet_unlockedBalance(wptr!, accountIndex: i); final availBalance = currentWallet!.unlockedBalance(accountIndex: i);
if (fullBalance > availBalance) { if (fullBalance > availBalance) {
if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isEmpty) { if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isEmpty) {
dummyTxs.add( dummyTxs.add(
@ -95,7 +97,7 @@ Future<List<Transaction>> getAllTransactions() async {
isSpend: false, isSpend: false,
hash: "pending", hash: "pending",
key: "", key: "",
txInfo: Pointer.fromAddress(0), txInfo: DummyTransaction(),
)..timeStamp = DateTime.now() )..timeStamp = DateTime.now()
); );
} }
@ -105,16 +107,21 @@ Future<List<Transaction>> getAllTransactions() async {
return list; return list;
} }
class DummyTransaction implements Wallet2TransactionInfo {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
Map<int, Map<String, Transaction>> txCache = {}; Map<int, Map<String, Transaction>> txCache = {};
Future<Transaction> getTransaction(String txId) async { Future<Transaction> getTransaction(String txId) async {
if (txCache[wptr!.address] != null && txCache[wptr!.address]![txId] != null) { if (txCache[currentWallet!.ffiAddress()] != null && txCache[currentWallet!.ffiAddress()]![txId] != null) {
return txCache[wptr!.address]![txId]!; return txCache[currentWallet!.ffiAddress()]![txId]!;
} }
await txHistoryMutex.acquire(); await txHistoryMutex.acquire();
final tx = monero.TransactionHistory_transactionById(txhistory!, txid: txId); final tx = txhistory!.transactionById(txId);
final txDart = Transaction(txInfo: tx); final txDart = Transaction(txInfo: tx);
txCache[wptr!.address] ??= {}; txCache[currentWallet!.ffiAddress()] ??= {};
txCache[wptr!.address]![txId] = txDart; txCache[currentWallet!.ffiAddress()]![txId] = txDart;
txHistoryMutex.release(); txHistoryMutex.release();
return txDart; return txDart;
} }
@ -127,9 +134,9 @@ Future<PendingTransactionDescription> createTransactionSync(
int accountIndex = 0, int accountIndex = 0,
List<String> preferredInputs = const []}) async { 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? // force reconnection in case the os killed the connection?
// fixes failed to get block height error. // fixes failed to get block height error.
@ -149,7 +156,7 @@ Future<PendingTransactionDescription> createTransactionSync(
final paymentIdAddr = paymentId_.address; final paymentIdAddr = paymentId_.address;
final preferredInputsAddr = preferredInputs_.address; final preferredInputsAddr = preferredInputs_.address;
final spaddr = monero.defaultSeparator.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( final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction(
Pointer.fromAddress(waddr), Pointer.fromAddress(waddr),
Pointer.fromAddress(addraddr).cast(), Pointer.fromAddress(addraddr).cast(),
@ -163,15 +170,16 @@ Future<PendingTransactionDescription> createTransactionSync(
); );
return tx.address; return tx.address;
})); }));
final Wallet2PendingTransaction pendingTx = MoneroPendingTransaction(pendingTxPtr);
calloc.free(address_); calloc.free(address_);
calloc.free(paymentId_); calloc.free(paymentId_);
calloc.free(preferredInputs_); calloc.free(preferredInputs_);
final String? error = (() { final String? error = (() {
final status = monero.PendingTransaction_status(pendingTx); final status = pendingTx.status();
if (status == 0) { if (status == 0) {
return null; return null;
} }
return monero.PendingTransaction_errorString(pendingTx); return pendingTx.errorString();
})(); })();
if (error != null) { if (error != null) {
@ -182,10 +190,10 @@ Future<PendingTransactionDescription> createTransactionSync(
throw CreationTransactionException(message: message); throw CreationTransactionException(message: message);
} }
final rAmt = monero.PendingTransaction_amount(pendingTx); final rAmt = pendingTx.amount();
final rFee = monero.PendingTransaction_fee(pendingTx); final rFee = pendingTx.fee();
final rHash = monero.PendingTransaction_txid(pendingTx, ''); final rHash = pendingTx.txid('');
final rHex = monero.PendingTransaction_hex(pendingTx, ''); final rHex = pendingTx.hex('');
final rTxKey = rHash; final rTxKey = rHash;
return PendingTransactionDescription( return PendingTransactionDescription(
@ -194,7 +202,7 @@ Future<PendingTransactionDescription> createTransactionSync(
hash: rHash, hash: rHash,
hex: rHex, hex: rHex,
txKey: rTxKey, txKey: rTxKey,
pointerAddress: pendingTx.address, pointerAddress: pendingTx.ffiAddress(),
); );
} }
@ -206,9 +214,9 @@ Future<PendingTransactionDescription> createTransactionMultDest(
List<String> preferredInputs = const []}) async { List<String> preferredInputs = const []}) async {
final dstAddrs = outputs.map((e) => e.address).toList(); 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 // force reconnection in case the os killed the connection
Isolate.run(() async { Isolate.run(() async {
@ -227,49 +235,50 @@ Future<PendingTransactionDescription> createTransactionMultDest(
).address; ).address;
})); }));
if (monero.PendingTransaction_status(txptr) != 0) { final Wallet2PendingTransaction tx = MoneroPendingTransaction(txptr);
throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr));
if (tx.status() != 0) {
throw CreationTransactionException(message: tx.errorString());
} }
return PendingTransactionDescription( return PendingTransactionDescription(
amount: monero.PendingTransaction_amount(txptr), amount: tx.amount(),
fee: monero.PendingTransaction_fee(txptr), fee: tx.fee(),
hash: monero.PendingTransaction_txid(txptr, ''), hash: tx.txid(''),
hex: monero.PendingTransaction_hex(txptr, ''), hex: tx.hex(''),
txKey: monero.PendingTransaction_txid(txptr, ''), txKey: tx.txid(''),
pointerAddress: txptr.address, pointerAddress: tx.ffiAddress(),
); );
} }
String? commitTransactionFromPointerAddress({required int address, required bool useUR}) => 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}) { String? commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) {
final transactionPointerAddress = transactionPointer.address;
final txCommit = useUR final txCommit = useUR
? monero.PendingTransaction_commitUR(transactionPointer, 120) ? tx.commitUR(120)
: Isolate.run(() { : Isolate.run(() {
monero.PendingTransaction_commit( monero.PendingTransaction_commit(
Pointer.fromAddress(transactionPointerAddress), Pointer.fromAddress(tx.ffiAddress()),
filename: '', filename: '',
overwrite: false, overwrite: false,
); );
}); });
String? error = (() { String? error = (() {
final status = monero.PendingTransaction_status(transactionPointer.cast()); final status = tx.status();
if (status == 0) { if (status == 0) {
return null; return null;
} }
return monero.PendingTransaction_errorString(transactionPointer.cast()); return tx.errorString();
})(); })();
if (error == null) { if (error == null) {
error = (() { error = (() {
final status = monero.Wallet_status(wptr!); final status = currentWallet!.status();
if (status == 0) { if (status == 0) {
return null; 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 { class Transaction {
final String displayLabel; final String displayLabel;
late final String subaddressLabel = monero.Wallet_getSubaddressLabel( late final String subaddressLabel = currentWallet!.getSubaddressLabel(
wptr!,
accountIndex: accountIndex, accountIndex: accountIndex,
addressIndex: addressIndex, addressIndex: addressIndex,
); );
@ -372,26 +347,26 @@ class Transaction {
// final SubAddress? subAddress; // final SubAddress? subAddress;
// List<Transfer> transfers = []; // List<Transfer> transfers = [];
// final int txIndex; // final int txIndex;
final monero.TransactionInfo txInfo; final Wallet2TransactionInfo txInfo;
Transaction({ Transaction({
required this.txInfo, required this.txInfo,
}) : displayLabel = monero.TransactionInfo_label(txInfo), }) : displayLabel = txInfo.label(),
hash = monero.TransactionInfo_hash(txInfo), hash = txInfo.hash(),
timeStamp = DateTime.fromMillisecondsSinceEpoch( timeStamp = DateTime.fromMillisecondsSinceEpoch(
monero.TransactionInfo_timestamp(txInfo) * 1000, txInfo.timestamp() * 1000,
), ),
isSpend = monero.TransactionInfo_direction(txInfo) == isSpend = txInfo.direction() ==
monero.TransactionInfo_Direction.Out, monero.TransactionInfo_Direction.Out.index,
amount = monero.TransactionInfo_amount(txInfo), amount = txInfo.amount(),
paymentId = monero.TransactionInfo_paymentId(txInfo), paymentId = txInfo.paymentId(),
accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), accountIndex = txInfo.subaddrAccount(),
addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0, addressIndex = int.tryParse(txInfo.subaddrIndex().split(", ")[0]) ?? 0,
addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(), addressIndexList = txInfo.subaddrIndex().split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
blockheight = monero.TransactionInfo_blockHeight(txInfo), blockheight = txInfo.blockHeight(),
confirmations = monero.TransactionInfo_confirmations(txInfo), confirmations = txInfo.confirmations(),
fee = monero.TransactionInfo_fee(txInfo), fee = txInfo.fee(),
description = monero.TransactionInfo_description(txInfo), description = txInfo.description(),
key = getTxKey(monero.TransactionInfo_hash(txInfo)); key = getTxKey(txInfo.hash());
Transaction.dummy({ Transaction.dummy({
required this.displayLabel, required this.displayLabel,

View file

@ -5,8 +5,6 @@ import 'dart:isolate';
import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_monero/api/account_list.dart'; import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/exceptions/setup_wallet_exception.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:monero/monero.dart' as monero;
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:polyseed/polyseed.dart'; import 'package:polyseed/polyseed.dart';
@ -15,36 +13,37 @@ bool debugMonero = false;
int getSyncingHeight() { int getSyncingHeight() {
// final height = monero.MONERO_cw_WalletListener_height(getWlptr()); // 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"); // printV("height: $height / $h2");
return h2; return h2;
} }
bool isNeededToRefresh() { bool isNeededToRefresh() {
final wlptr = getWlptr(); final wl = getWlptr();
if (wlptr == null) return false; if (wl == null) return false;
final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(wlptr); final ret = wl.isNeedToRefresh();
monero.MONERO_cw_WalletListener_resetNeedToRefresh(wlptr); wl.resetNeedToRefresh();
return ret; return ret;
} }
bool isNewTransactionExist() { bool isNewTransactionExist() {
final wlptr = getWlptr(); final wlptr = getWlptr();
if (wlptr == null) return false; if (wlptr == null) return false;
final ret = monero.MONERO_cw_WalletListener_isNewTransactionExist(wlptr); final ret = wlptr.isNewTransactionExist();
monero.MONERO_cw_WalletListener_resetIsNewTransactionExist(wlptr); wlptr.resetIsNewTransactionExist();
return ret; return ret;
} }
String getFilename() => monero.Wallet_filename(wptr!); String getFilename() => currentWallet!.filename();
String getSeed() { String getSeed() {
// monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed); // monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
final cakepolyseed = final cakepolyseed =
monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed"); currentWallet!.getCacheAttribute(key: "cakewallet.seed");
final cakepassphrase = getPassphrase(); final cakepassphrase = getPassphrase();
final weirdPolyseed = monero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase); final weirdPolyseed = currentWallet!.getPolyseed(passphrase: cakepassphrase);
if (weirdPolyseed != "") return weirdPolyseed; if (weirdPolyseed != "") return weirdPolyseed;
if (cakepolyseed != "") { if (cakepolyseed != "") {
@ -63,7 +62,7 @@ String getSeed() {
return cakepolyseed; 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; if(bip39.isNotEmpty) return bip39;
@ -85,29 +84,29 @@ String? getSeedLanguage(String? language) {
String getSeedLegacy(String? language) { String getSeedLegacy(String? language) {
final cakepassphrase = getPassphrase(); final cakepassphrase = getPassphrase();
language = getSeedLanguage(language); language = getSeedLanguage(language);
var legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); var legacy = currentWallet!.seed(seedOffset: cakepassphrase);
if (monero.Wallet_status(wptr!) != 0) { if (currentWallet!.status() != 0) {
if (monero.Wallet_errorString(wptr!).contains("seed_language")) { if (currentWallet!.errorString().contains("seed_language")) {
monero.Wallet_setSeedLanguage(wptr!, language: "English"); currentWallet!.setSeedLanguage(language: "English");
legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); legacy = currentWallet!.seed(seedOffset: cakepassphrase);
} }
} }
if (language != null) { if (language != null) {
monero.Wallet_setSeedLanguage(wptr!, language: language); currentWallet!.setSeedLanguage(language: language);
final status = monero.Wallet_status(wptr!); final status = currentWallet!.status();
if (status != 0) { if (status != 0) {
final err = monero.Wallet_errorString(wptr!); final err = currentWallet!.errorString();
if (legacy.isNotEmpty) { if (legacy.isNotEmpty) {
return "$err\n\n$legacy"; return "$err\n\n$legacy";
} }
return err; return err;
} }
legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase); legacy = currentWallet!.seed(seedOffset: cakepassphrase);
} }
if (monero.Wallet_status(wptr!) != 0) { if (currentWallet!.status() != 0) {
final err = monero.Wallet_errorString(wptr!); final err = currentWallet!.errorString();
if (legacy.isNotEmpty) { if (legacy.isNotEmpty) {
return "$err\n\n$legacy"; return "$err\n\n$legacy";
} }
@ -117,7 +116,7 @@ String getSeedLegacy(String? language) {
} }
String getPassphrase() { String getPassphrase() {
return monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase"); return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
} }
Map<int, Map<int, Map<int, String>>> addressCache = {}; 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}) { 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)}"); // 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 // 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 (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
if (monero.Wallet_numSubaddressAccounts(wptr!) < accountIndex) { if (currentWallet!.numSubaddressAccounts() < accountIndex) {
monero.Wallet_addSubaddressAccount(wptr!); currentWallet!.addSubaddressAccount();
} else { } else {
monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex); currentWallet!.addSubaddress(accountIndex: accountIndex);
} }
} }
addressCache[wptr!.address] ??= {}; addressCache[currentWallet!.ffiAddress()] ??= {};
addressCache[wptr!.address]![accountIndex] ??= {}; addressCache[currentWallet!.ffiAddress()]![accountIndex] ??= {};
addressCache[wptr!.address]![accountIndex]![addressIndex] ??= monero.Wallet_address(wptr!, addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex] ??= currentWallet!.address(
accountIndex: accountIndex, addressIndex: addressIndex); accountIndex: accountIndex, addressIndex: addressIndex);
return addressCache[wptr!.address]![accountIndex]![addressIndex]!; return addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex]!;
} }
int getFullBalance({int accountIndex = 0}) => int getFullBalance({int accountIndex = 0}) =>
monero.Wallet_balance(wptr!, accountIndex: accountIndex); currentWallet!.balance(accountIndex: accountIndex);
int getUnlockedBalance({int accountIndex = 0}) => 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( Future<bool> setupNodeSync(
{required String address, {required String address,
@ -168,7 +167,7 @@ Future<bool> setupNodeSync(
daemonPassword: $password ?? '' daemonPassword: $password ?? ''
} }
'''); ''');
final addr = wptr!.address; final addr = currentWallet!.ffiAddress();
printV("init: start"); printV("init: start");
await Isolate.run(() { await Isolate.run(() {
monero.Wallet_init(Pointer.fromAddress(addr), monero.Wallet_init(Pointer.fromAddress(addr),
@ -180,10 +179,10 @@ Future<bool> setupNodeSync(
}); });
printV("init: end"); printV("init: end");
final status = monero.Wallet_status(wptr!); final status = currentWallet!.status();
if (status != 0) { if (status != 0) {
final error = monero.Wallet_errorString(wptr!); final error = currentWallet!.errorString();
if (error != "no tx keys found for this txid") { if (error != "no tx keys found for this txid") {
printV("error: $error"); printV("error: $error");
throw SetupWalletException(message: error); throw SetupWalletException(message: error);
@ -191,8 +190,8 @@ Future<bool> setupNodeSync(
} }
if (true) { if (true) {
monero.Wallet_init3( currentWallet!.init3(
wptr!, argv0: '', argv0: '',
defaultLogBaseName: 'moneroc', defaultLogBaseName: 'moneroc',
console: true, console: true,
logPath: '', logPath: '',
@ -203,19 +202,19 @@ Future<bool> setupNodeSync(
} }
void startRefreshSync() { void startRefreshSync() {
monero.Wallet_refreshAsync(wptr!); currentWallet!.refreshAsync();
monero.Wallet_startRefresh(wptr!); currentWallet!.startRefresh();
} }
void setRefreshFromBlockHeight({required int height}) { void setRefreshFromBlockHeight({required int height}) {
monero.Wallet_setRefreshFromBlockHeight(wptr!, currentWallet!.setRefreshFromBlockHeight(
refresh_from_block_height: height); refresh_from_block_height: height);
} }
void setRecoveringFromSeed({required bool isRecovery}) { void setRecoveringFromSeed({required bool isRecovery}) {
monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery); currentWallet!.setRecoveringFromSeed(recoveringFromSeed: isRecovery);
monero.Wallet_store(wptr!); currentWallet!.store();
} }
final storeMutex = Mutex(); final storeMutex = Mutex();
@ -224,18 +223,18 @@ final storeMutex = Mutex();
int lastStorePointer = 0; int lastStorePointer = 0;
int lastStoreHeight = 0; int lastStoreHeight = 0;
void storeSync({bool force = false}) async { void storeSync({bool force = false}) async {
final addr = wptr!.address; final addr = currentWallet!.ffiAddress();
final synchronized = await Isolate.run(() { final synchronized = await Isolate.run(() {
return monero.Wallet_synchronized(Pointer.fromAddress(addr)); return monero.Wallet_synchronized(Pointer.fromAddress(addr));
}); });
if (lastStorePointer == wptr!.address && if (lastStorePointer == addr &&
lastStoreHeight + 5000 > monero.Wallet_blockChainHeight(wptr!) && lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
!synchronized && !synchronized &&
!force) { !force) {
return; return;
} }
lastStorePointer = wptr!.address; lastStorePointer = currentWallet!.ffiAddress();
lastStoreHeight = monero.Wallet_blockChainHeight(wptr!); lastStoreHeight = currentWallet!.blockChainHeight();
await storeMutex.acquire(); await storeMutex.acquire();
await Isolate.run(() { await Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr)); monero.Wallet_store(Pointer.fromAddress(addr));
@ -244,25 +243,25 @@ void storeSync({bool force = false}) async {
} }
void setPasswordSync(String password) { 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) { if (status != 0) {
throw Exception(monero.Wallet_errorString(wptr!)); throw Exception(currentWallet!.errorString());
} }
} }
void closeCurrentWallet() { 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 { class SyncListener {
SyncListener(this.onNewBlock, this.onNewTransaction) SyncListener(this.onNewBlock, this.onNewTransaction)
@ -360,52 +359,32 @@ Future<bool> _setupNodeSync(Map<String, Object?> args) async {
socksProxyAddress: socksProxyAddress); socksProxyAddress: socksProxyAddress);
} }
bool _isConnected(Object _) => isConnectedSync();
int _getNodeHeight(Object _) => getNodeHeightSync();
void startRefresh() => startRefreshSync(); 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<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) { String getSubaddressLabel(int accountIndex, int addressIndex) {
return monero.Wallet_getSubaddressLabel(wptr!, return currentWallet!.getSubaddressLabel(
accountIndex: accountIndex, addressIndex: addressIndex); accountIndex: accountIndex, addressIndex: addressIndex);
} }
Future setTrustedDaemon(bool trusted) async => 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 = ""}) { 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) { 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; 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/api/wallet.dart';
import 'package:cw_monero/ledger.dart'; import 'package:cw_monero/ledger.dart';
import 'package:flutter/foundation.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; import 'package:monero/monero.dart' as monero;
class MoneroCException implements Exception { class MoneroCException implements Exception {
@ -24,9 +26,10 @@ class MoneroCException implements Exception {
} }
void checkIfMoneroCIsFine() { void checkIfMoneroCIsFine() {
final cppCsCpp = monero.MONERO_checksum_wallet2_api_c_cpp(); final checksum = MoneroWalletChecksum();
final cppCsH = monero.MONERO_checksum_wallet2_api_c_h(); final cppCsCpp = checksum.checksum_wallet2_api_c_cpp();
final cppCsExp = monero.MONERO_checksum_wallet2_api_c_exp(); 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 dartCsCpp = monero.wallet2_api_c_cpp_sha256;
final dartCsH = monero.wallet2_api_c_h_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'"); 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; Wallet2WalletManager? _wmPtr;
final monero.WalletManager wmPtr = Pointer.fromAddress((() { Wallet2WalletManager wmPtr = (() {
try { try {
// Problems with the wallet? Crashes? Lags? this will print all calls to xmr // 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 // codebase, so it will be easier to debug what happens. At least easier
// than plugging gdb in. Especially on windows/android. // than plugging gdb in. Especially on windows/android.
monero.printStarts = false; monero.printStarts = false;
if (kDebugMode && debugMonero) { if (kDebugMode && debugMonero) {
monero.WalletManagerFactory_setLogLevel(4); MoneroWalletManagerFactory().setLogLevel(4);
} }
_wmPtr ??= monero.WalletManagerFactory_getWalletManager(); _wmPtr ??= MoneroWalletManagerFactory().getWalletManager();
if (kDebugMode && debugMonero) { if (kDebugMode && debugMonero) {
monero.WalletManagerFactory_setLogLevel(4); MoneroWalletManagerFactory().setLogLevel(4);
} }
printV("ptr: $_wmPtr"); printV("ptr: $_wmPtr");
} catch (e) { } catch (e) {
printV(e); printV(e);
rethrow; rethrow;
} }
return _wmPtr!.address; return _wmPtr!;
})()); })();
void createWalletPointer() { Wallet2Wallet createWalletPointer() {
final newWptr = monero.WalletManager_createWallet(wmPtr, final newWptr = wmPtr.createWallet(
path: "", password: "", language: "", networkType: 0); path: "", password: "", language: "", networkType: 0);
return newWptr;
wptr = newWptr;
} }
void createWalletSync( void createWallet(
{required String path, {required String path,
required String password, required String password,
required String language, required String language,
@ -81,28 +83,25 @@ void createWalletSync(
int nettype = 0}) { int nettype = 0}) {
txhistory = null; txhistory = null;
language = getSeedLanguage(language)!; language = getSeedLanguage(language)!;
final newWptr = monero.WalletManager_createWallet(wmPtr, final newW = wmPtr.createWallet(
path: path, password: password, language: language, networkType: 0); path: path, password: password, language: language, networkType: 0);
int status = monero.Wallet_status(newWptr); int status = newW.status();
if (status != 0) { if (status != 0) {
throw WalletCreationException(message: monero.Wallet_errorString(newWptr)); throw WalletCreationException(message: newW.errorString());
} }
setupBackgroundSync(password, newWptr); setupBackgroundSync(password, newW);
wptr = newWptr; currentWallet = newW;
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase); currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase);
monero.Wallet_store(wptr!, path: path); currentWallet!.store(path: path);
openedWalletsByPath[path] = wptr!; openedWalletsByPath[path] = currentWallet!;
_lastOpenedWallet = path; _lastOpenedWallet = path;
// is the line below needed?
// setupNodeSync(address: "node.moneroworld.com:18089");
} }
bool isWalletExistSync({required String path}) { bool isWalletExist({required String path}) {
return monero.WalletManager_walletExists(wmPtr, path); return wmPtr.walletExists(path);
} }
void restoreWalletFromSeedSync( void restoreWalletFromSeedSync(
@ -113,8 +112,7 @@ void restoreWalletFromSeedSync(
int nettype = 0, int nettype = 0,
int restoreHeight = 0}) { int restoreHeight = 0}) {
txhistory = null; txhistory = null;
final newWptr = monero.WalletManager_recoveryWallet( final newW = wmPtr.recoveryWallet(
wmPtr,
path: path, path: path,
password: password, password: password,
mnemonic: seed, mnemonic: seed,
@ -123,10 +121,10 @@ void restoreWalletFromSeedSync(
networkType: 0, networkType: 0,
); );
final status = monero.Wallet_status(newWptr); final status = newW.status();
if (status != 0) { if (status != 0) {
final error = monero.Wallet_errorString(newWptr); final error = newW.errorString();
if (error.contains('word list failed verification')) { if (error.contains('word list failed verification')) {
throw WalletRestoreFromSeedException( throw WalletRestoreFromSeedException(
message: "Seed verification failed, please make sure you entered the correct seed with the correct words order", 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); throw WalletRestoreFromSeedException(message: error);
} }
wptr = newWptr; currentWallet = newW;
setRefreshFromBlockHeight(height: restoreHeight); 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; _lastOpenedWallet = path;
} }
void restoreWalletFromKeysSync( void restoreWalletFromKeys(
{required String path, {required String path,
required String password, required String password,
required String language, required String language,
@ -157,8 +155,8 @@ void restoreWalletFromKeysSync(
int nettype = 0, int nettype = 0,
int restoreHeight = 0}) { int restoreHeight = 0}) {
txhistory = null; txhistory = null;
var newWptr = (spendKey != "") var newW = (spendKey != "")
? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr, ? wmPtr.createDeterministicWalletFromSpendKey(
path: path, path: path,
password: password, password: password,
language: language, language: language,
@ -166,8 +164,7 @@ void restoreWalletFromKeysSync(
newWallet: true, newWallet: true,
// TODO(mrcyjanek): safe to remove // TODO(mrcyjanek): safe to remove
restoreHeight: restoreHeight) restoreHeight: restoreHeight)
: monero.WalletManager_createWalletFromKeys( : wmPtr.createWalletFromKeys(
wmPtr,
path: path, path: path,
password: password, password: password,
restoreHeight: restoreHeight, restoreHeight: restoreHeight,
@ -177,22 +174,21 @@ void restoreWalletFromKeysSync(
nettype: 0, nettype: 0,
); );
int status = monero.Wallet_status(newWptr); int status = newW.status();
if (status != 0) { if (status != 0) {
throw WalletRestoreFromKeysException( throw WalletRestoreFromKeysException(
message: monero.Wallet_errorString(newWptr)); message: newW.errorString());
} }
// CW-712 - Try to restore deterministic wallet first, if the view key doesn't // CW-712 - Try to restore deterministic wallet first, if the view key doesn't
// match the view key provided // match the view key provided
if (spendKey != "") { if (spendKey != "") {
final viewKeyRestored = monero.Wallet_secretViewKey(newWptr); final viewKeyRestored = newW.secretViewKey();
if (viewKey != viewKeyRestored && viewKey != "") { if (viewKey != viewKeyRestored && viewKey != "") {
monero.WalletManager_closeWallet(wmPtr, newWptr, false); wmPtr.closeWallet(newW, false);
File(path).deleteSync(); File(path).deleteSync();
File(path + ".keys").deleteSync(); File(path + ".keys").deleteSync();
newWptr = monero.WalletManager_createWalletFromKeys( newW = wmPtr.createWalletFromKeys(
wmPtr,
path: path, path: path,
password: password, password: password,
restoreHeight: restoreHeight, restoreHeight: restoreHeight,
@ -201,19 +197,19 @@ void restoreWalletFromKeysSync(
spendKeyString: spendKey, spendKeyString: spendKey,
nettype: 0, nettype: 0,
); );
int status = monero.Wallet_status(newWptr); int status = newW.status();
if (status != 0) { if (status != 0) {
throw WalletRestoreFromKeysException( 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; _lastOpenedWallet = path;
} }
@ -228,8 +224,7 @@ void restoreWalletFromPolyseedWithOffset(
int nettype = 0}) { int nettype = 0}) {
txhistory = null; txhistory = null;
final newWptr = monero.WalletManager_createWalletFromPolyseed( final newW = wmPtr.createWalletFromPolyseed(
wmPtr,
path: path, path: path,
password: password, password: password,
networkType: nettype, networkType: nettype,
@ -240,24 +235,24 @@ void restoreWalletFromPolyseedWithOffset(
kdfRounds: 1, kdfRounds: 1,
); );
int status = monero.Wallet_status(newWptr); int status = newW.status();
if (status != 0) { if (status != 0) {
final err = monero.Wallet_errorString(newWptr); final err = newW.errorString();
printV("err: $err"); printV("err: $err");
throw WalletRestoreFromKeysException(message: 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);
monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset); currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: seedOffset);
monero.Wallet_store(wptr!); currentWallet!.store(path: path);
setupBackgroundSync(password, newWptr); setupBackgroundSync(password, currentWallet!);
storeSync(); storeSync();
openedWalletsByPath[path] = wptr!; openedWalletsByPath[path] = currentWallet!;
} }
@ -282,8 +277,7 @@ void restoreWalletFromSpendKeySync(
// ); // );
txhistory = null; txhistory = null;
final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey( final newW = wmPtr.createDeterministicWalletFromSpendKey(
wmPtr,
path: path, path: path,
password: password, password: password,
language: language, language: language,
@ -292,23 +286,23 @@ void restoreWalletFromSpendKeySync(
restoreHeight: restoreHeight, restoreHeight: restoreHeight,
); );
int status = monero.Wallet_status(newWptr); int status = newW.status();
if (status != 0) { if (status != 0) {
final err = monero.Wallet_errorString(newWptr); final err = newW.errorString();
printV("err: $err"); printV("err: $err");
throw WalletRestoreFromKeysException(message: 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(); storeSync();
setupBackgroundSync(password, newWptr); setupBackgroundSync(password, currentWallet!);
openedWalletsByPath[path] = wptr!; openedWalletsByPath[path] = currentWallet!;
_lastOpenedWallet = path; _lastOpenedWallet = path;
} }
@ -321,41 +315,42 @@ Future<void> restoreWalletFromHardwareWallet(
int nettype = 0, int nettype = 0,
int restoreHeight = 0}) async { int restoreHeight = 0}) async {
txhistory = null; txhistory = null;
final wmPtr = MoneroWalletManagerFactory().getWalletManager().ffiAddress();
final newWptrAddr = await Isolate.run(() { final newWptrAddr = await Isolate.run(() {
return monero.WalletManager_createWalletFromDevice(wmPtr, return monero.WalletManager_createWalletFromDevice(Pointer.fromAddress(wmPtr),
path: path, path: path,
password: password, password: password,
restoreHeight: restoreHeight, restoreHeight: restoreHeight,
deviceName: deviceName) deviceName: deviceName)
.address; .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) { if (status != 0) {
final error = monero.Wallet_errorString(newWptr); final error = newW.errorString();
throw WalletRestoreFromSeedException(message: error); throw WalletRestoreFromSeedException(message: error);
} }
wptr = newWptr; currentWallet = newW;
currentWallet!.store(path: path);
_lastOpenedWallet = path; _lastOpenedWallet = path;
openedWalletsByPath[path] = wptr!; openedWalletsByPath[path] = currentWallet!;
} }
Map<String, monero.wallet> openedWalletsByPath = {}; Map<String, Wallet2Wallet> openedWalletsByPath = {};
Future<void> loadWallet( Future<void> loadWallet(
{required String path, required String password, int nettype = 0}) async { {required String path, required String password, int nettype = 0}) async {
if (openedWalletsByPath[path] != null) { if (openedWalletsByPath[path] != null) {
txhistory = null; txhistory = null;
wptr = openedWalletsByPath[path]!; currentWallet = openedWalletsByPath[path]!;
return; return;
} }
if (wptr == null || path != _lastOpenedWallet) { if (currentWallet == null || path != _lastOpenedWallet) {
if (wptr != null) { if (currentWallet != null) {
final addr = wptr!.address; final addr = currentWallet!.ffiAddress();
Isolate.run(() { Isolate.run(() {
monero.Wallet_store(Pointer.fromAddress(addr)); monero.Wallet_store(Pointer.fromAddress(addr));
}); });
@ -366,19 +361,24 @@ Future<void> loadWallet(
/// 0: Software Wallet /// 0: Software Wallet
/// 1: Ledger /// 1: Ledger
/// 2: Trezor /// 2: Trezor
late final deviceType; var deviceType = 0;
if (Platform.isAndroid || Platform.isIOS) { if (Platform.isAndroid || Platform.isIOS) {
deviceType = monero.WalletManager_queryWalletDevice( deviceType = wmPtr.queryWalletDevice(
wmPtr,
keysFileName: "$path.keys", keysFileName: "$path.keys",
password: password, password: password,
kdfRounds: 1, kdfRounds: 1,
); );
final status = monero.WalletManager_errorString(wmPtr); final status = wmPtr.errorString();
if (status != "") { if (status != "") {
printV("loadWallet:"+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 { } else {
deviceType = 0; deviceType = 0;
@ -388,107 +388,47 @@ Future<void> loadWallet(
if (gLedger == null) { if (gLedger == null) {
throw Exception("Tried to open a ledger wallet with no ledger connected"); throw Exception("Tried to open a ledger wallet with no ledger connected");
} }
final dummyWPtr = wptr ?? final dummyWPtr = (currentWallet ??
monero.WalletManager_openWallet(wmPtr, path: '', password: ''); wmPtr.openWallet(path: '', password: ''));
enableLedgerExchange(dummyWPtr, gLedger!); enableLedgerExchange(dummyWPtr, gLedger!);
} }
final addr = wmPtr.address; final addr = wmPtr.ffiAddress();
final newWptrAddr = await Isolate.run(() { final newWptrAddr = await Isolate.run(() {
return monero.WalletManager_openWallet(Pointer.fromAddress(addr), return monero.WalletManager_openWallet(Pointer.fromAddress(addr),
path: path, password: password) path: path, password: password)
.address; .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) { if (status != 0) {
final err = monero.Wallet_errorString(newWptr); final err = newW.errorString();
printV("loadWallet:"+err); printV("loadWallet:"+err);
throw WalletOpeningException(message: err); throw WalletOpeningException(message: err);
} }
if (deviceType == 0) { if (deviceType == 0) {
setupBackgroundSync(password, newWptr); setupBackgroundSync(password, newW);
} }
wptr = newWptr; currentWallet = newW;
_lastOpenedWallet = path; _lastOpenedWallet = path;
openedWalletsByPath[path] = wptr!; openedWalletsByPath[path] = currentWallet!;
} }
} }
void setupBackgroundSync(String password, Pointer<Void>? wptrOverride) { void setupBackgroundSync(String password, Wallet2Wallet wallet) {
if (isViewOnlyBySpendKey(wptrOverride)) { if (isViewOnlyBySpendKey(wallet)) {
return; return;
} }
monero.Wallet_setupBackgroundSync(wptrOverride ?? wptr!, backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: ''); wallet.setupBackgroundSync(backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: '');
if (monero.Wallet_status(wptrOverride ?? wptr!) != 0) { if (wallet.status() != 0) {
// We simply ignore the error. // 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( Future<void> openWallet(
{required String path, {required String path,
@ -496,77 +436,4 @@ Future<void> openWallet(
int nettype = 0}) async => int nettype = 0}) async =>
loadWallet(path: path, password: password, nettype: nettype); loadWallet(path: path, password: password, nettype: nettype);
Future<void> openWalletAsync(Map<String, String> args) async => bool isViewOnlyBySpendKey(Wallet2Wallet? wallet) => int.tryParse((wallet??currentWallet!).secretSpendKey()) == 0;
_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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -32,23 +32,28 @@ class WalletLoadingService {
Future<void> renameWallet(WalletType type, String name, String newName, Future<void> renameWallet(WalletType type, String name, String newName,
{String? password}) async { {String? password}) async {
final walletService = walletServiceFactory.call(type); try {
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); 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 // Save the current wallet's password to the new wallet name's key
await keyService.saveWalletPassword(walletName: newName, password: walletPassword); 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);
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 // set shared preferences flag based on previous wallet name
if (type == WalletType.monero) { if (type == WalletType.monero) {
final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name); final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name);
final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false; final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false;
final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName); final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName);
await sharedPreferences.setBool(newNameKey, isPasswordUpdated); 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 @override
Future<void> addAccount(Object wallet, {required String label}) async { Future<void> addAccount(Object wallet, {required String label}) async {
final moneroWallet = wallet as MoneroWallet; final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList.addAccount(label: label); moneroWallet.walletAddresses.accountList.addAccount(label: label);
} }
@override @override
Future<void> setLabelAccount(Object wallet, Future<void> setLabelAccount(Object wallet,
{required int accountIndex, required String label}) async { {required int accountIndex, required String label}) async {
final moneroWallet = wallet as MoneroWallet; final moneroWallet = wallet as MoneroWallet;
await moneroWallet.walletAddresses.accountList moneroWallet.walletAddresses.accountList
.setLabelAccount(accountIndex: accountIndex, label: label); .setLabelAccount(accountIndex: accountIndex, label: label);
} }
} }

View file

@ -271,10 +271,19 @@ abstract class DashboardViewModelBase with Store {
}); });
_transactionDisposer?.reaction.dispose(); _transactionDisposer?.reaction.dispose();
_transactionDisposer = reaction( _transactionDisposer = reaction((_) {
(_) => appStore.wallet!.transactionHistory.transactions.length, final length = appStore.wallet!.transactionHistory.transactions.length;
_transactionDisposerCallback, 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) { if (hasSilentPayments) {
silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
@ -891,8 +900,19 @@ abstract class DashboardViewModelBase with Store {
_transactionDisposer?.reaction.dispose(); _transactionDisposer?.reaction.dispose();
_transactionDisposer = reaction((_) => appStore.wallet!.transactionHistory.transactions.length, _transactionDisposer = reaction((_) {
_transactionDisposerCallback); 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 @action

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_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(); final sharedPreferences = await SharedPreferences.getInstance();
await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String()); await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String());
unawaited(wallet.fetchTransactions());
state = TransactionCommitted(); state = TransactionCommitted();
} catch (e) { } catch (e) {
state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency)); state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));

View file

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