What changed, and why it matters
This commit updates the Monero wallet library used by Stack Wallet (cs_monero from version 2.0.0 to 3.1.0) and changes many wallet operations from synchronous to asynchronous. The visible changes are mostly mechanical: adding 'await' and 'Future' return types so the app waits for the underlying C++ Monero code to finish before continuing. There is also a small change to app-exit behavior on Linux and extra debug logging. The commit does not describe any security bug, and the diff itself does not show an obvious vulnerability. However, because it touches sensitive wallet operations (keys, balances, transaction keys, seeds, syncing) and upgrades a major dependency, it could indirectly affect security or stability if the new library version fixed or introduced issues not visible in this repository's diff.
Review the cs_monero 3.1.0 release notes and changelog for any security fixes or breaking changes, verify that the async conversion does not introduce new race conditions around wallet open/close or rescan, and test wallet exit, sync, and key/balance retrieval on Linux, macOS, Windows, Android, and iOS. Consider pinning transitive cs_monero_flutter_libs_* versions and running static analysis on the updated async call sites.
Security signals we found
Dependency upgrade of core cryptographic wallet library (cs_monero 2.0.0 -> 3.1.0) without disclosed changelog
Conversion of synchronous key/balance/seed operations to async, reducing risk of UI/main-thread blocking and potential race conditions
Addition of Linux to force-exit workaround for C++ sync hang on app close
No explicit security bug fix or CVE reference in commit message or diff
Evidence from the diff
The commit bumps cs_monero and cs_monero_flutter_libs to newer versions and adapts the Dart code to an async API. Synchronous calls such as getAddress, getBalance, getUnlockedBalance, getTxKey, getSeed, getPublic/PrivateView/SpendKey, start/stopSyncing, start/stopListeners, rescanBlockchain, getRefreshFromBlockHeight, setRefreshFromBlockHeight, and internalGetAddress are converted to Future-returning methods. Callers in lib_monero_wallet.dart, lib_salvium_wallet.dart, lib_wownero_wallet.dart, cryptonote_wallet.dart, churning_service.dart, and tx_key_widget.dart are updated to await them. The Linux desktop app-exit path now uses the same force-exit workaround as macOS/Windows to avoid hangs during C++ scanning. The XMR interface template adds an optional network parameter defaulting to mainnet. No explicit security fix or vulnerability disclosure is present in the commit message or diff.
Changed components
cs_monero dependency and native flutter libslib/wallets/wallet/intermediate/lib_monero_wallet.dartlib/wallets/wallet/intermediate/lib_salvium_wallet.dartlib/wallets/wallet/intermediate/lib_wownero_wallet.dartlib/wallets/wallet/intermediate/cryptonote_wallet.dartlib/services/churning_service.dartlib/widgets/tx_key_widget.dartlib/wl_gen/interfaces/cs_monero_interface.darttool/wl_templates/XMR_cs_monero_interface_impl.template.dartlib/main.dartInspect captured patch +204 / −151
diff --git a/lib/main.dart b/lib/main.dart
index b0d58af..dd35ae7 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -631,8 +631,10 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
@override
Future<AppExitResponse> didRequestAppExit() async {
debugPrint("didRequestAppExit called");
- if (Platform.isMacOS || Platform.isWindows) {
- // Monero will cause issues if in the middle of syncing.
+ if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
+ // Monero will cause app to stop responding if in the middle of doing
+ // things like a scan on the c++ side of things.
+
// On macOS, mwebd fails to shut down, hanging the app on close.
//
// Exiting is a hack fix for this issue.
diff --git a/lib/services/churning_service.dart b/lib/services/churning_service.dart
index a7f0819..d069987 100644
--- a/lib/services/churning_service.dart
+++ b/lib/services/churning_service.dart
@@ -32,9 +32,9 @@ class ChurningService extends ChangeNotifier {
bool done = false;
Object? lastSeenError;
- bool _canChurn() {
+ Future<bool> _canChurn() async {
if (wallet.wallet != null &&
- wallet.internalGetUnlockedBalance(accountIndex: kAccount)! >
+ await wallet.internalGetUnlockedBalance(accountIndex: kAccount) >
BigInt.zero) {
return true;
} else {
@@ -121,7 +121,7 @@ class ChurningService extends ChangeNotifier {
bool complete() => !continuous && roundsCompleted >= roundsToDo;
while (!complete() && _running) {
- if (_canChurn()) {
+ if (await _canChurn()) {
waitingForUnlockedBalance = ChurnStatus.success;
makingChurnTransaction = ChurnStatus.running;
notifyListeners();
@@ -186,7 +186,7 @@ class ChurningService extends ChangeNotifier {
}
Future<void> _churnTxSimple() async {
- final address = wallet.internalGetAddress(
+ final address = await wallet.internalGetAddress(
accountIndex: kAccount,
addressIndex: 0,
);
diff --git a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart
index 6023434..62a08ce 100644
--- a/lib/wallets/wallet/intermediate/cryptonote_wallet.dart
+++ b/lib/wallets/wallet/intermediate/cryptonote_wallet.dart
@@ -26,21 +26,21 @@ abstract class CryptonoteWallet<T extends CryptonoteCurrency>
Future<CWKeyData?> getKeys();
- String getTxKeyFor({required String txid});
+ Future<String> getTxKeyFor({required String txid});
Future<(String, String)>
hackToCreateNewViewOnlyWalletDataFromNewlyCreatedWalletThisFunctionShouldNotBeCalledUnlessYouKnowWhatYouAreDoing();
void setRefreshFromBlockHeight(int newHeight);
- int getRefreshFromBlockHeight();
+ Future<int> getRefreshFromBlockHeight();
- String internalGetAddress({
+ Future<String> internalGetAddress({
required int accountIndex,
required int addressIndex,
});
- BigInt? internalGetUnlockedBalance({int accountIndex = 0});
+ Future<BigInt> internalGetUnlockedBalance({int accountIndex = 0});
Future<List<CsOutput>> internalGetOutputs({
bool refresh = false,
bool includeSpent = false,
diff --git a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
index b1edd82..6c0c498 100644
--- a/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_monero_wallet.dart
@@ -168,7 +168,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
bool walletExists(String path);
@override
- String getTxKeyFor({required String txid}) {
+ Future<String> getTxKeyFor({required String txid}) {
if (wallet == null) {
throw Exception("Cannot get tx key in uninitialized libMoneroWallet");
}
@@ -218,7 +218,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
Address? currentAddress = await getCurrentReceivingAddress();
if (currentAddress == null) {
- currentAddress = addressFor(index: 0);
+ currentAddress = await addressFor(index: 0);
await mainDB.updateOrPutAddresses([currentAddress]);
}
if (info.cachedReceivingAddress != currentAddress.value) {
@@ -231,14 +231,14 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
if (wasNull) {
try {
_setSyncStatus(lib_monero_compat.ConnectingSyncStatus());
- csMonero.startSyncing(wallet!);
+ await csMonero.startSyncing(wallet!);
} catch (_) {
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
// TODO log
}
}
_setListener();
- csMonero.startListeners(wallet!);
+ await csMonero.startListeners(wallet!);
csMonero.startAutoSaving(wallet!);
unawaited(refresh());
@@ -267,8 +267,8 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
await csMonero.save(wallet!);
}
- Address addressFor({required int index, int account = 0}) {
- final address = csMonero.getAddress(
+ Future<Address> addressFor({required int index, int account = 0}) async {
+ final address = await csMonero.getAddress(
wallet!,
accountIndex: account,
addressIndex: index,
@@ -300,10 +300,10 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
try {
return CWKeyData(
walletId: walletId,
- publicViewKey: csMonero.getPublicViewKey(wallet!),
- privateViewKey: csMonero.getPrivateViewKey(wallet!),
- publicSpendKey: csMonero.getPublicSpendKey(wallet!),
- privateSpendKey: csMonero.getPrivateSpendKey(wallet!),
+ publicViewKey: await csMonero.getPublicViewKey(wallet!),
+ privateViewKey: await csMonero.getPrivateViewKey(wallet!),
+ publicSpendKey: await csMonero.getPublicSpendKey(wallet!),
+ privateSpendKey: await csMonero.getPrivateSpendKey(wallet!),
);
} catch (e, s) {
Logging.instance.f("getKeys failed: ", error: e, stackTrace: s);
@@ -330,7 +330,10 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
throw Exception("Password not found $e, $s");
}
wallet = await loadWallet(path: path, password: password);
- return (csMonero.getAddress(wallet!), csMonero.getPrivateViewKey(wallet!));
+ return (
+ await csMonero.getAddress(wallet!),
+ await csMonero.getPrivateViewKey(wallet!),
+ );
}
@override
@@ -355,7 +358,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
);
await info.updateRestoreHeight(
- newRestoreHeight: csMonero.getRefreshFromBlockHeight(wallet),
+ newRestoreHeight: await csMonero.getRefreshFromBlockHeight(wallet),
isar: mainDB.isar,
);
@@ -363,7 +366,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
// before wallet.init() is called
await secureStorageInterface.write(
key: Wallet.mnemonicKey(walletId: walletId),
- value: csMonero.getSeed(wallet),
+ value: await csMonero.getSeed(wallet),
);
await secureStorageInterface.write(
key: Wallet.mnemonicPassphraseKey(walletId: walletId),
@@ -390,9 +393,9 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
await mainDB.deleteWalletBlockchainData(walletId);
highestPercentCached = 0;
- unawaited(csMonero.rescanBlockchain(wallet!));
- csMonero.startSyncing(wallet!);
- // unawaited(save());
+ await csMonero.rescanBlockchain(wallet!);
+ await csMonero.startSyncing(wallet!);
+ unawaited(save());
});
unawaited(refresh());
return;
@@ -451,7 +454,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
walletId: walletId,
derivationIndex: 0,
derivationPath: null,
- value: csMonero.getAddress(this.wallet!),
+ value: await csMonero.getAddress(this.wallet!),
publicKey: [],
type: AddressType.cryptonote,
subType: AddressSubType.receiving,
@@ -470,11 +473,11 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
_setListener();
// libMoneroWallet?.setRecoveringFromSeed(isRecovery: true);
- unawaited(csMonero.rescanBlockchain(wallet!));
- csMonero.startSyncing(wallet!);
+ await csMonero.rescanBlockchain(wallet!);
+ await csMonero.startSyncing(wallet!);
// await save();
- csMonero.startListeners(wallet!);
+ await csMonero.startListeners(wallet!);
csMonero.startAutoSaving(wallet!);
} catch (e, s) {
Logging.instance.e(
@@ -503,7 +506,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
Future<void> updateNode() async {
final node = getCurrentNode();
- if (_torNodeMismatchGuard(node)) {
+ if (await _torNodeMismatchGuard(node)) {
throw Exception("TOR – clearnet mismatch");
}
@@ -548,8 +551,8 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
: "${proxy.host.address}:${proxy.port}",
);
}
- csMonero.startSyncing(wallet!);
- csMonero.startListeners(wallet!);
+ await csMonero.startSyncing(wallet!);
+ await csMonero.startListeners(wallet!);
csMonero.startAutoSaving(wallet!);
_setSyncStatus(lib_monero_compat.ConnectedSyncStatus());
@@ -705,7 +708,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
Future<Amount> get availableBalance async {
try {
return Amount(
- rawValue: csMonero.getUnlockedBalance(wallet!)!,
+ rawValue: await csMonero.getUnlockedBalance(wallet!),
fractionDigits: cryptoCurrency.fractionDigits,
);
} catch (_) {
@@ -715,28 +718,12 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
Future<Amount> get totalBalance async {
try {
- final full = csMonero.getBalance(wallet!);
- if (full != null) {
- return Amount(
- rawValue: full,
- fractionDigits: cryptoCurrency.fractionDigits,
- );
- } else {
- final transactions = await csMonero.getAllTxs(wallet!, refresh: true);
- BigInt transactionBalance = BigInt.zero;
- for (final tx in transactions) {
- if (!tx.isSpend) {
- transactionBalance += tx.amount;
- } else {
- transactionBalance += -tx.amount - tx.fee;
- }
- }
+ final full = await csMonero.getBalance(wallet!);
- return Amount(
- rawValue: transactionBalance,
- fractionDigits: cryptoCurrency.fractionDigits,
- );
- }
+ return Amount(
+ rawValue: full,
+ fractionDigits: cryptoCurrency.fractionDigits,
+ );
} catch (_) {
return info.cachedBalance.total;
}
@@ -744,13 +731,14 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
@override
Future<void> exit() async {
- Logging.instance.i("exit called on $wallet!");
+ Logging.instance.i("exit called on monero $walletId!");
if (wallet != null) {
csMonero.stopAutoSaving(wallet!);
- csMonero.stopListeners(wallet!);
- csMonero.stopSyncing(wallet!);
+ await csMonero.stopListeners(wallet!);
+ await csMonero.stopSyncing(wallet!);
await csMonero.save(wallet!);
}
+ Logging.instance.i("exit call completed monero $walletId!");
}
Future<String> pathForWalletDir({
@@ -1011,7 +999,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
}
}
- bool _torNodeMismatchGuard(NodeModel node) {
+ Future<bool> _torNodeMismatchGuard(NodeModel node) async {
_canPing = true; // Reset.
final bool mismatch =
@@ -1022,8 +1010,8 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
_canPing = false;
if (wallet != null) {
csMonero.stopAutoSaving(wallet!);
- csMonero.stopListeners(wallet!);
- csMonero.stopSyncing(wallet!);
+ await csMonero.stopListeners(wallet!);
+ await csMonero.stopSyncing(wallet!);
}
_setSyncStatus(lib_monero_compat.FailedSyncStatus());
}
@@ -1120,36 +1108,75 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
// Awaiting this lock could be dangerous.
// Since refresh is periodic (generally)
if (refreshMutex.isLocked) {
+ Logging.instance.t(
+ "$runtimeType refresh() refreshMutex.isLocked=true, returning...",
+ );
return;
}
+ // this acquire should be almost instant due to above check.
+ // Slight possibility of race but should be irrelevant
+ Logging.instance.t(
+ "$runtimeType refresh() refreshMutex.acquire() waiting...",
+ );
+ await refreshMutex.acquire();
+ Logging.instance.t(
+ "$runtimeType refresh() refreshMutex.acquire() acquired!",
+ );
+
+ Logging.instance.t("$runtimeType refresh() final node = getCurrentNode();");
final node = getCurrentNode();
- if (_torNodeMismatchGuard(node)) {
+ Logging.instance.i(
+ "$runtimeType refresh() await _torNodeMismatchGuard(node)",
+ );
+ if (await _torNodeMismatchGuard(node)) {
throw Exception("TOR – clearnet mismatch");
}
- // this acquire should be almost instant due to above check.
- // Slight possibility of race but should be irrelevant
- await refreshMutex.acquire();
-
- csMonero.startSyncing(wallet!);
+ Logging.instance.t(
+ "$runtimeType refresh() it csMonero.startSyncing(wallet!);",
+ );
+ await csMonero.startSyncing(wallet!);
+ Logging.instance.t(
+ "$runtimeType refresh() _setSyncStatus(lib_monero_compat.StartingSyncStatus());",
+ );
_setSyncStatus(lib_monero_compat.StartingSyncStatus());
+ Logging.instance.t("$runtimeType refresh() await updateTransactions();");
await updateTransactions();
+ Logging.instance.t("$runtimeType refresh() await updateBalance();");
await updateBalance();
+ Logging.instance.t(
+ "$runtimeType refresh() await checkReceivingAddressForTransactions();",
+ );
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
await checkReceivingAddressForTransactions();
}
+ Logging.instance.t(
+ "$runtimeType refresh() refreshMutex.isLocked=${refreshMutex.isLocked} pre release.",
+ );
if (refreshMutex.isLocked) {
refreshMutex.release();
+ Logging.instance.t(
+ "$runtimeType refresh() refreshMutex.isLocked manually released.",
+ );
}
+ Logging.instance.t(
+ "$runtimeType refresh() wallet != null && await csMonero.isSynced(wallet!)",
+ );
final synced = wallet != null && await csMonero.isSynced(wallet!);
+ Logging.instance.t(
+ "$runtimeType refresh() wallet != null && await csMonero.isSynced(wallet!) == $synced",
+ );
if (synced) {
+ Logging.instance.t(
+ "$runtimeType refresh() _setSyncStatus(lib_monero_compat.SyncedSyncStatus());",
+ );
_setSyncStatus(lib_monero_compat.SyncedSyncStatus());
}
}
@@ -1163,7 +1190,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
? 0
: currentReceiving.derivationIndex + 1;
- final newReceivingAddress = addressFor(index: newReceivingIndex);
+ final newReceivingAddress = await addressFor(index: newReceivingIndex);
// Add that new receiving address
await mainDB.putAddress(newReceivingAddress);
@@ -1217,7 +1244,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
final newReceivingIndex = curIndex + 1;
// Use new index to derive a new receiving address
- final newReceivingAddress = addressFor(index: newReceivingIndex);
+ final newReceivingAddress = await addressFor(index: newReceivingIndex);
final existing = await mainDB
.getAddresses(walletId)
@@ -1421,7 +1448,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
}
@override
- int getRefreshFromBlockHeight() => wallet == null
+ Future<int> getRefreshFromBlockHeight() => wallet == null
? throw Exception(
"Cannot getRefreshFromBlockHeight when wallet is not open",
)
@@ -1471,7 +1498,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
}
@override
- String internalGetAddress({
+ Future<String> internalGetAddress({
required int accountIndex,
required int addressIndex,
}) {
@@ -1501,7 +1528,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
}
@override
- BigInt? internalGetUnlockedBalance({int accountIndex = 0}) {
+ Future<BigInt> internalGetUnlockedBalance({int accountIndex = 0}) {
if (wallet == null) {
throw Exception("Cannot internalCommitTx when wallet is not open");
}
@@ -1563,7 +1590,7 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
walletId: walletId,
derivationIndex: 0,
derivationPath: null,
- value: csMonero.getAddress(this.wallet!),
+ value: await csMonero.getAddress(this.wallet!),
publicKey: [],
type: AddressType.cryptonote,
subType: AddressSubType.receiving,
@@ -1578,11 +1605,11 @@ abstract class LibMoneroWallet<T extends CryptonoteCurrency>
await updateNode();
_setListener();
- unawaited(csMonero.rescanBlockchain(this.wallet!));
- csMonero.startSyncing(this.wallet!);
+ await csMonero.rescanBlockchain(this.wallet!);
+ await csMonero.startSyncing(this.wallet!);
// await save();
- csMonero.startListeners(this.wallet!);
+ await csMonero.startListeners(this.wallet!);
csMonero.startAutoSaving(this.wallet!);
} catch (e, s) {
Logging.instance.e(
diff --git a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
index cd81916..4517f14 100644
--- a/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_salvium_wallet.dart
@@ -164,7 +164,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
bool walletExists(String path);
@override
- String getTxKeyFor({required String txid}) {
+ Future<String> getTxKeyFor({required String txid}) async {
if (wallet == null) {
throw Exception("Cannot get tx key in uninitialized libSalviumWallet");
}
@@ -1414,7 +1414,7 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
}
@override
- int getRefreshFromBlockHeight() => wallet == null
+ Future<int> getRefreshFromBlockHeight() async => wallet == null
? throw Exception(
"Cannot getRefreshFromBlockHeight when wallet is not open",
)
@@ -1464,10 +1464,10 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
}
@override
- String internalGetAddress({
+ Future<String> internalGetAddress({
required int accountIndex,
required int addressIndex,
- }) {
+ }) async {
if (wallet == null) {
throw Exception("Cannot internalCommitTx when wallet is not open");
}
@@ -1494,11 +1494,11 @@ abstract class LibSalviumWallet<T extends CryptonoteCurrency>
}
@override
- BigInt? internalGetUnlockedBalance({int accountIndex = 0}) {
+ Future<BigInt> internalGetUnlockedBalance({int accountIndex = 0}) async {
if (wallet == null) {
throw Exception("Cannot internalCommitTx when wallet is not open");
}
- return csSalvium.getUnlockedBalance(wallet!, accountIndex: accountIndex);
+ return csSalvium.getUnlockedBalance(wallet!, accountIndex: accountIndex)!;
}
@override
diff --git a/lib/wallets/wallet/intermediate/lib_wownero_wallet.dart b/lib/wallets/wallet/intermediate/lib_wownero_wallet.dart
index 0dcdaec..5ebd219 100644
--- a/lib/wallets/wallet/intermediate/lib_wownero_wallet.dart
+++ b/lib/wallets/wallet/intermediate/lib_wownero_wallet.dart
@@ -170,7 +170,7 @@ abstract class LibWowneroWallet<T extends CryptonoteCurrency>
bool walletExists(String path);
@override
- String getTxKeyFor({required String txid}) {
+ Future<String> getTxKeyFor({required String txid}) async {
if (wallet == null) {
throw Exception("Cannot get tx key in uninitialized LibWowneroWallet");
}
@@ -1426,7 +1426,7 @@ abstract class LibWowneroWallet<T extends CryptonoteCurrency>
}
@override
- int getRefreshFromBlockHeight() => wallet == null
+ Future<int> getRefreshFromBlockHeight() async => wallet == null
? throw Exception(
"Cannot getRefreshFromBlockHeight when wallet is not open",
)
@@ -1476,10 +1476,10 @@ abstract class LibWowneroWallet<T extends CryptonoteCurrency>
}
@override
- String internalGetAddress({
+ Future<String> internalGetAddress({
required int accountIndex,
required int addressIndex,
- }) {
+ }) async {
if (wallet == null) {
throw Exception("Cannot internalCommitTx when wallet is not open");
}
@@ -1506,11 +1506,11 @@ abstract class LibWowneroWallet<T extends CryptonoteCurrency>
}
@override
- BigInt? internalGetUnlockedBalance({int accountIndex = 0}) {
+ Future<BigInt> internalGetUnlockedBalance({int accountIndex = 0}) async {
if (wallet == null) {
throw Exception("Cannot internalCommitTx when wallet is not open");
}
- return csWownero.getUnlockedBalance(wallet!, accountIndex: accountIndex);
+ return csWownero.getUnlockedBalance(wallet!, accountIndex: accountIndex)!;
}
@override
diff --git a/lib/widgets/tx_key_widget.dart b/lib/widgets/tx_key_widget.dart
index 8222fdb..6872bbd 100644
--- a/lib/widgets/tx_key_widget.dart
+++ b/lib/widgets/tx_key_widget.dart
@@ -53,7 +53,7 @@ class _TxKeyWidgetState extends ConsumerState<TxKeyWidget> {
final wallet =
ref.read(pWallets).getWallet(widget.walletId) as CryptonoteWallet;
- _private = wallet.getTxKeyFor(txid: widget.txid);
+ _private = await wallet.getTxKeyFor(txid: widget.txid);
if (_private!.isEmpty) {
_private = "Unavailable";
}
diff --git a/lib/wl_gen/interfaces/cs_monero_interface.dart b/lib/wl_gen/interfaces/cs_monero_interface.dart
index 4541c95..f9f30d5 100644
--- a/lib/wl_gen/interfaces/cs_monero_interface.dart
+++ b/lib/wl_gen/interfaces/cs_monero_interface.dart
@@ -27,7 +27,7 @@ abstract class CsMoneroInterface {
required String password,
});
- String getAddress(
+ Future<String> getAddress(
WrappedWallet wallet, {
int accountIndex = 0,
int addressIndex = 0,
@@ -58,32 +58,32 @@ abstract class CsMoneroInterface {
int height = 0,
});
- String getTxKey(WrappedWallet wallet, String txid);
+ Future<String> getTxKey(WrappedWallet wallet, String txid);
Future<void> save(WrappedWallet wallet);
- String getPublicViewKey(WrappedWallet wallet);
- String getPrivateViewKey(WrappedWallet wallet);
- String getPublicSpendKey(WrappedWallet wallet);
- String getPrivateSpendKey(WrappedWallet wallet);
+ Future<String> getPublicViewKey(WrappedWallet wallet);
+ Future<String> getPrivateViewKey(WrappedWallet wallet);
+ Future<String> getPublicSpendKey(WrappedWallet wallet);
+ Future<String> getPrivateSpendKey(WrappedWallet wallet);
Future<bool> isSynced(WrappedWallet wallet);
- void startSyncing(WrappedWallet wallet);
- void stopSyncing(WrappedWallet wallet);
+ Future<void> startSyncing(WrappedWallet wallet);
+ Future<void> stopSyncing(WrappedWallet wallet);
void startAutoSaving(WrappedWallet wallet);
void stopAutoSaving(WrappedWallet wallet);
bool hasListeners(WrappedWallet wallet);
void addListener(WrappedWallet wallet, CsWalletListener listener);
- void startListeners(WrappedWallet wallet);
- void stopListeners(WrappedWallet wallet);
+ Future<void> startListeners(WrappedWallet wallet);
+ Future<void> stopListeners(WrappedWallet wallet);
- Future<bool> rescanBlockchain(WrappedWallet wallet);
+ Future<void> rescanBlockchain(WrappedWallet wallet);
Future<bool> isConnectedToDaemon(WrappedWallet wallet);
- int getRefreshFromBlockHeight(WrappedWallet wallet);
- void setRefreshFromBlockHeight(WrappedWallet wallet, int height);
+ Future<int> getRefreshFromBlockHeight(WrappedWallet wallet);
+ Future<void> setRefreshFromBlockHeight(WrappedWallet wallet, int height);
Future<void> connect(
WrappedWallet wallet, {
@@ -101,8 +101,11 @@ abstract class CsMoneroInterface {
bool refresh = false,
});
- BigInt? getBalance(WrappedWallet wallet, {int accountIndex = 0});
- BigInt? getUnlockedBalance(WrappedWallet wallet, {int accountIndex = 0});
+ Future<BigInt> getBalance(WrappedWallet wallet, {int accountIndex = 0});
+ Future<BigInt> getUnlockedBalance(
+ WrappedWallet wallet, {
+ int accountIndex = 0,
+ });
Future<List<CsTransaction>> getAllTxs(
WrappedWallet wallet, {
@@ -154,7 +157,7 @@ abstract class CsMoneroInterface {
bool validateAddress(String address, int network);
- String getSeed(WrappedWallet wallet);
+ Future<String> getSeed(WrappedWallet wallet);
Future<void> close(WrappedWallet wallet, {bool save = false});
}
diff --git a/pubspec.lock b/pubspec.lock
index 9fd1e98..9597031 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -439,74 +439,74 @@ packages:
dependency: "direct main"
description:
name: cs_monero
- sha256: "2bc89f862b4a4bc5312999a35d266db035d2e8760736662e148f07d2ab36e43d"
+ sha256: "6370649167f46ead5cffac46d164dd749cb4b07989e9f0e08fe081c74c4e6b61"
url: "https://pub.dev"
source: hosted
- version: "2.0.0"
+ version: "3.1.0"
cs_monero_flutter_libs:
dependency: "direct main"
description:
name: cs_monero_flutter_libs
- sha256: "759272ed87908572c0b7bb47edae2c49744bb981a632fc6156526a33a4d17f74"
+ sha256: "459542acbfc01ee6f30446c656cba670c7f1b90e52b7921a4aa0dcbc275b9eca"
url: "https://pub.dev"
source: hosted
- version: "2.0.0"
+ version: "2.0.1"
cs_monero_flutter_libs_android:
dependency: transitive
description:
name: cs_monero_flutter_libs_android
- sha256: eafe9b72370f92135e94ba8f25acf8776300d0442e0fdedc31c0ce715aa80daa
+ sha256: f0785f34bcf9872347823303f09409b1238b2ed7e535b9722633b0022d6188f5
url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.1.2"
cs_monero_flutter_libs_android_arm64_v8a:
dependency: transitive
description:
name: cs_monero_flutter_libs_android_arm64_v8a
- sha256: d754cc1effdefdf8d1bf016fe69288f5a9cdde83269b5c153d4ac191dd18fa30
+ sha256: "0b836dff1ead29229535a3228c7c57517127bea8b19c4c2d9bdae2770526f8ca"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_android_armeabi_v7a:
dependency: transitive
description:
name: cs_monero_flutter_libs_android_armeabi_v7a
- sha256: "21c00dcbd506a737750dd228a36395b3f6a9b142ecc9032ee60a746fca80d731"
+ sha256: "7955bbf91e1c3ec66e352a33e36edbab509808db6db6debfbea06f1ad2396205"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_android_x86_64:
dependency: transitive
description:
name: cs_monero_flutter_libs_android_x86_64
- sha256: d13fb62be52f44d0fa3aedeaabc33809284c4452728f4dfb9b4147038cae6fa2
+ sha256: f51f95aa4a09be497befe020621b0d62d749d900f4dfd585fe60b7c9692010a8
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_ios:
dependency: transitive
description:
name: cs_monero_flutter_libs_ios
- sha256: "04673ca9a46f77ad0493f9dcd1cfe8e93ceacb78a2c46a0e733d0c6925231552"
+ sha256: dbc149c0787a7702a3842b4974b9bc30bad654daaa57886f874823c29c390ba7
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_linux:
dependency: transitive
description:
name: cs_monero_flutter_libs_linux
- sha256: "4390b77d529cae362ed14ba4b5906c4e0f9bb4da5f3c8a0963f27c3e9c3197ff"
+ sha256: "5b8bbc68a7d2bb39efdea4834097ada1aa99fd7e0b1641943c4e06c89f96616e"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_macos:
dependency: transitive
description:
name: cs_monero_flutter_libs_macos
- sha256: "1cf88d9d04f327b577d245b579b15e75ad4c43ab7f1996bd369ddc0fa84aec53"
+ sha256: ee02b78184b4168bc2bdb49c7ef71cc5019ffbed54c0feabcebdbc4cae5819ee
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_monero_flutter_libs_platform_interface:
dependency: transitive
description:
@@ -519,10 +519,10 @@ packages:
dependency: transitive
description:
name: cs_monero_flutter_libs_windows
- sha256: "3be0fe15bdcb6d1619b70d8dde52eaa02fdd028bd3cc522a4c472742f72a09a7"
+ sha256: "9db54230f83ec07e2dce39b6b90711616ba4ab1144c7f68e4b1c13b161a18cd3"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.3.0"
cs_salvium:
dependency: "direct main"
description:
diff --git a/scripts/app_config/templates/pubspec.template.yaml b/scripts/app_config/templates/pubspec.template.yaml
index 2878e1f..48ca00e 100644
--- a/scripts/app_config/templates/pubspec.template.yaml
+++ b/scripts/app_config/templates/pubspec.template.yaml
@@ -61,8 +61,8 @@ dependencies:
# %%END_ENABLE_TOR%%
# %%ENABLE_XMR%%
-# cs_monero: 2.0.0
-# cs_monero_flutter_libs: 2.0.0
+# cs_monero: 3.1.0
+# cs_monero_flutter_libs: 2.0.1
# %%END_ENABLE_XMR%%
# %%ENABLE_WOW%%
diff --git a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
index 0d4d728..b957ad3 100644
--- a/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
+++ b/tool/wl_templates/XMR_cs_monero_interface_impl.template.dart
@@ -66,9 +66,16 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
String walletId, {
required String path,
required String password,
+ int network = 0, // default to mainnet
}) async {
return WrappedWallet(
- await lib_monero.MoneroWallet.loadWallet(path: path, password: password),
+ await lib_monero.MoneroWallet.loadWallet(
+ path: path,
+ password: password,
+ networkType: lib_monero.Network.values.firstWhere(
+ (e) => e.value == network,
+ ),
+ ),
);
}
@@ -82,14 +89,14 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
int getTxPriorityNormal() => lib_monero.TransactionPriority.normal.value;
@override
- String getAddress(
+ Future<String> getAddress(
WrappedWallet wallet, {
int accountIndex = 0,
int addressIndex = 0,
- }) => wallet
- .get<lib_monero.Wallet>()
- .getAddress(accountIndex: accountIndex, addressIndex: addressIndex)
- .value;
+ }) async => (await wallet.get<lib_monero.Wallet>().getAddress(
+ accountIndex: accountIndex,
+ addressIndex: addressIndex,
+ )).value;
@override
Future<WrappedWallet> getCreatedWallet({
@@ -97,6 +104,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
required String password,
required int wordCount,
required String seedOffset,
+ int network = 0, // default to mainnet
}) async {
final type = switch (wordCount) {
16 => lib_monero.MoneroSeedType.sixteen,
@@ -109,6 +117,9 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
password: password,
seedType: type,
seedOffset: seedOffset,
+ networkType: lib_monero.Network.values.firstWhere(
+ (e) => e.value == network,
+ ),
);
return WrappedWallet(wallet);
@@ -121,6 +132,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
required String password,
required String mnemonic,
required String seedOffset,
+ int network = 0, // default to mainnet
int height = 0,
}) async {
return WrappedWallet(
@@ -130,6 +142,9 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
seed: mnemonic,
restoreHeight: height,
seedOffset: seedOffset,
+ networkType: lib_monero.Network.values.firstWhere(
+ (e) => e.value == network,
+ ),
),
);
}
@@ -141,6 +156,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
required String password,
required String address,
required String privateViewKey,
+ int network = 0, // default to mainnet
int height = 0,
}) async {
return WrappedWallet(
@@ -150,12 +166,15 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
address: address,
viewKey: privateViewKey,
restoreHeight: height,
+ networkType: lib_monero.Network.values.firstWhere(
+ (e) => e.value == network,
+ ),
),
);
}
@override
- String getTxKey(WrappedWallet wallet, String txid) =>
+ Future<String> getTxKey(WrappedWallet wallet, String txid) =>
wallet.get<lib_monero.Wallet>().getTxKey(txid);
@override
@@ -163,19 +182,19 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
wallet.get<lib_monero.Wallet>().save();
@override
- String getPublicViewKey(WrappedWallet wallet) =>
+ Future<String> getPublicViewKey(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getPublicViewKey();
@override
- String getPrivateViewKey(WrappedWallet wallet) =>
+ Future<String> getPrivateViewKey(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getPrivateViewKey();
@override
- String getPublicSpendKey(WrappedWallet wallet) =>
+ Future<String> getPublicSpendKey(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getPublicSpendKey();
@override
- String getPrivateSpendKey(WrappedWallet wallet) =>
+ Future<String> getPrivateSpendKey(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getPrivateSpendKey();
@override
@@ -183,11 +202,11 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
wallet.get<lib_monero.Wallet>().isSynced();
@override
- void startSyncing(WrappedWallet wallet) =>
+ Future<void> startSyncing(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().startSyncing();
@override
- void stopSyncing(WrappedWallet wallet) =>
+ Future<void> stopSyncing(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().stopSyncing();
@override
@@ -214,23 +233,23 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
);
@override
- void startListeners(WrappedWallet wallet) =>
+ Future<void> startListeners(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().startListeners();
@override
- void stopListeners(WrappedWallet wallet) =>
+ Future<void> stopListeners(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().stopListeners();
@override
- int getRefreshFromBlockHeight(WrappedWallet wallet) =>
+ Future<int> getRefreshFromBlockHeight(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getRefreshFromBlockHeight();
@override
- void setRefreshFromBlockHeight(WrappedWallet wallet, int height) =>
+ Future<void> setRefreshFromBlockHeight(WrappedWallet wallet, int height) =>
wallet.get<lib_monero.Wallet>().setRefreshFromBlockHeight(height);
@override
- Future<bool> rescanBlockchain(WrappedWallet wallet) =>
+ Future<void> rescanBlockchain(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().rescanBlockchain();
@override
@@ -266,14 +285,16 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
}) => wallet.get<lib_monero.Wallet>().getAllTxids(refresh: refresh);
@override
- BigInt? getBalance(WrappedWallet wallet, {int accountIndex = 0}) =>
+ Future<BigInt> getBalance(WrappedWallet wallet, {int accountIndex = 0}) =>
wallet.get<lib_monero.Wallet>().getBalance(accountIndex: accountIndex);
@override
- BigInt? getUnlockedBalance(WrappedWallet wallet, {int accountIndex = 0}) =>
- wallet.get<lib_monero.Wallet>().getUnlockedBalance(
- accountIndex: accountIndex,
- );
+ Future<BigInt> getUnlockedBalance(
+ WrappedWallet wallet, {
+ int accountIndex = 0,
+ }) => wallet.get<lib_monero.Wallet>().getUnlockedBalance(
+ accountIndex: accountIndex,
+ );
@override
Future<List<CsTransaction>> getAllTxs(
@@ -496,7 +517,7 @@ class _CsMoneroInterfaceImpl extends CsMoneroInterface {
xmr_wallet_ffi.validateAddress(address, network);
@override
- String getSeed(WrappedWallet wallet) =>
+ Future<String> getSeed(WrappedWallet wallet) =>
wallet.get<lib_monero.Wallet>().getSeed();
@override
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.