Skip to content

Conversation

@rafael-xmr
Copy link

@rafael-xmr rafael-xmr commented Nov 6, 2025

Note

Introduces complete subaddress functionality across the stack.

  • FFI/C++ (monero-sys): Patch WalletImpl to expose balancePerSubaddress/unlockedBalancePerSubaddress; add bridge APIs for subaddress indices/amounts, labels, creation; new SubaddressSummary type and received_address on transactions.
  • Core/Tauri: New commands get_monero_subaddresses, create_monero_subaddress, set_monero_subaddress_label; emit MoneroWalletUpdate::SubaddressesUpdate and wire through RPC; harness adds helpers (address_at, create_subaddress, summaries, per-subaddress balances).
  • GUI: New SubaddressesModal to list/copy addresses, create, and label; show subaddress chip on incoming transactions; Redux state/actions/hooks for subaddresses; load/update on startup and via background events; minor UI utilities (truncate, onComplete).
  • Tests: Add subaddresses.rs covering creation, sending, and per-subaddress balance verification.
  • Docs: Update changelog to note GUI subaddress support.

Written by Cursor Bugbot for commit 37e73f5. This will update automatically on new commits. Configure here.

@binarybaron
Copy link

very cool! I'll review this ASAP

@binarybaron
Copy link

Compilations fails with this error on my machine:

/Users/_/Development/eigenwallet/wallet/target/debug/build/monero-sys-c81930dca039ffc4/out/cxxbridge/sources/monero-sys/src/bridge.rs.cc:1031:10: error: cannot initialize a variable of type 'bool (*)(::Monero::Wallet &, ::std::uint32_t, const ::std::string &)' (aka 'bool (*)(Monero::Wallet &, unsigned int, const basic_string<char> &)') with an lvalue of type 'bool (Wallet *, uint32_t, const std::string &)' (aka 'bool (Monero::Wallet *, unsigned int, const basic_string<char> &)'): type mismatch at 1st parameter ('::Monero::Wallet &' (aka 'Monero::Wallet &') vs 'Wallet *')
 1031 |   bool (*addSubaddress$)(::Monero::Wallet &, ::std::uint32_t, ::std::string const &) = ::Monero::addSubaddress;
      |          ^                                                                             ~~~~~~~~~~~~~~~~~~~~~~~
/Users/_/Development/eigenwallet/wallet/target/debug/build/monero-sys-c81930dca039ffc4/out/cxxbridge/sources/monero-sys/src/bridge.rs.cc:1043:10: error: cannot initialize a variable of type 'bool (*)(::Monero::Wallet &, ::std::uint32_t, ::std::uint32_t, const ::std::string &)' (aka 'bool (*)(Monero::Wallet &, unsigned int, unsigned int, const basic_string<char> &)') with an lvalue of type 'bool (Wallet *, uint32_t, uint32_t, const std::string &)' (aka 'bool (Monero::Wallet *, unsigned int, unsigned int, const basic_string<char> &)'): type mismatch at 1st parameter ('::Monero::Wallet &' (aka 'Monero::Wallet &') vs 'Wallet *')
 1043 |   bool (*setSubaddressLabel$)(::Monero::Wallet &, ::std::uint32_t, ::std::uint32_t, ::std::string const &) = ::Monero::setSubaddressLabel;
      |          ^                                                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 warnings and 2 errors generated.

@binarybaron
Copy link

bugbot run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no bugs!


Copy link

@binarybaron binarybaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some more comments

@binarybaron
Copy link

The changes look good! Please also write a test for the functionality that was added to monero-sys. See monero-tests/tests/transaction_keys.rs for ex example.

Copy link

@binarybaron binarybaron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks promising! I'm impressed for quickly you got a hold of the codebase. This is definitely going into the right direction.

I'm reviewing this fairly strictly / in-depth because my hope is that I can pass onto you some of the unofficial and undocumented guidelines / best practices we have for the project.

@binarybaron
Copy link

The monero-sys crate fails with this error on my machine:

warning: monero-sys@0.1.0: monero/src/cryptonote_basic/cryptonote_boost_serialization.h:39:10: fatal error: 'boost/archive/portable_binary_iarchive.hpp' file not found
warning: monero-sys@0.1.0:    39 | #include <boost/archive/portable_binary_iarchive.hpp>
warning: monero-sys@0.1.0:       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
warning: monero-sys@0.1.0: 41 warnings and 1 error generated.
error: failed to run custom build command for `monero-sys v0.1.0 (/Users/mohan/Development/eigenwallet/wallet/monero-sys)`

@binarybaron
Copy link

The monero-sys crate fails with this error on my machine:

warning: monero-sys@0.1.0: monero/src/cryptonote_basic/cryptonote_boost_serialization.h:39:10: fatal error: 'boost/archive/portable_binary_iarchive.hpp' file not found
warning: monero-sys@0.1.0:    39 | #include <boost/archive/portable_binary_iarchive.hpp>
warning: monero-sys@0.1.0:       |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
warning: monero-sys@0.1.0: 41 warnings and 1 error generated.
error: failed to run custom build command for `monero-sys v0.1.0 (/Users/mohan/Development/eigenwallet/wallet/monero-sys)`

Does this compile on your machine? @rafael-xmr

Copy link

@Einliterflasche Einliterflasche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am getting this error:

[monero-sys 0.1.0] cargo:warning=In file included from /Users/me/code/xmr-btc-swap/target/debug/build/monero-sys-b1c8e6bd6f43bf85/out/cxxbridge/sources/monero-sys/src/bridge.rs.cc:2:
[monero-sys 0.1.0] cargo:warning=src/bridge.h:686:48: error: functions that differ only in their return type cannot be overloaded
[monero-sys 0.1.0] cargo:warning=  686 |     inline std::unique_ptr<std::vector<TxKey>> pendingTransactionTxKeys(const PendingTransaction &tx, const std::string &tx_hash)
[monero-sys 0.1.0] cargo:warning=      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
[monero-sys 0.1.0] cargo:warning=src/bridge.h:384:54: note: previous definition is here
[monero-sys 0.1.0] cargo:warning=  384 |     inline std::unique_ptr<std::vector<std::string>> pendingTransactionTxKeys(const PendingTransaction &tx, const std::string &tx_hash)
[monero-sys 0.1.0] cargo:warning=      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^

It seems you added another pendingTxKeys function. But the existing one should work too. Getting rid of the new function should also remove the other two errors:

[monero-sys 0.1.0] cargo:warning=src/bridge.h:390:18: error: no matching member function for call to 'push_back'
[monero-sys 0.1.0] cargo:warning=  390 |             vec->push_back(std::move(key));
[monero-sys 0.1.0] cargo:warning=      |             ~~~~~^~~~~~~~~
[monero-sys 0.1.0] cargo:warning=/Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__vector/vector.h:452:60: note: candidate function not viable: no known conversion from '__libcpp_remove_reference_t<tuple<string, string, string> &>' (aka 'std::tuple<std::string, std::string, std::string>') to 'const value_type' (aka 'const std::string') for 1st argument
[monero-sys 0.1.0] cargo:warning=  452 |   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void push_back(const_reference __x) { emplace_back(__x); }
[monero-sys 0.1.0] cargo:warning=      |                                                            ^         ~~~~~~~~~~~~~~~~~~~

C++ compilation errors in monero-sys are a bit hard to debug, because there is so much output. The gist is: compile with -vv (or -- -vv for tauri), scroll up until you see the colored warning: monero-sys@0.1.0: Applying patch to file: src/wallet/wallet2.h lines. Above that, when the colors stop are the real c++ compilation errors/warnings.

@binarybaron
Copy link

Ok cool, compiles on my machine. The Rust code looks fairly good to me but @Einliterflasche should give this another review since this is changing some things about monero-sys which is his area of expertise.

I really like how snappy it feels. It's good that the addresses are cached and I think it's good that you made a modal for the subaddresses.

Regarding the layout, I think we can still make some improvements:

I'm unsure if this is the best place to display the subaddress. We should also consider not displaying it if its just the primary address.

In its current form it is fairly wide and causes the column to be quite wide as well which leaves less space for the timestamp and the other things (which are arguably more important to the user).

Screenshot 2025-11-20 at 17 40 44

We may want to go into more of the direction of Feather Wallet here and use an actual table. I'm not a huge fan when clicking things changes the layout (when you click the "Edit label" button). The label could just be a TextField (like in Feather wallet). It'd have the current label pre-filled and editing would change it. You could do this by adding some logic that if the user did not change the text in the field for more than 5s, we save the label in the wallet. I'm also unsure if we even need to display the "tx count" for each subaddress.

Screenshot 2025-11-20 at 17 40 38 Screenshot 2025-11-20 at 17 50 55 Screenshot 2025-11-20 at 17 48 25

@Einliterflasche
Copy link

How about something like this for the subaddresses?

diff --git a/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx b/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx
index f6aef9d3..20ccb2b8 100644
--- a/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx
+++ b/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx
@@ -6,7 +6,7 @@ import {
   MenuItem,
   Typography,
 } from "@mui/material";
-import { TransactionDirection, TransactionInfo } from "models/tauriModel";
+import { SubaddressSummary, TransactionDirection, TransactionInfo } from "models/tauriModel";
 import {
   CallReceived as IncomingIcon,
   MoreVert as MoreVertIcon,
@@ -22,6 +22,8 @@ import { isTestnet } from "store/config";
 import { open } from "@tauri-apps/plugin-shell";
 import dayjs from "dayjs";
 import { useState } from "react";
+import { useAppSelector } from "store/hooks";
+import _ from "lodash";
 
 interface TransactionItemProps {
   transaction: TransactionInfo;
@@ -40,6 +42,21 @@ export default function TransactionItem({ transaction }: TransactionItemProps) {
   const [menuAnchorEl, setMenuAnchorEl] = useState<null | HTMLElement>(null);
   const menuOpen = Boolean(menuAnchorEl);
 
+  // Show the subaddress label if we have one, else the truncated address
+  const subaddresses: SubaddressSummary[] = useAppSelector((s) => s.wallet.state.subaddresses);
+  const subaddress: SubaddressSummary | undefined = subaddresses.find((s) => s.address === transaction.received_address);
+
+  let addressLabel: string | null = null;
+  if (isIncoming && transaction.received_address) {
+
+    if (subaddress) {
+      addressLabel = subaddress.label;
+    } else {
+      addressLabel = _.truncate(transaction.received_address, { length: 6 });
+    }
+  }
+
+
   return (
     <Box
       sx={{
@@ -107,11 +124,11 @@ export default function TransactionItem({ transaction }: TransactionItemProps) {
           <Typography variant="caption" sx={{ gridArea: "2 / 2" }}>
             <FiatPiconeroAmount amount={transaction.amount} />
           </Typography>
-          {isIncoming && transaction.received_address && (
+          {isIncoming && transaction.received_address && subaddress && (
             <Chip
               size="small"
               variant="outlined"
-              label={transaction.received_address}
+              label={<>{`Address #${subaddress.address_index}`}<i>{`"${addressLabel}"`}</i></>}
               sx={{ gridArea: "3 / 2", maxWidth: 360 }}
             />
           )}
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 7225cf35..ef7da39c 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -7,16 +7,16 @@ use swap::cli::{
         request::{
             BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ChangeMoneroNodeArgs,
             CheckElectrumNodeArgs, CheckElectrumNodeResponse, CheckMoneroNodeArgs,
-            CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, DfxAuthenticateResponse,
-            ExportBitcoinWalletArgs, GetBitcoinAddressArgs, GetCurrentSwapArgs, GetDataDirArgs,
-            GetHistoryArgs, GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs,
-            GetMoneroHistoryArgs, GetMoneroMainAddressArgs, GetMoneroSeedArgs,
-            GetMoneroSubaddressesArgs, GetMoneroSyncProgressArgs, GetPendingApprovalsResponse,
-            GetRestoreHeightArgs, GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs,
-            MoneroRecoveryArgs, RedactArgs, RejectApprovalArgs, RejectApprovalResponse,
-            ResolveApprovalArgs, ResumeSwapArgs, SendMoneroArgs, SetMoneroSubaddressLabelArgs,
-            SetMoneroWalletPasswordArgs, SetRestoreHeightArgs, SuspendCurrentSwapArgs,
-            WithdrawBtcArgs,
+            CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, CreateMoneroSubaddressArgs,
+            DfxAuthenticateResponse, ExportBitcoinWalletArgs, GetBitcoinAddressArgs,
+            GetCurrentSwapArgs, GetDataDirArgs, GetHistoryArgs, GetLogsArgs,
+            GetMoneroAddressesArgs, GetMoneroBalanceArgs, GetMoneroHistoryArgs,
+            GetMoneroMainAddressArgs, GetMoneroSeedArgs, GetMoneroSubaddressesArgs,
+            GetMoneroSyncProgressArgs, GetPendingApprovalsResponse, GetRestoreHeightArgs,
+            GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs,
+            RejectApprovalArgs, RejectApprovalResponse, ResolveApprovalArgs, ResumeSwapArgs,
+            SendMoneroArgs, SetMoneroSubaddressLabelArgs, SetMoneroWalletPasswordArgs,
+            SetRestoreHeightArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs,
         },
         tauri_bindings::{ContextStatus, TauriSettings},
         ContextBuilder,

@binarybaron
Copy link

I apologize that this is taking so long to be merged... we were a bit overwhelmed with the p2p improvements and protocol changes. Should get better fairly soon.

I think the UI is still a bit too cluttered. The add address button doesn't need a label field in my opinion and can be integrated into the table itself in some way. I don't think most users think about subaddresses in the way of "creating them". You usually expect them to just be present and to be able to give them labels. Similiar to how Feather Wallet does it.

@binarybaron
Copy link

cargo:debug=[make depends] Undefined symbols for architecture arm64:
  cargo:debug=[make depends]   "std::__1::__hash_memory(void const*, unsigned long)", referenced from:
  cargo:debug=[make depends]       std::__1::__hash_const_iterator<std::__1::__hash_node<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, void*>*> std::__1::__hash_table<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>>>::find<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) const in regexp-2384b4.o
  cargo:debug=[make depends]       std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, void*>*>, bool> std::__1::__hash_table<std::__1::__hash_value_type<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const, std::__1::unique_ptr<b2::regex::regex_prog, b2::jam::unique_jptr_deleter<b2::regex::regex_prog>>>>>::__emplace_unique_key_args<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&&>, std::__1::tuple<>>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&&>&&, std::__1::tuple<>&&) in regexp-2384b4.o
  cargo:debug=[make depends] ld: symbol(s) not found for architecture arm64
  cargo:debug=[make depends] clang++: error: linker command failed with exit code 1 (use -v to see invocation)
  cargo:debug=[make depends]
  cargo:debug=[make depends] Failed to build B2 build engine
  cargo:debug=[make depends] make[1]: *** [/Users/_/Development/eigenwallet/wallet/target/debug/monero-depends/aarch64-apple-darwin/work/build/aarch64-apple-darwin/boost/1_85_0-9b83e02d56f/./.stamp_configured] Error 1
  cargo:debug=[make depends] make: *** [/Users/_/Development/eigenwallet/wallet/target/debug/monero-depends/aarch64-apple-darwin/built/aarch64-apple-darwin/boost/.stamp_cached_or_prebuilt-9b83e02d56f] Error 2

Compilation fails on my machine. @Einliterflasche does it compile for you? Not sure if this is an issue with my machine.

@binarybaron
Copy link

Got it compiling. I can merge this once the conflicts are fixed!

let indices =
ffi::walletBalancePerSubaddrIndices(self.inner.pinned(), account_index, false);
let amounts =
ffi::walletBalancePerSubaddrAmounts(self.inner.pinned(), account_index, false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Race condition between separate FFI calls for balance data

The code makes two sequential FFI calls to fetch subaddress balance data: walletBalancePerSubaddrIndices followed by walletBalancePerSubaddrAmounts. Each C++ function independently calls impl->balancePerSubaddress(), meaning the underlying wallet state is queried twice. If the wallet's background sync thread updates the balance cache between these calls, the returned indices and amounts could become misaligned, causing incorrect balance values to be associated with wrong subaddress indices. The same issue exists with walletUnlockedBalancePerSubaddrIndices and walletUnlockedBalancePerSubaddrAmounts. A single FFI function returning both indices and amounts from one balancePerSubaddress() call would prevent this inconsistency.

Additional Locations (2)

Fix in Cursor Fix in Web

received[address_index] = received[address_index].saturating_add(amount);
tx_count[address_index] = tx_count[address_index].saturating_add(1);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transaction amount double-counted across multiple subaddress indices

When a single transaction involves multiple subaddress indices (as returned by transactionInfoSubaddrIndices), the code iterates over each index and adds the full transaction amount to each subaddress's received total. Since tx_info.amount() returns the total transaction amount (not the per-subaddress portion), this causes the same amount to be counted multiple times. For example, a 10 XMR transaction split across subaddresses 1 and 2 would incorrectly add 10 XMR to both, showing 20 XMR total received instead of 10 XMR. The tx_count field is similarly inflated by counting the same transaction once per subaddress index.

Fix in Cursor Fix in Web

@binarybaron binarybaron merged commit ac37576 into eigenwallet:master Dec 28, 2025
1 check passed
@binarybaron
Copy link

thanks for your work! @rafael-xmr, I'm very happy that we finally have this feature!

export default function SubaddressesModal({ open, onClose }: Props) {
const subaddresses = useMoneroSubaddresses();
const [isCreating, setIsCreating] = useState(false);
const [labelEdits, setLabelEdits] = useState<Record<number, string>>({});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale label edits override fresh backend data

The labelEdits state is never cleared after the modal closes. When the user reopens the modal, stale values in labelEdits take precedence over fresh backend data because labelDraft uses labelEdits[s.address_index] ?? subaddressesByIndex[s.address_index]?.label. If the backend label changed (via error recovery, another session, or any other means), the UI will display the outdated locally-cached edit instead of the current value. The state should be reset in handleClose or when the modal reopens.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants