Skip to content

feat: load real transaction history in demo app#102

Open
j-kon wants to merge 16 commits into
bitcoindevkit:mainfrom
j-kon:feat/bdk-demo-real-transaction-history
Open

feat: load real transaction history in demo app#102
j-kon wants to merge 16 commits into
bitcoindevkit:mainfrom
j-kon:feat/bdk-demo-real-transaction-history

Conversation

@j-kon

@j-kon j-kon commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace transaction history sample rows with active-wallet-backed data
  • map BDK wallet transactions into demo-app history rows
  • add a clear no-active-wallet state before loading transaction history
  • scope transaction list and detail state by logical wallet record ID
  • add real provider-chain regression coverage for active-wallet mounting and wallet switching
  • keep widget tests lightweight by using fake transaction repositories without constructing real BDK wallets

Context

Continues the transaction presentation scaffold from #62 by wiring the list/detail flow to active wallet transaction data instead of sample rows.

The production transaction repository still uses the BDK Dart API to read wallet transactions. Transaction state is now held by an auto-disposed provider family keyed by logical wallet ID, so changing wallets immediately exposes a fresh state while replacing the FFI wallet object for the same record does not reset loaded rows.

The wallet-switch widget regression creates a real ProviderContainer, sets activeWalletRecordProvider, and mounts TransactionsListPage with UncontrolledProviderScope. It does not override activeWalletIdProvider.

Verification

  • dart format --output=none --set-exit-if-changed lib test example bdk_demo/lib bdk_demo/test
  • dart analyze --fatal-infos --fatal-warnings lib test example
  • dart test
  • flutter analyze inside bdk_demo
  • flutter test --no-pub test inside bdk_demo
  • flutter test --no-pub test/features/transactions/transactions_controller_test.dart
  • flutter test --no-pub test/presentation/transactions/transactions_list_page_test.dart

Validation passed:

  • root Dart tests: 22 passed, 5 environment-gated integration tests skipped
  • bdk_demo Flutter tests: 189 passed
  • transaction controller tests: 6 passed
  • transaction list widget tests: 7 passed
  • no analyzer or formatter issues

Also confirmed:

  • delayed wallet-A results cannot update wallet B state
  • transaction detail caches are isolated by wallet ID
  • replacing an FFI wallet under the same logical record ID does not reset transaction list state
  • no widget test creates a real BDK Wallet
  • production transactions_repository.dart still imports and uses BDK

@j-kon
j-kon marked this pull request as ready for review June 30, 2026 14:35
@Shamsudeen12

Copy link
Copy Markdown

Hi @j-kon, nice work here

I noticed something. The transactionsControllerProvider isn't auto-disposed, so it outlives the TransactionsListPage and persists the data for the previous session. It gates on hasActiveWalletProvider, which is a boolean and switching from wallet A to wallet B through the Active Wallets screen (active_wallets_page.dart → activeWalletProvider.notifier.set(...)) leaves that provider at true, so the controller's build() never re-runs and the list keeps rendering wallet A's transactions under wallet B.

Steps to reproduce: load wallet A → open Transactions → Load Transaction History → switch to wallet B → reopen Transactions. It still shows wallet A's transactions; tapping one resolves against B's repository and 404s as "Transaction not found."

The sibling providers already handle this by keying off wallet identity, blockchain_providers.dart resets when activeWalletRecordProvider.id changes. Might be worth having the controller watch an activeWalletId (or ref.listen the record and reset) instead of a bool. Happy to be corrected if in-place switching isn't a supported flow.

@j-kon

j-kon commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed. You were right that the transaction state was scoped only to wallet availability rather than the logical active wallet.

I updated the transaction list and detail flow to use the active wallet record ID. Switching from wallet A to wallet B now clears the previous wallet’s transaction state, and stale asynchronous results are ignored if the active wallet changes before loading completes.

I also added coverage for wallet switching so transaction list and detail data cannot leak between active wallets.

@Shamsudeen12

Copy link
Copy Markdown

Thanks @j-kon
Everything looks good from my end 🙌🏾

@override
TransactionsState build() => const TransactionsState.idle();
TransactionsState build() {
final activeWalletId = ref.watch(activeWalletIdProvider);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tNACK e4c458c

Navigating to Transaction History on device throws:

setState() or markNeedsBuild() called during build. UncontrolledProviderScope … already building widget currently being built: TransactionsListPage

pls fix the rebuild + listen pattern, and add a widget coverage that mounts the list page with an active wallet so this can’t fail silently.

@j-kon j-kon Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 969da23.

Transaction state is now an auto-disposed provider family keyed by wallet ID. Switching wallets clears old rows, and stale results cannot update the new wallet.

I also added real provider-chain widget coverage for mounting and switching wallets without overriding activeWalletIdProvider. All local tests and CI pass.

@Johnosezele Johnosezele left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Every time I leave the wallet/Home flow and navigate back to Transaction History, I land on "Transaction history not loaded yet" and have to tap Load Transaction History again, even right after a successful sync with txs already known to the wallet (Home shows the balance).

Likely related to transactionsControllerProvider being NotifierProvider.autoDispose.family keyed by wallet ID: leaving the page disposes the controller, so remount always returns TransactionsState.idle().

Please either:

  1. keep loaded history for the active wallet across navigations (drop autoDispose, or cache last success by wallet ID), or
  2. auto-load on page open when an active wallet is present,

and add widget coverage that pumps the list page, navigates away, returns and still shows (or auto-reloads) the previously loaded rows without an extra tap.

@Johnosezele Johnosezele left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

After an incoming tx confirms, Home correctly updates (Balance + Trusted spendable reflect the synced wallet), but Transaction History can still show the earlier pending row until I leave, come back, and tap Reload Transaction History.

So for a while Home and History disagree: spendable says confirmed funds are available, history still says "Awaiting confirmation."

Root cause looks like split data paths:

  • Home reads wallet.balance() via balanceSnapshotProvider on every successful sync
  • History only refreshes on explicit Load/Reload of transactionsControllerProvider (and with autoDispose it also drops to idle when leaving the route)

Please invalidate or auto-reload transaction history for the active wallet when sync completes (same moment balance is applied), so pending, confirmed cannot lag behind Home.
Widget coverage: load history while pending, then, apply post-sync wallet, and assert list shows confirmed without a manual reload.

@j-kon

j-kon commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Johnosezele for the detailed review! I've updated PR #102 in commits e9b32f0 and 1958c9d:

  1. Auto-load on Navigation: Added microtask auto-loading in TransactionsController.build(). Navigating to TransactionsListPage now automatically loads transaction history without showing an un-loaded state or requiring a manual button tap.
  2. Auto-reload on Wallet Sync & Post-Broadcast: TransactionsController now listens to activeWalletProvider and syncStatusProvider (SyncStatus.synced), and send_page.dart triggers background transaction reloads upon broadcast success. As soon as a sync completes or a transaction confirms on-chain, transaction history auto-refreshes in the background without UI flicker or manual reloading.
  3. Widget Coverage: Added widget tests covering automatic initial load, navigation away and back, auto-reload on wallet sync (pending -> confirmed transition), and wallet switching.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants