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

cyan committed May 2, 2025 at 14:30 UTC a2294c4a061c40194223600a12aabacca3b15bc4
25 files changed +578 -715
cw_decred/lib/wallet.dart
+20 -1
@@ -1,6 +1,7 @@
1 import 'dart:async';
2 import 'dart:convert';
3 import 'dart:io';
4 +import 'package:path/path.dart' as p;
5 import 'package:cw_core/exceptions.dart';
6 import 'package:cw_core/transaction_direction.dart';
7 import 'package:cw_core/utils/print_verbose.dart';
@@ -602,7 +603,25 @@ abstract class DecredWalletBase
603 throw "wallet already exists at $newDirPath";
604 }
605
605 - await Directory(currentDirPath).rename(newDirPath);
606 + final sourceDir = Directory(currentDirPath);
607 + final targetDir = Directory(newDirPath);
608 +
609 + if (!targetDir.existsSync()) {
610 + await targetDir.create(recursive: true);
611 + }
612 +
613 + await for (final entity in sourceDir.list(recursive: true)) {
614 + final relativePath = entity.path.substring(sourceDir.path.length+1);
615 + final targetPath = p.join(targetDir.path, relativePath);
616 +
617 + if (entity is File) {
618 + await entity.rename(targetPath);
619 + } else if (entity is Directory) {
620 + await Directory(targetPath).create(recursive: true);
621 + }
622 + }
623 +
624 + await sourceDir.delete(recursive: true);
625 }
626
627 @override
cw_decred/lib/wallet_service.dart
+4
@@ -118,6 +118,10 @@ class DecredWalletService extends WalletService<
118 currentWalletInfo.derivationInfo?.derivationPath == pubkeyRestorePathTestnet
119 ? testnet
120 : mainnet;
121 + if (libwallet == null) {
122 + libwallet = await Libwallet.spawn();
123 + libwallet!.initLibdcrwallet("", "err");
124 + }
125 final currentWallet = DecredWallet(
126 currentWalletInfo, password, this.unspentCoinsInfoSource, libwallet!, closeLibwallet);
127
cw_monero/lib/api/account_list.dart
+23 -40
@@ -2,31 +2,31 @@ import 'dart:async';
2
3 import 'package:cw_monero/api/wallet.dart';
4 import 'package:cw_monero/monero_account_list.dart';
5 -import 'package:monero/monero.dart' as monero;
5 +import 'package:monero/src/wallet2.dart';
6 +import 'package:monero/src/monero.dart';
7
7 -monero.wallet? wptr = null;
8 -bool get isViewOnly => int.tryParse(monero.Wallet_secretSpendKey(wptr!)) == 0;
8 +Wallet2Wallet? currentWallet = null;
9 +bool get isViewOnly => int.tryParse(currentWallet!.secretSpendKey()) == 0;
10
11 int _wlptrForW = 0;
11 -monero.WalletListener? _wlptr = null;
12 +Wallet2WalletListener? _wlptr = null;
13
13 -monero.WalletListener? getWlptr() {
14 - if (wptr == null) return null;
15 - if (wptr!.address == _wlptrForW) return _wlptr!;
16 - _wlptrForW = wptr!.address;
17 - _wlptr = monero.MONERO_cw_getWalletListener(wptr!);
14 +Wallet2WalletListener? getWlptr() {
15 + if (currentWallet == null) return null;
16 + _wlptrForW = currentWallet!.ffiAddress();
17 + _wlptr = currentWallet!.getWalletListener();
18 return _wlptr!;
19 }
20
21 -monero.SubaddressAccount? subaddressAccount;
21 +Wallet2SubaddressAccount? subaddressAccount;
22
23 bool isUpdating = false;
24
25 void refreshAccounts() {
26 try {
27 isUpdating = true;
28 - subaddressAccount = monero.Wallet_subaddressAccount(wptr!);
29 - monero.SubaddressAccount_refresh(subaddressAccount!);
28 + subaddressAccount = currentWallet!.subaddressAccount();
29 + subaddressAccount!.refresh();
30 isUpdating = false;
31 } catch (e) {
32 isUpdating = false;
@@ -34,45 +34,28 @@ void refreshAccounts() {
34 }
35 }
36
37 -List<monero.SubaddressAccountRow> getAllAccount() {
37 + List<Wallet2SubaddressAccountRow> getAllAccount() {
38 // final size = monero.Wallet_numSubaddressAccounts(wptr!);
39 refreshAccounts();
40 - int size = monero.SubaddressAccount_getAll_size(subaddressAccount!);
40 + int size = subaddressAccount!.getAll_size();
41 if (size == 0) {
42 - monero.Wallet_addSubaddressAccount(wptr!);
43 - monero.Wallet_status(wptr!);
42 + currentWallet!.addSubaddressAccount();
43 + currentWallet!.status();
44 return [];
45 }
46 return List.generate(size, (index) {
47 - return monero.SubaddressAccount_getAll_byIndex(subaddressAccount!, index: index);
47 + return subaddressAccount!.getAll_byIndex(index);
48 });
49 }
50
51 -void addAccountSync({required String label}) {
52 - monero.Wallet_addSubaddressAccount(wptr!, label: label);
51 +void addAccount({required String label}) {
52 + currentWallet!.addSubaddressAccount(label: label);
53 + unawaited(store());
54 }
55
55 -void setLabelForAccountSync({required int accountIndex, required String label}) {
56 - monero.SubaddressAccount_setLabel(subaddressAccount!, accountIndex: accountIndex, label: label);
57 - MoneroAccountListBase.cachedAccounts[wptr!.address] = [];
56 +void setLabelForAccount({required int accountIndex, required String label}) {
57 + subaddressAccount!.setLabel(accountIndex: accountIndex, label: label);
58 + MoneroAccountListBase.cachedAccounts[currentWallet!.ffiAddress()] = [];
59 refreshAccounts();
59 -}
60 -
61 -void _addAccount(String label) => addAccountSync(label: label);
62 -
63 -void _setLabelForAccount(Map<String, dynamic> args) {
64 - final label = args['label'] as String;
65 - final accountIndex = args['accountIndex'] as int;
66 -
67 - setLabelForAccountSync(label: label, accountIndex: accountIndex);
68 -}
69 -
70 -Future<void> addAccount({required String label}) async {
71 - _addAccount(label);
60 unawaited(store());
61 }
74 -
75 -Future<void> setLabelForAccount({required int accountIndex, required String label}) async {
76 - _setLabelForAccount({'accountIndex': accountIndex, 'label': label});
77 - unawaited(store());
78 -}
\ No newline at end of file
cw_monero/lib/api/coins_info.dart
+10 -9
@@ -3,17 +3,18 @@ import 'dart:isolate';
3
4 import 'package:cw_monero/api/account_list.dart';
5 import 'package:monero/monero.dart' as monero;
6 +import 'package:monero/src/wallet2.dart';
7 import 'package:mutex/mutex.dart';
8
8 -monero.Coins? coins = null;
9 +Wallet2Coins? coins = null;
10 final coinsMutex = Mutex();
11
12 Future<void> refreshCoins(int accountIndex) async {
13 if (coinsMutex.isLocked) {
14 return;
15 }
15 - coins = monero.Wallet_coins(wptr!);
16 - final coinsPtr = coins!.address;
16 + coins = currentWallet!.coins();
17 + final coinsPtr = coins!.ffiAddress();
18 await coinsMutex.acquire();
19 await Isolate.run(() => monero.Coins_refresh(Pointer.fromAddress(coinsPtr)));
20 coinsMutex.release();
@@ -21,14 +22,14 @@ Future<void> refreshCoins(int accountIndex) async {
22
23 Future<int> countOfCoins() async {
24 await coinsMutex.acquire();
24 - final count = monero.Coins_count(coins!);
25 + final count = coins!.count();
26 coinsMutex.release();
27 return count;
28 }
29
29 -Future<monero.CoinsInfo> getCoin(int index) async {
30 +Future<Wallet2CoinsInfo> getCoin(int index) async {
31 await coinsMutex.acquire();
31 - final coin = monero.Coins_coin(coins!, index);
32 + final coin = coins!.coin(index);
33 coinsMutex.release();
34 return coin;
35 }
@@ -37,7 +38,7 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
38 final count = await countOfCoins();
39 for (int i = 0; i < count; i++) {
40 final coin = await getCoin(i);
40 - final coinAddress = monero.CoinsInfo_keyImage(coin);
41 + final coinAddress = coin.keyImage;
42 if (keyImage == coinAddress) {
43 return i;
44 }
@@ -47,14 +48,14 @@ Future<int?> getCoinByKeyImage(String keyImage) async {
48
49 Future<void> freezeCoin(int index) async {
50 await coinsMutex.acquire();
50 - final coinsPtr = coins!.address;
51 + final coinsPtr = coins!.ffiAddress();
52 await Isolate.run(() => monero.Coins_setFrozen(Pointer.fromAddress(coinsPtr), index: index));
53 coinsMutex.release();
54 }
55
56 Future<void> thawCoin(int index) async {
57 await coinsMutex.acquire();
57 - final coinsPtr = coins!.address;
58 + final coinsPtr = coins!.ffiAddress();
59 await Isolate.run(() => monero.Coins_thaw(Pointer.fromAddress(coinsPtr), index: index));
60 coinsMutex.release();
61 }
cw_monero/lib/api/subaddress_list.dart
+20 -48
@@ -2,7 +2,8 @@
2 import 'package:cw_monero/api/account_list.dart';
3 import 'package:cw_monero/api/transaction_history.dart';
4 import 'package:cw_monero/api/wallet.dart';
5 -import 'package:monero/monero.dart' as monero;
5 +import 'package:monero/monero.dart';
6 +import 'package:monero/src/monero.dart';
7
8 bool isUpdating = false;
9
@@ -16,7 +17,7 @@ class SubaddressInfoMetadata {
17 SubaddressInfoMetadata? subaddress = null;
18
19 String getRawLabel({required int accountIndex, required int addressIndex}) {
19 - return monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
20 + return currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex);
21 }
22
23 void refreshSubaddresses({required int accountIndex}) {
@@ -46,7 +47,7 @@ class Subaddress {
47 final int received;
48 final int txCount;
49 String get label {
49 - final localLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex);
50 + final localLabel = currentWallet!.getSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex);
51 if (localLabel.startsWith("#$addressIndex")) return localLabel; // don't duplicate the ID if it was user-providen
52 return "#$addressIndex ${localLabel}".trim();
53 }
@@ -66,26 +67,26 @@ int lastTxCount = 0;
67 List<TinyTransactionDetails> ttDetails = [];
68
69 List<Subaddress> getAllSubaddresses() {
69 - txhistory = monero.Wallet_history(wptr!);
70 - final txCount = monero.TransactionHistory_count(txhistory!);
71 - if (lastTxCount != txCount && lastWptr != wptr!.address) {
70 + txhistory = currentWallet!.history();
71 + final txCount = txhistory!.count();
72 + if (lastTxCount != txCount && lastWptr != currentWallet!.ffiAddress()) {
73 final List<TinyTransactionDetails> newttDetails = [];
74 lastTxCount = txCount;
74 - lastWptr = wptr!.address;
75 + lastWptr = currentWallet!.ffiAddress();
76 for (var i = 0; i < txCount; i++) {
76 - final tx = monero.TransactionHistory_transaction(txhistory!, index: i);
77 - if (monero.TransactionInfo_direction(tx) == monero.TransactionInfo_Direction.Out) continue;
78 - final subaddrs = monero.TransactionInfo_subaddrIndex(tx).split(",");
79 - final account = monero.TransactionInfo_subaddrAccount(tx);
77 + final tx = txhistory!.transaction(i);
78 + if (tx.direction() == TransactionInfo_Direction.Out.index) continue;
79 + final subaddrs = tx.subaddrIndex().split(",");
80 + final account = tx.subaddrAccount();
81 newttDetails.add(TinyTransactionDetails(
82 address: List.generate(subaddrs.length, (index) => getAddress(accountIndex: account, addressIndex: int.tryParse(subaddrs[index])??0)),
82 - amount: monero.TransactionInfo_amount(tx),
83 + amount: tx.amount(),
84 ));
85 }
86 ttDetails.clear();
87 ttDetails.addAll(newttDetails);
88 }
88 - final size = monero.Wallet_numSubaddresses(wptr!, accountIndex: subaddress!.accountIndex);
89 + final size = currentWallet!.numSubaddresses(accountIndex: subaddress!.accountIndex);
90 final list = List.generate(size, (index) {
91 final ttDetailsLocal = ttDetails.where((element) {
92 final address = getAddress(
@@ -119,46 +120,17 @@ List<Subaddress> getAllSubaddresses() {
120 }
121
122 int numSubaddresses(int subaccountIndex) {
122 - return monero.Wallet_numSubaddresses(wptr!, accountIndex: subaccountIndex);
123 -}
124 -
125 -void addSubaddressSync({required int accountIndex, required String label}) {
126 - monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex, label: label);
127 - refreshSubaddresses(accountIndex: accountIndex);
128 -}
129 -
130 -void setLabelForSubaddressSync(
131 - {required int accountIndex, required int addressIndex, required String label}) {
132 - monero.Wallet_setSubaddressLabel(wptr!, accountIndex: accountIndex, addressIndex: addressIndex, label: label);
133 -}
134 -
135 -void _addSubaddress(Map<String, dynamic> args) {
136 - final label = args['label'] as String;
137 - final accountIndex = args['accountIndex'] as int;
138 -
139 - addSubaddressSync(accountIndex: accountIndex, label: label);
140 -}
141 -
142 -void _setLabelForSubaddress(Map<String, dynamic> args) {
143 - final label = args['label'] as String;
144 - final accountIndex = args['accountIndex'] as int;
145 - final addressIndex = args['addressIndex'] as int;
146 -
147 - setLabelForSubaddressSync(
148 - accountIndex: accountIndex, addressIndex: addressIndex, label: label);
123 + return currentWallet!.numSubaddresses(accountIndex: subaccountIndex);
124 }
125
126 Future<void> addSubaddress({required int accountIndex, required String label}) async {
152 - _addSubaddress({'accountIndex': accountIndex, 'label': label});
127 + currentWallet!.addSubaddress(accountIndex: accountIndex, label: label);
128 + refreshSubaddresses(accountIndex: accountIndex);
129 await store();
130 }
131
132 Future<void> setLabelForSubaddress(
157 - {required int accountIndex, required int addressIndex, required String label}) async {
158 - _setLabelForSubaddress({
159 - 'accountIndex': accountIndex,
160 - 'addressIndex': addressIndex,
161 - 'label': label
162 - });
133 + {required int accountIndex, required int addressIndex, required String label}) async {
134 + currentWallet!.setSubaddressLabel(accountIndex: accountIndex, addressIndex: addressIndex, label: label);
135 await store();
164 -}
136 +}
\ No newline at end of file
cw_monero/lib/api/transaction_history.dart
+84 -109
@@ -9,36 +9,38 @@ import 'package:cw_monero/api/structs/pending_transaction.dart';
9 import 'package:cw_monero/api/wallet.dart';
10 import 'package:cw_monero/exceptions/monero_transaction_creation_exception.dart';
11 import 'package:ffi/ffi.dart';
12 +import 'package:monero/src/monero.dart';
13 import 'package:monero/monero.dart' as monero;
14 +import 'package:monero/src/wallet2.dart';
15 import 'package:monero/src/generated_bindings_monero.g.dart' as monero_gen;
16 import 'package:mutex/mutex.dart';
17
18
19 Map<int, Map<String, String>> txKeys = {};
20 String getTxKey(String txId) {
19 - txKeys[wptr!.address] ??= {};
20 - if (txKeys[wptr!.address]![txId] != null) {
21 - return txKeys[wptr!.address]![txId]!;
21 + txKeys[currentWallet!.ffiAddress()] ??= {};
22 + if (txKeys[currentWallet!.ffiAddress()]![txId] != null) {
23 + return txKeys[currentWallet!.ffiAddress()]![txId]!;
24 }
23 - final txKey = monero.Wallet_getTxKey(wptr!, txid: txId);
24 - final status = monero.Wallet_status(wptr!);
25 + final txKey = currentWallet!.getTxKey(txid: txId);
26 + final status = currentWallet!.status();
27 if (status != 0) {
26 - monero.Wallet_errorString(wptr!);
27 - txKeys[wptr!.address]![txId] = "";
28 + currentWallet!.errorString();
29 + txKeys[currentWallet!.ffiAddress()]![txId] = "";
30 return "";
31 }
30 - txKeys[wptr!.address]![txId] = txKey;
32 + txKeys[currentWallet!.ffiAddress()]![txId] = txKey;
33 return txKey;
34 }
35
36 final txHistoryMutex = Mutex();
35 -monero.TransactionHistory? txhistory;
37 +Wallet2TransactionHistory? txhistory;
38 bool isRefreshingTx = false;
39 Future<void> refreshTransactions() async {
40 if (isRefreshingTx == true) return;
41 isRefreshingTx = true;
40 - txhistory ??= monero.Wallet_history(wptr!);
41 - final ptr = txhistory!.address;
42 + txhistory ??= currentWallet!.history();
43 + final ptr = txhistory!.ffiAddress();
44 await txHistoryMutex.acquire();
45 await Isolate.run(() {
46 monero.TransactionHistory_refresh(Pointer.fromAddress(ptr));
@@ -48,14 +50,14 @@ Future<void> refreshTransactions() async {
50 isRefreshingTx = false;
51 }
52
51 -int countOfTransactions() => monero.TransactionHistory_count(txhistory!);
53 +int countOfTransactions() => txhistory!.count();
54
55 Future<List<Transaction>> getAllTransactions() async {
56 List<Transaction> dummyTxs = [];
57
58 await txHistoryMutex.acquire();
57 - txhistory ??= monero.Wallet_history(wptr!);
58 - final startAddress = txhistory!.address * wptr!.address;
59 + txhistory ??= currentWallet!.history();
60 + final startAddress = txhistory!.ffiAddress() * currentWallet!.ffiAddress();
61 int size = countOfTransactions();
62 final list = <Transaction>[];
63 for (int index = 0; index < size; index++) {
@@ -63,21 +65,21 @@ Future<List<Transaction>> getAllTransactions() async {
65 // Give main thread a chance to do other things.
66 await Future.delayed(Duration.zero);
67 }
66 - if (txhistory!.address * wptr!.address != startAddress) {
68 + if (txhistory!.ffiAddress() * currentWallet!.ffiAddress() != startAddress) {
69 printV("Loop broken because txhistory!.address * wptr!.address != startAddress");
70 break;
71 }
70 - final txInfo = monero.TransactionHistory_transaction(txhistory!, index: index);
71 - final txHash = monero.TransactionInfo_hash(txInfo);
72 - txCache[wptr!.address] ??= {};
73 - txCache[wptr!.address]![txHash] = Transaction(txInfo: txInfo);
74 - list.add(txCache[wptr!.address]![txHash]!);
72 + final txInfo = txhistory!.transaction(index);
73 + final txHash = txInfo.hash();
74 + txCache[currentWallet!.ffiAddress()] ??= {};
75 + txCache[currentWallet!.ffiAddress()]![txHash] = Transaction(txInfo: txInfo);
76 + list.add(txCache[currentWallet!.ffiAddress()]![txHash]!);
77 }
78 txHistoryMutex.release();
77 - final accts = monero.Wallet_numSubaddressAccounts(wptr!);
79 + final accts = currentWallet!.numSubaddressAccounts();
80 for (var i = 0; i < accts; i++) {
79 - final fullBalance = monero.Wallet_balance(wptr!, accountIndex: i);
80 - final availBalance = monero.Wallet_unlockedBalance(wptr!, accountIndex: i);
81 + final fullBalance = currentWallet!.balance(accountIndex: i);
82 + final availBalance = currentWallet!.unlockedBalance(accountIndex: i);
83 if (fullBalance > availBalance) {
84 if (list.where((element) => element.accountIndex == i && element.isConfirmed == false).isEmpty) {
85 dummyTxs.add(
@@ -95,7 +97,7 @@ Future<List<Transaction>> getAllTransactions() async {
97 isSpend: false,
98 hash: "pending",
99 key: "",
98 - txInfo: Pointer.fromAddress(0),
100 + txInfo: DummyTransaction(),
101 )..timeStamp = DateTime.now()
102 );
103 }
@@ -105,16 +107,21 @@ Future<List<Transaction>> getAllTransactions() async {
107 return list;
108 }
109
110 +class DummyTransaction implements Wallet2TransactionInfo {
111 + @override
112 + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
113 +}
114 +
115 Map<int, Map<String, Transaction>> txCache = {};
116 Future<Transaction> getTransaction(String txId) async {
110 - if (txCache[wptr!.address] != null && txCache[wptr!.address]![txId] != null) {
111 - return txCache[wptr!.address]![txId]!;
117 + if (txCache[currentWallet!.ffiAddress()] != null && txCache[currentWallet!.ffiAddress()]![txId] != null) {
118 + return txCache[currentWallet!.ffiAddress()]![txId]!;
119 }
120 await txHistoryMutex.acquire();
114 - final tx = monero.TransactionHistory_transactionById(txhistory!, txid: txId);
121 + final tx = txhistory!.transactionById(txId);
122 final txDart = Transaction(txInfo: tx);
116 - txCache[wptr!.address] ??= {};
117 - txCache[wptr!.address]![txId] = txDart;
123 + txCache[currentWallet!.ffiAddress()] ??= {};
124 + txCache[currentWallet!.ffiAddress()]![txId] = txDart;
125 txHistoryMutex.release();
126 return txDart;
127 }
@@ -127,9 +134,9 @@ Future<PendingTransactionDescription> createTransactionSync(
134 int accountIndex = 0,
135 List<String> preferredInputs = const []}) async {
136
130 - final amt = amount == null ? 0 : monero.Wallet_amountFromString(amount);
137 + final amt = amount == null ? 0 : currentWallet!.amountFromString(amount);
138
132 - final waddr = wptr!.address;
139 + final waddr = currentWallet!.ffiAddress();
140
141 // force reconnection in case the os killed the connection?
142 // fixes failed to get block height error.
@@ -149,7 +156,7 @@ Future<PendingTransactionDescription> createTransactionSync(
156 final paymentIdAddr = paymentId_.address;
157 final preferredInputsAddr = preferredInputs_.address;
158 final spaddr = monero.defaultSeparator.address;
152 - final pendingTx = Pointer<Void>.fromAddress(await Isolate.run(() {
159 + final pendingTxPtr = Pointer<Void>.fromAddress(await Isolate.run(() {
160 final tx = monero_gen.MoneroC(DynamicLibrary.open(monero.libPath)).MONERO_Wallet_createTransaction(
161 Pointer.fromAddress(waddr),
162 Pointer.fromAddress(addraddr).cast(),
@@ -163,15 +170,16 @@ Future<PendingTransactionDescription> createTransactionSync(
170 );
171 return tx.address;
172 }));
173 + final Wallet2PendingTransaction pendingTx = MoneroPendingTransaction(pendingTxPtr);
174 calloc.free(address_);
175 calloc.free(paymentId_);
176 calloc.free(preferredInputs_);
177 final String? error = (() {
170 - final status = monero.PendingTransaction_status(pendingTx);
178 + final status = pendingTx.status();
179 if (status == 0) {
180 return null;
181 }
174 - return monero.PendingTransaction_errorString(pendingTx);
182 + return pendingTx.errorString();
183 })();
184
185 if (error != null) {
@@ -182,10 +190,10 @@ Future<PendingTransactionDescription> createTransactionSync(
190 throw CreationTransactionException(message: message);
191 }
192
185 - final rAmt = monero.PendingTransaction_amount(pendingTx);
186 - final rFee = monero.PendingTransaction_fee(pendingTx);
187 - final rHash = monero.PendingTransaction_txid(pendingTx, '');
188 - final rHex = monero.PendingTransaction_hex(pendingTx, '');
193 + final rAmt = pendingTx.amount();
194 + final rFee = pendingTx.fee();
195 + final rHash = pendingTx.txid('');
196 + final rHex = pendingTx.hex('');
197 final rTxKey = rHash;
198
199 return PendingTransactionDescription(
@@ -194,7 +202,7 @@ Future<PendingTransactionDescription> createTransactionSync(
202 hash: rHash,
203 hex: rHex,
204 txKey: rTxKey,
197 - pointerAddress: pendingTx.address,
205 + pointerAddress: pendingTx.ffiAddress(),
206 );
207 }
208
@@ -206,9 +214,9 @@ Future<PendingTransactionDescription> createTransactionMultDest(
214 List<String> preferredInputs = const []}) async {
215
216 final dstAddrs = outputs.map((e) => e.address).toList();
209 - final amounts = outputs.map((e) => monero.Wallet_amountFromString(e.amount)).toList();
217 + final amounts = outputs.map((e) => currentWallet!.amountFromString(e.amount)).toList();
218
211 - final waddr = wptr!.address;
219 + final waddr = currentWallet!.ffiAddress();
220
221 // force reconnection in case the os killed the connection
222 Isolate.run(() async {
@@ -227,49 +235,50 @@ Future<PendingTransactionDescription> createTransactionMultDest(
235 ).address;
236 }));
237
230 - if (monero.PendingTransaction_status(txptr) != 0) {
231 - throw CreationTransactionException(message: monero.PendingTransaction_errorString(txptr));
238 + final Wallet2PendingTransaction tx = MoneroPendingTransaction(txptr);
239 +
240 + if (tx.status() != 0) {
241 + throw CreationTransactionException(message: tx.errorString());
242 }
243
244 return PendingTransactionDescription(
235 - amount: monero.PendingTransaction_amount(txptr),
236 - fee: monero.PendingTransaction_fee(txptr),
237 - hash: monero.PendingTransaction_txid(txptr, ''),
238 - hex: monero.PendingTransaction_hex(txptr, ''),
239 - txKey: monero.PendingTransaction_txid(txptr, ''),
240 - pointerAddress: txptr.address,
245 + amount: tx.amount(),
246 + fee: tx.fee(),
247 + hash: tx.txid(''),
248 + hex: tx.hex(''),
249 + txKey: tx.txid(''),
250 + pointerAddress: tx.ffiAddress(),
251 );
252 }
253
254 String? commitTransactionFromPointerAddress({required int address, required bool useUR}) =>
245 - commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR);
255 + commitTransaction(tx: MoneroPendingTransaction(Pointer.fromAddress(address)), useUR: useUR);
256
247 -String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) {
248 - final transactionPointerAddress = transactionPointer.address;
257 +String? commitTransaction({required Wallet2PendingTransaction tx, required bool useUR}) {
258 final txCommit = useUR
250 - ? monero.PendingTransaction_commitUR(transactionPointer, 120)
259 + ? tx.commitUR(120)
260 : Isolate.run(() {
261 monero.PendingTransaction_commit(
253 - Pointer.fromAddress(transactionPointerAddress),
262 + Pointer.fromAddress(tx.ffiAddress()),
263 filename: '',
264 overwrite: false,
265 );
266 });
267
268 String? error = (() {
260 - final status = monero.PendingTransaction_status(transactionPointer.cast());
269 + final status = tx.status();
270 if (status == 0) {
271 return null;
272 }
264 - return monero.PendingTransaction_errorString(transactionPointer.cast());
273 + return tx.errorString();
274 })();
275 if (error == null) {
276 error = (() {
268 - final status = monero.Wallet_status(wptr!);
277 + final status = currentWallet!.status();
278 if (status == 0) {
279 return null;
280 }
272 - return monero.Wallet_errorString(wptr!);
281 + return currentWallet!.errorString();
282 })();
283
284 }
@@ -283,43 +292,9 @@ String? commitTransaction({required monero.PendingTransaction transactionPointer
292 }
293 }
294
286 -Future<PendingTransactionDescription> _createTransactionSync(Map args) async {
287 - final address = args['address'] as String;
288 - final paymentId = args['paymentId'] as String;
289 - final amount = args['amount'] as String?;
290 - final priorityRaw = args['priorityRaw'] as int;
291 - final accountIndex = args['accountIndex'] as int;
292 - final preferredInputs = args['preferredInputs'] as List<String>;
293 -
294 - return createTransactionSync(
295 - address: address,
296 - paymentId: paymentId,
297 - amount: amount,
298 - priorityRaw: priorityRaw,
299 - accountIndex: accountIndex,
300 - preferredInputs: preferredInputs);
301 -}
302 -
303 -Future<PendingTransactionDescription> createTransaction(
304 - {required String address,
305 - required int priorityRaw,
306 - String? amount,
307 - String paymentId = '',
308 - int accountIndex = 0,
309 - List<String> preferredInputs = const []}) async =>
310 - _createTransactionSync({
311 - 'address': address,
312 - 'paymentId': paymentId,
313 - 'amount': amount,
314 - 'priorityRaw': priorityRaw,
315 - 'accountIndex': accountIndex,
316 - 'preferredInputs': preferredInputs
317 - });
318 -
295 class Transaction {
296 final String displayLabel;
321 - late final String subaddressLabel = monero.Wallet_getSubaddressLabel(
322 - wptr!,
297 + late final String subaddressLabel = currentWallet!.getSubaddressLabel(
298 accountIndex: accountIndex,
299 addressIndex: addressIndex,
300 );
@@ -372,26 +347,26 @@ class Transaction {
347 // final SubAddress? subAddress;
348 // List<Transfer> transfers = [];
349 // final int txIndex;
375 - final monero.TransactionInfo txInfo;
350 + final Wallet2TransactionInfo txInfo;
351 Transaction({
352 required this.txInfo,
378 - }) : displayLabel = monero.TransactionInfo_label(txInfo),
379 - hash = monero.TransactionInfo_hash(txInfo),
353 + }) : displayLabel = txInfo.label(),
354 + hash = txInfo.hash(),
355 timeStamp = DateTime.fromMillisecondsSinceEpoch(
381 - monero.TransactionInfo_timestamp(txInfo) * 1000,
356 + txInfo.timestamp() * 1000,
357 ),
383 - isSpend = monero.TransactionInfo_direction(txInfo) ==
384 - monero.TransactionInfo_Direction.Out,
385 - amount = monero.TransactionInfo_amount(txInfo),
386 - paymentId = monero.TransactionInfo_paymentId(txInfo),
387 - accountIndex = monero.TransactionInfo_subaddrAccount(txInfo),
388 - addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0,
389 - addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
390 - blockheight = monero.TransactionInfo_blockHeight(txInfo),
391 - confirmations = monero.TransactionInfo_confirmations(txInfo),
392 - fee = monero.TransactionInfo_fee(txInfo),
393 - description = monero.TransactionInfo_description(txInfo),
394 - key = getTxKey(monero.TransactionInfo_hash(txInfo));
358 + isSpend = txInfo.direction() ==
359 + monero.TransactionInfo_Direction.Out.index,
360 + amount = txInfo.amount(),
361 + paymentId = txInfo.paymentId(),
362 + accountIndex = txInfo.subaddrAccount(),
363 + addressIndex = int.tryParse(txInfo.subaddrIndex().split(", ")[0]) ?? 0,
364 + addressIndexList = txInfo.subaddrIndex().split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
365 + blockheight = txInfo.blockHeight(),
366 + confirmations = txInfo.confirmations(),
367 + fee = txInfo.fee(),
368 + description = txInfo.description(),
369 + key = getTxKey(txInfo.hash());
370
371 Transaction.dummy({
372 required this.displayLabel,
cw_monero/lib/api/wallet.dart
+68 -89
@@ -5,8 +5,6 @@ import 'dart:isolate';
5 import 'package:cw_core/utils/print_verbose.dart';
6 import 'package:cw_monero/api/account_list.dart';
7 import 'package:cw_monero/api/exceptions/setup_wallet_exception.dart';
8 -import 'package:cw_monero/api/wallet_manager.dart';
9 -import 'package:flutter/foundation.dart';
8 import 'package:monero/monero.dart' as monero;
9 import 'package:mutex/mutex.dart';
10 import 'package:polyseed/polyseed.dart';
@@ -15,36 +13,37 @@ bool debugMonero = false;
13
14 int getSyncingHeight() {
15 // final height = monero.MONERO_cw_WalletListener_height(getWlptr());
18 - final h2 = monero.Wallet_blockChainHeight(wptr!);
16 + if (currentWallet == null) return 0;
17 + final h2 = currentWallet!.blockChainHeight();
18 // printV("height: $height / $h2");
19 return h2;
20 }
21
22 bool isNeededToRefresh() {
24 - final wlptr = getWlptr();
25 - if (wlptr == null) return false;
26 - final ret = monero.MONERO_cw_WalletListener_isNeedToRefresh(wlptr);
27 - monero.MONERO_cw_WalletListener_resetNeedToRefresh(wlptr);
23 + final wl = getWlptr();
24 + if (wl == null) return false;
25 + final ret = wl.isNeedToRefresh();
26 + wl.resetNeedToRefresh();
27 return ret;
28 }
29
30 bool isNewTransactionExist() {
31 final wlptr = getWlptr();
32 if (wlptr == null) return false;
34 - final ret = monero.MONERO_cw_WalletListener_isNewTransactionExist(wlptr);
35 - monero.MONERO_cw_WalletListener_resetIsNewTransactionExist(wlptr);
33 + final ret = wlptr.isNewTransactionExist();
34 + wlptr.resetIsNewTransactionExist();
35 return ret;
36 }
37
39 -String getFilename() => monero.Wallet_filename(wptr!);
38 +String getFilename() => currentWallet!.filename();
39
40 String getSeed() {
41 // monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
42 final cakepolyseed =
44 - monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed");
43 + currentWallet!.getCacheAttribute(key: "cakewallet.seed");
44 final cakepassphrase = getPassphrase();
45
47 - final weirdPolyseed = monero.Wallet_getPolyseed(wptr!, passphrase: cakepassphrase);
46 + final weirdPolyseed = currentWallet!.getPolyseed(passphrase: cakepassphrase);
47 if (weirdPolyseed != "") return weirdPolyseed;
48
49 if (cakepolyseed != "") {
@@ -63,7 +62,7 @@ String getSeed() {
62 return cakepolyseed;
63 }
64
66 - final bip39 = monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed.bip39");
65 + final bip39 = currentWallet!.getCacheAttribute(key: "cakewallet.seed.bip39");
66
67 if(bip39.isNotEmpty) return bip39;
68
@@ -85,29 +84,29 @@ String? getSeedLanguage(String? language) {
84 String getSeedLegacy(String? language) {
85 final cakepassphrase = getPassphrase();
86 language = getSeedLanguage(language);
88 - var legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
89 - if (monero.Wallet_status(wptr!) != 0) {
90 - if (monero.Wallet_errorString(wptr!).contains("seed_language")) {
91 - monero.Wallet_setSeedLanguage(wptr!, language: "English");
92 - legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
87 + var legacy = currentWallet!.seed(seedOffset: cakepassphrase);
88 + if (currentWallet!.status() != 0) {
89 + if (currentWallet!.errorString().contains("seed_language")) {
90 + currentWallet!.setSeedLanguage(language: "English");
91 + legacy = currentWallet!.seed(seedOffset: cakepassphrase);
92 }
93 }
94
95 if (language != null) {
97 - monero.Wallet_setSeedLanguage(wptr!, language: language);
98 - final status = monero.Wallet_status(wptr!);
96 + currentWallet!.setSeedLanguage(language: language);
97 + final status = currentWallet!.status();
98 if (status != 0) {
100 - final err = monero.Wallet_errorString(wptr!);
99 + final err = currentWallet!.errorString();
100 if (legacy.isNotEmpty) {
101 return "$err\n\n$legacy";
102 }
103 return err;
104 }
106 - legacy = monero.Wallet_seed(wptr!, seedOffset: cakepassphrase);
105 + legacy = currentWallet!.seed(seedOffset: cakepassphrase);
106 }
107
109 - if (monero.Wallet_status(wptr!) != 0) {
110 - final err = monero.Wallet_errorString(wptr!);
108 + if (currentWallet!.status() != 0) {
109 + final err = currentWallet!.errorString();
110 if (legacy.isNotEmpty) {
111 return "$err\n\n$legacy";
112 }
@@ -117,7 +116,7 @@ String getSeedLegacy(String? language) {
116 }
117
118 String getPassphrase() {
120 - return monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.passphrase");
119 + return currentWallet!.getCacheAttribute(key: "cakewallet.passphrase");
120 }
121
122 Map<int, Map<int, Map<int, String>>> addressCache = {};
@@ -125,31 +124,31 @@ Map<int, Map<int, Map<int, String>>> addressCache = {};
124 String getAddress({int accountIndex = 0, int addressIndex = 0}) {
125 // printV("getaddress: ${accountIndex}/${addressIndex}: ${monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)}: ${monero.Wallet_address(wptr!, accountIndex: accountIndex, addressIndex: addressIndex)}");
126 // this could be a while loop, but I'm in favor of making it if to not cause freezes
128 - if (monero.Wallet_numSubaddresses(wptr!, accountIndex: accountIndex)-1 < addressIndex) {
129 - if (monero.Wallet_numSubaddressAccounts(wptr!) < accountIndex) {
130 - monero.Wallet_addSubaddressAccount(wptr!);
127 + if (currentWallet!.numSubaddresses(accountIndex: accountIndex)-1 < addressIndex) {
128 + if (currentWallet!.numSubaddressAccounts() < accountIndex) {
129 + currentWallet!.addSubaddressAccount();
130 } else {
132 - monero.Wallet_addSubaddress(wptr!, accountIndex: accountIndex);
131 + currentWallet!.addSubaddress(accountIndex: accountIndex);
132 }
133 }
135 - addressCache[wptr!.address] ??= {};
136 - addressCache[wptr!.address]![accountIndex] ??= {};
137 - addressCache[wptr!.address]![accountIndex]![addressIndex] ??= monero.Wallet_address(wptr!,
134 + addressCache[currentWallet!.ffiAddress()] ??= {};
135 + addressCache[currentWallet!.ffiAddress()]![accountIndex] ??= {};
136 + addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex] ??= currentWallet!.address(
137 accountIndex: accountIndex, addressIndex: addressIndex);
139 - return addressCache[wptr!.address]![accountIndex]![addressIndex]!;
138 + return addressCache[currentWallet!.ffiAddress()]![accountIndex]![addressIndex]!;
139 }
140
141 int getFullBalance({int accountIndex = 0}) =>
143 - monero.Wallet_balance(wptr!, accountIndex: accountIndex);
142 + currentWallet!.balance(accountIndex: accountIndex);
143
144 int getUnlockedBalance({int accountIndex = 0}) =>
146 - monero.Wallet_unlockedBalance(wptr!, accountIndex: accountIndex);
145 + currentWallet!.unlockedBalance(accountIndex: accountIndex);
146
148 -int getCurrentHeight() => monero.Wallet_blockChainHeight(wptr!);
147 +int getCurrentHeight() => currentWallet!.blockChainHeight();
148
150 -int getNodeHeightSync() => monero.Wallet_daemonBlockChainHeight(wptr!);
149 +int getNodeHeightSync() => currentWallet!.daemonBlockChainHeight();
150
152 -bool isConnectedSync() => monero.Wallet_connected(wptr!) != 0;
151 +bool isConnectedSync() => currentWallet!.connected() != 0;
152
153 Future<bool> setupNodeSync(
154 {required String address,
@@ -168,7 +167,7 @@ Future<bool> setupNodeSync(
167 daemonPassword: $password ?? ''
168 }
169 ''');
171 - final addr = wptr!.address;
170 + final addr = currentWallet!.ffiAddress();
171 printV("init: start");
172 await Isolate.run(() {
173 monero.Wallet_init(Pointer.fromAddress(addr),
@@ -180,10 +179,10 @@ Future<bool> setupNodeSync(
179 });
180 printV("init: end");
181
183 - final status = monero.Wallet_status(wptr!);
182 + final status = currentWallet!.status();
183
184 if (status != 0) {
186 - final error = monero.Wallet_errorString(wptr!);
185 + final error = currentWallet!.errorString();
186 if (error != "no tx keys found for this txid") {
187 printV("error: $error");
188 throw SetupWalletException(message: error);
@@ -191,8 +190,8 @@ Future<bool> setupNodeSync(
190 }
191
192 if (true) {
194 - monero.Wallet_init3(
195 - wptr!, argv0: '',
193 + currentWallet!.init3(
194 + argv0: '',
195 defaultLogBaseName: 'moneroc',
196 console: true,
197 logPath: '',
@@ -203,19 +202,19 @@ Future<bool> setupNodeSync(
202 }
203
204 void startRefreshSync() {
206 - monero.Wallet_refreshAsync(wptr!);
207 - monero.Wallet_startRefresh(wptr!);
205 + currentWallet!.refreshAsync();
206 + currentWallet!.startRefresh();
207 }
208
209
210 void setRefreshFromBlockHeight({required int height}) {
212 - monero.Wallet_setRefreshFromBlockHeight(wptr!,
211 + currentWallet!.setRefreshFromBlockHeight(
212 refresh_from_block_height: height);
213 }
214
215 void setRecoveringFromSeed({required bool isRecovery}) {
217 - monero.Wallet_setRecoveringFromSeed(wptr!, recoveringFromSeed: isRecovery);
218 - monero.Wallet_store(wptr!);
216 + currentWallet!.setRecoveringFromSeed(recoveringFromSeed: isRecovery);
217 + currentWallet!.store();
218 }
219
220 final storeMutex = Mutex();
@@ -224,18 +223,18 @@ final storeMutex = Mutex();
223 int lastStorePointer = 0;
224 int lastStoreHeight = 0;
225 void storeSync({bool force = false}) async {
227 - final addr = wptr!.address;
226 + final addr = currentWallet!.ffiAddress();
227 final synchronized = await Isolate.run(() {
228 return monero.Wallet_synchronized(Pointer.fromAddress(addr));
229 });
231 - if (lastStorePointer == wptr!.address &&
232 - lastStoreHeight + 5000 > monero.Wallet_blockChainHeight(wptr!) &&
230 + if (lastStorePointer == addr &&
231 + lastStoreHeight + 5000 > currentWallet!.blockChainHeight() &&
232 !synchronized &&
233 !force) {
234 return;
235 }
237 - lastStorePointer = wptr!.address;
238 - lastStoreHeight = monero.Wallet_blockChainHeight(wptr!);
236 + lastStorePointer = currentWallet!.ffiAddress();
237 + lastStoreHeight = currentWallet!.blockChainHeight();
238 await storeMutex.acquire();
239 await Isolate.run(() {
240 monero.Wallet_store(Pointer.fromAddress(addr));
@@ -244,25 +243,25 @@ void storeSync({bool force = false}) async {
243 }
244
245 void setPasswordSync(String password) {
247 - monero.Wallet_setPassword(wptr!, password: password);
246 + currentWallet!.setPassword(password: password);
247
249 - final status = monero.Wallet_status(wptr!);
248 + final status = currentWallet!.status();
249 if (status != 0) {
251 - throw Exception(monero.Wallet_errorString(wptr!));
250 + throw Exception(currentWallet!.errorString());
251 }
252 }
253
254 void closeCurrentWallet() {
256 - monero.Wallet_stop(wptr!);
255 + currentWallet!.stop();
256 }
257
259 -String getSecretViewKey() => monero.Wallet_secretViewKey(wptr!);
258 +String getSecretViewKey() => currentWallet!.secretViewKey();
259
261 -String getPublicViewKey() => monero.Wallet_publicViewKey(wptr!);
260 +String getPublicViewKey() => currentWallet!.publicViewKey();
261
263 -String getSecretSpendKey() => monero.Wallet_secretSpendKey(wptr!);
262 +String getSecretSpendKey() => currentWallet!.secretSpendKey();
263
265 -String getPublicSpendKey() => monero.Wallet_publicSpendKey(wptr!);
264 +String getPublicSpendKey() => currentWallet!.publicSpendKey();
265
266 class SyncListener {
267 SyncListener(this.onNewBlock, this.onNewTransaction)
@@ -360,52 +359,32 @@ Future<bool> _setupNodeSync(Map<String, Object?> args) async {
359 socksProxyAddress: socksProxyAddress);
360 }
361
363 -bool _isConnected(Object _) => isConnectedSync();
364 -
365 -int _getNodeHeight(Object _) => getNodeHeightSync();
366 -
362 void startRefresh() => startRefreshSync();
363
369 -Future<void> setupNode(
370 - {required String address,
371 - String? login,
372 - String? password,
373 - bool useSSL = false,
374 - String? socksProxyAddress,
375 - bool isLightWallet = false}) async =>
376 - _setupNodeSync({
377 - 'address': address,
378 - 'login': login,
379 - 'password': password,
380 - 'useSSL': useSSL,
381 - 'isLightWallet': isLightWallet,
382 - 'socksProxyAddress': socksProxyAddress
383 - });
384 -
364 Future<void> store() async => _storeSync(0);
365
387 -Future<bool> isConnected() async => _isConnected(0);
366 +Future<bool> isConnected() async => isConnectedSync();
367
389 -Future<int> getNodeHeight() async => _getNodeHeight(0);
368 +Future<int> getNodeHeight() async => getNodeHeightSync();
369
391 -void rescanBlockchainAsync() => monero.Wallet_rescanBlockchainAsync(wptr!);
370 +void rescanBlockchainAsync() => currentWallet!.rescanBlockchainAsync();
371
372 String getSubaddressLabel(int accountIndex, int addressIndex) {
394 - return monero.Wallet_getSubaddressLabel(wptr!,
373 + return currentWallet!.getSubaddressLabel(
374 accountIndex: accountIndex, addressIndex: addressIndex);
375 }
376
377 Future setTrustedDaemon(bool trusted) async =>
399 - monero.Wallet_setTrustedDaemon(wptr!, arg: trusted);
378 + currentWallet!.setTrustedDaemon(arg: trusted);
379
401 -Future<bool> trustedDaemon() async => monero.Wallet_trustedDaemon(wptr!);
380 +Future<bool> trustedDaemon() async => currentWallet!.trustedDaemon();
381
382 String signMessage(String message, {String address = ""}) {
404 - return monero.Wallet_signMessage(wptr!, message: message, address: address);
383 + return currentWallet!.signMessage(message: message, address: address);
384 }
385
386 bool verifyMessage(String message, String address, String signature) {
408 - return monero.Wallet_verifySignedMessage(wptr!, message: message, address: address, signature: signature);
387 + return currentWallet!.verifySignedMessage(message: message, address: address, signature: signature);
388 }
389
390 Map<String, List<int>> debugCallLength() => monero.debugCallLength;
cw_monero/lib/api/wallet_manager.dart
+103 -236
@@ -12,6 +12,8 @@ import 'package:cw_monero/api/transaction_history.dart';
12 import 'package:cw_monero/api/wallet.dart';
13 import 'package:cw_monero/ledger.dart';
14 import 'package:flutter/foundation.dart';
15 +import 'package:monero/src/monero.dart';
16 +import 'package:monero/src/wallet2.dart';
17 import 'package:monero/monero.dart' as monero;
18
19 class MoneroCException implements Exception {
@@ -24,9 +26,10 @@ class MoneroCException implements Exception {
26 }
27
28 void checkIfMoneroCIsFine() {
27 - final cppCsCpp = monero.MONERO_checksum_wallet2_api_c_cpp();
28 - final cppCsH = monero.MONERO_checksum_wallet2_api_c_h();
29 - final cppCsExp = monero.MONERO_checksum_wallet2_api_c_exp();
29 + final checksum = MoneroWalletChecksum();
30 + final cppCsCpp = checksum.checksum_wallet2_api_c_cpp();
31 + final cppCsH = checksum.checksum_wallet2_api_c_h();
32 + final cppCsExp = checksum.checksum_wallet2_api_c_exp();
33
34 final dartCsCpp = monero.wallet2_api_c_cpp_sha256;
35 final dartCsH = monero.wallet2_api_c_h_sha256;
@@ -44,36 +47,35 @@ void checkIfMoneroCIsFine() {
47 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'");
48 }
49 }
47 -monero.WalletManager? _wmPtr;
48 -final monero.WalletManager wmPtr = Pointer.fromAddress((() {
50 +Wallet2WalletManager? _wmPtr;
51 +Wallet2WalletManager wmPtr = (() {
52 try {
53 // Problems with the wallet? Crashes? Lags? this will print all calls to xmr
54 // codebase, so it will be easier to debug what happens. At least easier
55 // than plugging gdb in. Especially on windows/android.
56 monero.printStarts = false;
57 if (kDebugMode && debugMonero) {
55 - monero.WalletManagerFactory_setLogLevel(4);
58 + MoneroWalletManagerFactory().setLogLevel(4);
59 }
57 - _wmPtr ??= monero.WalletManagerFactory_getWalletManager();
60 + _wmPtr ??= MoneroWalletManagerFactory().getWalletManager();
61 if (kDebugMode && debugMonero) {
59 - monero.WalletManagerFactory_setLogLevel(4);
62 + MoneroWalletManagerFactory().setLogLevel(4);
63 }
64 printV("ptr: $_wmPtr");
65 } catch (e) {
66 printV(e);
67 rethrow;
68 }
66 - return _wmPtr!.address;
67 -})());
69 + return _wmPtr!;
70 +})();
71
69 -void createWalletPointer() {
70 - final newWptr = monero.WalletManager_createWallet(wmPtr,
72 +Wallet2Wallet createWalletPointer() {
73 + final newWptr = wmPtr.createWallet(
74 path: "", password: "", language: "", networkType: 0);
72 -
73 - wptr = newWptr;
75 + return newWptr;
76 }
77
76 -void createWalletSync(
78 +void createWallet(
79 {required String path,
80 required String password,
81 required String language,
@@ -81,28 +83,25 @@ void createWalletSync(
83 int nettype = 0}) {
84 txhistory = null;
85 language = getSeedLanguage(language)!;
84 - final newWptr = monero.WalletManager_createWallet(wmPtr,
86 + final newW = wmPtr.createWallet(
87 path: path, password: password, language: language, networkType: 0);
88
87 - int status = monero.Wallet_status(newWptr);
89 + int status = newW.status();
90 if (status != 0) {
89 - throw WalletCreationException(message: monero.Wallet_errorString(newWptr));
91 + throw WalletCreationException(message: newW.errorString());
92 }
93
92 - setupBackgroundSync(password, newWptr);
94 + setupBackgroundSync(password, newW);
95
94 - wptr = newWptr;
95 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
96 - monero.Wallet_store(wptr!, path: path);
97 - openedWalletsByPath[path] = wptr!;
96 + currentWallet = newW;
97 + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase);
98 + currentWallet!.store(path: path);
99 + openedWalletsByPath[path] = currentWallet!;
100 _lastOpenedWallet = path;
99 -
100 - // is the line below needed?
101 - // setupNodeSync(address: "node.moneroworld.com:18089");
101 }
102
104 -bool isWalletExistSync({required String path}) {
105 - return monero.WalletManager_walletExists(wmPtr, path);
103 +bool isWalletExist({required String path}) {
104 + return wmPtr.walletExists(path);
105 }
106
107 void restoreWalletFromSeedSync(
@@ -113,8 +112,7 @@ void restoreWalletFromSeedSync(
112 int nettype = 0,
113 int restoreHeight = 0}) {
114 txhistory = null;
116 - final newWptr = monero.WalletManager_recoveryWallet(
117 - wmPtr,
115 + final newW = wmPtr.recoveryWallet(
116 path: path,
117 password: password,
118 mnemonic: seed,
@@ -123,10 +121,10 @@ void restoreWalletFromSeedSync(
121 networkType: 0,
122 );
123
126 - final status = monero.Wallet_status(newWptr);
124 + final status = newW.status();
125
126 if (status != 0) {
129 - final error = monero.Wallet_errorString(newWptr);
127 + final error = newW.errorString();
128 if (error.contains('word list failed verification')) {
129 throw WalletRestoreFromSeedException(
130 message: "Seed verification failed, please make sure you entered the correct seed with the correct words order",
@@ -134,20 +132,20 @@ void restoreWalletFromSeedSync(
132 }
133 throw WalletRestoreFromSeedException(message: error);
134 }
137 - wptr = newWptr;
135 + currentWallet = newW;
136
137 setRefreshFromBlockHeight(height: restoreHeight);
140 - setupBackgroundSync(password, newWptr);
138 + setupBackgroundSync(password, newW);
139
142 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase);
140 + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase);
141
144 - openedWalletsByPath[path] = wptr!;
142 + openedWalletsByPath[path] = currentWallet!;
143
146 - monero.Wallet_store(wptr!);
144 + currentWallet!.store(path: path);
145 _lastOpenedWallet = path;
146 }
147
150 -void restoreWalletFromKeysSync(
148 +void restoreWalletFromKeys(
149 {required String path,
150 required String password,
151 required String language,
@@ -157,8 +155,8 @@ void restoreWalletFromKeysSync(
155 int nettype = 0,
156 int restoreHeight = 0}) {
157 txhistory = null;
160 - var newWptr = (spendKey != "")
161 - ? monero.WalletManager_createDeterministicWalletFromSpendKey(wmPtr,
158 + var newW = (spendKey != "")
159 + ? wmPtr.createDeterministicWalletFromSpendKey(
160 path: path,
161 password: password,
162 language: language,
@@ -166,8 +164,7 @@ void restoreWalletFromKeysSync(
164 newWallet: true,
165 // TODO(mrcyjanek): safe to remove
166 restoreHeight: restoreHeight)
169 - : monero.WalletManager_createWalletFromKeys(
170 - wmPtr,
167 + : wmPtr.createWalletFromKeys(
168 path: path,
169 password: password,
170 restoreHeight: restoreHeight,
@@ -177,22 +174,21 @@ void restoreWalletFromKeysSync(
174 nettype: 0,
175 );
176
180 - int status = monero.Wallet_status(newWptr);
177 + int status = newW.status();
178 if (status != 0) {
179 throw WalletRestoreFromKeysException(
183 - message: monero.Wallet_errorString(newWptr));
180 + message: newW.errorString());
181 }
182
183 // CW-712 - Try to restore deterministic wallet first, if the view key doesn't
184 // match the view key provided
185 if (spendKey != "") {
189 - final viewKeyRestored = monero.Wallet_secretViewKey(newWptr);
186 + final viewKeyRestored = newW.secretViewKey();
187 if (viewKey != viewKeyRestored && viewKey != "") {
191 - monero.WalletManager_closeWallet(wmPtr, newWptr, false);
188 + wmPtr.closeWallet(newW, false);
189 File(path).deleteSync();
190 File(path + ".keys").deleteSync();
194 - newWptr = monero.WalletManager_createWalletFromKeys(
195 - wmPtr,
191 + newW = wmPtr.createWalletFromKeys(
192 path: path,
193 password: password,
194 restoreHeight: restoreHeight,
@@ -201,19 +197,19 @@ void restoreWalletFromKeysSync(
197 spendKeyString: spendKey,
198 nettype: 0,
199 );
204 - int status = monero.Wallet_status(newWptr);
200 + int status = newW.status();
201 if (status != 0) {
202 throw WalletRestoreFromKeysException(
207 - message: monero.Wallet_errorString(newWptr));
203 + message: newW.errorString());
204 }
205
210 - setupBackgroundSync(password, newWptr);
206 + setupBackgroundSync(password, newW);
207 }
208 }
209
214 - wptr = newWptr;
210 + currentWallet = newW;
211
216 - openedWalletsByPath[path] = wptr!;
212 + openedWalletsByPath[path] = currentWallet!;
213 _lastOpenedWallet = path;
214 }
215
@@ -228,8 +224,7 @@ void restoreWalletFromPolyseedWithOffset(
224 int nettype = 0}) {
225
226 txhistory = null;
231 - final newWptr = monero.WalletManager_createWalletFromPolyseed(
232 - wmPtr,
227 + final newW = wmPtr.createWalletFromPolyseed(
228 path: path,
229 password: password,
230 networkType: nettype,
@@ -240,24 +235,24 @@ void restoreWalletFromPolyseedWithOffset(
235 kdfRounds: 1,
236 );
237
243 - int status = monero.Wallet_status(newWptr);
238 + int status = newW.status();
239
240 if (status != 0) {
246 - final err = monero.Wallet_errorString(newWptr);
241 + final err = newW.errorString();
242 printV("err: $err");
243 throw WalletRestoreFromKeysException(message: err);
244 }
245
251 - wptr = newWptr;
246 + currentWallet = newW;
247
253 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
254 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: seedOffset);
255 - monero.Wallet_store(wptr!);
248 + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
249 + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: seedOffset);
250 + currentWallet!.store(path: path);
251
257 - setupBackgroundSync(password, newWptr);
252 + setupBackgroundSync(password, currentWallet!);
253 storeSync();
254
260 - openedWalletsByPath[path] = wptr!;
255 + openedWalletsByPath[path] = currentWallet!;
256 }
257
258
@@ -282,8 +277,7 @@ void restoreWalletFromSpendKeySync(
277 // );
278
279 txhistory = null;
285 - final newWptr = monero.WalletManager_createDeterministicWalletFromSpendKey(
286 - wmPtr,
280 + final newW = wmPtr.createDeterministicWalletFromSpendKey(
281 path: path,
282 password: password,
283 language: language,
@@ -292,23 +286,23 @@ void restoreWalletFromSpendKeySync(
286 restoreHeight: restoreHeight,
287 );
288
295 - int status = monero.Wallet_status(newWptr);
289 + int status = newW.status();
290
291 if (status != 0) {
298 - final err = monero.Wallet_errorString(newWptr);
292 + final err = newW.errorString();
293 printV("err: $err");
294 throw WalletRestoreFromKeysException(message: err);
295 }
296
303 - wptr = newWptr;
297 + currentWallet = newW;
298
305 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
299 + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
300
301 storeSync();
302
309 - setupBackgroundSync(password, newWptr);
303 + setupBackgroundSync(password, currentWallet!);
304
311 - openedWalletsByPath[path] = wptr!;
305 + openedWalletsByPath[path] = currentWallet!;
306 _lastOpenedWallet = path;
307 }
308
@@ -321,41 +315,42 @@ Future<void> restoreWalletFromHardwareWallet(
315 int nettype = 0,
316 int restoreHeight = 0}) async {
317 txhistory = null;
324 -
318 + final wmPtr = MoneroWalletManagerFactory().getWalletManager().ffiAddress();
319 final newWptrAddr = await Isolate.run(() {
326 - return monero.WalletManager_createWalletFromDevice(wmPtr,
320 + return monero.WalletManager_createWalletFromDevice(Pointer.fromAddress(wmPtr),
321 path: path,
322 password: password,
323 restoreHeight: restoreHeight,
324 deviceName: deviceName)
325 .address;
326 });
333 - final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
327 + final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr));
328
335 - final status = monero.Wallet_status(newWptr);
329 + final status = newW.status();
330
331 if (status != 0) {
338 - final error = monero.Wallet_errorString(newWptr);
332 + final error = newW.errorString();
333 throw WalletRestoreFromSeedException(message: error);
334 }
335
342 - wptr = newWptr;
336 + currentWallet = newW;
337 + currentWallet!.store(path: path);
338 _lastOpenedWallet = path;
344 - openedWalletsByPath[path] = wptr!;
339 + openedWalletsByPath[path] = currentWallet!;
340 }
341
347 -Map<String, monero.wallet> openedWalletsByPath = {};
342 +Map<String, Wallet2Wallet> openedWalletsByPath = {};
343
344 Future<void> loadWallet(
345 {required String path, required String password, int nettype = 0}) async {
346 if (openedWalletsByPath[path] != null) {
347 txhistory = null;
353 - wptr = openedWalletsByPath[path]!;
348 + currentWallet = openedWalletsByPath[path]!;
349 return;
350 }
356 - if (wptr == null || path != _lastOpenedWallet) {
357 - if (wptr != null) {
358 - final addr = wptr!.address;
351 + if (currentWallet == null || path != _lastOpenedWallet) {
352 + if (currentWallet != null) {
353 + final addr = currentWallet!.ffiAddress();
354 Isolate.run(() {
355 monero.Wallet_store(Pointer.fromAddress(addr));
356 });
@@ -366,19 +361,24 @@ Future<void> loadWallet(
361 /// 0: Software Wallet
362 /// 1: Ledger
363 /// 2: Trezor
369 - late final deviceType;
364 + var deviceType = 0;
365
366 if (Platform.isAndroid || Platform.isIOS) {
372 - deviceType = monero.WalletManager_queryWalletDevice(
373 - wmPtr,
367 + deviceType = wmPtr.queryWalletDevice(
368 keysFileName: "$path.keys",
369 password: password,
370 kdfRounds: 1,
371 );
378 - final status = monero.WalletManager_errorString(wmPtr);
372 + final status = wmPtr.errorString();
373 if (status != "") {
374 printV("loadWallet:"+status);
381 - throw WalletOpeningException(message: status);
375 + // This is most likely closeWallet call leaking error. This is fine.
376 + if (status.contains("failed to save file")) {
377 + printV("loadWallet: error leaked: $status");
378 + deviceType = 0;
379 + } else {
380 + throw WalletOpeningException(message: status);
381 + }
382 }
383 } else {
384 deviceType = 0;
@@ -388,107 +388,47 @@ Future<void> loadWallet(
388 if (gLedger == null) {
389 throw Exception("Tried to open a ledger wallet with no ledger connected");
390 }
391 - final dummyWPtr = wptr ??
392 - monero.WalletManager_openWallet(wmPtr, path: '', password: '');
391 + final dummyWPtr = (currentWallet ??
392 + wmPtr.openWallet(path: '', password: ''));
393 enableLedgerExchange(dummyWPtr, gLedger!);
394 }
395
396 - final addr = wmPtr.address;
396 + final addr = wmPtr.ffiAddress();
397 final newWptrAddr = await Isolate.run(() {
398 return monero.WalletManager_openWallet(Pointer.fromAddress(addr),
399 path: path, password: password)
400 .address;
401 });
402
403 - final newWptr = Pointer<Void>.fromAddress(newWptrAddr);
403 + final newW = MoneroWallet(Pointer.fromAddress(newWptrAddr));
404
405 - int status = monero.Wallet_status(newWptr);
405 + int status = newW.status();
406 if (status != 0) {
407 - final err = monero.Wallet_errorString(newWptr);
407 + final err = newW.errorString();
408 printV("loadWallet:"+err);
409 throw WalletOpeningException(message: err);
410 }
411 if (deviceType == 0) {
412 - setupBackgroundSync(password, newWptr);
412 + setupBackgroundSync(password, newW);
413 }
414
415 - wptr = newWptr;
415 + currentWallet = newW;
416 _lastOpenedWallet = path;
417 - openedWalletsByPath[path] = wptr!;
417 + openedWalletsByPath[path] = currentWallet!;
418 }
419 }
420
421 -void setupBackgroundSync(String password, Pointer<Void>? wptrOverride) {
422 - if (isViewOnlyBySpendKey(wptrOverride)) {
421 +void setupBackgroundSync(String password, Wallet2Wallet wallet) {
422 + if (isViewOnlyBySpendKey(wallet)) {
423 return;
424 }
425 - monero.Wallet_setupBackgroundSync(wptrOverride ?? wptr!, backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: '');
426 - if (monero.Wallet_status(wptrOverride ?? wptr!) != 0) {
425 + wallet.setupBackgroundSync(backgroundSyncType: 2, walletPassword: password, backgroundCachePassword: '');
426 + if (wallet.status() != 0) {
427 // We simply ignore the error.
428 - printV("setupBackgroundSync: ${monero.Wallet_errorString(wptrOverride ?? wptr!)}");
428 + printV("setupBackgroundSync: ${wallet.errorString()}");
429 }
430 }
431
432 -void _createWallet(Map<String, dynamic> args) {
433 - final path = args['path'] as String;
434 - final password = args['password'] as String;
435 - final language = args['language'] as String;
436 - final passphrase = args['passphrase'] as String;
437 -
438 - createWalletSync(path: path, password: password, language: language, passphrase: passphrase);
439 -}
440 -
441 -void _restoreFromSeed(Map<String, dynamic> args) {
442 - final path = args['path'] as String;
443 - final password = args['password'] as String;
444 - final passphrase = args['passphrase'] as String;
445 - final seed = args['seed'] as String;
446 - final restoreHeight = args['restoreHeight'] as int;
447 -
448 - return restoreWalletFromSeedSync(
449 - path: path, password: password, passphrase: passphrase, seed: seed, restoreHeight: restoreHeight);
450 -}
451 -
452 -void _restoreFromKeys(Map<String, dynamic> args) {
453 - final path = args['path'] as String;
454 - final password = args['password'] as String;
455 - final language = args['language'] as String;
456 - final restoreHeight = args['restoreHeight'] as int;
457 - final address = args['address'] as String;
458 - final viewKey = args['viewKey'] as String;
459 - final spendKey = args['spendKey'] as String;
460 -
461 - restoreWalletFromKeysSync(
462 - path: path,
463 - password: password,
464 - language: language,
465 - restoreHeight: restoreHeight,
466 - address: address,
467 - viewKey: viewKey,
468 - spendKey: spendKey);
469 -}
470 -
471 -void _restoreFromSpendKey(Map<String, dynamic> args) {
472 - final path = args['path'] as String;
473 - final password = args['password'] as String;
474 - final seed = args['seed'] as String;
475 - final language = args['language'] as String;
476 - final spendKey = args['spendKey'] as String;
477 - final restoreHeight = args['restoreHeight'] as int;
478 -
479 - restoreWalletFromSpendKeySync(
480 - path: path,
481 - password: password,
482 - seed: seed,
483 - language: language,
484 - restoreHeight: restoreHeight,
485 - spendKey: spendKey);
486 -}
487 -
488 -Future<void> _openWallet(Map<String, String> args) async => loadWallet(
489 - path: args['path'] as String, password: args['password'] as String);
490 -
491 -bool _isWalletExist(String path) => isWalletExistSync(path: path);
432
433 Future<void> openWallet(
434 {required String path,
@@ -496,77 +436,4 @@ Future<void> openWallet(
436 int nettype = 0}) async =>
437 loadWallet(path: path, password: password, nettype: nettype);
438
499 -Future<void> openWalletAsync(Map<String, String> args) async =>
500 - _openWallet(args);
501 -
502 -Future<void> createWallet(
503 - {required String path,
504 - required String password,
505 - required String language,
506 - required String passphrase,
507 - int nettype = 0}) async =>
508 - _createWallet({
509 - 'path': path,
510 - 'password': password,
511 - 'language': language,
512 - 'passphrase': passphrase,
513 - 'nettype': nettype
514 - });
515 -
516 -void restoreFromSeed(
517 - {required String path,
518 - required String password,
519 - required String passphrase,
520 - required String seed,
521 - int nettype = 0,
522 - int restoreHeight = 0}) =>
523 - _restoreFromSeed({
524 - 'path': path,
525 - 'password': password,
526 - 'passphrase': passphrase,
527 - 'seed': seed,
528 - 'nettype': nettype,
529 - 'restoreHeight': restoreHeight
530 - });
531 -
532 -Future<void> restoreFromKeys(
533 - {required String path,
534 - required String password,
535 - required String language,
536 - required String address,
537 - required String viewKey,
538 - required String spendKey,
539 - int nettype = 0,
540 - int restoreHeight = 0}) async =>
541 - _restoreFromKeys({
542 - 'path': path,
543 - 'password': password,
544 - 'language': language,
545 - 'address': address,
546 - 'viewKey': viewKey,
547 - 'spendKey': spendKey,
548 - 'nettype': nettype,
549 - 'restoreHeight': restoreHeight
550 - });
551 -
552 -Future<void> restoreFromSpendKey(
553 - {required String path,
554 - required String password,
555 - required String seed,
556 - required String language,
557 - required String spendKey,
558 - int nettype = 0,
559 - int restoreHeight = 0}) async =>
560 - _restoreFromSpendKey({
561 - 'path': path,
562 - 'password': password,
563 - 'seed': seed,
564 - 'language': language,
565 - 'spendKey': spendKey,
566 - 'nettype': nettype,
567 - 'restoreHeight': restoreHeight
568 - });
569 -
570 -bool isWalletExist({required String path}) => _isWalletExist(path);
571 -
572 -bool isViewOnlyBySpendKey(Pointer<Void>? wptrOverride) => int.tryParse(monero.Wallet_secretSpendKey(wptrOverride ?? wptr!)) == 0;
439 +bool isViewOnlyBySpendKey(Wallet2Wallet? wallet) => int.tryParse((wallet??currentWallet!).secretSpendKey()) == 0;
cw_monero/lib/ledger.dart
+8 -8
@@ -7,26 +7,26 @@ import 'package:cw_core/utils/print_verbose.dart';
7 import 'package:ffi/ffi.dart';
8 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
9 import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
10 -import 'package:monero/monero.dart' as monero;
10 +import 'package:monero/src/wallet2.dart';
11
12 LedgerConnection? gLedger;
13
14 Timer? _ledgerExchangeTimer;
15 Timer? _ledgerKeepAlive;
16
17 -void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
17 +void enableLedgerExchange(Wallet2Wallet wallet, LedgerConnection connection) {
18 _ledgerExchangeTimer?.cancel();
19 _ledgerExchangeTimer = Timer.periodic(Duration(milliseconds: 1), (_) async {
20 - final ledgerRequestLength = monero.Wallet_getSendToDeviceLength(ptr);
21 - final ledgerRequest = monero.Wallet_getSendToDevice(ptr)
20 + final ledgerRequestLength = wallet.getSendToDeviceLength();
21 + final ledgerRequest = wallet.getSendToDevice()
22 .cast<Uint8>()
23 .asTypedList(ledgerRequestLength);
24 if (ledgerRequestLength > 0) {
25 _ledgerKeepAlive?.cancel();
26
27 final Pointer<Uint8> emptyPointer = malloc<Uint8>(0);
28 - monero.Wallet_setDeviceSendData(
29 - ptr, emptyPointer.cast<UnsignedChar>(), 0);
28 + wallet.setDeviceSendData(
29 + emptyPointer.cast<UnsignedChar>(), 0);
30 malloc.free(emptyPointer);
31
32 _logLedgerCommand(ledgerRequest, false);
@@ -45,8 +45,8 @@ void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
45 result.asTypedList(response.length)[i] = response[i];
46 }
47
48 - monero.Wallet_setDeviceReceivedData(
49 - ptr, result.cast<UnsignedChar>(), response.length);
48 + wallet.setDeviceReceivedData(
49 + result.cast<UnsignedChar>(), response.length);
50 malloc.free(result);
51 keepAlive(connection);
52 }
cw_monero/lib/monero_account_list.dart
+14 -14
@@ -4,7 +4,7 @@ import 'package:cw_monero/api/wallet_manager.dart';
4 import 'package:mobx/mobx.dart';
5 import 'package:cw_core/account.dart';
6 import 'package:cw_monero/api/account_list.dart' as account_list;
7 -import 'package:monero/monero.dart' as monero;
7 +import 'package:monero/src/monero.dart';
8
9 part 'monero_account_list.g.dart';
10
@@ -50,32 +50,32 @@ abstract class MoneroAccountListBase with Store {
50 List<Account> getAll() {
51 final allAccounts = account_list.getAllAccount();
52 final currentCount = allAccounts.length;
53 - cachedAccounts[account_list.wptr!.address] ??= [];
53 + cachedAccounts[account_list.currentWallet!.ffiAddress()] ??= [];
54
55 - if (cachedAccounts[account_list.wptr!.address]!.length == currentCount) {
56 - return cachedAccounts[account_list.wptr!.address]!;
55 + if (cachedAccounts[account_list.currentWallet!.ffiAddress()]!.length == currentCount) {
56 + return cachedAccounts[account_list.currentWallet!.ffiAddress()]!;
57 }
58
59 - cachedAccounts[account_list.wptr!.address] = allAccounts.map((accountRow) {
60 - final balance = monero.SubaddressAccountRow_getUnlockedBalance(accountRow);
59 + cachedAccounts[account_list.currentWallet!.ffiAddress()] = allAccounts.map((accountRow) {
60 + final balance = accountRow.getUnlockedBalance();
61
62 return Account(
63 - id: monero.SubaddressAccountRow_getRowId(accountRow),
64 - label: monero.SubaddressAccountRow_getLabel(accountRow),
65 - balance: moneroAmountToString(amount: monero.Wallet_amountFromString(balance)),
63 + id: accountRow.getRowId(),
64 + label: accountRow.getLabel(),
65 + balance: moneroAmountToString(amount: account_list.currentWallet!.amountFromString(balance)),
66 );
67 }).toList();
68
69 - return cachedAccounts[account_list.wptr!.address]!;
69 + return cachedAccounts[account_list.currentWallet!.ffiAddress()]!;
70 }
71
72 - Future<void> addAccount({required String label}) async {
73 - await account_list.addAccount(label: label);
72 + void addAccount({required String label}) {
73 + account_list.addAccount(label: label);
74 update();
75 }
76
77 - Future<void> setLabelAccount({required int accountIndex, required String label}) async {
78 - await account_list.setLabelForAccount(accountIndex: accountIndex, label: label);
77 + void setLabelAccount({required int accountIndex, required String label}) {
78 + account_list.setLabelForAccount(accountIndex: accountIndex, label: label);
79 update();
80 }
81
cw_monero/lib/monero_unspent.dart
+1 -1
@@ -1,7 +1,7 @@
1 import 'package:cw_core/unspent_transaction_output.dart';
2 import 'package:cw_core/utils/print_verbose.dart';
3 import 'package:cw_monero/api/coins_info.dart';
4 -import 'package:monero/monero.dart' as monero;
4 +import 'package:monero/src/monero.dart';
5
6 class MoneroUnspent extends Unspent {
7 static Future<MoneroUnspent> fromUnspent(String address, String hash, String keyImage, int value, bool isFrozen, bool isUnlocked) async {
cw_monero/lib/monero_wallet.dart
+96 -75
@@ -39,6 +39,7 @@ import 'package:flutter/foundation.dart';
39 import 'package:hive/hive.dart';
40 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
41 import 'package:mobx/mobx.dart';
42 +import 'package:monero/src/monero.dart' as m;
43 import 'package:monero/monero.dart' as monero;
44
45 part 'monero_wallet.g.dart';
@@ -84,7 +85,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
85 monero_wallet.getUnlockedBalance(accountIndex: account.id))
86 });
87 _updateSubAddress(isEnabledAutoGenerateSubaddress, account: account);
87 - _askForUpdateTransactionHistory();
88 + unawaited(updateTransactions());
89 });
90
91 reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
@@ -139,7 +140,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
140 passphrase: monero_wallet.getPassphrase());
141
142 int? get restoreHeight =>
142 - transactionHistory.transactions.values.firstOrNull?.height ?? monero.Wallet_getRefreshFromBlockHeight(wptr!);
143 + transactionHistory.transactions.values.firstOrNull?.height ?? currentWallet?.getRefreshFromBlockHeight();
144
145 monero_wallet.SyncListener? _listener;
146 ReactionDisposer? _onAccountChangeReaction;
@@ -169,7 +170,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
170 if (monero_wallet.getCurrentHeight() <= 1) {
171 monero_wallet.setRefreshFromBlockHeight(
172 height: walletInfo.restoreHeight);
172 - setupBackgroundSync(password, wptr!);
173 + setupBackgroundSync(password, currentWallet!);
174 }
175 }
176
@@ -189,14 +190,23 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
190 final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
191 if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) {
192 printV("closing wallet");
192 - final wmaddr = wmPtr.address;
193 - final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address;
194 - await Isolate.run(() {
195 - monero.WalletManager_closeWallet(
196 - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
197 - });
193 + final wmaddr = wmPtr.ffiAddress();
194 + final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.ffiAddress();
195 openedWalletsByPath.remove("$currentWalletDirPath/$name");
199 - wptr = null;
196 + if (Platform.isWindows) {
197 + await Isolate.run(() {
198 + monero.WalletManager_closeWallet(
199 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
200 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
201 + });
202 + } else {
203 + unawaited(Isolate.run(() {
204 + monero.WalletManager_closeWallet(
205 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
206 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
207 + }));
208 + }
209 + currentWallet = null;
210 printV("wallet closed");
211 }
212 }
@@ -211,7 +221,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
221 Future<void> connectToNode({required Node node}) async {
222 try {
223 syncStatus = ConnectingSyncStatus();
214 - await monero_wallet.setupNode(
224 + await monero_wallet.setupNodeSync(
225 address: node.uri.toString(),
226 login: node.login,
227 password: node.password,
@@ -237,10 +247,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
247 isBackgroundSyncRunning = true;
248 await save();
249
240 - monero.Wallet_startBackgroundSync(wptr!);
241 - final status = monero.Wallet_status(wptr!);
250 + currentWallet!.startBackgroundSync();
251 + final status = currentWallet!.status();
252 if (status != 0) {
243 - final err = monero.Wallet_errorString(wptr!);
253 + final err = currentWallet!.errorString();
254 isBackgroundSyncRunning = false;
255 printV("startBackgroundSync: $err");
256 }
@@ -256,9 +266,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
266 Future<void> stopSync() async {
267 if (isBackgroundSyncRunning) {
268 printV("Stopping background sync");
259 - monero.Wallet_store(wptr!);
260 - monero.Wallet_stopBackgroundSync(wptr!, '');
261 - monero_wallet.store();
269 + currentWallet!.store();
270 + currentWallet!.stopBackgroundSync('');
271 + currentWallet!.store();
272 isBackgroundSyncRunning = false;
273 }
274 await save();
@@ -269,9 +279,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
279 Future<void> stopBackgroundSync(String password) async {
280 if (isBackgroundSyncRunning) {
281 printV("Stopping background sync");
272 - monero.Wallet_store(wptr!);
273 - monero.Wallet_stopBackgroundSync(wptr!, password);
274 - monero.Wallet_store(wptr!);
282 + currentWallet!.store();
283 + currentWallet!.stopBackgroundSync(password);
284 + currentWallet!.store();
285 isBackgroundSyncRunning = false;
286 }
287 }
@@ -308,44 +318,44 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
318 }
319
320 Future<bool> submitTransactionUR(String ur) async {
311 - final retStatus = monero.Wallet_submitTransactionUR(wptr!, ur);
312 - final status = monero.Wallet_status(wptr!);
321 + final retStatus = currentWallet!.submitTransactionUR(ur);
322 + final status = currentWallet!.status();
323 if (status != 0) {
314 - final err = monero.Wallet_errorString(wptr!);
324 + final err = currentWallet!.errorString();
325 throw MoneroTransactionCreationException("unable to broadcast signed transaction: $err");
326 }
327 return retStatus;
328 }
329
330 bool importKeyImagesUR(String ur) {
321 - final retStatus = monero.Wallet_importKeyImagesUR(wptr!, ur);
322 - final status = monero.Wallet_status(wptr!);
331 + final retStatus = currentWallet!.importKeyImagesUR(ur);
332 + final status = currentWallet!.status();
333 if (status != 0) {
324 - final err = monero.Wallet_errorString(wptr!);
334 + final err = currentWallet!.errorString();
335 throw Exception("unable to import key images: $err");
336 }
337 return retStatus;
338 }
339
340 String exportOutputsUR(bool all) {
331 - final str = monero.Wallet_exportOutputsUR(wptr!, all: all);
332 - final status = monero.Wallet_status(wptr!);
341 + final str = currentWallet!.exportOutputsUR(all: all);
342 + final status = currentWallet!.status();
343 if (status != 0) {
334 - final err = monero.Wallet_errorString(wptr!);
344 + final err = currentWallet!.errorString();
345 throw MoneroTransactionCreationException("unable to export UR: $err");
346 }
347 return str;
348 }
349
350 bool needExportOutputs(int amount) {
341 - if (int.tryParse(monero.Wallet_secretSpendKey(wptr!)) != 0) {
351 + if (int.tryParse(currentWallet!.secretSpendKey()) != 0) {
352 return false;
353 }
354 // viewOnlyBalance - balance that we can spend
355 // TODO(mrcyjanek): remove hasUnknownKeyImages when we cleanup coin control
346 - return (monero.Wallet_viewOnlyBalance(wptr!,
356 + return (currentWallet!.viewOnlyBalance(
357 accountIndex: walletAddresses.account!.id) < amount) ||
348 - monero.Wallet_hasUnknownKeyImages(wptr!);
358 + currentWallet!.hasUnknownKeyImages();
359 }
360
361 @override
@@ -425,12 +435,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
435 if (inputs.isEmpty) MoneroTransactionCreationException(
436 'No inputs selected');
437 pendingTransactionDescription =
428 - await transaction_history.createTransaction(
438 + await transaction_history.createTransactionSync(
439 address: address!,
440 amount: amount,
441 priorityRaw: _credentials.priority.serialize(),
442 accountIndex: walletAddresses.account!.id,
433 - preferredInputs: inputs);
443 + preferredInputs: inputs,
444 + paymentId: '');
445 }
446
447 // final status = monero.PendingTransaction_status(pendingTransactionDescription);
@@ -485,14 +496,25 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
496 final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
497 if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) {
498 // NOTE: this is realistically only required on windows.
499 + // That's why we await it only on that platform - other platforms actually understand
500 + // the concept of a file properly...
501 printV("closing wallet");
489 - final wmaddr = wmPtr.address;
490 - final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address;
491 - await Isolate.run(() {
492 - monero.WalletManager_closeWallet(
493 - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
494 - });
502 + final wmaddr = wmPtr.ffiAddress();
503 + final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.ffiAddress();
504 openedWalletsByPath.remove("$currentWalletDirPath/$name");
505 + if (Platform.isWindows) {
506 + await Isolate.run(() {
507 + monero.WalletManager_closeWallet(
508 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
509 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
510 + });
511 + } else {
512 + unawaited(Isolate.run(() {
513 + monero.WalletManager_closeWallet(
514 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
515 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
516 + }));
517 + }
518 printV("wallet closed");
519 }
520 try {
@@ -501,32 +523,33 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
523 Directory(await pathForWalletDir(name: name, type: type));
524 final newWalletDirPath =
525 await pathForWalletDir(name: newWalletName, type: type);
504 - await currentWalletDir.rename(newWalletDirPath);
505 -
506 - // -- use new waller folder to rename files with old names still --
507 - final renamedWalletPath = newWalletDirPath + '/$name';
508 -
509 - final currentCacheFile = File(renamedWalletPath);
510 - final currentKeysFile = File('$renamedWalletPath.keys');
511 - final currentAddressListFile = File('$renamedWalletPath.address.txt');
512 - final backgroundSyncFile = File('$renamedWalletPath.background');
513 -
514 - final newWalletPath =
515 - await pathForWallet(name: newWalletName, type: type);
526 +
527 + // Create new directory if it doesn't exist
528 + await Directory(newWalletDirPath).create(recursive: true);
529 +
530 + // -- use new waller folder to copy files with old names still --
531 + final currentWalletPath = currentWalletDir.path + '/$name';
532 +
533 + final currentCacheFile = File(currentWalletPath);
534 + final currentKeysFile = File('$currentWalletPath.keys');
535 + final currentAddressListFile = File('$currentWalletPath.address.txt');
536 + final backgroundSyncFile = File('$currentWalletPath.background');
537
538 if (currentCacheFile.existsSync()) {
518 - await currentCacheFile.rename(newWalletPath);
539 + await currentCacheFile.copy("${newWalletDirPath}/$newWalletName");
540 }
541 if (currentKeysFile.existsSync()) {
521 - await currentKeysFile.rename('$newWalletPath.keys');
542 + await currentKeysFile.copy("${newWalletDirPath}/$newWalletName.keys");
543 }
544 if (currentAddressListFile.existsSync()) {
524 - await currentAddressListFile.rename('$newWalletPath.address.txt');
545 + await currentAddressListFile.copy("${newWalletDirPath}/$newWalletName.address.txt");
546 }
547 if (backgroundSyncFile.existsSync()) {
527 - await backgroundSyncFile.rename('$newWalletPath.background');
548 + await backgroundSyncFile.copy("${newWalletDirPath}/$newWalletName.background");
549 }
550
551 + await currentWalletDir.delete(recursive: true);
552 +
553 await backupWalletFiles(newWalletName);
554 } catch (e) {
555 final currentWalletPath = await pathForWallet(name: name, type: type);
@@ -572,12 +595,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
595 walletInfo.restoreHeight = height;
596 walletInfo.isRecovery = true;
597 monero_wallet.setRefreshFromBlockHeight(height: height);
575 - setupBackgroundSync(password, wptr!);
598 + setupBackgroundSync(password, currentWallet!);
599 monero_wallet.rescanBlockchainAsync();
600 await startSync();
601 _askForUpdateBalance();
602 walletAddresses.accountList.update();
580 - await _askForUpdateTransactionHistory();
603 + await updateTransactions();
604 await save();
605 await walletInfo.save();
606 }
@@ -591,15 +614,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
614 final coinCount = await countOfCoins();
615 for (var i = 0; i < coinCount; i++) {
616 final coin = await getCoin(i);
594 - final coinSpent = monero.CoinsInfo_spent(coin);
595 - if (coinSpent == false && monero.CoinsInfo_subaddrAccount(coin) == walletAddresses.account!.id) {
617 + final coinSpent = coin.spent();
618 + if (coinSpent == false && coin.subaddrAccount() == walletAddresses.account!.id) {
619 final unspent = await MoneroUnspent.fromUnspent(
597 - monero.CoinsInfo_address(coin),
598 - monero.CoinsInfo_hash(coin),
599 - monero.CoinsInfo_keyImage(coin),
600 - monero.CoinsInfo_amount(coin),
601 - monero.CoinsInfo_frozen(coin),
602 - monero.CoinsInfo_unlocked(coin),
620 + coin.address(),
621 + coin.hash(),
622 + coin.keyImage(),
623 + coin.amount(),
624 + coin.frozen(),
625 + coin.unlocked(),
626 );
627 // TODO: double-check the logic here
628 if (unspent.hash.isNotEmpty) {
@@ -704,6 +727,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
727 acc[tx.id] = tx;
728 return acc;
729 });
730 + // This is needed to update the transaction history when new transaction is made.
731 + unawaited(updateTransactions());
732 return resp;
733 }
734
@@ -792,7 +817,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
817
818 monero_wallet.setRecoveringFromSeed(isRecovery: true);
819 monero_wallet.setRefreshFromBlockHeight(height: height);
795 - setupBackgroundSync(password, wptr!);
820 + setupBackgroundSync(password, currentWallet!);
821 }
822
823 int _getHeightDistance(DateTime date) {
@@ -831,9 +856,6 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
856 }
857 }
858
834 - Future<void> _askForUpdateTransactionHistory() async =>
835 - await updateTransactions();
836 -
859 int _getUnlockedBalance() => monero_wallet.getUnlockedBalance(
860 accountIndex: walletAddresses.account!.id);
861
@@ -852,13 +874,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
874 printV("onNewBlock: $height, $blocksLeft, $ptc");
875 try {
876 if (walletInfo.isRecovery) {
855 - await _askForUpdateTransactionHistory();
877 + await updateTransactions();
878 _askForUpdateBalance();
879 walletAddresses.accountList.update();
880 }
881
882 if (blocksLeft < 100) {
861 - await _askForUpdateTransactionHistory();
883 + await updateTransactions();
884 _askForUpdateBalance();
885 walletAddresses.accountList.update();
886 syncStatus = SyncedSyncStatus();
@@ -881,7 +903,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
903
904 void _onNewTransaction() async {
905 try {
884 - await _askForUpdateTransactionHistory();
906 + await updateTransactions();
907 _askForUpdateBalance();
908 await Future<void>.delayed(Duration(seconds: 1));
909 } catch (e) {
@@ -917,8 +939,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
939 }
940
941 void setLedgerConnection(LedgerConnection connection) {
920 - final dummyWPtr = wptr ??
921 - monero.WalletManager_openWallet(wmPtr, path: '', password: '');
942 + final dummyWPtr = createWalletPointer();
943 enableLedgerExchange(dummyWPtr, connection);
944 }
945
cw_monero/lib/monero_wallet_service.dart
+37 -20
@@ -1,5 +1,7 @@
1 +import 'dart:async';
2 import 'dart:ffi';
3 import 'dart:io';
4 +import 'dart:isolate';
5
6 import 'package:collection/collection.dart';
7 import 'package:cw_core/get_height_by_date.dart';
@@ -20,6 +22,7 @@ import 'package:cw_monero/ledger.dart';
22 import 'package:cw_monero/monero_wallet.dart';
23 import 'package:hive/hive.dart';
24 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
25 +import 'package:monero/src/monero.dart' as m;
26 import 'package:monero/monero.dart' as monero;
27 import 'package:polyseed/polyseed.dart';
28
@@ -139,7 +142,7 @@ class MoneroWalletService extends WalletService<
142 overrideHeight: heightOverride, passphrase: credentials.passphrase);
143 }
144
142 - await monero_wallet_manager.createWallet(
145 + monero_wallet_manager.createWallet(
146 path: path,
147 password: credentials.password!,
148 language: credentials.language,
@@ -179,7 +182,7 @@ class MoneroWalletService extends WalletService<
182 if (walletFilesExist(path)) await repairOldAndroidWallet(name);
183
184 await monero_wallet_manager
182 - .openWalletAsync({'path': path, 'password': password});
185 + .openWallet(path: path, password: password);
186 final walletInfo = walletInfoSource.values
187 .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
188 final wallet = MoneroWallet(
@@ -217,13 +220,23 @@ class MoneroWalletService extends WalletService<
220 if (openedWalletsByPath["$path/$wallet"] != null) {
221 // NOTE: this is realistically only required on windows.
222 printV("closing wallet");
220 - final wmaddr = wmPtr.address;
221 - final waddr = openedWalletsByPath["$path/$wallet"]!.address;
222 - // await Isolate.run(() {
223 - monero.WalletManager_closeWallet(
224 - Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), false);
225 - // });
223 + final w = openedWalletsByPath["$path/$wallet"]!;
224 + final wmaddr = wmPtr.ffiAddress();
225 + final waddr = w.ffiAddress();
226 openedWalletsByPath.remove("$path/$wallet");
227 + if (Platform.isWindows) {
228 + await Isolate.run(() {
229 + monero.WalletManager_closeWallet(
230 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
231 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
232 + });
233 + } else {
234 + unawaited(Isolate.run(() {
235 + monero.WalletManager_closeWallet(
236 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
237 + monero.WalletManager_errorString(Pointer.fromAddress(wmaddr));
238 + }));
239 + }
240 printV("wallet closed");
241 }
242
@@ -263,7 +276,7 @@ class MoneroWalletService extends WalletService<
276 {bool? isTestnet}) async {
277 try {
278 final path = await pathForWallet(name: credentials.name, type: getType());
266 - await monero_wallet_manager.restoreFromKeys(
279 + monero_wallet_manager.restoreWalletFromKeys(
280 path: path,
281 password: credentials.password!,
282 language: credentials.language,
@@ -293,9 +306,13 @@ class MoneroWalletService extends WalletService<
306 final password = credentials.password;
307 final height = credentials.height;
308
296 - if (wptr == null) monero_wallet_manager.createWalletPointer();
309 + if (currentWallet == null) {
310 + final tmpWptr = monero_wallet_manager.createWalletPointer();
311 + enableLedgerExchange(tmpWptr, credentials.ledgerConnection);
312 + } else {
313 + enableLedgerExchange(currentWallet!, credentials.ledgerConnection);
314 + }
315
298 - enableLedgerExchange(wptr!, credentials.ledgerConnection);
316 await monero_wallet_manager.restoreWalletFromHardwareWallet(
317 path: path,
318 password: password!,
@@ -352,7 +369,7 @@ class MoneroWalletService extends WalletService<
369 try {
370 final path = await pathForWallet(name: credentials.name, type: getType());
371
355 - monero_wallet_manager.restoreFromSeed(
372 + monero_wallet_manager.restoreWalletFromSeedSync(
373 path: path,
374 password: credentials.password!,
375 passphrase: credentials.passphrase,
@@ -393,7 +410,7 @@ class MoneroWalletService extends WalletService<
410 walletInfo.isRecovery = true;
411 walletInfo.restoreHeight = height;
412
396 - monero_wallet_manager.restoreFromSeed(
413 + monero_wallet_manager.restoreWalletFromSeedSync(
414 path: path,
415 password: password,
416 passphrase: '',
@@ -401,12 +418,12 @@ class MoneroWalletService extends WalletService<
418 restoreHeight: height,
419 );
420
404 - monero.Wallet_setCacheAttribute(wptr!,
421 + currentWallet!.setCacheAttribute(
422 key: "cakewallet.seed.bip39", value: mnemonic);
406 - monero.Wallet_setCacheAttribute(wptr!,
423 + currentWallet!.setCacheAttribute(
424 key: "cakewallet.passphrase", value: passphrase ?? '');
425
409 - monero.Wallet_store(wptr!);
426 + currentWallet!.store();
427
428 final wallet = MoneroWallet(
429 walletInfo: walletInfo,
@@ -472,7 +489,7 @@ class MoneroWalletService extends WalletService<
489 walletInfo.isRecovery = true;
490 walletInfo.restoreHeight = height;
491
475 - await monero_wallet_manager.restoreFromSpendKey(
492 + monero_wallet_manager.restoreWalletFromSpendKeySync(
493 path: path,
494 password: password,
495 seed: seed,
@@ -481,8 +498,8 @@ class MoneroWalletService extends WalletService<
498 spendKey: spendKey);
499
500
484 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.seed", value: seed);
485 - monero.Wallet_setCacheAttribute(wptr!, key: "cakewallet.passphrase", value: passphrase??'');
501 + currentWallet!.setCacheAttribute(key: "cakewallet.seed", value: seed);
502 + currentWallet!.setCacheAttribute(key: "cakewallet.passphrase", value: passphrase??'');
503
504 final wallet = MoneroWallet(
505 walletInfo: walletInfo,
@@ -529,7 +546,7 @@ class MoneroWalletService extends WalletService<
546 if (walletFilesExist(path)) await repairOldAndroidWallet(name);
547
548 await monero_wallet_manager
532 - .openWalletAsync({'path': path, 'password': password});
549 + .openWallet(path: path, password: password);
550 final walletInfo = walletInfoSource.values
551 .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
552 final wallet = MoneroWallet(
cw_monero/pubspec.lock
+2 -2
@@ -573,8 +573,8 @@ packages:
573 dependency: "direct main"
574 description:
575 path: "impls/monero.dart"
576 - ref: "84e52393e395d75f449bcd81e23028889538118f"
577 - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
576 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
577 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
578 url: "https://github.com/mrcyjanek/monero_c"
579 source: git
580 version: "0.0.0"
cw_monero/pubspec.yaml
+1 -1
@@ -27,7 +27,7 @@ dependencies:
27 monero:
28 git:
29 url: https://github.com/mrcyjanek/monero_c
30 - ref: 84e52393e395d75f449bcd81e23028889538118f
30 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
31 path: impls/monero.dart
32 mutex: ^3.1.0
33 ledger_flutter_plus: ^1.4.1
cw_wownero/pubspec.lock
+2 -2
@@ -480,8 +480,8 @@ packages:
480 dependency: "direct main"
481 description:
482 path: "impls/monero.dart"
483 - ref: "84e52393e395d75f449bcd81e23028889538118f"
484 - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
483 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
484 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
485 url: "https://github.com/mrcyjanek/monero_c"
486 source: git
487 version: "0.0.0"
cw_wownero/pubspec.yaml
+1 -1
@@ -25,7 +25,7 @@ dependencies:
25 monero:
26 git:
27 url: https://github.com/mrcyjanek/monero_c
28 - ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash
28 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash
29 path: impls/monero.dart
30 mutex: ^3.1.0
31
cw_zano/pubspec.lock
+2 -2
@@ -485,8 +485,8 @@ packages:
485 dependency: "direct main"
486 description:
487 path: "impls/monero.dart"
488 - ref: "84e52393e395d75f449bcd81e23028889538118f"
489 - resolved-ref: "84e52393e395d75f449bcd81e23028889538118f"
488 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
489 + resolved-ref: b335585a7fb94b315eb52bd88f2da6d3489fa508
490 url: "https://github.com/mrcyjanek/monero_c"
491 source: git
492 version: "0.0.0"
cw_zano/pubspec.yaml
+1 -1
@@ -26,7 +26,7 @@ dependencies:
26 monero:
27 git:
28 url: https://github.com/mrcyjanek/monero_c
29 - ref: 84e52393e395d75f449bcd81e23028889538118f # monero_c hash
29 + ref: b335585a7fb94b315eb52bd88f2da6d3489fa508 # monero_c hash
30 path: impls/monero.dart
31 dev_dependencies:
32 flutter_test:
ios/Podfile.lock
+27 -29
@@ -208,42 +208,40 @@ EXTERNAL SOURCES:
208 :path: ".symlinks/plugins/wakelock_plus/ios"
209
210 SPEC CHECKSUMS:
211 - connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
211 + connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf
212 CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90
213 - cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a
214 - cw_mweb: 22cd01dfb8ad2d39b15332006f22046aaa8352a3
215 - device_display_brightness: 1510e72c567a1f6ce6ffe393dcd9afd1426034f7
216 - device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
217 - devicelocale: 35ba84dc7f45f527c3001535d8c8d104edd5d926
213 + cw_decred: a02cf30175a46971c1e2fa22c48407534541edc6
214 + cw_mweb: 3aea2fb35b2bd04d8b2d21b83216f3b8fb768d85
215 + device_display_brightness: 04374ebd653619292c1d996f00f42877ea19f17f
216 + device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
217 + devicelocale: bd64aa714485a8afdaded0892c1e7d5b7f680cf8
218 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
219 DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
220 - fast_scanner: 44c00940355a51258cd6c2085734193cd23d95bc
221 - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
220 + fast_scanner: 2cb1ad3e69e645e9980fb4961396ce5804caa3e3
221 + file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
222 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
223 - flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
224 - flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
225 - flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
226 - flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
227 - flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
228 - fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
229 - in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
230 - integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
223 + flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
224 + flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
225 + flutter_mailer: 3a8cd4f36c960fb04528d5471097270c19fec1c4
226 + flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
227 + fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
228 + in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
229 + integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
230 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
232 - package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
233 - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
234 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
235 - reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089
231 + package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
232 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
233 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
234 + ReachabilitySwift: 32793e867593cfc1177f5d16491e3a197d2fccda
235 SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
237 - sensitive_clipboard: d4866e5d176581536c27bb1618642ee83adca986
238 - share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
239 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
240 - sp_scanner: eaa617fa827396b967116b7f1f43549ca62e9a12
236 + sensitive_clipboard: 161e9abc3d56b3131309d8a321eb4690a803c16b
237 + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
238 + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
239 + sp_scanner: b1bc9321690980bdb44bba7ec85d5543e716d1b5
240 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
242 - uni_links: d97da20c7701486ba192624d99bffaaffcfc298a
243 - universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
244 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
245 - wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
246 - YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966
241 + uni_links: ed8c961e47ed9ce42b6d91e1de8049e38a4b3152
242 + universal_ble: ff19787898040d721109c6324472e5dd4bc86adc
243 + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
244 + wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
245
246 PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a
247
lib/core/wallet_loading_service.dart
+22 -17
@@ -32,23 +32,28 @@ class WalletLoadingService {
32
33 Future<void> renameWallet(WalletType type, String name, String newName,
34 {String? password}) async {
35 - final walletService = walletServiceFactory.call(type);
36 - final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
37 -
38 - // Save the current wallet's password to the new wallet name's key
39 - await keyService.saveWalletPassword(walletName: newName, password: walletPassword);
40 - // Delete previous wallet name from keyService to keep only new wallet's name
41 - // otherwise keeps duplicate (old and new names)
42 - await keyService.deleteWalletPassword(walletName: name);
43 -
44 - await walletService.rename(name, walletPassword, newName);
45 -
46 - // set shared preferences flag based on previous wallet name
47 - if (type == WalletType.monero) {
48 - final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name);
49 - final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false;
50 - final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName);
51 - await sharedPreferences.setBool(newNameKey, isPasswordUpdated);
35 + try {
36 + final walletService = walletServiceFactory.call(type);
37 + final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
38 +
39 + // Save the current wallet's password to the new wallet name's key
40 + await keyService.saveWalletPassword(walletName: newName, password: walletPassword);
41 +
42 + await walletService.rename(name, walletPassword, newName);
43 + // Delete previous wallet name from keyService to keep only new wallet's name
44 + // otherwise keeps duplicate (old and new names)
45 + await keyService.deleteWalletPassword(walletName: name);
46 +
47 + // set shared preferences flag based on previous wallet name
48 + if (type == WalletType.monero) {
49 + final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name);
50 + final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false;
51 + final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName);
52 + await sharedPreferences.setBool(newNameKey, isPasswordUpdated);
53 + }
54 + } catch (error, stack) {
55 + await ExceptionHandler.resetLastPopupDate();
56 + await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
57 }
58 }
59
lib/monero/cw_monero.dart
+2 -2
@@ -39,14 +39,14 @@ class CWMoneroAccountList extends MoneroAccountList {
39 @override
40 Future<void> addAccount(Object wallet, {required String label}) async {
41 final moneroWallet = wallet as MoneroWallet;
42 - await moneroWallet.walletAddresses.accountList.addAccount(label: label);
42 + moneroWallet.walletAddresses.accountList.addAccount(label: label);
43 }
44
45 @override
46 Future<void> setLabelAccount(Object wallet,
47 {required int accountIndex, required String label}) async {
48 final moneroWallet = wallet as MoneroWallet;
49 - await moneroWallet.walletAddresses.accountList
49 + moneroWallet.walletAddresses.accountList
50 .setLabelAccount(accountIndex: accountIndex, label: label);
51 }
52 }
lib/view_model/dashboard/dashboard_view_model.dart
+26 -6
@@ -271,10 +271,19 @@ abstract class DashboardViewModelBase with Store {
271 });
272
273 _transactionDisposer?.reaction.dispose();
274 - _transactionDisposer = reaction(
275 - (_) => appStore.wallet!.transactionHistory.transactions.length,
276 - _transactionDisposerCallback,
277 - );
274 + _transactionDisposer = reaction((_) {
275 + final length = appStore.wallet!.transactionHistory.transactions.length;
276 + if (length == 0) {
277 + return 0;
278 + }
279 + int confirmations = 1;
280 + if (![WalletType.solana, WalletType.tron].contains(wallet.type)) {
281 + try {
282 + confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1;
283 + } catch (_) {}
284 + }
285 + return length * confirmations;
286 + }, _transactionDisposerCallback);
287
288 if (hasSilentPayments) {
289 silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet);
@@ -891,8 +900,19 @@ abstract class DashboardViewModelBase with Store {
900
901 _transactionDisposer?.reaction.dispose();
902
894 - _transactionDisposer = reaction((_) => appStore.wallet!.transactionHistory.transactions.length,
895 - _transactionDisposerCallback);
903 + _transactionDisposer = reaction((_) {
904 + final length = appStore.wallet!.transactionHistory.transactions.length;
905 + if (length == 0) {
906 + return 0;
907 + }
908 + int confirmations = 1;
909 + if (![WalletType.solana, WalletType.tron].contains(wallet.type)) {
910 + try {
911 + confirmations = appStore.wallet!.transactionHistory.transactions.values.first.confirmations + 1;
912 + } catch (_) {}
913 + }
914 + return length * confirmations;
915 + }, _transactionDisposerCallback);
916 }
917
918 @action
lib/view_model/send/send_view_model.dart
+3 -1
@@ -1,3 +1,5 @@
1 +import 'dart:async';
2 +
3 import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 import 'package:cake_wallet/core/address_validator.dart';
5 import 'package:cake_wallet/core/amount_validator.dart';
@@ -591,7 +593,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
593 }
594 final sharedPreferences = await SharedPreferences.getInstance();
595 await sharedPreferences.setString(PreferencesKey.backgroundSyncLastTrigger(wallet.name), DateTime.now().add(Duration(minutes: 1)).toIso8601String());
594 -
596 + unawaited(wallet.fetchTransactions());
597 state = TransactionCommitted();
598 } catch (e) {
599 state = FailureState(translateErrorMessage(e, wallet.type, wallet.currency));
scripts/prepare_moneroc.sh
+1 -1
@@ -8,7 +8,7 @@ if [[ ! -d "monero_c/.git" ]];
8 then
9 git clone https://github.com/mrcyjanek/monero_c --branch master monero_c
10 cd monero_c
11 - git checkout 84e52393e395d75f449bcd81e23028889538118f
11 + git checkout b335585a7fb94b315eb52bd88f2da6d3489fa508
12 git reset --hard
13 git submodule update --init --force --recursive
14 ./apply_patches.sh monero