Skip to content

feat(gui): Monero wallet#442

Merged
binarybaron merged 133 commits intomasterfrom
feat/monero-wallet
Jul 18, 2025
Merged

feat(gui): Monero wallet#442
binarybaron merged 133 commits intomasterfrom
feat/monero-wallet

Conversation

@binarybaron
Copy link
Copy Markdown

@binarybaron binarybaron commented Jun 26, 2025

This pull request will add support for the GUI to be used as a full functioning Monero wallet, similar to Feather Wallet or the Monero Wallet GUI.

TODO

  • Display a confirmation dialog when user sends funds (amounts, fees, ...), only publish transaction after user confirms
  • Register WalletListener and send update events from Rust to Frontend
  • Use our seed.pem to restore the wallet
  • Make sure the wallet is saved periodically
  • Why is the wallet rescanning on every restart? It also seems to me that we are losing the transaction history? Maybe we are still re-setting the restore height every time?
  • Migration for old seem.pem to our new system where the seed lives inside the Monero wallet file (If seed.pem exists, create a new wallet autocreated_wallet in the eigenwallet directory. Copy all files inside the top level data dir (sqlite, monero folder, ...) into the corresponding identities subdirectory, then create a .multi_wallet_migration_done to let us skip this step later on) I think we cannot cleanly migrate. Users have to rollback if they still have funds on the old version.
  • When the user rejects the password input (clicks cancel) go back to the wallet selection step without failing the entire context init
  • Improve "Set restore height" modal layout, and allow entering date
  • [ ] Allow setting restore height when creating wallet from existing seed
  • Display the restore height somewhere
  • Either remove "Send max balance" or let this actually use the sweep functionality of wallet2 instead of the naive implementation that's currently used (fixed fee subtracted)
  • Try opening wallet with empty password ("") before requesting password
  • Call virtual bool store(const std::string &path) = 0; when we receive or send funds
  • Silent failure when amount has too much precision ("invalid args args for command send_monero: invalid type: floating point 135938976171.99998, expected u64")
  • "View logs" stopped working
  • Review everything roughly
  • Test manually

Some issues I encountered

  • Runtime panic: thread 'wallet-swap-tool-blockchain-monitoring-wallet' panicked at monero-sys/src/lib.rs:728:18: failed to send result back to caller: Any { .. } note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

Summary by CodeRabbit

New Features

  • Introduced a full-featured Monero wallet interface with balance, transaction history, sending XMR, and restore height management.
  • Added Monero wallet approval flows including password and transaction approvals via modal dialogs.
  • Enabled fiat/XMR conversion, max send functionality, and currency toggling in send forms.
  • Integrated recent wallet selection and wallet file opening in seed selection dialog.
  • Implemented wallet sync progress tracking and event-driven UI updates.
  • Added wallet event listener system bridging C++ and Rust with callback forwarding.
  • Added asynchronous transfer with approval callback and enhanced wallet state management.
  • Introduced new Tauri commands and RPC functions for Monero wallet operations and approval handling.
  • Added database support with migrations and query caching for tracking recent wallet accesses.
  • Added a generic throttling utility to limit invocation rates of closures.

Enhancements

  • Improved navigation with separate Monero and Bitcoin wallet pages.
  • Enhanced UI components including numeric input, transaction history, wallet actions, and state indicators.
  • Added localization support and new button and chip theme variants.
  • Refined seed selection dialog with wallet file selection and recent wallet integration.
  • Added detailed wallet sync and transaction state indicators with pulsating UI elements.
  • Improved approval request handling with rejection support and expanded event types.
  • Enhanced error handling and async flow in wallet operations and UI updates.

Bug Fixes

  • Addressed UI consistency and navigation issues.
  • Improved error handling for approval and wallet operations.
  • Fixed deprecated unwraps and improved validation in tests.

Chores

  • Added new dependencies and scripts for Monero support, SQL migrations, and throttling utilities.
  • Updated database schema to track recent wallet usage.
  • Reorganized imports and cleaned up unused dependencies across crates.
  • Removed unused code and consolidated imports for clarity.
  • Updated workspace dependencies to include new crates and reordered existing ones.

Documentation

  • Extended and updated component and type documentation for new wallet features.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 26, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This update introduces comprehensive Monero wallet integration, including transaction history, wallet state management, and event-driven UI updates across backend, database, and frontend layers. A new throttling utility crate is added. The GUI now features a Monero wallet page, transaction sending, approval dialogs, and real-time wallet status. Backend APIs, Tauri commands, and database migrations support wallet usage tracking and approval workflows.

Changes

File(s) / Path(s) Change Summary
throttle/, throttle/Cargo.toml, throttle/src/throttle.rs New generic throttling utility crate added, with thread-based rate-limiting for closure invocations.
monero-sys/src/database.rs, .sqlx/, migrations/20250710170522_create_recent_wallets.sql, regenerate_sqlx_cache.sh Added SQLite-based recent wallet tracking, migrations, and SQLx cache regeneration script.
monero-sys/Cargo.toml, monero-sys/src/bridge.h, monero-sys/src/bridge.rs, monero-sys/src/lib.rs Major Monero FFI, wallet listener, transaction history, and event bridge updates; chrono/serde dependency added.
swap/Cargo.toml, swap-fs/src/lib.rs, swap-env/Cargo.toml, swap-env/src/config.rs, swap-env/src/env.rs, swap-env/src/lib.rs, swap-serde/Cargo.toml, swap-serde/src/bitcoin.rs, swap-serde/src/electrum.rs, swap-serde/src/lib.rs, swap-serde/src/libp2p.rs, swap-serde/src/monero.rs Dependency, import, and module order changes; added eigenwallet data dir utility.
swap/src/cli/api.rs, swap/src/cli/api/request.rs, swap/src/cli/api/tauri_bindings.rs, swap/src/monero/wallet.rs, swap/src/seed.rs Monero wallet initialization, approval, password, and transaction APIs; wallet listener; seed-from-wallet support; recent wallet usage.
swap/src/bitcoin.rs, swap/src/asb/command.rs, swap/src/common/tracing_util.rs, swap/src/database.rs, swap/src/database/sqlite.rs, swap/src/monero.rs, swap/src/network/swap_setup/alice.rs, swap/src/network/swap_setup/bob.rs, swap/src/network/swarm.rs, swap/src/protocol/alice.rs, swap/src/protocol/alice/state.rs, swap/src/protocol/alice/swap.rs, swap/src/protocol/bob/state.rs, swap/src/protocol/bob/swap.rs, swap/tests/harness/mod.rs Import order/formatting improvements, minor error message tweaks.
monero-harness/src/lib.rs, monero-rpc-pool/src/database.rs, monero-rpc-pool/src/lib.rs, monero-rpc-pool/src/proxy.rs Formatting, new HTTP client in app state, improved error handling, and connection pooling for Monero RPC pool.
src-tauri/src/lib.rs, src-tauri/Cargo.toml, src-tauri/capabilities/default.json State management refactor, new Monero wallet and approval Tauri commands, sysinfo dependency removed, dialog permissions added.
src-gui/package.json, src-gui/src/models/tauriModelExt.ts, src-gui/src/store/combinedReducer.ts, src-gui/src/store/features/rpcSlice.ts, src-gui/src/store/features/walletSlice.ts, src-gui/src/store/hooks.ts, src-gui/src/store/middleware/storeListener.ts Redux wallet slice added, new approval types, Monero wallet state management, and initialization in store logic.
src-gui/src/renderer/rpc.ts, src-gui/src/utils/conversionUtils.ts Monero wallet RPCs, approval handling, and conversion utilities for XMR/piconero.
src-gui/src/renderer/background.ts Monero wallet event handling in event listener, polling interval change.
src-gui/src/renderer/components/App.tsx, navigation/NavigationHeader.tsx, navigation/RouteListItemIconButton.tsx Routing and navigation updated for Monero/Bitcoin wallets, improved route handling.
src-gui/src/renderer/components/pages/monero/, components/pages/monero/components/ New Monero wallet page, transaction send, approval, overview, history, state indicator, and loading state components.
src-gui/src/renderer/components/pages/monero/SetRestoreHeightModal.tsx Modal dialog for setting Monero restore height by block or date.
src-gui/src/renderer/components/pages/monero/SendTransactionModal.tsx, components/pages/monero/components/SendTransactionContent.tsx, components/pages/monero/components/SendApprovalContent.tsx, components/pages/monero/components/SendSuccessContent.tsx, components/pages/monero/components/SendAmountInput.tsx Transaction send flow, approval UI, success confirmation, and amount input for Monero.
src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx Wallet action buttons for sending, swapping, and restore height.
src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx Wallet overview card with balance, sync status, and pending funds.
src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx Monero transaction history table with explorer links.
src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx Loading state UI for Monero wallet page.
src-gui/src/renderer/components/pages/monero/components/StateIndicator.tsx Animated status indicator for wallet sync and state.
src-gui/src/renderer/components/pages/monero/components/index.ts Index file re-exporting Monero wallet components.
src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx Modal dialog for pending password approval requests.
src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx Refactored seed selection dialog, recent wallet support, wallet file browsing.
src-gui/src/renderer/components/inputs/NumberInput.tsx, components/inputs/MoneroAddressTextField.tsx New dynamic number input, Monero address field helperText made optional.
src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx, MonospaceTextBox.tsx, RenderedCliLog.tsx, ScrollablePaperTextBox.tsx, TruncatedText.tsx, Units.tsx UI improvements: styling, minHeight, optional props, new fiat/piconero component, precision options.
src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx, components/alert/DaemonStatusAlert.tsx, components/alert/SwapStatusAlert/SwapStatusAlert.tsx Alert navigation and conditional rendering tweaks, added React import.
src-gui/src/renderer/components/pages/history/HistoryPage.tsx, pages/history/table/HistoryTable.tsx, pages/history/table/SwapLogFileOpenButton.tsx, pages/wallet/WalletPage.tsx UI: headings removed, empty state and skeletons for history, minHeight for logs.
src-gui/src/renderer/components/pages/swap/swap/components/TransactionInfoBox.tsx, pages/swap/swap/init/InitPage.tsx Import consolidation, swap redeem address tabbed selection UI.
src-gui/src/renderer/components/modal/swap/SwapDialogTitle.tsx Swap dialog title component removed.
src-gui/src/renderer/components/modal/swap/pages/DebugPage.tsx MinHeight added to logs box in debug page.
src-gui/src/renderer/components/theme.tsx Theme extended: new Button/Chip variants and sizes.
justfile Added --verbose flag to Tauri dev command.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Frontend (React)
    participant Tauri as Tauri Backend
    participant Swap as Rust Core/Swap
    participant Monero as Monero Wallet/FFI
    participant DB as SQLite DB

    %% Initialization
    UI->>Tauri: initialize_context()
    Tauri->>Swap: ContextBuilder::build()
    Swap->>DB: Database::new() / get_recent_wallets()
    Swap->>UI: request_seed_selection_with_recent_wallets()
    UI->>Swap: SeedChoice (RandomSeed/FromSeed/FromWalletPath)
    Swap->>Monero: Open/Create Wallet (with password if needed)
    Monero->>Swap: WalletHandle
    Swap->>DB: record_wallet_access(wallet_path)
    Swap->>Tauri: Context ready

    %% Wallet State Fetch
    UI->>Tauri: get_monero_main_address()
    Tauri->>Swap: GetMoneroMainAddressArgs
    Swap->>Monero: get_main_address()
    Monero->>Swap: Address
    Swap->>Tauri: Response
    Tauri->>UI: Address

    UI->>Tauri: get_monero_balance()
    Tauri->>Swap: GetMoneroBalanceArgs
    Swap->>Monero: get_balance()
    Monero->>Swap: Balances
    Swap->>Tauri: Response
    Tauri->>UI: Balances

    UI->>Tauri: get_monero_history()
    Tauri->>Swap: GetMoneroHistoryArgs
    Swap->>Monero: get_history()
    Monero->>Swap: Transactions
    Swap->>Tauri: Response
    Tauri->>UI: Transactions

    %% Sending Transaction
    UI->>Tauri: send_monero(address, amount)
    Tauri->>Swap: SendMoneroArgs
    Swap->>UI: request_approval(SendMoneroDetails)
    UI->>Swap: resolve_approval(request_id, accept/reject)
    alt accepted
        Swap->>Monero: transfer_with_approval()
        Monero->>Swap: TxReceipt
        Swap->>Tauri: SendMoneroResponse
        Tauri->>UI: Success
    else rejected
        Swap->>Tauri: SendMoneroResponse (rejected)
        Tauri->>UI: Failure
    end

    %% Wallet Event Updates
    Monero-->>Swap: WalletListener: balance/sync/history update
    Swap-->>Tauri: emit MoneroWalletUpdate event
    Tauri-->>UI: MoneroWalletUpdate event (Redux update)
Loading

Poem

🐰
In burrows deep, I code and write,
Monero wallet shines so bright!
With history, balance, sync in tow,
And throttled threads that softly flow.
Approvals hop from UI to core—
Now sending XMR is never a chore!
🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@binarybaron
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 26, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
.vscode/settings.json (1)

75-76: No-op positional change – consider avoiding diff noise

Re-ordering keys inside the JSON object has zero functional impact but clutters the history. Unless a formatter enforces this ordering, you can drop this hunk to keep the PR focused on the Monero wallet feature.

src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (1)

15-46: Clean up commented code for production readiness.

The data fetching logic is well-implemented with proper error handling and loading states. However, consider:

  1. Remove the commented test error line (line 18) before production
  2. Either implement the transaction history feature or remove the commented code (lines 21-25, 34-35) to avoid confusion

Apply this diff to clean up the commented code:

-        // throw new Error("test");
         setIsLoading(true);
         setError(null);
-        /*const [historyResponse, addressResponse]: [GetMoneroHistoryResponse, GetMoneroMainAddressResponse] = await Promise.all([
-          getMoneroHistory(),
-          getMoneroMainAddress(),
-        ]);
-        setHistory(historyResponse.transactions);*/
         // Fetch all data in parallel if desired, or sequentially
         const mainAddressResponse = await getMoneroMainAddress();
         setMainAddress(mainAddressResponse.address);

         const balanceResponse = await getMoneroBalance();
         setBalance(balanceResponse);

-        // History fetching can be re-enabled when stable
-        // const historyResponse = await getMoneroHistory(); 
-        // setHistory(historyResponse.transactions);
swap/src/cli/api/request.rs (1)

501-530: Consider concurrent balance queries for better performance.

The implementation is correct but could be optimized by making the balance queries concurrent since they're independent operations.

Apply this diff to make the balance queries concurrent:

-        let total_balance = wallet.total_balance().await;
-        let unlocked_balance = wallet.unlocked_balance().await;
+        let (total_balance, unlocked_balance) = tokio::join!(
+            wallet.total_balance(),
+            wallet.unlocked_balance()
+        );

This would reduce the total request time when both balances need to be fetched.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c30ef4 and 8b28944.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .vscode/settings.json (1 hunks)
  • justfile (2 hunks)
  • monero-rpc/src/monerod.rs (1 hunks)
  • monero-sys/Cargo.toml (1 hunks)
  • monero-sys/src/bridge.h (1 hunks)
  • monero-sys/src/bridge.rs (2 hunks)
  • monero-sys/src/lib.rs (5 hunks)
  • src-gui/package.json (1 hunks)
  • src-gui/src/renderer/components/App.tsx (2 hunks)
  • src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx (2 hunks)
  • src-gui/src/renderer/components/modal/swap/TransactionInfoBox.tsx (2 hunks)
  • src-gui/src/renderer/components/modal/swap/pages/in_progress/SwapSetupInflightPage.tsx (2 hunks)
  • src-gui/src/renderer/components/navigation/NavigationHeader.tsx (2 hunks)
  • src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (1 hunks)
  • src-gui/src/renderer/rpc.ts (2 hunks)
  • src-tauri/src/lib.rs (4 hunks)
  • swap/Cargo.toml (1 hunks)
  • swap/src/cli/api/request.rs (1 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Rust wrapper types (such as WalletManager, Wallet) should provide idiomatic and safe interfaces, never expose raw pointers to users, implement Send and Sync as appropriate, and use Pin for C++ objects requiring stable memory addresses.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Memory safety is enforced by wrapping raw pointers in safe Rust types, using 'unsafe' only at the FFI boundary, and employing patterns like OnceLock to ensure singletons (e.g., WalletManager).
.vscode/settings.json (1)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
monero-sys/Cargo.toml (3)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The build process should use CMake to target the wallet_api, link both libwallet.a and libwallet_api.a, and configure CXX to bridge Rust and C++ code, ensuring all required static and dynamic libraries are linked.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
src-gui/src/renderer/components/modal/swap/pages/in_progress/SwapSetupInflightPage.tsx (1)
Learnt from: nabijaczleweli
PR: UnstoppableSwap/core#391
File: src-gui/src/renderer/components/pages/help/SettingsBox.tsx:54-57
Timestamp: 2025-06-15T15:52:41.735Z
Learning: In the UnstoppableSwap GUI codebase, top-level await is used as a convention for infallible RPC calls like getTorForced() and getMatches(), as seen in files like SettingsBox.tsx and config.ts. This is an intentional architectural decision by the team.
src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx (1)
Learnt from: nabijaczleweli
PR: UnstoppableSwap/core#391
File: src-gui/src/renderer/components/pages/help/SettingsBox.tsx:54-57
Timestamp: 2025-06-15T15:52:41.735Z
Learning: In the UnstoppableSwap GUI codebase, top-level await is used as a convention for infallible RPC calls like getTorForced() and getMatches(), as seen in files like SettingsBox.tsx and config.ts. This is an intentional architectural decision by the team.
monero-rpc/src/monerod.rs (1)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
src-tauri/src/lib.rs (4)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
monero-sys/src/bridge.rs (6)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Rust wrapper types (such as WalletManager, Wallet) should provide idiomatic and safe interfaces, never expose raw pointers to users, implement Send and Sync as appropriate, and use Pin for C++ objects requiring stable memory addresses.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Memory safety is enforced by wrapping raw pointers in safe Rust types, using 'unsafe' only at the FFI boundary, and employing patterns like OnceLock to ensure singletons (e.g., WalletManager).
monero-sys/src/bridge.h (3)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (1)
Learnt from: nabijaczleweli
PR: UnstoppableSwap/core#391
File: src-gui/src/renderer/components/pages/help/SettingsBox.tsx:54-57
Timestamp: 2025-06-15T15:52:41.735Z
Learning: In the UnstoppableSwap GUI codebase, top-level await is used as a convention for infallible RPC calls like getTorForced() and getMatches(), as seen in files like SettingsBox.tsx and config.ts. This is an intentional architectural decision by the team.
src-gui/src/renderer/rpc.ts (1)
Learnt from: nabijaczleweli
PR: UnstoppableSwap/core#391
File: src-gui/src/renderer/components/pages/help/SettingsBox.tsx:54-57
Timestamp: 2025-06-15T15:52:41.735Z
Learning: In the UnstoppableSwap GUI codebase, top-level await is used as a convention for infallible RPC calls like getTorForced() and getMatches(), as seen in files like SettingsBox.tsx and config.ts. This is an intentional architectural decision by the team.
monero-sys/src/lib.rs (6)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Rust wrapper types (such as WalletManager, Wallet) should provide idiomatic and safe interfaces, never expose raw pointers to users, implement Send and Sync as appropriate, and use Pin for C++ objects requiring stable memory addresses.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: The bridge architecture between Rust and Monero C++ should use the cxx::bridge macro to define FFI interfaces, wrap static C++ methods as free functions in the C++ header, and use std::unique_ptr for string returns.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Memory safety is enforced by wrapping raw pointers in safe Rust types, using 'unsafe' only at the FFI boundary, and employing patterns like OnceLock to ensure singletons (e.g., WalletManager).
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When working with Rust FFI to C++ (specifically Monero), use the Rust 2021 edition and restrict the use of 'unsafe' to FFI interactions only.
swap/src/cli/api/request.rs (3)
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Always verify method signatures in the Monero C++ headers (wallet2_api.h) before adding them to the Rust bridge to ensure correctness in function names, parameters, and return types.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: When adding new functionality to the Rust/C++ bridge, the process should be: locate the function in wallet2_api.h, add its declaration to bridge.rs, implement the Rust wrapper in lib.rs, and add C++ helpers in bridge.h if needed for CXX limitations.
Learnt from: CR
PR: UnstoppableSwap/core#0
File: monero-sys/CLAUDE.md:0-0
Timestamp: 2025-06-23T13:49:46.393Z
Learning: Rust wrapper types (such as WalletManager, Wallet) should provide idiomatic and safe interfaces, never expose raw pointers to users, implement Send and Sync as appropriate, and use Pin for C++ objects requiring stable memory addresses.
🧬 Code Graph Analysis (2)
src-gui/src/renderer/components/App.tsx (1)
src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (1)
  • MoneroWalletPage (8-102)
monero-sys/src/bridge.rs (2)
monero-sys/src/bridge.h (2)
  • walletHistory (232-238)
  • walletHistory (232-232)
monero-sys/src/lib.rs (5)
  • count (1936-1940)
  • transaction (1943-1958)
  • amount (1963-1967)
  • fee (1970-1974)
  • confirmations (1977-1981)
🔇 Additional comments (36)
swap/Cargo.toml (1)

84-84: Verify the typeshare version update is necessary and compatible.

Ensure that the typeshare version update from "1.0.3" to "1.0.4" is required for the new Monero wallet functionality and that there are no breaking changes.

#!/bin/bash
# Description: Verify typeshare version 1.0.4 exists and check for any breaking changes

# Check if version 1.0.4 exists on crates.io
curl -s https://crates.io/api/v1/crates/typeshare | jq '.versions[] | select(.num == "1.0.4") | {num, yanked, created_at}'

# Check recent versions and any potential breaking changes
curl -s https://crates.io/api/v1/crates/typeshare | jq '.versions[] | select(.num | test("^1\\.0\\.[3-5]$")) | {num, yanked, created_at}' | head -10
src-gui/src/renderer/components/navigation/NavigationHeader.tsx (2)

10-10: LGTM!

The import for AttachMoneyIcon is correctly added and follows the established import pattern.


29-31: LGTM!

The new "Monero Wallet" navigation item follows the established pattern and uses an appropriate icon. The route naming is consistent with the existing navigation structure.

src-gui/package.json (1)

7-9: LGTM!

Adding the ../monero-sys/src path to the typeshare commands is necessary to generate TypeScript bindings for the new Monero wallet FFI types. The change is consistently applied across all related scripts.

justfile (2)

29-29: LGTM!

Adding the --verbose flag will provide helpful debugging information during development of the new Monero wallet features.


108-108: LGTM!

The trailing newline addition is a good formatting improvement.

src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx (2)

1-1: LGTM!

Adding the explicit React import improves compatibility and follows modern React best practices.


8-8: LGTM!

Changing the type from JSX.Element to React.ReactElement provides better type safety and is more specific. This aligns with similar improvements mentioned in other components.

src-gui/src/renderer/components/modal/swap/pages/in_progress/SwapSetupInflightPage.tsx (2)

8-8: LGTM: Proper Theme type import added.

Good addition of the explicit Theme import to support the type annotation improvement below.


484-484: Excellent type safety improvement.

Replacing the any type with the specific Theme type from Material-UI provides better type safety and IDE support. This follows TypeScript best practices.

monero-sys/Cargo.toml (3)

10-10: LGTM: Dependency formatting improvements.

The formatting changes for the existing dependencies improve consistency and readability.

Also applies to: 12-12


11-11: New serde dependency aligns with serialization needs.

The addition of serde version 1.0.219 supports the new transaction history and wallet data serialization features mentioned in the AI summary.


14-14: New typeshare dependency supports type sharing.

The addition of typeshare version 1.0.4 enables type sharing between Rust and TypeScript, which is essential for the new Monero wallet FFI bindings.

src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx (2)

1-1: LGTM: Explicit React import added.

Adding the explicit React import supports the return type annotation change and improves code clarity.


243-243: Good type annotation improvement.

Changing from JSX.Element | null to React.ReactElement | null provides more explicit typing and aligns with the codebase consistency improvements mentioned in the AI summary.

src-gui/src/renderer/components/App.tsx (2)

23-23: LGTM: MoneroWalletPage import added.

The import is correctly placed and follows the existing import pattern for other page components.


75-75: Proper route integration for Monero wallet.

The new route follows the established pattern and correctly integrates the MoneroWalletPage component into the application routing structure. Based on the relevant code snippet, the MoneroWalletPage displays wallet data including balance and main address.

src-gui/src/renderer/components/modal/swap/TransactionInfoBox.tsx (2)

1-1: LGTM: Import consolidation improves organization.

Consolidating the React and ReactNode imports into a single import statement improves code organization and follows React best practices.


12-12: Good type specificity improvement.

Changing the icon prop type from JSX.Element to React.ReactElement provides more explicit typing and aligns with the broader type consistency improvements across the codebase.

monero-rpc/src/monerod.rs (1)

232-232: LGTM: Simplified lifetime annotation.

The anonymous lifetime '_ is appropriate here since the lifetime parameter isn't used elsewhere in the trait implementation. This simplification improves code readability.

monero-sys/src/bridge.h (1)

229-238: LGTM: Well-implemented C++ bridge function.

The walletHistory function follows the established patterns in this file:

  • Proper null pointer check for defensive programming
  • Correct use of std::unique_ptr for safe ownership transfer
  • Consistent with the bridge architecture for wrapping C++ methods

The implementation aligns with the learnings about using unique_ptr for string/object returns in the CXX bridge.

src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (2)

1-14: LGTM: Proper imports and state management setup.

The component follows React best practices with proper TypeScript typing for state variables and appropriate Material-UI imports.


48-102: Verify the transaction history table data mapping.

The rendering logic is well-structured with proper conditional rendering and Material-UI usage. However, there's a potential data mapping issue:

Line 89 displays tx.block_height in the "Confirmations" column. Block height and confirmations are different values - confirmations typically represent the number of blocks since the transaction was mined.

Please verify that TransactionInfo.block_height should be displayed as confirmations, or if a separate confirmations field should be used.

The rest of the component implementation looks solid with proper error handling and loading states.

src-gui/src/renderer/rpc.ts (2)

31-33: LGTM: Consistent import additions.

The new import additions follow the established pattern and are properly typed.


392-402: LGTM: Well-implemented RPC functions.

All three new functions follow the established patterns in this codebase:

  • Consistent naming convention
  • Proper use of invokeNoArgs helper for parameter-less commands
  • Correct TypeScript return type annotations
  • Consistent with existing RPC function implementations
src-tauri/src/lib.rs (4)

13-16: LGTM: Clean import additions.

The new import additions are properly organized and follow the established pattern in the file.


198-200: LGTM: Proper command registration.

The new Tauri commands are correctly added to the invoke_handler following the established pattern.


251-254: LGTM: Consistent tauri_command! usage.

The new command declarations follow the established pattern using the tauri_command! macro with the no_args variant, which is appropriate for commands that don't require parameters.


417-417: LGTM: Consistent command implementation.

The get_monero_balance command follows the same pattern as the other commands.

monero-sys/src/bridge.rs (2)

57-61: LGTM: FFI type declarations follow established patterns.

The new opaque types TransactionHistory and TransactionInfo are properly declared and will enable safe access to Monero transaction history functionality through the FFI bridge.


280-296: LGTM: FFI method signatures follow established safety patterns.

The method signatures correctly follow the established FFI patterns:

  • walletHistory properly uses unsafe and returns UniquePtr for memory management
  • Methods taking references (&TransactionHistory, &TransactionInfo) are safe
  • Return types are appropriate for their respective operations

The implementation aligns with the retrieved learnings about proper bridge architecture using the cxx::bridge macro and safe memory management patterns.

swap/src/cli/api/request.rs (2)

457-477: LGTM: Transaction history request implementation is correct.

The implementation properly:

  • Handles potential absence of monero manager with appropriate error context
  • Follows the established async Request pattern
  • Uses proper type annotations for frontend integration

479-499: LGTM: Main address request implementation is correct.

The implementation follows the established pattern with proper error handling and type serialization for the Monero address.

monero-sys/src/lib.rs (3)

477-484: LGTM!

The async history() method correctly uses the established pattern to execute on the wallet thread and returns serializable data.


1703-1714: Appropriate use of unsafe for FFI interaction

The implementation correctly wraps the unsafe FFI call and immediately serializes the data, avoiding prolonged exposure of raw pointers. This follows the established pattern from the learnings about restricting unsafe to FFI boundaries.


2002-2023: Well-implemented custom serialization module

The monero_serde module correctly handles serialization of monero::Amount as u64 piconeros, following serde best practices.

@Einliterflasche
Copy link
Copy Markdown

Einliterflasche commented Jul 2, 2025

For the wallet listener, we should do it like this:

  • In bridge.h create a c++ class which stores function pointers for the different events and extends the WalletListener class (not sure about the terminology). Create another function which takes function pointers for the different events and constructs the object
  • In bridge.rs declare the c++ class and function such that we can use them from rust
  • In lib.rs create a channel, one end of which goes into WalletHandle and another into a closure (me might need to allocate the closure to avoid the pointer becoming invalid).
    • Create an event enum (e.g. enum Event { BalanceChanged, NewBlock, ... }).
    • When creating a new wallet, also create (probably heap allocated => wrapped in Box<...>) the closures which are to be passed to the wallet listener.
    • The closures send an event via the channel.
    • Create the wallet listener with the closures using the c++ function
    • Pass the wallet listener to the wallet.

@binarybaron binarybaron force-pushed the feat/monero-wallet branch from 2cbe765 to 7b959f3 Compare July 7, 2025 13:15
@binarybaron binarybaron marked this pull request as ready for review July 18, 2025 10:59
Copy link
Copy Markdown

@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.

Bug: Unintended Debug Logging Causes Performance Issues

The debug print statement std::cout << "WalletListener::Updated" << std::endl; was accidentally committed in the FunctionBasedListener::updated() method. This will cause excessive log output and could impact performance.

monero-sys/src/bridge.h#L344-L346

{
std::cout << "WalletListener::Updated" << std::endl;
if (on_updated_) {

Fix in CursorFix in Web


Bug: Redundant Refresh Causes Unnecessary API Calls

The resolveApproval function uses a setTimeout to refresh approvals again after already awaiting refreshApprovals(). This "just to make sure" pattern suggests a race condition workaround rather than a proper fix, leading to unnecessary API calls and masking underlying synchronization issues.

src-gui/src/renderer/rpc.ts#L574-L582

} finally {
// Always refresh the approval list
await refreshApprovals();
// Refresh the approval list a few miliseconds later to again
// Just to make sure :)
setTimeout(() => {
refreshApprovals();
}, 200);

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🔭 Outside diff range comments (1)
monero-sys/src/lib.rs (1)

1-2780: Consider breaking this large file into smaller modules

This file has grown to nearly 3000 lines and contains many different responsibilities. Consider breaking it into logical modules to improve maintainability:

  • wallet_handle.rs - WalletHandle and related functionality
  • wallet_manager.rs - WalletManager implementation
  • ffi_wallet.rs - FfiWallet and FFI interactions
  • listeners.rs - Event listener implementations
  • transaction_history.rs - Transaction history handling
  • types.rs - Shared types and structs

This would improve:

  • Code organization and readability
  • Maintainability and testing
  • Separation of concerns
  • Easier code review in the future
♻️ Duplicate comments (13)
src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx (3)

33-34: Add null safety for balance calculation.

The pendingBalance calculation accesses balance.total_balance and balance.unlocked_balance without checking if balance is defined, which could cause a runtime error.

Add null safety:

  const pendingBalance =
-    parseFloat(balance.total_balance) - parseFloat(balance.unlocked_balance);
+    balance ? parseFloat(balance.total_balance) - parseFloat(balance.unlocked_balance) : 0;

37-37: Add safety check for blocks left calculation.

The calculation of blocksLeft and its usage in toLocaleString() could fail if syncProgress properties are undefined.

Add safety checks:

-  const blocksLeft = syncProgress?.target_block - syncProgress?.current_block;
+  const blocksLeft = syncProgress ? syncProgress.target_block - syncProgress.current_block : 0;

And update the display:

           {isSyncing && (
             <Typography variant="body2" color="text.secondary">
-              {blocksLeft.toLocaleString()} blocks left
+              {blocksLeft?.toLocaleString() || '0'} blocks left
             </Typography>
           )}

Also applies to: 139-143


86-98: Fix padding inconsistency for "Pending" label.

The "Pending" label has inconsistent padding compared to "Available Funds" as noted in previous feedback.

Ensure the "Pending" label aligns properly with "Available Funds" by adjusting the grid row alignment:

             <Typography
               variant="body2"
               color="warning"
               sx={{
                 mb: 1,
                 animation: "pulse 2s infinite",
                 gridColumn: "2",
                 gridRow: "1",
-                alignSelf: "end",
               }}
             >
               Pending
             </Typography>
monero-sys/src/bridge.rs (1)

563-608: Consider reducing log levels for some events

Based on the past review comment, some of these log events should be changed to trace level to reduce noise. The money-related events (spent, received, unconfirmed) are likely too verbose at info level.

Consider changing the log levels for frequent events to trace level as suggested in the past review comment.

swap/src/cli/api/request.rs (3)

565-632: Address past review comment about date parsing

The current implementation handles date parsing in Rust, but there's a past review comment suggesting this should be moved to the frontend. The date validation logic is appropriate for the backend.

The date parsing is handled correctly in Rust with proper validation, but consider the past review comment about moving date parsing to the frontend.


634-642: Add explanation comment for pause-stop-refresh sequence

The sequence of pausing, stopping, and refreshing needs explanation as requested in the past review comment.

Add a comment explaining why we pause, stop, and then refresh again as requested in the past review comment.


675-677: Use .into() for amount conversion

As suggested in the past review comment, use .into() instead of the explicit conversion method.

Consider using .into() for the amount conversion as suggested in the past review comment.

monero-sys/src/lib.rs (2)

1031-1031: Create a constant for empty password to improve clarity

The empty string "" for password is used in multiple places but its purpose isn't immediately clear.

As mentioned in the previous review, create a constant to make this more explicit:

impl WalletManager {
+    const EMPTY_PASSWORD: &str = "";
    /// For now we don't support custom difficulty
    const DEFAULT_KDF_ROUNDS: u64 = 1;
    
    // ... later in the code
-    let_cxx_string!(password = password.map_or("", |s| s.as_str()));
+    let_cxx_string!(password = password.map_or(Self::EMPTY_PASSWORD, |s| s.as_str()));

2431-2461: Memory safety concern: Raw pointer usage without lifetime tracking

The TransactionHistoryHandle and TransactionInfoHandle structs hold raw pointers without lifetime guarantees, which creates potential use-after-free vulnerabilities.

As mentioned in previous reviews, consider adding lifetime parameters or refactoring to eliminate these handle types:

-pub struct TransactionHistoryHandle(*mut TransactionHistory);
-pub struct TransactionInfoHandle(*mut ffi::TransactionInfo);
+pub struct TransactionHistoryHandle<'a> {
+    ptr: *mut TransactionHistory,
+    _phantom: std::marker::PhantomData<&'a TransactionHistory>,
+}

Alternatively, since the data is immediately serialized, consider removing these handle types entirely and directly converting to TransactionInfo within the history() method.

src-gui/src/renderer/rpc.ts (2)

439-458: Address the unresolved feedback about using an enum for date format.

The past review comment indicates this function should accept an enum { year: u32, month: u32, day: u32 } instead of a string. The current implementation correctly handles the Date object conversion, but this doesn't align with the requested enum format in the backend.

Run the following script to verify the expected format in the backend:

#!/bin/bash
# Description: Check the SetRestoreHeightArgs type definition in request.rs

# Search for SetRestoreHeightArgs definition
ast-grep --pattern 'struct SetRestoreHeightArgs {
  $$$
}'

# Also check for any enum definitions related to restore height
rg -A 10 "SetRestoreHeightArgs|RestoreHeight.*enum" swap/src/cli/api/request.rs

505-507: Error handling still only logs to console without user notification.

The past review comment about propagating errors to the UI hasn't been addressed. Users won't know if wallet initialization fails.

swap/src/cli/api.rs (2)

333-337: Store the spawned task handle for proper cleanup.

The spawned task for pool status updates should be stored to enable cleanup when the context is destroyed.


721-754: Add security controls to the password verification loop.

The infinite retry loop still lacks security controls and could enable brute-force attempts.

🧹 Nitpick comments (5)
src-gui/src/renderer/components/pages/monero/SetRestoreHeightModal.tsx (1)

1-14: Consolidate Material UI imports.

The Material UI imports are split across two statements. Consider consolidating them for better organization.

 import {
   Accordion,
   AccordionDetails,
   AccordionSummary,
   Button,
   Dialog,
   DialogActions,
   DialogContent,
+  DialogTitle,
   TextField,
   Typography,
   Radio,
 } from "@mui/material";
-
-import { DialogTitle } from "@mui/material";
throttle/src/throttle.rs (2)

1-7: Remove commented out imports.

Clean up the unused imports that are commented out.

-use std::time::{self, /* SystemTime, UNIX_EPOCH, */ Duration};
+use std::time::{self, Duration};

162-162: Translate test comment to English.

Maintain consistent language in test comments.

-            assert_eq!(*effect_run_times.lock().unwrap(), 2); // delay后执行最有一个参数
+            assert_eq!(*effect_run_times.lock().unwrap(), 2); // Execute last parameter after delay
swap/src/monero/wallet.rs (1)

214-214: Remove or document the commented code.

This commented line should either be removed or have a clear explanation of why it's commented out.

    fn on_refreshed(&self) {
-        //self.wallet.start_refresh_thread();
        self.send_balance_update();
monero-sys/src/lib.rs (1)

858-892: Static method doesn't need to be on WalletHandle

The verify_wallet_password method is implemented as a static method on WalletHandle but doesn't use any instance data. Consider moving this to a separate utility module or the WalletManager struct where it logically belongs.

impl WalletHandle {
-    /// Verify the password for a wallet at the given path.
-    /// This function spawns a thread to perform the verification and returns the result via a oneshot channel.
-    /// Returns `Ok(true)` if the password is correct, `Ok(false)` if incorrect.
-    pub fn verify_wallet_password(path: String, password: String) -> anyhow::Result<bool> {
-        // ... implementation
-    }
}

And add it to WalletManager instead:

impl WalletManager {
+    /// Verify the password for a wallet at the given path.
+    /// Returns `Ok(true)` if the password is correct, `Ok(false)` if incorrect.
+    pub fn verify_wallet_password(path: String, password: String) -> anyhow::Result<bool> {
+        // ... implementation
+    }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79f12e6 and 2e4cc7f.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • src-gui/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (68)
  • Cargo.toml (2 hunks)
  • monero-harness/src/lib.rs (1 hunks)
  • monero-rpc-pool/src/database.rs (1 hunks)
  • monero-rpc-pool/src/lib.rs (3 hunks)
  • monero-rpc-pool/src/proxy.rs (12 hunks)
  • monero-sys/Cargo.toml (1 hunks)
  • monero-sys/src/bridge.h (5 hunks)
  • monero-sys/src/bridge.rs (9 hunks)
  • monero-sys/src/lib.rs (42 hunks)
  • monero-sys/tests/sign_message.rs (1 hunks)
  • src-gui/package.json (2 hunks)
  • src-gui/src/renderer/components/App.tsx (3 hunks)
  • src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx (1 hunks)
  • src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx (1 hunks)
  • src-gui/src/renderer/components/other/Units.tsx (3 hunks)
  • src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/SendTransactionModal.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/SetRestoreHeightModal.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/SendAmountInput.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/SendApprovalContent.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/SendSuccessContent.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/SendTransactionContent.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx (1 hunks)
  • src-gui/src/renderer/components/pages/monero/components/index.ts (1 hunks)
  • src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx (2 hunks)
  • src-gui/src/renderer/rpc.ts (2 hunks)
  • src-gui/src/store/features/walletSlice.ts (1 hunks)
  • src-gui/src/utils/conversionUtils.ts (1 hunks)
  • src-tauri/Cargo.toml (0 hunks)
  • src-tauri/src/lib.rs (13 hunks)
  • swap-env/Cargo.toml (1 hunks)
  • swap-env/src/config.rs (2 hunks)
  • swap-env/src/env.rs (1 hunks)
  • swap-env/src/lib.rs (1 hunks)
  • swap-fs/src/lib.rs (1 hunks)
  • swap-serde/Cargo.toml (1 hunks)
  • swap-serde/src/bitcoin.rs (2 hunks)
  • swap-serde/src/electrum.rs (1 hunks)
  • swap-serde/src/lib.rs (1 hunks)
  • swap-serde/src/libp2p.rs (2 hunks)
  • swap-serde/src/monero.rs (3 hunks)
  • swap/Cargo.toml (1 hunks)
  • swap/src/asb/command.rs (20 hunks)
  • swap/src/bin/asb.rs (6 hunks)
  • swap/src/bitcoin.rs (1 hunks)
  • swap/src/cli/api.rs (17 hunks)
  • swap/src/cli/api/request.rs (7 hunks)
  • swap/src/cli/behaviour.rs (2 hunks)
  • swap/src/cli/command.rs (1 hunks)
  • swap/src/common/tracing_util.rs (1 hunks)
  • swap/src/database.rs (1 hunks)
  • swap/src/monero.rs (2 hunks)
  • swap/src/monero/wallet.rs (6 hunks)
  • swap/src/network/swap_setup/alice.rs (1 hunks)
  • swap/src/network/swap_setup/bob.rs (1 hunks)
  • swap/src/network/swarm.rs (1 hunks)
  • swap/src/protocol/alice.rs (1 hunks)
  • swap/src/protocol/alice/state.rs (1 hunks)
  • swap/src/protocol/alice/swap.rs (1 hunks)
  • swap/src/protocol/bob/state.rs (2 hunks)
  • swap/src/protocol/bob/swap.rs (1 hunks)
  • swap/src/seed.rs (6 hunks)
  • swap/tests/harness/mod.rs (1 hunks)
  • throttle/Cargo.toml (1 hunks)
  • throttle/src/throttle.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • src-tauri/Cargo.toml
✅ Files skipped from review due to trivial changes (23)
  • swap-serde/src/electrum.rs
  • swap/src/network/swarm.rs
  • swap/src/protocol/alice.rs
  • swap-serde/src/bitcoin.rs
  • swap-env/src/lib.rs
  • swap/src/protocol/bob/swap.rs
  • swap/src/bitcoin.rs
  • swap-env/src/env.rs
  • Cargo.toml
  • swap-serde/src/libp2p.rs
  • swap/src/database.rs
  • swap/tests/harness/mod.rs
  • swap/src/network/swap_setup/bob.rs
  • swap/src/cli/behaviour.rs
  • monero-rpc-pool/src/database.rs
  • monero-sys/tests/sign_message.rs
  • throttle/Cargo.toml
  • swap-env/src/config.rs
  • swap-serde/Cargo.toml
  • swap-serde/src/lib.rs
  • swap-env/Cargo.toml
  • swap/src/protocol/alice/swap.rs
  • src-gui/src/renderer/components/pages/monero/components/SendApprovalContent.tsx
🚧 Files skipped from review as they are similar to previous changes (18)
  • monero-sys/Cargo.toml
  • swap/Cargo.toml
  • monero-harness/src/lib.rs
  • src-gui/package.json
  • src-gui/src/renderer/components/App.tsx
  • src-gui/src/renderer/components/pages/swap/swap/init/InitPage.tsx
  • src-gui/src/utils/conversionUtils.ts
  • src-gui/src/renderer/components/pages/monero/MoneroWalletPage.tsx
  • swap/src/monero.rs
  • src-gui/src/renderer/components/modal/password-entry/PasswordEntryDialog.tsx
  • swap/src/protocol/bob/state.rs
  • swap/src/bin/asb.rs
  • src-gui/src/renderer/components/pages/monero/components/WalletActionButtons.tsx
  • src-gui/src/renderer/components/other/Units.tsx
  • src-gui/src/store/features/walletSlice.ts
  • src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx
  • src-gui/src/renderer/components/pages/monero/components/SendAmountInput.tsx
  • src-gui/src/renderer/components/pages/monero/components/SendTransactionContent.tsx
🧰 Additional context used
📓 Path-based instructions (5)
monero-sys/**/bridge.h

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
monero-sys/src/bridge.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
monero-sys/{src/bridge.rs,src/lib.rs}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
monero-sys/**/*.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
monero-sys/src/lib.rs

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
🧬 Code Graph Analysis (4)
swap/src/cli/command.rs (1)
swap-serde/src/monero.rs (1)
  • validate_is_testnet (135-145)
swap/src/asb/command.rs (2)
swap/src/bin/asb.rs (1)
  • config (450-455)
swap-env/src/config.rs (3)
  • get_config_file_defaults (18-18)
  • get_config_file_defaults (32-46)
  • get_config_file_defaults (50-64)
src-gui/src/renderer/components/pages/monero/components/SendSuccessContent.tsx (4)
src-gui/src/utils/conversionUtils.ts (1)
  • getMoneroTxExplorerUrl (40-45)
src-gui/src/store/config.ts (1)
  • isTestnet (16-18)
src-gui/src/renderer/components/other/Units.tsx (2)
  • PiconeroAmount (164-177)
  • FiatPiconeroAmount (42-70)
src-gui/src/renderer/components/other/MonospaceTextBox.tsx (1)
  • MonospaceTextBox (8-36)
monero-sys/src/bridge.h (3)
monero-sys/src/bridge.rs (43)
  • rescanBlockchainAsync (226-226)
  • pauseRefresh (229-229)
  • stop (232-232)
  • startRefresh (188-188)
  • pendingTransactionFee (291-291)
  • pendingTransactionAmount (294-294)
  • transactionInfoHash (337-337)
  • on_new_block (491-491)
  • on_new_block (528-530)
  • on_new_block (591-591)
  • on_updated (492-492)
  • on_updated (532-534)
  • on_updated (593-593)
  • on_refreshed (493-493)
  • on_refreshed (536-538)
  • on_refreshed (595-595)
  • on_reorg (420-425)
  • on_reorg (473-480)
  • on_reorg (494-494)
  • on_reorg (540-543)
  • on_reorg (597-605)
  • on_pool_tx_removed (495-495)
  • on_pool_tx_removed (545-547)
  • on_pool_tx_removed (607-607)
  • new_block (417-417)
  • new_block (461-463)
  • updated (418-418)
  • updated (465-467)
  • refreshed (419-419)
  • refreshed (469-471)
  • pool_tx_removed (426-426)
  • pool_tx_removed (482-484)
  • new (505-507)
  • new (558-560)
  • amount (325-325)
  • money_spent (410-410)
  • money_spent (449-451)
  • money_received (411-411)
  • money_received (453-455)
  • unconfirmed_money_received (412-416)
  • unconfirmed_money_received (457-459)
  • create_rust_listener_adapter (440-442)
  • destroy_rust_listener_adapter (444-444)
monero-sys/src/lib.rs (21)
  • stop (554-556)
  • stop (1633-1635)
  • on_new_block (1324-1328)
  • on_new_block (2550-2550)
  • on_updated (1330-1334)
  • on_updated (2552-2552)
  • on_refreshed (1336-1340)
  • on_refreshed (2554-2562)
  • on_reorg (1342-1346)
  • on_reorg (2564-2564)
  • on_pool_tx_removed (1348-1352)
  • on_pool_tx_removed (2566-2566)
  • new (55-57)
  • new (920-931)
  • new (974-990)
  • new (1276-1278)
  • new (1359-1403)
  • new (2329-2334)
  • new (2383-2385)
  • new (2537-2540)
  • amount (2465-2467)
swap/src/monero/wallet.rs (7)
  • on_new_block (203-207)
  • on_updated (209-211)
  • on_refreshed (213-218)
  • on_reorg (220-224)
  • on_pool_tx_removed (226-230)
  • new (79-165)
  • new (240-293)
🪛 Biome (1.9.4)
src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx

[error] 77-77: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🪛 ast-grep (0.38.6)
monero-rpc-pool/src/lib.rs

[warning] 107-109: Dangerously accepting invalid TLS
Context: reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.danger_accept_invalid_certs(true)
Note: [CWE-295]: Improper Certificate [REFERENCES]
- https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.danger_accept_invalid_hostnames
- https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.danger_accept_invalid_certs

(reqwest-accept-invalid-rust)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor BugBot
🔇 Additional comments (53)
swap/src/protocol/alice/state.rs (1)

17-17: LGTM: Clean import reorganization

The import reordering maintains functionality while improving code organization. This is consistent with similar import adjustments across other swap protocol files.

monero-rpc-pool/src/lib.rs (3)

42-42: LGTM: Good architectural improvement for connection reuse.

Adding the shared HTTP client to AppState enables efficient connection pooling and reuse across proxy requests.


63-67: LGTM: Clean conversion implementation.

The Into<String> implementation for ServerInfo provides a convenient way to format server information as HTTP URLs.


117-120: LGTM: Proper AppState initialization.

The AppState initialization correctly includes the new http_client field alongside the existing node_pool.

monero-rpc-pool/src/proxy.rs (7)

14-16: LGTM: Useful helper function for consistent node URL formatting.

The display_node helper function provides consistent formatting for node URLs across error messages and logs.


23-23: LGTM: Enhanced error classification with JsonRpcError variant.

Adding the JsonRpcError variant to HandlerError enables better error categorization and handling, particularly useful for the fail-fast mechanism.

Also applies to: 33-33


69-69: LGTM: Proper integration of shared HTTP client.

The function signature changes to accept a shared reqwest::Client parameter eliminate redundant client creation and enable connection pooling benefits.

Also applies to: 177-177


231-231: LGTM: Intelligent fail-fast mechanism for JSON-RPC errors.

The implementation correctly distinguishes between network/connection errors (which may succeed on other nodes) and JSON-RPC errors (which are likely request-specific). The MAX_JSONRPC_ERRORS threshold of 3 provides reasonable balance between resilience and efficiency.

Also applies to: 245-245


290-298: LGTM: Consistent use of shared HTTP client.

The function call correctly uses the shared HTTP client from state.http_client, enabling connection reuse and consistent configuration across all proxy requests.


327-359: LGTM: Well-implemented fail-fast logic with proper error tracking.

The implementation correctly:

  • Counts JSON-RPC errors using type-safe pattern matching
  • Provides detailed logging for debugging
  • Records failures only for non-JSON-RPC errors (since JSON-RPC errors aren't node failures)
  • Returns comprehensive error information

This approach prevents unnecessary retries when the issue is with the request itself rather than node availability.


458-466: LGTM: Comprehensive error response handling.

The new JsonRpcError case in the error response handling provides structured error information consistent with other error types, improving API usability and debugging capabilities.

swap/src/network/swap_setup/alice.rs (1)

22-22: Import reorganization looks good.

The swap_env::env import has been moved to maintain consistency with the import ordering standardization across the codebase.

swap/src/asb/command.rs (2)

9-11: Import reorganization is consistent.

The swap_env related imports have been moved to maintain consistency with the import ordering across the codebase.


405-407: Improved readability with multi-line formatting.

The chained method calls have been formatted across multiple lines for better readability, which is a good practice for complex expressions.

swap-fs/src/lib.rs (1)

23-32: New eigenwallet data directory function follows established patterns.

The system_data_dir_eigenwallet function correctly implements the same pattern as system_data_dir, with appropriate testnet/mainnet directory selection and proper error handling.

swap/src/common/tracing_util.rs (1)

70-79: Proper logging configuration for new workspace crates.

The addition of "seed", "swap_env", "swap_fs", and "swap_serde" to OUR_CRATES ensures these new workspace crates are logged at the appropriate level alongside the existing "swap" and "asb" crates.

swap-serde/src/monero.rs (4)

1-1: Import reordering is fine.

The change from Network, Amount to Amount, Network is a minor formatting improvement.


14-14: Good practice to move hex import to top.

Moving the hex import to the top of the module's import list improves code organization.


146-146: Good practice to add newline at end of file.

Adding a newline at the end of the file follows standard formatting conventions.


4-11: Keep the network enum for serde’s remote mapping

The pub enum network in swap-serde/src/monero.rs is still required as the remote representation of monero::Network for Serde. It’s referenced via #[serde(with = "swap_serde::monero::network")] in multiple configs and environment structs, so it must remain:

• swap-env/src/env.rs – pub monero_network: monero::Network
• swap-env/src/config.rs – pub network: monero::Network
• swap/src/network/swap_setup.rs – pub monero: monero::Network

No removal is needed; this enum enables correct (de)serialization of the external monero::Network type.

swap/src/cli/command.rs (1)

71-75: LGTM! Improved readability with multi-line formatting.

The reformatting of the validate_is_testnet call improves code readability without changing functionality. This is a good practice for complex chained operations.

src-gui/src/renderer/components/alert/DaemonStatusAlert.tsx (1)

190-193: Verify the UX impact of removing loading feedback.

The change removes loading spinner feedback for null and NotInitialized states. While this may reduce UI noise, ensure users have alternative feedback during daemon initialization to avoid confusion about whether the app is working.

src-gui/src/renderer/components/pages/monero/components/index.ts (1)

1-5: LGTM! Clean barrel export pattern.

This index file provides a clean centralized entry point for importing Monero wallet components. The barrel export pattern is a good practice for organizing related components.

src-gui/src/renderer/components/pages/monero/SendTransactionModal.tsx (1)

17-55: LGTM! Well-structured modal flow management.

The component effectively manages the three-state flow (input → approval → success) for Monero transactions. The conditional rendering logic is clear, state management is proper, and the cleanup in handleClose ensures no stale state persists.

monero-sys/src/bridge.rs (11)

57-61: LGTM: Clean addition of transaction history types

The addition of TransactionHistory and TransactionInfo types follows the established pattern of mirroring C++ types in the FFI bridge.


104-104: LGTM: WalletListener type declaration

The WalletListener type is properly declared following the established FFI pattern.


123-136: LGTM: New wallet operations correctly declared

The new wallet operations (store, verifyWalletPassword, setSeedLanguage, setListener) follow the established pattern and mirror the C++ API correctly.


223-233: LGTM: Blockchain operations properly declared

The blockchain operations (getBlockchainHeightByDate, rescanBlockchainAsync, pauseRefresh, stop) are correctly declared following the established FFI pattern.


290-295: LGTM: Pending transaction operations

The addition of pendingTransactionFee and pendingTransactionAmount functions follows the established pattern for pending transaction operations.


312-347: LGTM: Transaction history operations

The transaction history operations (history, count, transaction, refresh, amount, fee, confirmations, direction, transactionInfoHash) are properly declared and follow the established pattern.


402-446: LGTM: Well-designed wallet listener bridge

The wallet listener bridge using cxx's virtual table approach is well-designed. The separation of concerns between the Rust trait and C++ adapter is clean and follows best practices for FFI.


448-484: LGTM: Callback bridge functions

The callback bridge functions correctly convert between C++ types and Rust trait methods. The string conversion using to_string() is appropriate here.


486-496: LGTM: Well-defined trait interface

The WalletEventListener trait is well-defined with appropriate bounds (Send + Sync) for thread safety. The method signatures are clean and consistent.


498-548: LGTM: Clean wrapper implementation

The WalletListenerBox wrapper is a clean solution for working with trait objects in CXX. The forwarding implementation is correct and the convenience methods are well-designed.


550-561: LGTM: TraceListener struct design

The TraceListener struct is well-designed with a clear purpose and appropriate field for context logging.

swap/src/cli/api/request.rs (10)

424-424: LGTM: Updated log directory path

The change to use the provided logs directory or fall back to the context's log directory is correct and handles the data directory restructuring mentioned in the PR objectives.


458-482: LGTM: Clean GetRestoreHeight implementation

The implementation properly accesses the Monero wallet through the manager and includes appropriate error handling with context.


504-527: LGTM: Transaction history retrieval

The GetMoneroHistoryArgs implementation is clean and properly accesses the wallet to retrieve transaction history.


529-552: LGTM: Main address retrieval

The GetMoneroMainAddressArgs implementation correctly retrieves the main address from the wallet.


554-573: LGTM: Date structure definition

The Date struct is well-defined with appropriate types for year, month, and day components.


648-680: LGTM: Monero balance retrieval

The GetMoneroBalanceArgs implementation properly retrieves both total and unlocked balances with appropriate error handling.


682-706: LGTM: SendMonero request structure

The SendMoneroArgs and SendMoneroAmount enum are well-designed, supporting both specific amounts and sweep operations.


707-778: LGTM: Comprehensive send implementation with approval flow

The SendMoneroArgs implementation is comprehensive and well-designed:

  • Proper address validation
  • Async approval callback integration
  • Clean error handling
  • Appropriate use of Arc for thread safety
  • Good separation of concerns between UI approval and wallet operations

The approval callback implementation is particularly well-designed, properly handling the async nature of user approval.


930-940: LGTM: Rejection approval request types

The RejectApprovalArgs and RejectApprovalResponse types are properly defined and follow the established pattern for API requests.


963-997: LGTM: Sync progress monitoring

The GetMoneroSyncProgressArgs implementation properly retrieves sync progress information and calculates the percentage correctly.

monero-sys/src/lib.rs (6)

160-161: Field name confirmations correctly reflects the data stored

Good correction from the previous misleading block_height field name. The field now accurately represents the number of confirmations.


1364-1367: Good addition of thread-safe listener collection

The addition of Arc<Mutex<Vec<Box<dyn WalletEventListener>>>> for the listeners field properly handles thread safety for the listener collection.


1825-1834: Well-structured transaction method refactoring

The refactoring of the transfer method to use separate create_pending_transaction, publish_pending_transaction, and dispose_pending_transaction methods improves code organization and enables the approval workflow functionality.


1305-1353: Well-implemented event listener forwarding

The implementation of WalletEventListener for Arc<Mutex<Vec<Box<dyn WalletEventListener>>>> properly forwards events to all registered listeners with appropriate thread safety.


2569-2589: Good serde implementation for monero::Amount

The custom serde implementation properly handles Amount serialization/deserialization using piconero units, which is the correct approach for network compatibility.


2623-2623: Correct fix for inclusive range validation

Good correction to use (0.0..=1.0).contains(&p) instead of the previous exclusive range. This properly validates that percentages are between 0 and 1 inclusive.

src-tauri/src/lib.rs (1)

96-119: Good refactoring of state management with internal synchronous locking.

The new design with RwLock<Option<Arc<Context>>> inside the State struct and synchronous try_read() access pattern is cleaner and prevents potential deadlocks. The error messages are also clear and informative.

monero-sys/src/bridge.h (1)

540-597: Well-implemented Rust listener adapter with proper ownership semantics.

The RustListenerAdapter class correctly manages the Rust box ownership and forwards all callbacks appropriately. The RAII pattern with proper cleanup in the destroy function ensures no memory leaks.

Comment on lines +76 to +80
{Array.from({ length: 2 }).map((_) => (
<Skeleton variant="rounded" sx={{ borderRadius: "100px" }}>
<Chip label="Loading..." variant="button" />
</Skeleton>
))}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix missing key prop in array mapping.

The static analysis correctly identifies a missing key prop for the mapped elements. This can cause React rendering issues and warnings.

Apply this fix:

-      {Array.from({ length: 2 }).map((_) => (
-        <Skeleton variant="rounded" sx={{ borderRadius: "100px" }}>
+      {Array.from({ length: 2 }).map((_, index) => (
+        <Skeleton key={index} variant="rounded" sx={{ borderRadius: "100px" }}>
           <Chip label="Loading..." variant="button" />
         </Skeleton>
       ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{Array.from({ length: 2 }).map((_) => (
<Skeleton variant="rounded" sx={{ borderRadius: "100px" }}>
<Chip label="Loading..." variant="button" />
</Skeleton>
))}
{Array.from({ length: 2 }).map((_, index) => (
<Skeleton key={index} variant="rounded" sx={{ borderRadius: "100px" }}>
<Chip label="Loading..." variant="button" />
</Skeleton>
))}
🧰 Tools
🪛 Biome (1.9.4)

[error] 77-77: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🤖 Prompt for AI Agents
In
src-gui/src/renderer/components/pages/monero/components/WalletPageLoadingState.tsx
between lines 76 and 80, the elements generated by mapping over the array lack a
unique key prop, which can cause React rendering warnings. Add a unique key prop
to the Skeleton component inside the map function, for example by using the
index parameter of the map callback or a unique identifier, to ensure each
element has a stable key.

Comment on lines +38 to +42
let monero_seed =
MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone())).map_err(
|e| anyhow::anyhow!("Failed to parse seed from wallet (Error: {:?})", e),
)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling to preserve error details.

The error handling discards useful error information by converting to a generic message.

         let monero_seed =
-            MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone())).map_err(
-                |e| anyhow::anyhow!("Failed to parse seed from wallet (Error: {:?})", e),
-            )?;
+            MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone()))
+                .with_context(|| "Failed to parse seed from wallet")?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let monero_seed =
MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone())).map_err(
|e| anyhow::anyhow!("Failed to parse seed from wallet (Error: {:?})", e),
)?;
let monero_seed =
MoneroSeed::from_string(Language::English, Zeroizing::new(mnemonic.clone()))
.with_context(|| "Failed to parse seed from wallet")?;
🤖 Prompt for AI Agents
In swap/src/seed.rs around lines 38 to 42, the error handling converts the
original error into a generic message, losing detailed error information. Modify
the error handling to preserve the original error context by using error
chaining or wrapping techniques, such as using the `context` or `with_context`
method from the anyhow crate, so the detailed error is retained and accessible
for debugging.

Comment on lines +535 to +547
async function refreshWalletDataAfterTransaction() {
try {
const [newBalance, newHistory] = await Promise.all([
getMoneroBalance(),
getMoneroHistory(),
]);
store.dispatch(setBalance(newBalance));
store.dispatch(setHistory(newHistory));
} catch (err) {
console.error("Failed to refresh wallet data after transaction:", err);
// Maybe show a non-blocking notification to user
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused function refreshWalletDataAfterTransaction.

This function is defined but never called anywhere in the code. The same logic is already implemented inline in sendMoneroTransaction (lines 517-526).

Apply this diff to remove the dead code:

-async function refreshWalletDataAfterTransaction() {
-  try {
-    const [newBalance, newHistory] = await Promise.all([
-      getMoneroBalance(),
-      getMoneroHistory(),
-    ]);
-    store.dispatch(setBalance(newBalance));
-    store.dispatch(setHistory(newHistory));
-  } catch (err) {
-    console.error("Failed to refresh wallet data after transaction:", err);
-    // Maybe show a non-blocking notification to user
-  }
-}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function refreshWalletDataAfterTransaction() {
try {
const [newBalance, newHistory] = await Promise.all([
getMoneroBalance(),
getMoneroHistory(),
]);
store.dispatch(setBalance(newBalance));
store.dispatch(setHistory(newHistory));
} catch (err) {
console.error("Failed to refresh wallet data after transaction:", err);
// Maybe show a non-blocking notification to user
}
}
🤖 Prompt for AI Agents
In src-gui/src/renderer/rpc.ts around lines 535 to 547, the function
refreshWalletDataAfterTransaction is defined but never used, and its logic
duplicates code already present inline in sendMoneroTransaction (lines 517-526).
Remove the entire refreshWalletDataAfterTransaction function to eliminate dead
code and avoid redundancy.

Comment on lines 554 to 562
pub fn cleanup(&self) -> Result<()> {
// TODO: close all monero wallets

// call store(..) on all wallets
// if let Some(monero_manager) = &self.monero_manager {
// let wallet = monero_manager.main_wallet().await;
// wallet.store().await?;
// }
Ok(())
}
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Jul 18, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement proper wallet cleanup to prevent data loss.

The cleanup function has commented-out code that should save wallet state. Without this, wallet data may be lost on shutdown.

The TODO comment and past review indicate this needs implementation. Would you like me to help implement the proper cleanup logic that iterates through all wallets and calls store() on each?

🤖 Prompt for AI Agents
In swap/src/cli/api.rs around lines 554 to 562, the cleanup function currently
has commented-out code and a TODO for saving wallet states, which risks data
loss on shutdown. Implement the cleanup method to asynchronously iterate over
all wallets managed by monero_manager, calling the store() method on each wallet
to persist their state properly before returning Ok(()). Ensure to handle any
errors from store() calls appropriately to maintain robustness.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@Matroskine We wanted to save the wallets here

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +232 to 234
if let Err(err) = context.cleanup() {
println!("Cleanup failed {}", err);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use proper logging instead of println for cleanup errors.

Errors during cleanup should be logged properly using the logging framework rather than println.

Apply this diff to improve error logging:

-                if let Err(err) = context.cleanup() {
-                    println!("Cleanup failed {}", err);
-                }
+                if let Err(err) = context.cleanup() {
+                    tracing::error!("Context cleanup failed: {}", err);
+                }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if let Err(err) = context.cleanup() {
println!("Cleanup failed {}", err);
}
if let Err(err) = context.cleanup() {
tracing::error!("Context cleanup failed: {}", err);
}
🤖 Prompt for AI Agents
In src-tauri/src/lib.rs around lines 232 to 234, replace the println! macro used
for logging cleanup errors with the appropriate logging framework method, such
as log::error! or another configured logger. This change ensures that cleanup
errors are recorded properly in the application's logs instead of just printing
to standard output.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx (2)

72-81: Add file type filtering for wallet selection.

The wallet file browser should filter for wallet files to improve UX and prevent selection of invalid files.


336-337: Fix cross-platform path handling.

The path splitting assumes Unix-style paths and will fail on Windows systems.

🧹 Nitpick comments (1)
src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx (1)

64-70: Improve dependency handling for recent wallets.

The auto-selection logic is good for UX, but the dependency array [recentWallets.length] won't trigger when wallet content changes with the same length. Consider using a more robust dependency:

- }, [recentWallets.length]);
+ }, [recentWallets]);

Or use useMemo to create a stable reference if performance is a concern.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff6195f and 89240c3.

📒 Files selected for processing (1)
  • src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build (aarch64-apple-darwin, macos-latest)
  • GitHub Check: build (x86_64-apple-darwin, macos-13)
  • GitHub Check: test (macos-latest)
  • GitHub Check: Cursor BugBot
🔇 Additional comments (6)
src-gui/src/renderer/components/modal/seed-selection/SeedSelectionDialog.tsx (6)

12-31: LGTM - Import additions support the new functionality.

The new imports are well-organized and necessary for the enhanced card-based UI, file selection, and async operations.


35-48: LGTM - Well-structured state management.

The type-safe approach for selectedOption and proper extraction of approval and recentWallets with appropriate guards is well implemented.


83-104: LGTM - Well-structured handler functions.

The functions properly handle all seed choice types with appropriate error checking and type-safe construction of the SeedChoice object.


110-118: LGTM - Comprehensive validation logic.

The validation properly handles all seed choice types with appropriate conditions for each scenario.


120-331: LGTM - Excellent UI redesign with improved UX.

The card-based interface is intuitive and well-implemented with proper conditional rendering, styling, and accessibility considerations.


363-380: LGTM - Well-structured dialog actions.

The button layout with Legacy option and proper async handling using PromiseInvokeButton is well-implemented.

Copy link
Copy Markdown

@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.

Bug: Monero Wallets Not Saved on Exit

Monero wallets are not saved on application shutdown. The wallet.store() call in the cleanup function is commented out. If uncommented, the await call would cause a compile error as cleanup is not an async function. This leads to potential data loss as wallet state is not persisted.

swap/src/cli/api.rs#L553-L562

core/swap/src/cli/api.rs

Lines 553 to 562 in 89240c3

pub fn cleanup(&self) -> Result<()> {
// TODO: close all monero wallets
// call store(..) on all wallets
// if let Some(monero_manager) = &self.monero_manager {
// let wallet = monero_manager.main_wallet().await;
// wallet.store().await?;
// }
Ok(())
}

Fix in CursorFix in Web


Bug: VList Height Mismatch Causes Overflow

The VList component's height was changed to 100vh, but its parent container is constrained by minHeight and maxHeight: minHeight. This mismatch causes the VList to overflow its parent, leading to layout issues and clipping. It should use height: "100%" to respect its parent's dimensions.

src-gui/src/renderer/components/other/ScrollablePaperTextBox.tsx#L83-L84

>
<VList ref={virtuaEl} style={{ height: "100vh", width: "100%" }}>

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Maksim Kirillov and others added 2 commits July 18, 2025 14:50
Copy link
Copy Markdown

@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.

Bug: VList Height Prop Ignored

The VList component's height is hardcoded to "100vh", ignoring the minHeight prop. This causes layout issues and inconsistent behavior, as the VList takes up the full viewport height while the parent Box correctly uses minHeight for its maxHeight and minHeight styles. It previously used MIN_HEIGHT.

src-gui/src/renderer/components/other/ScrollablePaperTextBox.tsx#L83-L84

>
<VList ref={virtuaEl} style={{ height: "100vh", width: "100%" }}>

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (3)
swap/src/cli/api.rs (3)

285-391: Verify the task handle storage for pool status updates.

The spawned task for pool status updates should be stored to enable proper cleanup when the context is destroyed.

Consider storing the task handle as suggested in the previous review. The current implementation lacks proper task lifecycle management.


745-778: Add security controls to password verification loop.

The infinite password retry loop lacks security controls and could enable brute-force attempts or trap users.

The previous review comment about adding maximum retry attempts, rate limiting, and cancellation options is still valid and should be implemented.


554-564: Implement proper wallet cleanup to prevent data loss.

The cleanup function has been partially implemented but spawns a task without waiting for completion, which could lead to data loss if the process exits before the wallet is saved.

The current implementation doesn't guarantee the wallet is saved before the process exits. Consider making this synchronous or properly awaiting the task:

 pub fn cleanup(&self) -> Result<()> {
     // TODO: close all monero wallets
     // call store(..) on all wallets
     
-    let monero_manager = self.monero_manager.clone();
-    tokio::spawn(async move {
-        if let Some(monero_manager) = monero_manager {
-            let wallet = monero_manager.main_wallet().await;
-            wallet.store(None).await;
-        }
-    });
+    // Use tokio::task::block_in_place to ensure wallet is saved before returning
+    if let Some(monero_manager) = &self.monero_manager {
+        let wallet = tokio::task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(async {
+                monero_manager.main_wallet().await
+            })
+        });
+        tokio::task::block_in_place(|| {
+            tokio::runtime::Handle::current().block_on(async {
+                wallet.store(None).await
+            })
+        });
+    }

     Ok(())
 }
🧹 Nitpick comments (1)
monero-sys/src/bridge.h (1)

400-418: Consider using safer factory function interface.

The C-style factory functions expose raw pointers which could lead to memory leaks if not properly managed. Consider returning smart pointers or adding documentation about ownership.

Add documentation about ownership:

 extern "C" {
+    // Returns a new FunctionBasedListener instance. 
+    // Caller is responsible for calling destroy_listener() to avoid memory leaks.
     WalletListener* create_listener(
         CB_StringU64 on_spent,
         CB_StringU64 on_received,
         CB_StringU64 on_unconfirmed_received,
         CB_U64       on_new_block,
         CB_Void      on_updated,
         CB_Void      on_refreshed,
         CB_Reorg     on_reorg,
         CB_String    on_pool_tx_removed,
         CB_GetPassword on_get_password)
     {
         return new FunctionBasedListener(
             on_spent,on_received,on_unconfirmed_received,on_new_block,
             on_updated,on_refreshed,on_reorg,on_pool_tx_removed,on_get_password);
     }

+    // Destroys a FunctionBasedListener created by create_listener().
+    // Pointer must not be used after this call.
     void destroy_listener(FunctionBasedListener* p) { delete p; }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89240c3 and 08fe1d0.

📒 Files selected for processing (4)
  • monero-sys/src/bridge.h (5 hunks)
  • swap-env/src/config.rs (4 hunks)
  • swap/src/bin/asb.rs (5 hunks)
  • swap/src/cli/api.rs (17 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • swap-env/src/config.rs
  • swap/src/bin/asb.rs
🧰 Additional context used
📓 Path-based instructions (1)
monero-sys/**/bridge.h

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • monero-sys/CLAUDE.md
🧬 Code Graph Analysis (1)
monero-sys/src/bridge.h (3)
monero-sys/src/bridge.rs (44)
  • rescanBlockchainAsync (226-226)
  • pauseRefresh (229-229)
  • stop (232-232)
  • startRefresh (188-188)
  • pendingTransactionFee (291-291)
  • pendingTransactionAmount (294-294)
  • transactionInfoHash (337-337)
  • transactionInfoTimestamp (340-340)
  • on_new_block (494-494)
  • on_new_block (531-533)
  • on_new_block (594-594)
  • on_updated (495-495)
  • on_updated (535-537)
  • on_updated (596-596)
  • on_refreshed (496-496)
  • on_refreshed (539-541)
  • on_refreshed (598-598)
  • on_reorg (423-428)
  • on_reorg (476-483)
  • on_reorg (497-497)
  • on_reorg (543-546)
  • on_reorg (600-608)
  • on_pool_tx_removed (498-498)
  • on_pool_tx_removed (548-550)
  • on_pool_tx_removed (610-610)
  • new_block (420-420)
  • new_block (464-466)
  • updated (421-421)
  • updated (468-470)
  • refreshed (422-422)
  • refreshed (472-474)
  • pool_tx_removed (429-429)
  • pool_tx_removed (485-487)
  • new (508-510)
  • new (561-563)
  • amount (325-325)
  • money_spent (413-413)
  • money_spent (452-454)
  • money_received (414-414)
  • money_received (456-458)
  • unconfirmed_money_received (415-419)
  • unconfirmed_money_received (460-462)
  • create_rust_listener_adapter (443-445)
  • destroy_rust_listener_adapter (447-447)
monero-sys/src/lib.rs (21)
  • stop (556-558)
  • stop (1635-1637)
  • on_new_block (1326-1330)
  • on_new_block (2559-2559)
  • on_updated (1332-1336)
  • on_updated (2561-2561)
  • on_refreshed (1338-1342)
  • on_refreshed (2563-2571)
  • on_reorg (1344-1348)
  • on_reorg (2573-2573)
  • on_pool_tx_removed (1350-1354)
  • on_pool_tx_removed (2575-2575)
  • new (55-57)
  • new (922-933)
  • new (976-992)
  • new (1278-1280)
  • new (1361-1405)
  • new (2331-2336)
  • new (2385-2387)
  • new (2546-2549)
  • amount (2467-2469)
swap/src/monero/wallet.rs (7)
  • on_new_block (203-207)
  • on_updated (209-211)
  • on_refreshed (213-218)
  • on_reorg (220-224)
  • on_pool_tx_removed (226-230)
  • new (79-165)
  • new (240-293)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (macos-latest)
  • GitHub Check: build (aarch64-apple-darwin, macos-latest)
  • GitHub Check: build (x86_64-apple-darwin, macos-13)
🔇 Additional comments (9)
monero-sys/src/bridge.h (5)

19-21: Good documentation improvement for memory safety.

The added attention note about unique_ptr ownership is important for preventing memory leaks and double-free issues in the FFI boundary.


64-82: LGTM - Clean wrapper functions for wallet control.

These inline wrapper functions properly expose wallet control methods (rescanBlockchainAsync, pauseRefresh, stop, startRefresh) to the FFI layer while maintaining the same signature semantics.


248-256: Good addition of transaction accessor functions.

These functions provide clean access to PendingTransaction properties (fee, amount) that are needed for the approval workflow.


270-284: Proper TransactionInfo accessor implementation.

The functions correctly wrap TransactionInfo methods and handle the timestamp conversion safely with explicit casting.


545-604: Well-designed Rust listener adapter.

The RustListenerAdapter class provides a clean bridge between C++ wallet listeners and Rust trait objects. The implementation correctly forwards all callbacks and handles the rust::Box ownership properly.

swap/src/cli/api.rs (4)

4-4: Good import addition for wallet selection.

The SeedChoice import is properly added to support the new interactive wallet selection functionality.


43-43: Proper addition of log directory field.

The log_dir field is correctly added to the Config struct to support the logging initialization changes.


615-632: Good separation of legacy wallet handling.

The request_and_open_monero_wallet_legacy function properly handles the CLI-only wallet opening flow using the existing seed.pem approach.


857-865: Good modular approach for eigenwallet data directory.

The new eigenwallet_data module provides a clean abstraction for the eigenwallet-specific data directory handling.

Comment on lines +300 to +398
class FunctionBasedListener final : public Monero::WalletListener {
public:
FunctionBasedListener(
CB_StringU64 on_spent,
CB_StringU64 on_received,
CB_StringU64 on_unconfirmed_received,
CB_U64 on_new_block,
CB_Void on_updated,
CB_Void on_refreshed,
CB_Reorg on_reorg,
CB_String on_pool_tx_removed,
CB_GetPassword on_get_password)
:
on_spent_(on_spent),
on_received_(on_received),
on_unconfirmed_received_(on_unconfirmed_received),
on_new_block_(on_new_block),
on_updated_(on_updated),
on_refreshed_(on_refreshed),
on_reorg_(on_reorg),
on_pool_tx_removed_(on_pool_tx_removed),
on_get_password_(on_get_password) {}

void moneySpent(const std::string& txid, uint64_t amt) override {
if (on_spent_) {
auto* spent = reinterpret_cast<void(*)(const std::string&, uint64_t)>(on_spent_);
spent(txid, amt);
}
}

void moneyReceived(const std::string& txid, uint64_t amt) override
{ if (on_received_) {
auto* received = reinterpret_cast<void(*)(const std::string&, uint64_t)>(on_received_);
received(txid, amt);
}
}

void unconfirmedMoneyReceived(const std::string& txid, uint64_t amt) override
{ if (on_unconfirmed_received_) {
auto* unconfirmed_received = reinterpret_cast<void(*)(const std::string&, uint64_t)>(on_unconfirmed_received_);
unconfirmed_received(txid, amt);
}
}

void newBlock(uint64_t h) override
{ if (on_new_block_) {
auto* new_block = reinterpret_cast<void(*)(uint64_t)>(on_new_block_);
new_block(h);
}
}

void updated() override
{
if (on_updated_) {
auto* updated = reinterpret_cast<void(*)()>(on_updated_);
updated();
}
}

void refreshed() override
{ if (on_refreshed_) {
auto* refreshed = reinterpret_cast<void(*)()>(on_refreshed_);
refreshed();
}
}

void onReorg(uint64_t h, uint64_t d, size_t t) override
{ if (on_reorg_) {
auto* reorg = reinterpret_cast<void(*)(uint64_t, uint64_t, size_t)>(on_reorg_);
reorg(h, d, t);
}
}

void onPoolTxRemoved(const std::string& txid) override
{ if (on_pool_tx_removed_) {
auto* pool_tx_removed = reinterpret_cast<void(*)(const std::string&)>(on_pool_tx_removed_);
pool_tx_removed(txid);
}
}

optional<std::string> onGetPassword(const char* reason) override {
if (on_get_password_) {
auto* get_password = reinterpret_cast<const char*(*)(const std::string&)>(on_get_password_);
return std::string(get_password(reason));
}
return optional<std::string>();
}

private:
CB_StringU64 on_spent_;
CB_StringU64 on_received_;
CB_StringU64 on_unconfirmed_received_;
CB_U64 on_new_block_;
CB_Void on_updated_;
CB_Void on_refreshed_;
CB_Reorg on_reorg_;
CB_String on_pool_tx_removed_;
CB_GetPassword on_get_password_;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security concern with raw function pointer casting.

The FunctionBasedListener class uses reinterpret_cast to convert opaque uintptr_t values to function pointers. This is inherently unsafe and could lead to crashes or code execution if incorrect pointers are provided.

Consider adding validation or using a safer callback mechanism:

  1. Add null pointer checks before casting
  2. Consider using a vtable-based approach instead of raw function pointers
  3. Add documentation about the expected function signatures
 void moneySpent(const std::string& txid, uint64_t amt) override { 
     if (on_spent_) {
+        // Add basic validation
+        if (on_spent_ == 0) return;
         auto* spent = reinterpret_cast<void(*)(const std::string&, uint64_t)>(on_spent_);
         spent(txid, amt); 
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In monero-sys/src/bridge.h between lines 300 and 398, the FunctionBasedListener
class unsafely uses reinterpret_cast to convert opaque uintptr_t values to
function pointers without validation, risking crashes or security issues. To fix
this, add explicit null pointer checks before each cast and invocation to ensure
the pointers are valid. Additionally, consider refactoring to use a safer
callback mechanism such as a vtable or std::function to avoid raw pointer
casting. Finally, document the expected function signatures clearly to guide
correct usage and prevent misuse.

Comment on lines +286 to +298
// bridge.h
#pragma once
#include <string>
#include <cstdint>
#include "wallet/api/wallet2_api.h"


using CB_StringU64 = uintptr_t;
using CB_U64 = uintptr_t;
using CB_Void = uintptr_t;
using CB_Reorg = uintptr_t;
using CB_String = uintptr_t;
using CB_GetPassword = uintptr_t;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove duplicate pragma and includes.

The file already has #pragma once at the top. These duplicate preprocessor directives and includes should be removed.

Apply this diff to remove the duplicates:

-// bridge.h
-#pragma once
-#include <string>
-#include <cstdint>
-#include "wallet/api/wallet2_api.h"
-
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// bridge.h
#pragma once
#include <string>
#include <cstdint>
#include "wallet/api/wallet2_api.h"
using CB_StringU64 = uintptr_t;
using CB_U64 = uintptr_t;
using CB_Void = uintptr_t;
using CB_Reorg = uintptr_t;
using CB_String = uintptr_t;
using CB_GetPassword = uintptr_t;
using CB_StringU64 = uintptr_t;
using CB_U64 = uintptr_t;
using CB_Void = uintptr_t;
using CB_Reorg = uintptr_t;
using CB_String = uintptr_t;
using CB_GetPassword = uintptr_t;
🤖 Prompt for AI Agents
In monero-sys/src/bridge.h around lines 286 to 298, there are duplicate #pragma
once directives and repeated include statements. Remove the redundant #pragma
once and duplicate #include lines so that each directive and include appears
only once at the top of the file to avoid unnecessary repetition.

Comment on lines +380 to +386
optional<std::string> onGetPassword(const char* reason) override {
if (on_get_password_) {
auto* get_password = reinterpret_cast<const char*(*)(const std::string&)>(on_get_password_);
return std::string(get_password(reason));
}
return optional<std::string>();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential memory safety issue with password callback.

The onGetPassword implementation assumes the returned const char* from the callback is valid and can be safely converted to std::string. This could cause undefined behavior if the callback returns an invalid pointer.

Add null pointer check and consider lifetime management:

 optional<std::string> onGetPassword(const char* reason) override {
     if (on_get_password_) {
         auto* get_password = reinterpret_cast<const char*(*)(const std::string&)>(on_get_password_);
-        return std::string(get_password(reason));
+        const char* result = get_password(reason);
+        if (result != nullptr) {
+            return std::string(result);
+        }
     }
     return optional<std::string>();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
optional<std::string> onGetPassword(const char* reason) override {
if (on_get_password_) {
auto* get_password = reinterpret_cast<const char*(*)(const std::string&)>(on_get_password_);
return std::string(get_password(reason));
}
return optional<std::string>();
}
optional<std::string> onGetPassword(const char* reason) override {
if (on_get_password_) {
auto* get_password = reinterpret_cast<const char*(*)(const std::string&)>(on_get_password_);
const char* result = get_password(reason);
if (result != nullptr) {
return std::string(result);
}
}
return optional<std::string>();
}
🤖 Prompt for AI Agents
In monero-sys/src/bridge.h around lines 380 to 386, the onGetPassword method
assumes the callback returns a valid const char* pointer, which may lead to
undefined behavior if it is null or invalid. To fix this, add a null pointer
check after calling the callback before converting to std::string. If the
pointer is null, return an empty optional or handle the error safely. Also,
ensure the lifetime of the returned pointer is managed properly to avoid
dangling references.

Comment on lines +643 to 836
async fn request_and_open_monero_wallet(
tauri_handle: Option<TauriHandle>,
) -> Result<Arc<Wallets>> {
let network = env_config.monero_network;
let wallet_dir = data_dir.join("monero").join("monero-data");
eigenwallet_data_dir: &PathBuf,
legacy_data_dir: &PathBuf,
env_config: EnvConfig,
daemon: &monero_sys::Daemon,
wallet_database: &monero_sys::Database,
) -> Result<(monero_sys::WalletHandle, Seed), Error> {
let eigenwallet_wallets_dir = eigenwallet_data_dir.join("wallets");

let wallet = match tauri_handle {
Some(tauri_handle) => {
// Get recent wallets from database
let recent_wallets: Vec<String> = wallet_database
.get_recent_wallets(5)
.await
.unwrap_or_default()
.into_iter()
.map(|w| w.wallet_path)
.collect();

// This loop continually requests the user to select a wallet file
// It then requests the user to provide a password.
// It repeats until the user provides a valid password or rejects the password request
// When the user rejects the password request, we prompt him to select a wallet again
loop {
let seed_choice = tauri_handle
.request_seed_selection_with_recent_wallets(recent_wallets.clone())
.await?;

let daemon = monero_sys::Daemon {
address: monero_daemon_address,
ssl: false,
};
let _monero_progress_handle = tauri_handle
.new_background_process_with_initial_progress(
TauriBackgroundProgress::OpeningMoneroWallet,
(),
);

// This is the name of a wallet we only use for blockchain monitoring
const DEFAULT_WALLET: &str = "swap-tool-blockchain-monitoring-wallet";

// Remove the monitoring wallet if it exists
// It doesn't contain any coins
// Deleting it ensures we never have issues at startup
// And we reset the restore height
let wallet_path = wallet_dir.join(DEFAULT_WALLET);
if wallet_path.exists() {
tracing::debug!(
wallet_path = %wallet_path.display(),
"Removing monitoring wallet"
);
let _ = tokio::fs::remove_file(&wallet_path).await;
}
let keys_path = wallet_path.with_extension("keys");
if keys_path.exists() {
tracing::debug!(
keys_path = %keys_path.display(),
"Removing monitoring wallet keys"
);
let _ = tokio::fs::remove_file(keys_path).await;
}
fn new_wallet_path(eigenwallet_wallets_dir: &PathBuf) -> Result<PathBuf> {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();

let wallets = monero::Wallets::new(
wallet_dir,
DEFAULT_WALLET.to_string(),
daemon,
network,
false,
tauri_handle,
)
.await
.context("Failed to initialize Monero wallets")?;
let wallet_path = eigenwallet_wallets_dir.join(format!("wallet_{}", timestamp));

if let Some(parent) = wallet_path.parent() {
swap_fs::ensure_directory_exists(parent)
.context("Failed to create wallet directory")?;
}

Ok(wallet_path)
}

let wallet = match seed_choice {
SeedChoice::RandomSeed => {
// Create wallet with Unix timestamp as name
let wallet_path = new_wallet_path(&eigenwallet_wallets_dir)
.context("Failed to determine path for new wallet")?;

monero::Wallet::open_or_create(
wallet_path.display().to_string(),
daemon.clone(),
env_config.monero_network,
true,
)
.await
.context("Failed to create wallet from random seed")?
}
SeedChoice::FromSeed { seed: mnemonic } => {
// Create wallet from provided seed
let wallet_path = new_wallet_path(&eigenwallet_wallets_dir)
.context("Failed to determine path for new wallet")?;

monero::Wallet::open_or_create_from_seed(
wallet_path.display().to_string(),
mnemonic,
env_config.monero_network,
0,
true,
daemon.clone(),
)
.await
.context("Failed to create wallet from provided seed")?
}
SeedChoice::FromWalletPath { wallet_path } => {
// Helper function to verify password
let verify_password = |password: String| -> Result<bool> {
monero_sys::WalletHandle::verify_wallet_password(
wallet_path.clone(),
password,
)
.map_err(|e| anyhow::anyhow!("Failed to verify wallet password: {}", e))
};

// Request and verify password before opening wallet
let wallet_password: Option<String> = {
const WALLET_EMPTY_PASSWORD: &str = "";

// First try empty password
if verify_password(WALLET_EMPTY_PASSWORD.to_string())? {
Some(WALLET_EMPTY_PASSWORD.to_string())
} else {
// If empty password fails, ask user for password
loop {
// Request password from user
let password = tauri_handle
.request_password(wallet_path.clone())
.await
.inspect_err(|e| {
tracing::error!(
"Failed to get password from user: {}",
e
);
})
.ok();

// If the user rejects the password request (presses cancel)
// We prompt him to select a wallet again
let password = match password {
Some(password) => password,
None => break None,
};

// Verify the password using the helper function
match verify_password(password.clone()) {
Ok(true) => {
break Some(password);
}
Ok(false) => {
// Continue loop to request password again
continue;
}
Err(e) => {
return Err(e);
}
}
}
}
};

let password = match wallet_password {
Some(password) => password,
// None means the user rejected the password request
// We prompt him to select a wallet again
None => {
continue;
}
};

// Open existing wallet with verified password
monero::Wallet::open_or_create_with_password(
wallet_path.clone(),
password,
daemon.clone(),
env_config.monero_network,
true,
)
.await
.context("Failed to open wallet from provided path")?
}

SeedChoice::Legacy => {
let wallet = request_and_open_monero_wallet_legacy(legacy_data_dir, env_config, daemon).await?;
let seed = Seed::from_file_or_generate(legacy_data_dir)
.await
.context("Failed to extract seed from wallet")?;

break (wallet, seed);
}
};

// Extract seed from the wallet
tracing::info!("Extracting seed from wallet directory: {}", legacy_data_dir.display());
let seed = Seed::from_monero_wallet(&wallet)
.await
.context("Failed to extract seed from wallet")?;

break (wallet, seed);
}
}

Ok(Arc::new(wallets))
// If we don't have a tauri handle, we use the seed.pem file
// This is used for the CLI to monitor the blockchain
None => {
let wallet = request_and_open_monero_wallet_legacy(legacy_data_dir, env_config, daemon).await?;
let seed = Seed::from_file_or_generate(legacy_data_dir)
.await
.context("Failed to extract seed from wallet")?;

(wallet, seed)
}
};

Ok(wallet)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Complex wallet selection logic needs validation.

The request_and_open_monero_wallet function implements a comprehensive wallet selection flow but has several areas that need attention:

  1. Password verification loop security - Already flagged in previous reviews
  2. Error handling - Some error paths may not provide clear feedback to users
  3. Wallet path validation - No validation that wallet paths are secure/valid

Consider adding:

  • Wallet path sanitization to prevent directory traversal
  • Better error messages for users
  • Validation of wallet file existence before attempting to open
  • Timeout mechanism for the outer wallet selection loop
 // Add path validation
+fn validate_wallet_path(path: &str) -> Result<PathBuf> {
+    let path = PathBuf::from(path);
+    let canonical = path.canonicalize()
+        .context("Invalid wallet path")?;
+    
+    // Ensure path is within expected directory
+    if !canonical.starts_with(expected_wallet_dir) {
+        bail!("Wallet path is outside allowed directory");
+    }
+    
+    Ok(canonical)
+}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In swap/src/cli/api.rs from lines 643 to 836, improve the wallet selection logic
by adding validation and security checks: sanitize wallet paths to prevent
directory traversal attacks before using them; check that wallet files exist
before attempting to open them and provide clear user-facing error messages if
not; enhance error handling to give more informative feedback on failures; and
implement a timeout or maximum retry count for the outer wallet selection loop
to avoid infinite looping if the user repeatedly cancels or fails password
verification.

@binarybaron binarybaron merged commit a7823d7 into master Jul 18, 2025
3 of 31 checks passed
Copy link
Copy Markdown

@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.

Bug: Badge Displays Incorrect Swap Count

The UnfinishedSwapsBadge component calculates displayedResumableSwapsCount (which subtracts 1 if a swap is running) and uses it to determine badge visibility. However, the badge content incorrectly displays the original resumableSwapsCount instead of the adjusted displayedResumableSwapsCount, leading to an inconsistent and unadjusted count being shown.

src-gui/src/renderer/components/navigation/UnfinishedSwapsCountBadge.tsx#L12-L20

const displayedResumableSwapsCount = isSwapRunning ? resumableSwapsCount - 1 : resumableSwapsCount;
if (displayedResumableSwapsCount > 0) {
return (
<Badge badgeContent={resumableSwapsCount} color="primary">
{children}
</Badge>
);

Fix in CursorFix in Web


Bug: ScrollablePaperTextBox Ignores MinHeight Prop

The VList height in ScrollablePaperTextBox is hardcoded to "100vh". This change, made to fix an undefined MIN_HEIGHT reference, causes the component to always occupy full viewport height, disregarding its minHeight prop and breaking its sizing contract. This can lead to layout issues and overflow when used in height-constrained containers.

src-gui/src/renderer/components/other/ScrollablePaperTextBox.tsx#L83-L84

>
<VList ref={virtuaEl} style={{ height: "100vh", width: "100%" }}>

Fix in CursorFix in Web


Was this report helpful? Give feedback by reacting with 👍 or 👎

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.

4 participants