Skip to content

fix: deduplicate multi-wallet BDK events and correct onchain payment direction#52

Merged
ovitrif merged 2 commits intomainfrom
fix/multi-wallet-event-dedup-and-direction
Feb 20, 2026
Merged

fix: deduplicate multi-wallet BDK events and correct onchain payment direction#52
ovitrif merged 2 commits intomainfrom
fix/multi-wallet-event-dedup-and-direction

Conversation

@ben-kaufman
Copy link
Copy Markdown

This PR:

  1. Fixes duplicate onchain events emitted when a transaction touches multiple address-type wallets
  2. Fixes cross-wallet sends being incorrectly recorded as Inbound in list_payments
  3. Adds a never-downgrade guard to prevent a partial wallet view from transiently corrupting a correct Outbound direction

What was happening

With multiple address-type wallets enabled, each sub-wallet independently emits its own BdkWalletEvent when it observes a transaction. A single cross-wallet send (e.g. Legacy inputs + NativeSegwit change) produced two OnchainTransactionReceived events and two OnchainTransactionConfirmed events per sync cycle.

Additionally, PaymentDetails::update did not apply direction updates, so when the primary wallet synced first and saw only a change output, the payment was stored as Inbound and never corrected even after secondary wallets synced.

A secondary bug also existed: if a change-only wallet synced before an input-holding wallet, sent_and_received temporarily reported more received than sent, which would have flipped a correct Outbound back to Inbound.

What changed

src/chain/mod.rsprocess_wallet_events now maintains four per-type HashSets (received, confirmed, reorged, replaced) and skips duplicate txids. Per-type sets are used instead of a single set because two wallets with different sync history can legitimately emit different event types for the same txid in one cycle.

src/payment/store.rsPaymentDetails::update now applies direction updates from later wallet syncs, with a one-way guard: Inbound→Outbound corrections are allowed, Outbound→Inbound downgrades are blocked (a pure receive always has sent=0 and can never compute as Outbound, so Outbound is monotonically final).

Tests

Added 2 unit tests in src/payment/store.rs and 6 integration tests in tests/multi_address_types_tests.rs:

  • test_cross_wallet_send_emits_single_received_event
  • test_cross_wallet_send_emits_single_confirmed_event
  • test_cross_wallet_send_payment_direction_is_outbound
  • test_cross_wallet_reorg_deduplicates_events
  • test_send_all_payment_direction_is_outbound
  • test_receive_to_secondary_wallet_direction_is_inbound

QA Notes

Testing

  • cargo test passes (integration tests require ulimit -n 10000)
  • cargo clippy clean
  • cargo fmt --check clean

Integration

  • Tested against: bitkit-ios (fixes duplicate OnchainTransactionReceived log and wrong payment direction in listPayments for cross-wallet sends)

…direction

When a transaction touches multiple address-type wallets, each wallet
independently emits a BdkWalletEvent, causing duplicate onchain events
per sync cycle. Add per-type HashSet deduplication in process_wallet_events
so each txid produces at most one event of each type per sync.

Also fix PaymentDetails::update to apply direction corrections as secondary
wallets sync. When the primary wallet sees only a change output it records
the payment as Inbound; once secondary wallets reveal the spent inputs the
direction must update to Outbound. Add a never-downgrade guard so a
change-only wallet syncing before an input-holding wallet cannot transiently
flip a correct Outbound back to Inbound.

Add unit tests for the direction logic and integration tests covering event
deduplication, direction correction, send_all direction, secondary wallet
receives, and reorg deduplication.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@ovitrif ovitrif left a comment

Choose a reason for hiding this comment

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

LGTM

Nice tests too!

@ovitrif ovitrif merged commit d947a54 into main Feb 20, 2026
@ben-kaufman ben-kaufman deleted the fix/multi-wallet-event-dedup-and-direction branch February 20, 2026 08:35
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.

2 participants