Skip to content

walletd: Log of balance changes #1949

Description

@sdbondi

Bounty: walletd — Log of Balance Changes

Tier: L — 150,000 XTM

Description

Add a persistent, queryable log of account balance changes to the wallet daemon and surface it in the web UI. Every time a vault's balance changes (revealed and/or confidential), record a structured entry capturing the before/after balances, the signed delta, and an enumerated sourceTransaction (linked to the transaction that caused it), Scan (funds discovered by re-reading on-chain state, e.g. inbound transfers the wallet didn't author), or Recovery (confidential UTXO recovery). All vault balance mutations already funnel through a single chokepoint (AccountScanner::refresh_vault), which is the place to record entries. The work spans a new additive SQLite migration, SDK models/reader/writer methods, a new accounts.get_balance_changes JSON-RPC method, regenerated TS bindings + JS client method, and a web UI surface (a per-vault "View changes" action plus an account-wide log).

Acceptance Criteria

  • A finalized transaction that changes a vault's revealed and/or confidential balance persists a balance-change entry linked to that transaction; a single transaction affecting multiple vaults/accounts (e.g. self-transfer) produces one entry per affected vault, all sharing the same transaction_id.
  • Each entry carries an enumerated source (Transaction { transaction_id }, Scan, or Recovery); non-transaction sources carry a null transaction_id. Each entry records before/after for both revealed and confidential balances plus the signed delta.
  • Entries are idempotent: re-scanning or re-processing the same finalized transaction creates no duplicates, and only non-zero balance changes are logged.
  • An additive diesel migration creates the account_balance_changes table with down.sql; schema.rs is regenerated; existing wallets upgrade without data loss or reset. SDK WalletStoreReader/WalletStoreWriter expose insert + paginated query methods, covered by storage tests.
  • accounts.get_balance_changes returns an account's log newest-first with offset/limit pagination and optional resource_address / transaction_id filters; each entry includes vault, resource, before/after, delta, source, nullable transaction_id, and timestamp.
  • Request/response types are exported via ts-rs into bindings/, a typed method is added to the JS WalletDaemonClient, and web_ui/dist is rebuilt/committed.
  • The web UI provides a per-vault "View changes" action (scoped via the vault/resource filter) and an account-wide log showing timestamp, resource, signed delta, new balance, and source; Transaction entries link to the transaction detail page while Scan/Recovery (and unknown future variants) render a meaningful label with no broken link.
  • Test coverage exists for: tx-driven change, multi-vault tx, non-tx (recovery) change, idempotent re-scan, and the JSON-RPC handler. cargo +nightly-2025-12-05 fmt --all is clean and the workspace builds.

Context

  • Issue opened by @sdbondi
  • Recording hook (single chokepoint): TransactionService (crates/wallet/sdk_services/src/transaction_service/service.rs) → AccountMonitor::on_event (crates/wallet/sdk_services/src/account_monitor/monitor.rs) → AccountScanner::process_resultAccountScanner::refresh_vault (crates/wallet/sdk_services/src/account_monitor/scanner.rs:156-268). The compare-and-write is at scanner.rs:252-265, which calls AccountsApi::update_vault_balance (crates/wallet/sdk/src/apis/accounts.rs:207-216) only when a balance changed.
  • Threading the source: refresh_vault does not currently receive a transaction id — the tx_id known at process_result (scanner.rs:516/:590) must be threaded through as Transaction { tx_id }, and the account-scan path (scanner.rs:143) must pass Scan.
  • Storage: crates/wallet/storage_sqlite/ (new migration, schema.rs, model, reader.rs/writer.rs). A generic wallet_events table already exists but is an unstructured event blob — a dedicated account_balance_changes table is recommended over overloading it.
  • SDK: new BalanceChange model + internally-tagged BalanceChangeSource enum in crates/wallet/sdk/src/models/; reader/writer trait methods in crates/wallet/sdk/src/storage/.
  • JSON-RPC: new method registered in applications/tari_walletd/src/server.rs (accounts.* arm) with a handler in src/handlers/accounts.rs; request/response types in clients/wallet_daemon_client/src/types.rs (with ts-rs derives, consistent with BalanceEntry).
  • UI: applications/tari_walletd/web_ui/ (React 19 + Vite + MUI) — hook in src/services/api/hooks/useAccounts.ts; per-vault action in AssetVault/Components/MyAssets.tsx / AssetVault/Tokens/Tokens.tsx / AccountDetails.tsx; tx links to src/routes/Transactions/TransactionDetails.tsx.
  • Full scope, code references, and open questions (granularity, confidential-amount semantics, retention, NFT vaults) are in the original issue body (preserved below on first sync).

How It Works

  1. Comment on this issue to signal intent (courtesy, not a lock)
  2. Fork the repo and do the work
  3. Submit a PR that references the issue with a closing keyword (e.g. Fixes #1949) so GitHub links your PR to the bounty automatically
  4. Bounties are competitive. Multiple PRs may be submitted for the same bounty. Review comments are public, and contributors may borrow ideas from each other's work. The maintainer selects the PR that best solves the problem, considering code quality, completeness, and contributor behavior. In cases where multiple contributors made significant contributions, the maintainer may split the bounty. The maintainer's decision is final.
  5. Earn XTM (processing may take up to a week; reach out to @Possum on Discord with your Tari address)

Notes

  • This touches wallet balance-tracking code — PR will require thorough review
  • AI-assisted development is expected and encouraged
  • If you get stuck, ask in Discord

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions