fix(purchases): resolve splits payout wallets historically#824
Merged
Conversation
v_usdc_purchases was joining users.spl_usdc_payout_wallet, i.e. the seller's *current* payout wallet. Anyone who changed wallets after a sale lost their splits[*].user_id attribution (resolved to NULL), which the route then couldn't match to the seller -- showing paid_to_artist=0 on the purchases page and likely contributing to the view's slow plan as well (sequential scan on unindexed users.spl_usdc_payout_wallet, per payment, per result row). Fix: replace the current-state users join with a LATERAL lookup on user_payout_wallet_history filtered by block_timestamp <= sp.created_at. Mirrors what Python's add_wallet_info_to_splits does in the legacy discovery-provider path. Add an index on user_payout_wallet_history (spl_usdc_payout_wallet, block_timestamp) -- the PK is keyed (user_id, block_timestamp), which is the wrong direction for our lookup (we know the wallet, want the user). Tests cover the gotchas: - seller changes payout wallet after a purchase: historical match - multiple historical wallets: picks the one current at purchase time - seller never set custom payout: sol_claimable_accounts fallback - unowned wallet (network share): resolves to null A couple of incidental cleanups along the way: - database.seed: add a blocks baseRow and seed "blocks" before user_payout_wallet_history (which has an FK on blocks.number) - existing tests that set users.spl_usdc_payout_wallet to fake the old current-state lookup now also seed user_payout_wallet_history Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
raymondjacobson
approved these changes
May 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The `v_usdc_purchases` view was joining the seller's current payout wallet (`users.spl_usdc_payout_wallet`). Anyone who changed payout wallets after a sale lost their `splits[*].user_id` attribution: the on-chain `sol_payments.to_account` (the wallet at purchase time) no longer equals the row's current `spl_usdc_payout_wallet`, so the LEFT JOIN produces NULL.
Two user-visible consequences:
Fix
Replace the current-state join with a LATERAL lookup on `user_payout_wallet_history` filtered by `block_timestamp <= sp.created_at`. This mirrors Python's `add_wallet_info_to_splits` (`discovery-provider/src/queries/get_extended_purchase_gate.py:171`) which is what populated the legacy `usdc_purchases.splits` JSON.
Plus an index on `user_payout_wallet_history (spl_usdc_payout_wallet, block_timestamp)` — the PK is keyed `(user_id, block_timestamp)`, which is the wrong direction for our lookup (we know the wallet, want the user).
The fallback chain (`sol_claimable_accounts` → `users.wallet`) is unchanged. It still handles users who never set a custom payout wallet (their `to_account` is the default USDC user-bank PDA, which is stable over time).
Tests
New `api/v_usdc_purchases_splits_test.go` covers the four splits-resolution gotchas:
Incidental cleanups
Test plan
`/v1/users/q7l8a/purchases?limit=50&offset=0&sort_direction=desc&sort_method=date`
🤖 Generated with Claude Code