Commit de53f22
Migrate /v1/users/{id}/transactions/audio to v_token_transactions_history (#844)
## Summary
Adds a mint-agnostic transactions-history view
(\`v_token_transactions_history\`) anchored on
\`sol_token_account_balance_changes\` and migrates the AUDIO
transactions endpoint to use it. The same view can power the USDC
endpoint and any future artist coin's history once two indexer gaps are
filled (called out below).
## The view
\`sol_token_account_balance_changes\` is the only sol_* table with both
\`mint\` and \`block_timestamp\` directly, so it's the natural hub for a
chronological transactions view. Per-row \`transaction_type\` is derived
via LEFT JOINs to the typed tables:
| Source | transaction_type |
|---|---|
| \`sol_reward_disbursements\` + \`challenges.type = 'trending'\` |
\`trending_reward\` |
| \`sol_reward_disbursements\` (else) | \`user_reward\` |
| \`sol_purchases\` (USDC content purchases via Payment Router) |
\`purchase_content\` |
| \`sol_claimable_account_transfers\` + both ends resolve to distinct
user_banks | \`tip\` |
| \`sol_claimable_account_transfers\` (else) | \`transfer\` |
| (no typed match) | \`transfer\` |
\`method\` (\`send\`/\`receive\`) derives from \`SIGN(change)\`. Legacy
semantic of unsigned \`change\` is preserved via \`ABS()\`.
Callers filter at query time: \`WHERE mint = '<mint>' AND user_id = X\`.
## Route migration
\`/v1/users/{id}/transactions/audio\` (and \`/count\`) replace the
\`audio_transactions_history JOIN user_bank_accounts JOIN users\` chain
with a single filter on the view. Response shape, sort options (\`date\`
/ \`transaction_type\`, asc/desc), and pagination are unchanged.
\`/v1/users/{id}/transactions/usdc\` and
\`/v1/users/{id}/withdrawals/download\` are **not** touched — see
"future work" below.
## Known regression
AUDIO Stripe/Coinbase top-ups, classified by the Python indexer as
\`PURCHASE_STRIPE\` / \`PURCHASE_COINBASE\` / \`PURCHASE_UNKNOWN\`
(parsed from memo instructions), now come back as bare \`transfer\` from
the new view. The Go indexer doesn't capture vendor memos yet. The
client adapter at
\`apps/packages/common/src/adapters/audioTransactions.ts\` will route
these to the TRANSFER icon/label until vendor-memo capture lands.
## Tests
New \`api/v_token_transactions_history_test.go\` covers each branch of
the type-derivation CASE:
- **TIP** — claimable transfer between two distinct user_banks. Both
sender and receiver see the row with method derived correctly and
counterpart user_id in metadata.
- **TRANSFER** — claimable transfer where the counterpart isn't a known
user_bank.
- **USER_REWARD** — reward disbursement on a non-trending challenge.
- **TRENDING_REWARD** — reward disbursement on a trending challenge.
- **Mint isolation** — a USDC balance change on the same user doesn't
leak into the AUDIO endpoint.
Plus the existing \`TestGetUserAudioTransactions\` and
\`TestGetUserAudioTransactionsCount\` continue to pass against the new
view (fixtures updated in
\`api/testdata/sol_audio_transactions_fixtures.go\`).
## Future work (not in this PR)
- **\`sol_withdrawals\` table** + Go indexer memo parsing — unblocks the
USDC route migration (legacy types: \`prepare_withdrawal\` /
\`recover_withdrawal\` / \`withdrawal\`).
- **Vendor-memo capture** on the Go indexer — restores
\`PURCHASE_STRIPE\` / \`COINBASE\` / \`UNKNOWN\` classification on the
AUDIO view.
- **USDC route migration** once both gaps above are filled. Will reuse
this same view, filtered by USDC mint.
- **Python \`index_user_bank\` / \`index_spl_token\` /
\`index_rewards_manager\` decommission** after both routes are off
legacy.
## Test plan
- [x] \`go test ./api/ -run
'TestGetUserAudioTransactions|TestVTokenTransactionsHistory|TestV1UsersPurchases|TestV1UsersSales'
-count=1\` — green locally
- [ ] Spot-check on prod replica: \`SELECT transaction_type, count(*)
FROM v_token_transactions_history WHERE mint =
'9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM' GROUP BY 1\` should show
\`transfer\` (largest, includes top-ups), \`user_reward\`, \`tip\`,
\`trending_reward\`. **No** \`purchase_stripe\` / \`coinbase\` /
\`unknown\` — verified-degraded.
- [ ] \`EXPLAIN (ANALYZE, BUFFERS)\` on the audio endpoint query for a
real user — should be an Index Scan on
\`sol_token_account_balance_changes_account_idx\` driving the per-row
joins via \`(signature, instruction_index)\` PK leftmost-prefix on the
typed tables.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 9fd7e23 commit de53f22
7 files changed
Lines changed: 541 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
87 | 89 | | |
88 | 90 | | |
89 | 91 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
12 | 15 | | |
13 | 16 | | |
14 | 17 | | |
| |||
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
41 | | - | |
| 44 | + | |
42 | 45 | | |
43 | | - | |
| 46 | + | |
44 | 47 | | |
45 | 48 | | |
46 | 49 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
52 | 53 | | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
| 59 | + | |
58 | 60 | | |
59 | 61 | | |
60 | 62 | | |
| |||
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
| 83 | + | |
| 84 | + | |
85 | 85 | | |
86 | 86 | | |
87 | 87 | | |
| 88 | + | |
88 | 89 | | |
89 | 90 | | |
90 | 91 | | |
| |||
0 commit comments