From dc8966c1cbf590315d98c7adaf520a2a628a0ffb Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Fri, 15 May 2026 18:30:36 -0500 Subject: [PATCH] Swap purchase readers from usdc_purchases to v_usdc_purchases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 2 of the purchases-domain cutover. Now that #815 (step 1) has backfilled sol_purchases and added the compatibility view, all ~17 Go API routes that joined usdc_purchases swap over to v_usdc_purchases. Code changes are minimal — table-name renames in route SQL. The view absorbs the schema differences (sol_purchases + sol_payments + users + tracks/playlists -> legacy column shape). Test fixtures are rewritten: callers seed sol_purchases + sol_payments instead of usdc_purchases. Drops fixture columns the view derives (seller_user_id, extra_amount, splits). For tests that assert on splits user_id, the seller's spl_usdc_payout_wallet is set so the view's lookup resolves. For tests that assert on non-zero extra_amount, a track_price_history row is seeded so the view's amount - base_price computation produces the expected value. Co-Authored-By: Claude Opus 4.7 --- api/comms/chat.go | 2 +- api/comms/chat_blast_test.go | 12 +- api/comms/validator.go | 2 +- api/comms_blasts.go | 2 +- api/comms_blasts_test.go | 48 +---- api/dbv1/access.go | 8 +- api/server_test.go | 3 +- api/testdata/usdc_purchases_fixtures.go | 54 +++--- api/v1_explore_best_selling.go | 2 +- api/v1_explore_best_selling_test.go | 236 +++--------------------- api/v1_users_library_playlists.go | 2 +- api/v1_users_library_tracks.go | 2 +- api/v1_users_library_tracks_test.go | 11 +- api/v1_users_purchasers.go | 2 +- api/v1_users_purchasers_count.go | 2 +- api/v1_users_purchasers_count_test.go | 46 +---- api/v1_users_purchasers_test.go | 46 +---- api/v1_users_purchases.go | 2 +- api/v1_users_purchases_count.go | 2 +- api/v1_users_purchases_count_test.go | 80 +------- api/v1_users_purchases_download.go | 2 +- api/v1_users_purchases_download_test.go | 87 ++++----- api/v1_users_purchases_test.go | 127 ++++++------- api/v1_users_sales.go | 2 +- api/v1_users_sales_aggregate.go | 2 +- api/v1_users_sales_aggregate_test.go | 68 +------ api/v1_users_sales_count.go | 2 +- api/v1_users_sales_count_test.go | 80 +------- api/v1_users_sales_download.go | 2 +- api/v1_users_sales_download_test.go | 86 ++++----- api/v1_users_sales_test.go | 90 ++------- database/seed.go | 29 +++ 32 files changed, 295 insertions(+), 846 deletions(-) diff --git a/api/comms/chat.go b/api/comms/chat.go index 71d96d47..fe493782 100644 --- a/api/comms/chat.go +++ b/api/comms/chat.go @@ -393,7 +393,7 @@ func getNewBlasts(tx dbv1.DBTX, ctx context.Context, arg getNewBlastsParams) ([] OR from_user_id IN ( -- customer_audience SELECT seller_user_id - FROM usdc_purchases p + FROM v_usdc_purchases p WHERE blast.audience = 'customer_audience' AND p.seller_user_id = blast.from_user_id AND p.buyer_user_id = @user_id diff --git a/api/comms/chat_blast_test.go b/api/comms/chat_blast_test.go index 1e2d75f5..4c277e92 100644 --- a/api/comms/chat_blast_test.go +++ b/api/comms/chat_blast_test.go @@ -773,16 +773,8 @@ func TestChatBlastPurchasers(t *testing.T) { "owner_id": 1, }, }, - "usdc_purchases": { - { - "buyer_user_id": 203, - "seller_user_id": 1, - "content_type": "track", - "content_id": 1, - "amount": 5990000, // 5.99USDC in micro-units - "signature": "purchase_sig_123", - "slot": 101, - }, + "sol_purchases": { + {"signature": "purchase_sig_123", "instruction_index": 0, "buyer_user_id": 203, "content_type": "track", "content_id": 1, "amount": 5990000, "slot": 101, "is_valid": true}, }, }) diff --git a/api/comms/validator.go b/api/comms/validator.go index 1d2948d5..af8d5260 100644 --- a/api/comms/validator.go +++ b/api/comms/validator.go @@ -623,7 +623,7 @@ func hasNewBlastFromUser(pool *dbv1.DBPools, ctx context.Context, userID int32, -- customer_audience (blast.audience = 'customer_audience' and exists ( SELECT 1 - FROM usdc_purchases p + FROM v_usdc_purchases p WHERE p.seller_user_id = blast.from_user_id AND p.buyer_user_id = $1 AND ( diff --git a/api/comms_blasts.go b/api/comms_blasts.go index 035b5ea4..cd7639ff 100644 --- a/api/comms_blasts.go +++ b/api/comms_blasts.go @@ -78,7 +78,7 @@ func (app *ApiServer) getNewBlasts(c *fiber.Ctx) error { OR from_user_id IN ( -- customer_audience SELECT seller_user_id - FROM usdc_purchases p + FROM v_usdc_purchases p WHERE blast.audience = 'customer_audience' AND p.seller_user_id = blast.from_user_id AND p.buyer_user_id = @user_id diff --git a/api/comms_blasts_test.go b/api/comms_blasts_test.go index 47eb7bb4..087b2227 100644 --- a/api/comms_blasts_test.go +++ b/api/comms_blasts_test.go @@ -97,37 +97,10 @@ func TestGetNewBlasts(t *testing.T) { "child_track_id": 2, }, }, - "usdc_purchases": { - { - "buyer_user_id": 2, - "seller_user_id": 1, - "content_type": "track", - "content_id": 1, - "amount": 1000000, // 1 USDC in micro-units - "created_at": now.Add(-time.Hour * 2), // Purchase before blast - "signature": "purchase_sig_123", - "slot": 101, - }, - { - "buyer_user_id": 2, - "seller_user_id": 1, - "content_type": "track", - "content_id": 2, - "amount": 2000000, // 2 USDC in micro-units - "created_at": now.Add(-time.Hour * 2), // Purchase before blast - "signature": "purchase_sig_456", - "slot": 102, - }, - { - "buyer_user_id": 3, - "seller_user_id": 1, - "content_type": "track", - "content_id": 1, // User 3 only bought track 1, not track 2 - "amount": 500000, // 0.5 USDC in micro-units - "created_at": now.Add(-time.Hour * 2), // Purchase before blast - "signature": "purchase_sig_789", - "slot": 103, - }, + "sol_purchases": { + {"signature": "purchase_sig_123", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 1, "amount": 1000000, "created_at": now.Add(-time.Hour * 2), "slot": 101, "is_valid": true}, + {"signature": "purchase_sig_456", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 2, "amount": 2000000, "created_at": now.Add(-time.Hour * 2), "slot": 102, "is_valid": true}, + {"signature": "purchase_sig_789", "instruction_index": 0, "buyer_user_id": 3, "content_type": "track", "content_id": 1, "amount": 500000, "created_at": now.Add(-time.Hour * 2), "slot": 103, "is_valid": true}, }, "artist_coins": { { @@ -665,17 +638,8 @@ func TestGetNewBlastsAudienceSpecificFiltering(t *testing.T) { "updated_at": now.Add(-time.Hour), }, }, - "usdc_purchases": { - { - "buyer_user_id": 2, - "seller_user_id": 1, - "content_type": "track", - "content_id": 1, // User only bought track 1 - "amount": 1000000, - "created_at": now.Add(-time.Hour), - "signature": "purchase_sig_123", - "slot": 101, - }, + "sol_purchases": { + {"signature": "purchase_sig_123", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 1, "amount": 1000000, "created_at": now.Add(-time.Hour), "slot": 101, "is_valid": true}, }, "chat_blast": { { diff --git a/api/dbv1/access.go b/api/dbv1/access.go index 6f667ec1..193b6a43 100644 --- a/api/dbv1/access.go +++ b/api/dbv1/access.go @@ -63,7 +63,7 @@ func (q *Queries) GetPlaylistAccess( err := q.db.QueryRow(ctx, ` SELECT EXISTS ( SELECT 1 - FROM usdc_purchases + FROM v_usdc_purchases WHERE buyer_user_id = $1 AND content_id = $2 AND content_type = 'album' @@ -261,7 +261,7 @@ func (q *Queries) GetBulkTrackAccess( g.Go(func() error { rows, err := q.db.Query(ctx, ` SELECT content_id - FROM usdc_purchases + FROM v_usdc_purchases WHERE buyer_user_id = $1 AND content_id = ANY($2) AND content_type = 'track' @@ -357,7 +357,7 @@ func (q *Queries) GetBulkTrackAccess( g.Go(func() error { rows, err := q.db.Query(ctx, ` SELECT content_id - FROM usdc_purchases + FROM v_usdc_purchases WHERE buyer_user_id = $1 AND content_id = ANY($2) AND content_type = 'album' @@ -390,7 +390,7 @@ func (q *Queries) GetBulkTrackAccess( g.Go(func() error { rows, err := q.db.Query(ctx, ` SELECT up.content_id - FROM usdc_purchases up + FROM v_usdc_purchases up JOIN jsonb_each_text($2) AS prev_playlists(playlist_id, removal_time) ON up.content_id = prev_playlists.playlist_id::integer WHERE up.buyer_user_id = $1 diff --git a/api/server_test.go b/api/server_test.go index c763362c..00e03213 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -102,7 +102,8 @@ func testAppWithFixtures(t *testing.T) *ApiServer { database.SeedTable(app.pool.Replicas[0], "track_trending_scores", testdata.TrackTrendingScoresFixtures) database.SeedTable(app.pool.Replicas[0], "trending_results", testdata.TrendingResultsFixtures) database.SeedTable(app.pool.Replicas[0], "track_routes", testdata.TrackRoutesFixtures) - database.SeedTable(app.pool.Replicas[0], "usdc_purchases", testdata.UsdcPurchasesFixtures) + database.SeedTable(app.pool.Replicas[0], "sol_purchases", testdata.SolPurchasesFixtures) + database.SeedTable(app.pool.Replicas[0], "sol_payments", testdata.SolPaymentsFixtures) database.SeedTable(app.pool.Replicas[0], "usdc_transactions_history", testdata.UsdcTransactionsHistoryFixtures) database.SeedTable(app.pool.Replicas[0], "user_bank_accounts", testdata.UserBankAccountsFixtures) database.SeedTable(app.pool.Replicas[0], "user_challenges", testdata.UserChallengesFixtures) diff --git a/api/testdata/usdc_purchases_fixtures.go b/api/testdata/usdc_purchases_fixtures.go index e354b3bf..3b430ab1 100644 --- a/api/testdata/usdc_purchases_fixtures.go +++ b/api/testdata/usdc_purchases_fixtures.go @@ -1,40 +1,30 @@ package testdata -var UsdcPurchasesFixtures = []map[string]any{ +// SolPurchasesFixtures + SolPaymentsFixtures populate the new tables backing +// v_usdc_purchases. Maintained alongside the legacy UsdcPurchasesFixtures +// during the purchases-cutover transition. +var SolPurchasesFixtures = []map[string]any{ { - "signature": "a", - "buyer_user_id": 11, - "seller_user_id": 3, - "content_id": 303, - "content_type": "track", - "amount": 135, - "splits": []map[string]any{ - { - "amount": 135000000, - "user_id": 3, - "eth_wallet": "0x123", - "percentage": 100, - }, - }, + "signature": "a", + "instruction_index": 0, + "buyer_user_id": 11, + "content_id": 303, + "content_type": "track", + "amount": 135, + "is_valid": true, }, { - "signature": "b", - "buyer_user_id": 11, - "seller_user_id": 3, - "content_id": 4, - "content_type": "album", - "amount": 135, - "splits": []map[string]any{ - { - "amount": 135000000, - "user_id": 3, - "eth_wallet": "0x123", - "percentage": 100, - }, - }, + "signature": "b", + "instruction_index": 0, + "buyer_user_id": 11, + "content_id": 4, + "content_type": "album", + "amount": 135, + "is_valid": true, }, } -// signature,buyer_user_id,seller_user_id,content_id,content_type,amount,splits -// a,11,3,303,track,135,"[{""amount"": 135000000, ""user_id"": 3, ""eth_wallet"": ""0x123"", ""percentage"": 100}]" -// b,11,3,4,album,135,"[{""amount"": 135000000, ""user_id"": 3, ""eth_wallet"": ""0x123"", ""percentage"": 100}]" +var SolPaymentsFixtures = []map[string]any{ + {"signature": "a", "instruction_index": 0, "route_index": 0, "to_account": "0x123", "amount": 135000000, "slot": 101}, + {"signature": "b", "instruction_index": 0, "route_index": 0, "to_account": "0x123", "amount": 135000000, "slot": 101}, +} diff --git a/api/v1_explore_best_selling.go b/api/v1_explore_best_selling.go index d5bc0484..8649d4a6 100644 --- a/api/v1_explore_best_selling.go +++ b/api/v1_explore_best_selling.go @@ -42,7 +42,7 @@ func (app *ApiServer) v1ExploreBestSelling(c *fiber.Ctx) error { sql := ` WITH ranked_sales AS ( SELECT content_id, content_type, COUNT(*) AS sales_count - FROM usdc_purchases + FROM v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` GROUP BY content_id, content_type ), diff --git a/api/v1_explore_best_selling_test.go b/api/v1_explore_best_selling_test.go index 7baa634b..16156db2 100644 --- a/api/v1_explore_best_selling_test.go +++ b/api/v1_explore_best_selling_test.go @@ -58,223 +58,43 @@ func TestExploreBestSelling(t *testing.T) { "users": users, "tracks": tracks, "playlists": albums, - "usdc_purchases": { + "sol_purchases": { // Track 1: 5 purchases - { - "buyer_user_id": 6, - "seller_user_id": 1, - "content_id": 1, - "content_type": "track", - "amount": 1000, - "signature": "0x1", - }, - { - "buyer_user_id": 7, - "seller_user_id": 1, - "content_id": 1, - "content_type": "track", - "amount": 1000, - "signature": "0x2", - }, - { - "buyer_user_id": 8, - "seller_user_id": 1, - "content_id": 1, - "content_type": "track", - "amount": 1000, - "signature": "0x3", - }, - { - "buyer_user_id": 9, - "seller_user_id": 1, - "content_id": 1, - "content_type": "track", - "amount": 1000, - "signature": "0x4", - }, - { - "buyer_user_id": 10, - "seller_user_id": 1, - "content_id": 1, - "content_type": "track", - "amount": 1000, - "signature": "0x5", - }, + {"signature": "0x1", "instruction_index": 0, "buyer_user_id": 6, "content_id": 1, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x2", "instruction_index": 0, "buyer_user_id": 7, "content_id": 1, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x3", "instruction_index": 0, "buyer_user_id": 8, "content_id": 1, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x4", "instruction_index": 0, "buyer_user_id": 9, "content_id": 1, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x5", "instruction_index": 0, "buyer_user_id": 10, "content_id": 1, "content_type": "track", "amount": 1000, "is_valid": true}, // Album 1: 4 purchases - { - "buyer_user_id": 6, - "seller_user_id": 1, - "content_id": 1, - "content_type": "album", - "amount": 2000, - "signature": "0x6", - }, - { - "buyer_user_id": 7, - "seller_user_id": 1, - "content_id": 1, - "content_type": "album", - "amount": 2000, - "signature": "0x7", - }, - { - "buyer_user_id": 8, - "seller_user_id": 1, - "content_id": 1, - "content_type": "album", - "amount": 2000, - "signature": "0x8", - }, - { - "buyer_user_id": 9, - "seller_user_id": 1, - "content_id": 1, - "content_type": "album", - "amount": 2000, - "signature": "0x9", - }, + {"signature": "0x6", "instruction_index": 0, "buyer_user_id": 6, "content_id": 1, "content_type": "album", "amount": 2000, "is_valid": true}, + {"signature": "0x7", "instruction_index": 0, "buyer_user_id": 7, "content_id": 1, "content_type": "album", "amount": 2000, "is_valid": true}, + {"signature": "0x8", "instruction_index": 0, "buyer_user_id": 8, "content_id": 1, "content_type": "album", "amount": 2000, "is_valid": true}, + {"signature": "0x9", "instruction_index": 0, "buyer_user_id": 9, "content_id": 1, "content_type": "album", "amount": 2000, "is_valid": true}, // Track 2: 3 purchases - { - "buyer_user_id": 6, - "seller_user_id": 2, - "content_id": 2, - "content_type": "track", - "amount": 1000, - "signature": "0x10", - }, - { - "buyer_user_id": 7, - "seller_user_id": 2, - "content_id": 2, - "content_type": "track", - "amount": 1000, - "signature": "0x11", - }, - { - "buyer_user_id": 8, - "seller_user_id": 2, - "content_id": 2, - "content_type": "track", - "amount": 1000, - "signature": "0x12", - }, + {"signature": "0x10", "instruction_index": 0, "buyer_user_id": 6, "content_id": 2, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x11", "instruction_index": 0, "buyer_user_id": 7, "content_id": 2, "content_type": "track", "amount": 1000, "is_valid": true}, + {"signature": "0x12", "instruction_index": 0, "buyer_user_id": 8, "content_id": 2, "content_type": "track", "amount": 1000, "is_valid": true}, // Album 2: 2 purchases - { - "buyer_user_id": 6, - "seller_user_id": 2, - "content_id": 2, - "content_type": "album", - "amount": 2000, - "signature": "0x13", - }, - { - "buyer_user_id": 7, - "seller_user_id": 2, - "content_id": 2, - "content_type": "album", - "amount": 2000, - "signature": "0x14", - }, + {"signature": "0x13", "instruction_index": 0, "buyer_user_id": 6, "content_id": 2, "content_type": "album", "amount": 2000, "is_valid": true}, + {"signature": "0x14", "instruction_index": 0, "buyer_user_id": 7, "content_id": 2, "content_type": "album", "amount": 2000, "is_valid": true}, // Track 5: 1 purchase - { - "buyer_user_id": 6, - "seller_user_id": 5, - "content_id": 5, - "content_type": "track", - "amount": 1000, - "signature": "0x15", - }, - //Album 5: 1 purchase - { - "buyer_user_id": 6, - "seller_user_id": 5, - "content_id": 5, - "content_type": "album", - "amount": 1000, - "signature": "0x16", - }, + {"signature": "0x15", "instruction_index": 0, "buyer_user_id": 6, "content_id": 5, "content_type": "track", "amount": 1000, "is_valid": true}, + // Album 5: 1 purchase + {"signature": "0x16", "instruction_index": 0, "buyer_user_id": 6, "content_id": 5, "content_type": "album", "amount": 1000, "is_valid": true}, // Track 3 (deleted): 1 purchase - { - "buyer_user_id": 6, - "seller_user_id": 3, - "content_id": 3, - "content_type": "track", - "amount": 1000, - "signature": "0x17", - }, + {"signature": "0x17", "instruction_index": 0, "buyer_user_id": 6, "content_id": 3, "content_type": "track", "amount": 1000, "is_valid": true}, // Track 4 (unlisted): 1 purchase - { - "buyer_user_id": 7, - "seller_user_id": 4, - "content_id": 4, - "content_type": "track", - "amount": 1000, - "signature": "0x18", - }, + {"signature": "0x18", "instruction_index": 0, "buyer_user_id": 7, "content_id": 4, "content_type": "track", "amount": 1000, "is_valid": true}, // Album 3 (deleted): 1 purchase - { - "buyer_user_id": 6, - "seller_user_id": 3, - "content_id": 3, - "content_type": "album", - "amount": 2000, - "signature": "0x19", - }, + {"signature": "0x19", "instruction_index": 0, "buyer_user_id": 6, "content_id": 3, "content_type": "album", "amount": 2000, "is_valid": true}, // Album 4 (unlisted): 1 purchase - { - "buyer_user_id": 7, - "seller_user_id": 4, - "content_id": 4, - "content_type": "album", - "amount": 2000, - "signature": "0x20", - }, - // Track 6 (purchases from before cutoff): 5 purchases - { - "buyer_user_id": 6, - "seller_user_id": 6, - "content_id": 6, - "content_type": "track", - "amount": 1000, - "created_at": time.Now().AddDate(0, -7, 0), - "signature": "0x21", - }, - { - "buyer_user_id": 7, - "seller_user_id": 6, - "content_id": 6, - "content_type": "track", - "amount": 1000, - "created_at": time.Now().AddDate(0, -7, 0), - "signature": "0x22", - }, - { - "buyer_user_id": 8, - "seller_user_id": 6, - "content_id": 6, - "content_type": "track", - "amount": 1000, - "created_at": time.Now().AddDate(0, -7, 0), - "signature": "0x23", - }, - { - "buyer_user_id": 9, - "seller_user_id": 6, - "content_id": 6, - "content_type": "track", - "amount": 1000, - "created_at": time.Now().AddDate(0, -7, 0), - "signature": "0x24", - }, - { - "buyer_user_id": 10, - "seller_user_id": 6, - "content_id": 6, - "content_type": "track", - "amount": 1000, - "created_at": time.Now().AddDate(0, -7, 0), - "signature": "0x25", - }, + {"signature": "0x20", "instruction_index": 0, "buyer_user_id": 7, "content_id": 4, "content_type": "album", "amount": 2000, "is_valid": true}, + // Track 6 (purchases from before 6mo cutoff): 5 purchases + {"signature": "0x21", "instruction_index": 0, "buyer_user_id": 6, "content_id": 6, "content_type": "track", "amount": 1000, "created_at": time.Now().AddDate(0, -7, 0), "is_valid": true}, + {"signature": "0x22", "instruction_index": 0, "buyer_user_id": 7, "content_id": 6, "content_type": "track", "amount": 1000, "created_at": time.Now().AddDate(0, -7, 0), "is_valid": true}, + {"signature": "0x23", "instruction_index": 0, "buyer_user_id": 8, "content_id": 6, "content_type": "track", "amount": 1000, "created_at": time.Now().AddDate(0, -7, 0), "is_valid": true}, + {"signature": "0x24", "instruction_index": 0, "buyer_user_id": 9, "content_id": 6, "content_type": "track", "amount": 1000, "created_at": time.Now().AddDate(0, -7, 0), "is_valid": true}, + {"signature": "0x25", "instruction_index": 0, "buyer_user_id": 10, "content_id": 6, "content_type": "track", "amount": 1000, "created_at": time.Now().AddDate(0, -7, 0), "is_valid": true}, }, } diff --git a/api/v1_users_library_playlists.go b/api/v1_users_library_playlists.go index 92a256f4..58a871cf 100644 --- a/api/v1_users_library_playlists.go +++ b/api/v1_users_library_playlists.go @@ -98,7 +98,7 @@ func (app *ApiServer) v1UsersLibraryPlaylists(c *fiber.Ctx) error { content_id as item_id, created_at as item_created_at, true as is_purchase - FROM usdc_purchases + FROM v_usdc_purchases WHERE content_type = @playlistType::usdc_purchase_content_type AND buyer_user_id = @userId AND @actionType in ('purchase', 'all') diff --git a/api/v1_users_library_tracks.go b/api/v1_users_library_tracks.go index 7fc74fd8..0dacfee4 100644 --- a/api/v1_users_library_tracks.go +++ b/api/v1_users_library_tracks.go @@ -92,7 +92,7 @@ func (app *ApiServer) v1UsersLibraryTracks(c *fiber.Ctx) error { content_id as item_id, created_at as item_created_at, true as is_purchase - FROM usdc_purchases + FROM v_usdc_purchases WHERE content_type = 'track' AND buyer_user_id = @userId AND @actionType in ('purchase', 'all') diff --git a/api/v1_users_library_tracks_test.go b/api/v1_users_library_tracks_test.go index 35085236..a6bb249d 100644 --- a/api/v1_users_library_tracks_test.go +++ b/api/v1_users_library_tracks_test.go @@ -107,15 +107,8 @@ func TestUsersLibraryTracksUnlistedFiltered(t *testing.T) { "save_type": "track", }, }, - "usdc_purchases": []map[string]any{ - { - "signature": "test1", - "buyer_user_id": 1, - "seller_user_id": 1, - "content_id": 201, // Purchase an unlisted track - "content_type": "track", - "amount": 100, - }, + "sol_purchases": []map[string]any{ + {"signature": "test1", "instruction_index": 0, "buyer_user_id": 1, "content_id": 201, "content_type": "track", "amount": 100, "is_valid": true}, }, } diff --git a/api/v1_users_purchasers.go b/api/v1_users_purchasers.go index 39fccaf9..21a7469d 100644 --- a/api/v1_users_purchasers.go +++ b/api/v1_users_purchasers.go @@ -36,7 +36,7 @@ func (app *ApiServer) v1UserPurchasers(c *fiber.Ctx) error { SELECT DISTINCT buyer_user_id FROM - usdc_purchases + v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ORDER BY diff --git a/api/v1_users_purchasers_count.go b/api/v1_users_purchasers_count.go index a8fad53b..f3a8d96d 100644 --- a/api/v1_users_purchasers_count.go +++ b/api/v1_users_purchasers_count.go @@ -33,7 +33,7 @@ func (app *ApiServer) v1UserPurchasersCount(c *fiber.Ctx) error { sql := ` SELECT count(DISTINCT buyer_user_id) as count FROM - usdc_purchases + v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ;` diff --git a/api/v1_users_purchasers_count_test.go b/api/v1_users_purchasers_count_test.go index 62bf0a04..c19b80dd 100644 --- a/api/v1_users_purchasers_count_test.go +++ b/api/v1_users_purchasers_count_test.go @@ -55,47 +55,11 @@ func TestV1UsersPurchasersCount(t *testing.T) { "wallet": "0x9876543210fedcba", }, }, - "usdc_purchases": []map[string]any{ - { - "slot": 1, - "signature": "purchase1", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 2, - "signature": "purchase2", - "seller_user_id": 1, - "buyer_user_id": 3, - "content_type": "track", - "content_id": 101, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 3, - "signature": "purchase3", - "seller_user_id": 1, - "buyer_user_id": 4, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 4, - "signature": "purchase4", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "playlist", - "content_id": 200, - "amount": 5000000, - "splits": "[]", - }, + "sol_purchases": []map[string]any{ + {"slot": 1, "signature": "purchase1", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 2, "signature": "purchase2", "instruction_index": 0, "buyer_user_id": 3, "content_type": "track", "content_id": 101, "amount": 2000000, "is_valid": true}, + {"slot": 3, "signature": "purchase3", "instruction_index": 0, "buyer_user_id": 4, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 4, "signature": "purchase4", "instruction_index": 0, "buyer_user_id": 2, "content_type": "playlist", "content_id": 200, "amount": 5000000, "is_valid": true}, }, } database.Seed(app.pool.Replicas[0], fixtures) diff --git a/api/v1_users_purchasers_test.go b/api/v1_users_purchasers_test.go index 94d61ed2..1f606a4d 100644 --- a/api/v1_users_purchasers_test.go +++ b/api/v1_users_purchasers_test.go @@ -56,47 +56,11 @@ func TestV1UsersPurchasers(t *testing.T) { "wallet": "0x9876543210fedcba", }, }, - "usdc_purchases": []map[string]any{ - { - "slot": 1, - "signature": "purchase1", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 2, - "signature": "purchase2", - "seller_user_id": 1, - "buyer_user_id": 3, - "content_type": "track", - "content_id": 101, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 3, - "signature": "purchase3", - "seller_user_id": 1, - "buyer_user_id": 4, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 4, - "signature": "purchase4", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "playlist", - "content_id": 200, - "amount": 5000000, - "splits": "[]", - }, + "sol_purchases": []map[string]any{ + {"slot": 1, "signature": "purchase1", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 2, "signature": "purchase2", "instruction_index": 0, "buyer_user_id": 3, "content_type": "track", "content_id": 101, "amount": 2000000, "is_valid": true}, + {"slot": 3, "signature": "purchase3", "instruction_index": 0, "buyer_user_id": 4, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 4, "signature": "purchase4", "instruction_index": 0, "buyer_user_id": 2, "content_type": "playlist", "content_id": 200, "amount": 5000000, "is_valid": true}, }, } database.Seed(app.pool.Replicas[0], fixtures) diff --git a/api/v1_users_purchases.go b/api/v1_users_purchases.go index 4f6dcd73..85347484 100644 --- a/api/v1_users_purchases.go +++ b/api/v1_users_purchases.go @@ -90,7 +90,7 @@ func (app *ApiServer) v1UsersPurchases(c *fiber.Ctx) error { sql := ` WITH purchases AS ( - SELECT * FROM usdc_purchases + SELECT * FROM v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ), purchases_with_content AS ( diff --git a/api/v1_users_purchases_count.go b/api/v1_users_purchases_count.go index 732bf703..6d962e80 100644 --- a/api/v1_users_purchases_count.go +++ b/api/v1_users_purchases_count.go @@ -34,7 +34,7 @@ func (app *ApiServer) v1UsersPurchasesCount(c *fiber.Ctx) error { } sql := ` - SELECT COUNT(*) FROM usdc_purchases + SELECT COUNT(*) FROM v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ;` diff --git a/api/v1_users_purchases_count_test.go b/api/v1_users_purchases_count_test.go index 0cd110f1..570ca2ed 100644 --- a/api/v1_users_purchases_count_test.go +++ b/api/v1_users_purchases_count_test.go @@ -30,79 +30,13 @@ func TestV1UsersPurchasesCount(t *testing.T) { {"playlist_id": 1, "playlist_name": "e", "playlist_owner_id": 5}, {"playlist_id": 2, "playlist_name": "f", "playlist_owner_id": 5, "is_album": true}, }, - "usdc_purchases": []map[string]any{ - { - "seller_user_id": 5, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "playlist", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - "signature": "gfsgf", - "extra_amount": 0, - }, - { - "seller_user_id": 5, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "album", - "content_id": 2, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - "signature": "faddf", - "extra_amount": 0, - }, - { - "seller_user_id": 3, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 3, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), - "signature": "adfdgad", - "extra_amount": 0, - }, - { - "seller_user_id": 4, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 4, - "splits": "[]", - "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), - "signature": "agadgafh", - "extra_amount": 0, - }, - { - "seller_user_id": 2, - "buyer_user_id": 1, - "access": "stream", - "amount": 1000000, - "content_type": "track", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), - "signature": "abc", - "extra_amount": 1000000, - }, - { - "seller_user_id": 3, - "buyer_user_id": 1, - "access": "download", - "amount": 2000000, - "content_type": "track", - "content_id": 2, - "splits": "[{\"user_id\": 3, \"payout_wallet\": \"something\", \"amount\": 1800000, \"percentage\": 100 },{\"payout_wallet\": \"network\", \"amount\": 200000, \"percentage\": 10 }]", - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "extra_amount": 0, - }, + "sol_purchases": []map[string]any{ + {"signature": "gfsgf", "instruction_index": 0, "buyer_user_id": 1, "amount": 2000000, "content_type": "playlist", "content_id": 1, "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "faddf", "instruction_index": 0, "buyer_user_id": 1, "amount": 2000000, "content_type": "album", "content_id": 2, "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "adfdgad", "instruction_index": 0, "buyer_user_id": 1, "amount": 2000000, "content_type": "track", "content_id": 3, "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "agadgafh", "instruction_index": 0, "buyer_user_id": 1, "amount": 2000000, "content_type": "track", "content_id": 4, "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "abc", "instruction_index": 0, "buyer_user_id": 1, "amount": 1000000, "content_type": "track", "content_id": 1, "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "def", "instruction_index": 0, "buyer_user_id": 1, "amount": 2000000, "content_type": "track", "content_id": 2, "access_type": "download", "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), "is_valid": true}, }, } diff --git a/api/v1_users_purchases_download.go b/api/v1_users_purchases_download.go index 2ab53912..5700dbf1 100644 --- a/api/v1_users_purchases_download.go +++ b/api/v1_users_purchases_download.go @@ -35,7 +35,7 @@ func (app *ApiServer) userPurchasesForDownload(c *fiber.Ctx) ([]UsdcPurchaseForD sql := ` WITH purchases AS ( - SELECT * FROM usdc_purchases + SELECT * FROM v_usdc_purchases WHERE buyer_user_id = @buyerUserId ), purchases_with_content AS ( diff --git a/api/v1_users_purchases_download_test.go b/api/v1_users_purchases_download_test.go index ab935e2f..89ff1ed6 100644 --- a/api/v1_users_purchases_download_test.go +++ b/api/v1_users_purchases_download_test.go @@ -15,7 +15,7 @@ func TestV1UsersPurchasesDownload(t *testing.T) { fixtures := database.FixtureMap{ "users": []map[string]any{ - {"user_id": 1, "handle": "seller", "name": "seller", "wallet": user1Wallet}, + {"user_id": 1, "handle": "seller", "name": "seller", "wallet": user1Wallet, "spl_usdc_payout_wallet": user1Wallet}, {"user_id": 2, "handle": "buyer1", "name": "buyer1", "wallet": user2Wallet}, {"user_id": 3, "handle": "buyer2", "name": "buyer2"}, }, @@ -31,33 +31,27 @@ func TestV1UsersPurchasesDownload(t *testing.T) { "collision_id": 0, }, }, - "usdc_purchases": []map[string]any{ + "sol_purchases": []map[string]any{ { - "seller_user_id": 1, - "buyer_user_id": 2, - "country": "US", - "content_type": "track", - "content_id": 1, - "amount": 1000000, - "extra_amount": 500000, - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "splits": []map[string]any{ - { - "user_id": 1, - "eth_wallet": user1Wallet, - "payout_wallet": user1Wallet, - "amount": 900000, - "percentage": 100, - }, - { - "payout_wallet": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), - "amount": 100000, - "percentage": 10, - }, - }, + "signature": "def", + "instruction_index": 0, + "buyer_user_id": 2, + "country": "US", + "content_type": "track", + "content_id": 1, + "amount": 1000000, + "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": user1Wallet, "amount": 900000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), "amount": 100000, "slot": 101}, + }, + // Drives extra_amount = 500000 (amount=1000000 - base_price=50_cents*10000=500000) + "track_price_history": []map[string]any{ + {"track_id": 1, "total_price_cents": 50, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "splits": "[]"}, + }, } database.Seed(app.writePool, fixtures) @@ -135,7 +129,7 @@ func TestV1UsersPurchasesDownloadWithGrantee(t *testing.T) { fixtures := database.FixtureMap{ "users": []map[string]any{ - {"user_id": 1, "handle": "seller", "name": "seller", "wallet": user1Wallet}, + {"user_id": 1, "handle": "seller", "name": "seller", "wallet": user1Wallet, "spl_usdc_payout_wallet": user1Wallet}, {"user_id": 2, "handle": "buyer1", "name": "buyer1", "wallet": user2Wallet}, {"user_id": 3, "handle": "grantee", "name": "grantee", "wallet": user3Wallet}, }, @@ -151,33 +145,26 @@ func TestV1UsersPurchasesDownloadWithGrantee(t *testing.T) { "collision_id": 0, }, }, - "usdc_purchases": []map[string]any{ + "sol_purchases": []map[string]any{ { - "seller_user_id": 1, - "buyer_user_id": 2, - "country": "US", - "content_type": "track", - "content_id": 1, - "amount": 1000000, - "extra_amount": 500000, - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "splits": []map[string]any{ - { - "user_id": 1, - "eth_wallet": user1Wallet, - "payout_wallet": user1Wallet, - "amount": 900000, - "percentage": 100, - }, - { - "payout_wallet": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), - "amount": 100000, - "percentage": 10, - }, - }, + "signature": "def", + "instruction_index": 0, + "buyer_user_id": 2, + "country": "US", + "content_type": "track", + "content_id": 1, + "amount": 1000000, + "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": user1Wallet, "amount": 900000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), "amount": 100000, "slot": 101}, + }, + "track_price_history": []map[string]any{ + {"track_id": 1, "total_price_cents": 50, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "splits": "[]"}, + }, "grants": []map[string]any{ { "user_id": 2, // buyer1 diff --git a/api/v1_users_purchases_test.go b/api/v1_users_purchases_test.go index c29fd85e..dc1f57dd 100644 --- a/api/v1_users_purchases_test.go +++ b/api/v1_users_purchases_test.go @@ -16,7 +16,7 @@ func TestV1UsersPurchases(t *testing.T) { "users": []map[string]any{ {"user_id": 1, "handle": "buyer", "wallet": user1Wallet}, {"user_id": 2, "handle": "seller1", "name": "c"}, - {"user_id": 3, "handle": "seller2", "name": "a"}, + {"user_id": 3, "handle": "seller2", "name": "a", "spl_usdc_payout_wallet": "something"}, {"user_id": 4, "handle": "seller3", "name": "b"}, {"user_id": 5, "handle": "seller4", "name": "d"}, }, @@ -30,80 +30,83 @@ func TestV1UsersPurchases(t *testing.T) { {"playlist_id": 1, "playlist_name": "e", "playlist_owner_id": 5}, {"playlist_id": 2, "playlist_name": "e", "playlist_owner_id": 5, "is_album": true}, }, - "usdc_purchases": []map[string]any{ + // For the purchase with non-zero extra_amount (signature "abc"), seed a track_price_history + // row so the view's amount - base_price computation produces extra_amount = 1000000. + "track_price_history": []map[string]any{ + {"track_id": 1, "total_price_cents": 0, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 2, 23, 0, 0, 0, time.UTC), "splits": "[]"}, + }, + "sol_purchases": []map[string]any{ { - "seller_user_id": 5, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "playlist", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - "signature": "gfsgf", - "extra_amount": 0, + "signature": "gfsgf", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "stream", + "amount": 2000000, + "content_type": "playlist", + "content_id": 1, + "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, { - "seller_user_id": 5, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "album", - "content_id": 2, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), - "signature": "faddf", - "extra_amount": 0, + "signature": "faddf", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "stream", + "amount": 2000000, + "content_type": "album", + "content_id": 2, + "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), + "is_valid": true, }, { - "seller_user_id": 3, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 3, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), - "signature": "adfdgad", - "extra_amount": 0, + "signature": "adfdgad", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "stream", + "amount": 2000000, + "content_type": "track", + "content_id": 3, + "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), + "is_valid": true, }, { - "seller_user_id": 4, - "buyer_user_id": 1, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 4, - "splits": "[]", - "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), - "signature": "agadgafh", - "extra_amount": 0, + "signature": "agadgafh", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "stream", + "amount": 2000000, + "content_type": "track", + "content_id": 4, + "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, { - "seller_user_id": 2, - "buyer_user_id": 1, - "access": "stream", - "amount": 1000000, - "content_type": "track", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), - "signature": "abc", - "extra_amount": 1000000, + "signature": "abc", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "stream", + "amount": 1000000, + "content_type": "track", + "content_id": 1, + "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, { - "seller_user_id": 3, - "buyer_user_id": 1, - "access": "download", - "amount": 2000000, - "content_type": "track", - "content_id": 2, - "splits": "[{\"user_id\": 3, \"payout_wallet\": \"something\", \"amount\": 1800000, \"percentage\": 100 },{\"payout_wallet\": \"network\", \"amount\": 200000, \"percentage\": 10 }]", - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "extra_amount": 0, + "signature": "def", + "instruction_index": 0, + "buyer_user_id": 1, + "access_type": "download", + "amount": 2000000, + "content_type": "track", + "content_id": 2, + "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": "something", "amount": 1800000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": "network", "amount": 200000, "slot": 101}, + }, } database.Seed(app.pool.Replicas[0], fixtures) diff --git a/api/v1_users_sales.go b/api/v1_users_sales.go index f37e06f0..5d8ba0d0 100644 --- a/api/v1_users_sales.go +++ b/api/v1_users_sales.go @@ -61,7 +61,7 @@ func (app *ApiServer) v1UsersSales(c *fiber.Ctx) error { sql := ` WITH purchases AS ( - SELECT * FROM usdc_purchases + SELECT * FROM v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ), purchases_with_content AS ( diff --git a/api/v1_users_sales_aggregate.go b/api/v1_users_sales_aggregate.go index a69834ee..4474491b 100644 --- a/api/v1_users_sales_aggregate.go +++ b/api/v1_users_sales_aggregate.go @@ -29,7 +29,7 @@ func (app *ApiServer) v1UsersSalesAggregate(c *fiber.Ctx) error { content_type, COUNT(buyer_user_id) as purchase_count FROM - usdc_purchases + v_usdc_purchases WHERE seller_user_id = @userId GROUP BY diff --git a/api/v1_users_sales_aggregate_test.go b/api/v1_users_sales_aggregate_test.go index fc20c8cf..9c396fdf 100644 --- a/api/v1_users_sales_aggregate_test.go +++ b/api/v1_users_sales_aggregate_test.go @@ -56,70 +56,16 @@ func TestV1UsersSalesAggregate(t *testing.T) { "handle_lc": "buyer3", }, }, - "usdc_purchases": []map[string]any{ + "sol_purchases": []map[string]any{ // Track 100: 3 purchases (most popular) - { - "slot": 1, - "signature": "purchase1", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 2, - "signature": "purchase2", - "seller_user_id": 1, - "buyer_user_id": 3, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, - { - "slot": 3, - "signature": "purchase3", - "seller_user_id": 1, - "buyer_user_id": 4, - "content_type": "track", - "content_id": 100, - "amount": 2000000, - "splits": "[]", - }, + {"slot": 1, "signature": "purchase1", "instruction_index": 0, "buyer_user_id": 2, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 2, "signature": "purchase2", "instruction_index": 0, "buyer_user_id": 3, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, + {"slot": 3, "signature": "purchase3", "instruction_index": 0, "buyer_user_id": 4, "content_type": "track", "content_id": 100, "amount": 2000000, "is_valid": true}, // Playlist 200: 2 purchases (second most popular) - { - "slot": 4, - "signature": "purchase4", - "seller_user_id": 1, - "buyer_user_id": 2, - "content_type": "playlist", - "content_id": 200, - "amount": 5000000, - "splits": "[]", - }, - { - "slot": 5, - "signature": "purchase5", - "seller_user_id": 1, - "buyer_user_id": 3, - "content_type": "playlist", - "content_id": 200, - "amount": 5000000, - "splits": "[]", - }, + {"slot": 4, "signature": "purchase4", "instruction_index": 0, "buyer_user_id": 2, "content_type": "playlist", "content_id": 200, "amount": 5000000, "is_valid": true}, + {"slot": 5, "signature": "purchase5", "instruction_index": 0, "buyer_user_id": 3, "content_type": "playlist", "content_id": 200, "amount": 5000000, "is_valid": true}, // Track 101: 1 purchase (least popular) - { - "slot": 6, - "signature": "purchase6", - "seller_user_id": 1, - "buyer_user_id": 4, - "content_type": "track", - "content_id": 101, - "amount": 1000000, - "splits": "[]", - }, + {"slot": 6, "signature": "purchase6", "instruction_index": 0, "buyer_user_id": 4, "content_type": "track", "content_id": 101, "amount": 1000000, "is_valid": true}, }, } database.Seed(app.pool.Replicas[0], fixtures) diff --git a/api/v1_users_sales_count.go b/api/v1_users_sales_count.go index c45e383b..bbd90767 100644 --- a/api/v1_users_sales_count.go +++ b/api/v1_users_sales_count.go @@ -34,7 +34,7 @@ func (app *ApiServer) v1UsersSalesCount(c *fiber.Ctx) error { } sql := ` - SELECT COUNT(*) FROM usdc_purchases + SELECT COUNT(*) FROM v_usdc_purchases WHERE ` + strings.Join(filters, " AND ") + ` ;` diff --git a/api/v1_users_sales_count_test.go b/api/v1_users_sales_count_test.go index 2713c50d..73753a30 100644 --- a/api/v1_users_sales_count_test.go +++ b/api/v1_users_sales_count_test.go @@ -30,79 +30,13 @@ func TestV1UsersSalesCount(t *testing.T) { {"playlist_id": 1, "playlist_name": "e", "playlist_owner_id": 1}, {"playlist_id": 2, "playlist_name": "e", "playlist_owner_id": 1, "is_album": true}, }, - "usdc_purchases": []map[string]any{ - { - "seller_user_id": 1, - "buyer_user_id": 5, - "access": "stream", - "amount": 2000000, - "content_type": "playlist", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - "signature": "gfsgf", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 5, - "access": "stream", - "amount": 2000000, - "content_type": "album", - "content_id": 2, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), - "signature": "faddf", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 3, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 3, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), - "signature": "adfdgad", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 4, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 4, - "splits": "[]", - "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), - "signature": "agadgafh", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 2, - "access": "stream", - "amount": 1000000, - "content_type": "track", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), - "signature": "abc", - "extra_amount": 1000000, - }, - { - "seller_user_id": 1, - "buyer_user_id": 3, - "access": "download", - "amount": 2000000, - "content_type": "track", - "content_id": 2, - "splits": "[{\"user_id\": 1, \"payout_wallet\": \"something\", \"amount\": 1800000, \"percentage\": 100 },{\"payout_wallet\": \"network\", \"amount\": 200000, \"percentage\": 10 }]", - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "extra_amount": 0, - }, + "sol_purchases": []map[string]any{ + {"signature": "gfsgf", "instruction_index": 0, "buyer_user_id": 5, "amount": 2000000, "content_type": "playlist", "content_id": 1, "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "faddf", "instruction_index": 0, "buyer_user_id": 5, "amount": 2000000, "content_type": "album", "content_id": 2, "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), "is_valid": true}, + {"signature": "adfdgad", "instruction_index": 0, "buyer_user_id": 3, "amount": 2000000, "content_type": "track", "content_id": 3, "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "agadgafh", "instruction_index": 0, "buyer_user_id": 4, "amount": 2000000, "content_type": "track", "content_id": 4, "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "abc", "instruction_index": 0, "buyer_user_id": 2, "amount": 1000000, "content_type": "track", "content_id": 1, "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "def", "instruction_index": 0, "buyer_user_id": 3, "amount": 2000000, "content_type": "track", "content_id": 2, "access_type": "download", "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), "is_valid": true}, }, } diff --git a/api/v1_users_sales_download.go b/api/v1_users_sales_download.go index 075b857d..2bb88b70 100644 --- a/api/v1_users_sales_download.go +++ b/api/v1_users_sales_download.go @@ -64,7 +64,7 @@ func (app *ApiServer) userSalesForDownload(c *fiber.Ctx) ([]UsdcSaleWithEmail, e sql := ` WITH purchases AS ( - SELECT * FROM usdc_purchases + SELECT * FROM v_usdc_purchases WHERE seller_user_id = @sellerUserId ), purchases_with_content AS ( diff --git a/api/v1_users_sales_download_test.go b/api/v1_users_sales_download_test.go index 8422d333..c144b1ae 100644 --- a/api/v1_users_sales_download_test.go +++ b/api/v1_users_sales_download_test.go @@ -15,7 +15,7 @@ func TestV1UsersSalesDownload(t *testing.T) { fixtures := database.FixtureMap{ "users": []map[string]any{ - {"user_id": 1, "handle": "seller", "wallet": user1Wallet}, + {"user_id": 1, "handle": "seller", "wallet": user1Wallet, "spl_usdc_payout_wallet": user1Wallet}, {"user_id": 2, "handle": "buyer1", "name": "buyer1"}, {"user_id": 3, "handle": "buyer2", "name": "buyer2"}, }, @@ -31,33 +31,26 @@ func TestV1UsersSalesDownload(t *testing.T) { "collision_id": 0, }, }, - "usdc_purchases": []map[string]any{ + "sol_purchases": []map[string]any{ { - "seller_user_id": 1, - "buyer_user_id": 2, - "country": "US", - "content_type": "track", - "content_id": 1, - "amount": 1000000, - "extra_amount": 500000, - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "splits": []map[string]any{ - { - "user_id": 1, - "eth_wallet": user1Wallet, - "payout_wallet": user1Wallet, - "amount": 900000, - "percentage": 100, - }, - { - "payout_wallet": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), - "amount": 100000, - "percentage": 10, - }, - }, + "signature": "def", + "instruction_index": 0, + "buyer_user_id": 2, + "country": "US", + "content_type": "track", + "content_id": 1, + "amount": 1000000, + "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": user1Wallet, "amount": 900000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), "amount": 100000, "slot": 101}, + }, + "track_price_history": []map[string]any{ + {"track_id": 1, "total_price_cents": 50, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "splits": "[]"}, + }, "encrypted_emails": []map[string]any{ { "id": 1, @@ -166,7 +159,7 @@ func TestV1UsersSalesDownloadWithGrantee(t *testing.T) { fixtures := database.FixtureMap{ "users": []map[string]any{ - {"user_id": 1, "handle": "seller", "wallet": user1Wallet}, + {"user_id": 1, "handle": "seller", "wallet": user1Wallet, "spl_usdc_payout_wallet": user1Wallet}, {"user_id": 2, "handle": "buyer1", "name": "buyer1", "wallet": user2Wallet}, {"user_id": 3, "handle": "grantee", "name": "grantee", "wallet": user3Wallet}, }, @@ -182,33 +175,26 @@ func TestV1UsersSalesDownloadWithGrantee(t *testing.T) { "collision_id": 0, }, }, - "usdc_purchases": []map[string]any{ + "sol_purchases": []map[string]any{ { - "seller_user_id": 1, - "buyer_user_id": 2, - "country": "US", - "content_type": "track", - "content_id": 1, - "amount": 1000000, - "extra_amount": 500000, - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "splits": []map[string]any{ - { - "user_id": 1, - "eth_wallet": user1Wallet, - "payout_wallet": user1Wallet, - "amount": 900000, - "percentage": 100, - }, - { - "payout_wallet": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), - "amount": 100000, - "percentage": 10, - }, - }, + "signature": "def", + "instruction_index": 0, + "buyer_user_id": 2, + "country": "US", + "content_type": "track", + "content_id": 1, + "amount": 1000000, + "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), + "is_valid": true, }, }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": user1Wallet, "amount": 900000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": app.solanaConfig.StakingBridgeUsdcTokenAccount.String(), "amount": 100000, "slot": 101}, + }, + "track_price_history": []map[string]any{ + {"track_id": 1, "total_price_cents": 50, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "splits": "[]"}, + }, "encrypted_emails": []map[string]any{ { "id": 1, diff --git a/api/v1_users_sales_test.go b/api/v1_users_sales_test.go index 9c01d0c4..7c260585 100644 --- a/api/v1_users_sales_test.go +++ b/api/v1_users_sales_test.go @@ -14,7 +14,7 @@ func TestV1UsersSales(t *testing.T) { fixtures := database.FixtureMap{ "users": []map[string]any{ - {"user_id": 1, "handle": "seller", "wallet": user1Wallet}, + {"user_id": 1, "handle": "seller", "wallet": user1Wallet, "spl_usdc_payout_wallet": "something"}, {"user_id": 2, "handle": "buyer1", "name": "c"}, {"user_id": 3, "handle": "buyer2", "name": "a"}, {"user_id": 4, "handle": "buyer3", "name": "b"}, @@ -30,79 +30,21 @@ func TestV1UsersSales(t *testing.T) { {"playlist_id": 1, "playlist_name": "e", "playlist_owner_id": 1}, {"playlist_id": 2, "playlist_name": "e", "playlist_owner_id": 1, "is_album": true}, }, - "usdc_purchases": []map[string]any{ - { - "seller_user_id": 1, - "buyer_user_id": 5, - "access": "stream", - "amount": 2000000, - "content_type": "playlist", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - "signature": "gfsgf", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 5, - "access": "stream", - "amount": 2000000, - "content_type": "album", - "content_id": 2, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), - "signature": "faddf", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 3, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 3, - "splits": "[]", - "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), - "signature": "adfdgad", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 4, - "access": "stream", - "amount": 2000000, - "content_type": "track", - "content_id": 4, - "splits": "[]", - "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), - "signature": "agadgafh", - "extra_amount": 0, - }, - { - "seller_user_id": 1, - "buyer_user_id": 2, - "access": "stream", - "amount": 1000000, - "content_type": "track", - "content_id": 1, - "splits": "[]", - "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), - "signature": "abc", - "extra_amount": 1000000, - }, - { - "seller_user_id": 1, - "buyer_user_id": 3, - "access": "download", - "amount": 2000000, - "content_type": "track", - "content_id": 2, - "splits": "[{\"user_id\": 1, \"payout_wallet\": \"something\", \"amount\": 1800000, \"percentage\": 100 },{\"payout_wallet\": \"network\", \"amount\": 200000, \"percentage\": 10 }]", - "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), - "signature": "def", - "extra_amount": 0, - }, + "track_price_history": []map[string]any{ + // Drives extra_amount = 1000000 for signature "abc" (amount=1000000, base_price=0) + {"track_id": 1, "total_price_cents": 0, "blocknumber": 100, "block_timestamp": time.Date(2024, 6, 2, 23, 0, 0, 0, time.UTC), "splits": "[]"}, + }, + "sol_purchases": []map[string]any{ + {"signature": "gfsgf", "instruction_index": 0, "buyer_user_id": 5, "amount": 2000000, "content_type": "playlist", "content_id": 1, "created_at": time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "faddf", "instruction_index": 0, "buyer_user_id": 5, "amount": 2000000, "content_type": "album", "content_id": 2, "created_at": time.Date(2024, 6, 1, 0, 1, 0, 0, time.UTC), "is_valid": true}, + {"signature": "adfdgad", "instruction_index": 0, "buyer_user_id": 3, "amount": 2000000, "content_type": "track", "content_id": 3, "created_at": time.Date(2024, 6, 1, 1, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "agadgafh", "instruction_index": 0, "buyer_user_id": 4, "amount": 2000000, "content_type": "track", "content_id": 4, "created_at": time.Date(2024, 6, 2, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "abc", "instruction_index": 0, "buyer_user_id": 2, "amount": 1000000, "content_type": "track", "content_id": 1, "created_at": time.Date(2024, 6, 3, 0, 0, 0, 0, time.UTC), "is_valid": true}, + {"signature": "def", "instruction_index": 0, "buyer_user_id": 3, "amount": 2000000, "content_type": "track", "content_id": 2, "access_type": "download", "created_at": time.Date(2024, 6, 4, 0, 0, 0, 0, time.UTC), "is_valid": true}, + }, + "sol_payments": []map[string]any{ + {"signature": "def", "instruction_index": 0, "route_index": 0, "to_account": "something", "amount": 1800000, "slot": 101}, + {"signature": "def", "instruction_index": 0, "route_index": 1, "to_account": "network", "amount": 200000, "slot": 101}, }, } diff --git a/database/seed.go b/database/seed.go index 8486d9a7..b616bb15 100644 --- a/database/seed.go +++ b/database/seed.go @@ -531,6 +531,35 @@ var ( "recipient_eth_address": nil, "created_at": time.Now(), }, + "sol_purchases": { + "signature": nil, + "instruction_index": 0, + "amount": nil, + "slot": 101, + "from_account": "from-account-placeholder", + "content_type": nil, + "content_id": nil, + "buyer_user_id": nil, + "access_type": "stream", + "valid_after_blocknumber": 0, + "is_valid": true, + "created_at": time.Now(), + }, + "sol_payments": { + "signature": nil, + "instruction_index": 0, + "route_index": 0, + "to_account": nil, + "amount": nil, + "slot": 101, + }, + "album_price_history": { + "playlist_id": nil, + "splits": "[]", + "total_price_cents": nil, + "blocknumber": 101, + "block_timestamp": time.Now(), + }, "sol_token_account_balance_changes": { "account": nil, "owner": "owner-acc",