What changed, and why it matters
This commit fixes a bug where Monero swap transactions were not appearing in the wallet's history. The root cause was that several subaddress-list updates were being started but not waited for, so the UI could read stale or incomplete address data before the update finished. The patch adds 'await' to those calls so the wallet waits for the update to complete before continuing. There is no direct security exploit here; it is a reliability/functional bug fix.
Treat as a routine functional bug fix. No immediate security response is required. Users experiencing missing Monero swap history should update to the patched version (6.1.2). Developers should audit other async wallet state updates for similar missing-await patterns.
Security signals we found
Race condition in asynchronous wallet state update
Missing await on async subaddress list operations
UI/history inconsistency due to incomplete state refresh
Version bump suggests release fix
Evidence from the diff
The change converts a number of synchronous-looking subaddress update methods to async and awaits them. Previously, calls like subaddressList.update(), updateSubaddressList(), updateUnusedSubaddress(), and getSubaddressList(…).update() were fire-and-forget inside async methods. Because these methods internally perform FFI/native calls and update observable state, not awaiting them created race conditions: subsequent code could run before the subaddress list was refreshed, causing swap-related transactions to be missing from history. The patch also bumps app version/build numbers for both Monero.com and Cake Wallet on Android and iOS, and removes an unused import (format_fixed).
Changed components
cw_monero/lib/monero_subaddress_list.dartcw_monero/lib/monero_wallet.dartcw_monero/lib/monero_wallet_addresses.dartlib/monero/cw_monero.dartlib/view_model/wallet_address_list/wallet_address_list_view_model.darttool/configure.dartInspect captured patch +24 / −25
diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart
index 779b1d30..0c5284d9 100644
--- a/cw_monero/lib/monero_subaddress_list.dart
+++ b/cw_monero/lib/monero_subaddress_list.dart
@@ -67,14 +67,14 @@ abstract class MoneroSubaddressListBase with Store {
Future<void> addSubaddress({required int accountIndex, required String label}) async {
await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label);
- update(accountIndex: accountIndex);
+ await update(accountIndex: accountIndex);
}
Future<void> setLabelSubaddress(
{required int accountIndex, required int addressIndex, required String label}) async {
await subaddress_list.setLabelForSubaddress(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
- update(accountIndex: accountIndex);
+ await update(accountIndex: accountIndex);
}
void refresh({required int accountIndex}) {
diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart
index 81771193..e315f24f 100644
--- a/cw_monero/lib/monero_wallet.dart
+++ b/cw_monero/lib/monero_wallet.dart
@@ -3,7 +3,6 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
-import 'package:cw_core/format_fixed.dart';
import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
@@ -180,7 +179,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
_autoSaveTimer = Timer.periodic(
Duration(seconds: _autoSaveInterval), (_) async => await save());
// update transaction details after restore
- walletAddresses.subaddressList.update(accountIndex: walletAddresses.account?.id??0);
+ await walletAddresses.subaddressList.update(accountIndex: walletAddresses.account?.id??0);
}
@override
@@ -504,7 +503,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await walletAddresses.updateUsedSubaddress();
if (isEnabledAutoGenerateSubaddress) {
- walletAddresses.updateUnusedSubaddress(
+ await walletAddresses.updateUnusedSubaddress(
accountIndex: walletAddresses.account?.id ?? 0,
defaultLabel: walletAddresses.account?.label ?? '');
}
@@ -951,14 +950,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
}
}
- void _updateSubAddress(bool enableAutoGenerate, {Account? account}) {
+ Future<void> _updateSubAddress(bool enableAutoGenerate, {Account? account}) async {
if (enableAutoGenerate) {
- walletAddresses.updateUnusedSubaddress(
+ await walletAddresses.updateUnusedSubaddress(
accountIndex: account?.id ?? 0,
defaultLabel: account?.label ?? '',
);
} else {
- walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
+ await walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
}
}
diff --git a/cw_monero/lib/monero_wallet_addresses.dart b/cw_monero/lib/monero_wallet_addresses.dart
index 9a7264c0..00fbf7cc 100644
--- a/cw_monero/lib/monero_wallet_addresses.dart
+++ b/cw_monero/lib/monero_wallet_addresses.dart
@@ -72,7 +72,7 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
Future<void> init() async {
accountList.update();
account = accountList.accounts.isEmpty ? Account(id: 0, label: "Primary address") : accountList.accounts.first;
- updateSubaddressList(accountIndex: account?.id ?? 0);
+ await updateSubaddressList(accountIndex: account?.id ?? 0);
await updateAddressesInBox();
}
@@ -84,8 +84,8 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
addressesMap.clear();
addressInfos.clear();
- accountList.accounts.forEach((account) {
- _subaddressList.update(accountIndex: account.id);
+ accountList.accounts.forEach((account) async {
+ await _subaddressList.update(accountIndex: account.id);
_subaddressList.subaddresses.forEach((subaddress) {
addressesMap[subaddress.address] = subaddress.label;
addressInfos[account.id] ??= [];
@@ -122,8 +122,8 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
return true;
}
- void updateSubaddressList({required int accountIndex}) {
- subaddressList.update(accountIndex: accountIndex);
+ Future<void> updateSubaddressList({required int accountIndex}) async {
+ await subaddressList.update(accountIndex: accountIndex);
address = subaddressList.subaddresses.isNotEmpty
? subaddressList.subaddresses.first.address
: getAddress();
diff --git a/lib/monero/cw_monero.dart b/lib/monero/cw_monero.dart
index 5cc0412b..08143b3b 100644
--- a/lib/monero/cw_monero.dart
+++ b/lib/monero/cw_monero.dart
@@ -77,9 +77,9 @@ class CWMoneroSubaddressList extends MoneroSubaddressList {
}
@override
- void update(Object wallet, {required int accountIndex}) {
+ Future<void> update(Object wallet, {required int accountIndex}) async {
final moneroWallet = wallet as MoneroWallet;
- moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
+ await moneroWallet.walletAddresses.subaddressList.update(accountIndex: accountIndex);
}
@override
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index 4fc00307..b4aaa2a9 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -409,7 +409,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
// update the address list:
await wallet.walletAddresses.saveAddressesInBox();
if (wallet.type == WalletType.monero) {
- monero!
+ await monero!
.getSubaddressList(wallet)
.update(wallet, accountIndex: monero!.getCurrentAccount(wallet).id);
} else if (wallet.type == WalletType.wownero) {
diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh
index 5874fff1..00bed6ce 100644
--- a/scripts/android/app_env.sh
+++ b/scripts/android/app_env.sh
@@ -14,15 +14,15 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="6.1.0"
-MONERO_COM_BUILD_NUMBER=4157
+MONERO_COM_VERSION="6.1.2"
+MONERO_COM_BUILD_NUMBER=4158
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="6.1.1"
-CAKEWALLET_BUILD_NUMBER=4409
+CAKEWALLET_VERSION="6.1.2"
+CAKEWALLET_BUILD_NUMBER=4410
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet"
diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh
index d3e4ba53..7f12ba31 100644
--- a/scripts/ios/app_env.sh
+++ b/scripts/ios/app_env.sh
@@ -12,13 +12,13 @@ TYPES=($MONERO_COM $CAKEWALLET)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="6.1.0"
-MONERO_COM_BUILD_NUMBER=162
+MONERO_COM_VERSION="6.1.2"
+MONERO_COM_BUILD_NUMBER=163
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="6.1.1"
-CAKEWALLET_BUILD_NUMBER=417
+CAKEWALLET_VERSION="6.1.2"
+CAKEWALLET_BUILD_NUMBER=418
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
diff --git a/tool/configure.dart b/tool/configure.dart
index 81def5b2..afbdae4f 100644
--- a/tool/configure.dart
+++ b/tool/configure.dart
@@ -490,7 +490,7 @@ WalletCredentials createMoneroNewWalletCredentials({required String name, requir
abstract class MoneroSubaddressList {
ObservableList<Subaddress> get subaddresses;
- void update(Object wallet, {required int accountIndex});
+ Future<void> update(Object wallet, {required int accountIndex});
void refresh(Object wallet, {required int accountIndex});
Future<List<Subaddress>> getAll(Object wallet);
Future<void> addSubaddress(Object wallet, {required int accountIndex, required String label});
Why this scored 16/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.