You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 source — Transaction (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.
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_result → AccountScanner::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
Comment on this issue to signal intent (courtesy, not a lock)
Fork the repo and do the work
Submit a PR that references the issue with a closing keyword (e.g. Fixes #1949) so GitHub links your PR to the bounty automatically
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.
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
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
source—Transaction(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), orRecovery(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 newaccounts.get_balance_changesJSON-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
transaction_id.source(Transaction { transaction_id },Scan, orRecovery); non-transaction sources carry a nulltransaction_id. Each entry records before/after for both revealed and confidential balances plus the signed delta.account_balance_changestable withdown.sql;schema.rsis regenerated; existing wallets upgrade without data loss or reset. SDKWalletStoreReader/WalletStoreWriterexpose insert + paginated query methods, covered by storage tests.accounts.get_balance_changesreturns an account's log newest-first withoffset/limitpagination and optionalresource_address/transaction_idfilters; each entry includes vault, resource, before/after, delta, source, nullabletransaction_id, and timestamp.bindings/, a typed method is added to the JSWalletDaemonClient, andweb_ui/distis rebuilt/committed.Transactionentries link to the transaction detail page whileScan/Recovery(and unknown future variants) render a meaningful label with no broken link.cargo +nightly-2025-12-05 fmt --allis clean and the workspace builds.Context
TransactionService(crates/wallet/sdk_services/src/transaction_service/service.rs) →AccountMonitor::on_event(crates/wallet/sdk_services/src/account_monitor/monitor.rs) →AccountScanner::process_result→AccountScanner::refresh_vault(crates/wallet/sdk_services/src/account_monitor/scanner.rs:156-268). The compare-and-write is atscanner.rs:252-265, which callsAccountsApi::update_vault_balance(crates/wallet/sdk/src/apis/accounts.rs:207-216) only when a balance changed.refresh_vaultdoes not currently receive a transaction id — thetx_idknown atprocess_result(scanner.rs:516/:590) must be threaded through asTransaction { tx_id }, and the account-scan path (scanner.rs:143) must passScan.crates/wallet/storage_sqlite/(new migration,schema.rs, model,reader.rs/writer.rs). A genericwallet_eventstable already exists but is an unstructured event blob — a dedicatedaccount_balance_changestable is recommended over overloading it.BalanceChangemodel + internally-taggedBalanceChangeSourceenum incrates/wallet/sdk/src/models/; reader/writer trait methods incrates/wallet/sdk/src/storage/.applications/tari_walletd/src/server.rs(accounts.*arm) with a handler insrc/handlers/accounts.rs; request/response types inclients/wallet_daemon_client/src/types.rs(with ts-rs derives, consistent withBalanceEntry).applications/tari_walletd/web_ui/(React 19 + Vite + MUI) — hook insrc/services/api/hooks/useAccounts.ts; per-vault action inAssetVault/Components/MyAssets.tsx/AssetVault/Tokens/Tokens.tsx/AccountDetails.tsx; tx links tosrc/routes/Transactions/TransactionDetails.tsx.How It Works
Fixes #1949) so GitHub links your PR to the bounty automaticallyNotes