Skip to content

feat(wallet): add wallet address book#1990

Open
0xPepeSilvia wants to merge 2 commits intotari-project:developmentfrom
0xPepeSilvia:feat/address-book-1931
Open

feat(wallet): add wallet address book#1990
0xPepeSilvia wants to merge 2 commits intotari-project:developmentfrom
0xPepeSilvia:feat/address-book-1931

Conversation

@0xPepeSilvia
Copy link
Copy Markdown

Summary

Implements a full-stack wallet address book feature (#1931) allowing users to save named contacts with their Ootle addresses.

Changes

  • Storage: New address_book SQLite table with Diesel migration, model, and reader/writer trait implementations
  • SDK: AddressBookApi with add/get/list/update/delete operations and dedicated error type
  • Wallet daemon: 5 JSON-RPC handlers under the address_book namespace, all gated behind JrpcPermission::Admin, with otl_loc_ prefix validation on add/update
  • Rust client: Typed methods on WalletDaemonClient for all 5 operations
  • JS/TS client: Equivalent methods and generated TypeScript bindings
  • Web UI:
    • New /address-book route with MUI DataGrid, add/edit/delete dialogs, duplicate-name detection
    • Address Book nav menu item
    • Autocomplete integration in Token and NFT transfer forms so users can pick from saved contacts

Closes #1931

Test plan

  • Migration applies cleanly against an existing wallet DB
  • Add/list/get/update/delete via JSON-RPC all work and enforce admin auth
  • Adding a non-otl_loc_ address returns an invalid params error
  • Unique-name constraint surfaces a clear error in the UI
  • Token transfer Autocomplete populates from the address book
  • NFT transfer Autocomplete populates from the address book

🤖 Generated with Claude Code

Full-stack implementation of an address book feature:
- SQLite migration for address_book table with unique name constraint
- Diesel storage model + SDK model with reader/writer trait methods
- SDK API layer (AddressBookApi) for CRUD operations
- JSON-RPC handler with address validation (otl_loc_ prefix)
- Server route registration for address_book.{add,list,get,update,delete}
- Rust and JS client methods for all endpoints
- TypeScript bindings for request/response types
- Web UI management page with DataGrid, add/edit/delete dialogs
- Address book integration in token and NFT transfer forms via Autocomplete
- Sidebar navigation entry

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive address book feature across the wallet daemon, SDK, and web UI. It includes backend handlers, database storage using SQLite, and a new frontend page for managing entries. Additionally, it integrates address book lookups into the NFT and token transfer workflows. A bug was identified in the web UI where the memo field of an address book entry cannot be cleared during an update because empty strings are treated as undefined.

Comment on lines +84 to +89
await updateMutation.mutateAsync({
name: editingEntry.name,
new_name: form.name !== editingEntry.name ? form.name.trim() : undefined,
address: form.address !== editingEntry.address ? form.address.trim() : undefined,
memo: form.memo.trim() || undefined,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

There's a bug in the handleSave function when updating an address book entry. The memo field cannot be cleared. If a user deletes the text in the memo field, the change is not sent to the backend because form.memo.trim() || undefined results in undefined for an empty string. The memo field should only be sent if it has changed, similar to how new_name and address are handled.

        const newMemo = form.memo.trim();
        const oldMemo = editingEntry.memo ?? "";
        await updateMutation.mutateAsync({
          name: editingEntry.name,
          new_name: form.name.trim() !== editingEntry.name ? form.name.trim() : undefined,
          address: form.address.trim() !== editingEntry.address ? form.address.trim() : undefined,
          memo: newMemo !== oldMemo ? newMemo : undefined,
        });

@0xPepeSilvia 0xPepeSilvia marked this pull request as draft April 10, 2026 06:15
Two fixes uncovered during self-audit of the address book PR:

1. crates/wallet/storage_sqlite/src/models/address_book_entry.rs used
   chrono::NaiveDateTime, which is not a dependency of the storage
   crate. Every other model uses time::PrimitiveDateTime. Switched to
   match, restoring compilation.

2. The JSON-RPC handler's validate_address only accepted addresses
   starting with "otl_loc_", which rejects addresses on every network
   except LocalNet (MainNet is "otl_", Esmeralda "otl_esm_", etc.).
   Replaced with OotleAddress::from_str, which parses Bech32 and
   accepts any recognised HRP. Added tari_ootle_address as a direct
   dependency of the walletd crate.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@0xPepeSilvia 0xPepeSilvia marked this pull request as ready for review April 10, 2026 06:24
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.

UX: Wallet address book

1 participant