Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/swagger/swagger-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16492,6 +16492,11 @@ components:
type: number
description: The balance of the coin in the user's account in USD
example: 1.23
accounts:
type: array
description: Per-token-account breakdown of the user's holdings for this coin. Populated by GET /v1/users/{id}/coins; omitted by GET /v1/wallet/{walletId}/coins.
items:
$ref: "#/components/schemas/user_coin_account"
track_feed_item:
required:
- item
Expand Down
32 changes: 28 additions & 4 deletions api/v1_users_coins.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,37 @@ func (app *ApiServer) v1UsersCoins(c *fiber.Ctx) error {
artist_coins.logo_uri,
artist_coins.banner_image_url,
COALESCE(balances_by_mint.balance, 0) AS balance,
(COALESCE(balances_by_mint.balance, 0) * COALESCE(coin_prices.price, 0)) / POWER(10, artist_coins.decimals) AS balance_usd
(COALESCE(balances_by_mint.balance, 0) * COALESCE(coin_prices.price, 0)) / POWER(10, artist_coins.decimals) AS balance_usd,
COALESCE(
JSON_AGG(
JSON_BUILD_OBJECT(
'account', balances.account,
'owner', balances.owner,
'balance', balances.balance,
'balance_usd', (balances.balance * COALESCE(coin_prices.price, 0)) / POWER(10, artist_coins.decimals),
'is_in_app_wallet', balances.is_in_app_wallet
)
ORDER BY balances.balance DESC
) FILTER (WHERE balances.account IS NOT NULL),
'[]'::json
) AS accounts
FROM artist_coins
LEFT JOIN balances_by_mint ON balances_by_mint.mint = artist_coins.mint
LEFT JOIN artist_coin_prices coin_prices ON coin_prices.mint = artist_coins.mint
LEFT JOIN balances ON balances.mint = artist_coins.mint
WHERE artist_coins.user_id = @user_id -- Show owned coins
OR balance > 0 -- Show coins with positive balance
OR ticker = 'AUDIO' -- Always show AUDIO
OR balances_by_mint.balance > 0 -- Show coins with positive balance
OR artist_coins.ticker = 'AUDIO' -- Always show AUDIO
GROUP BY
artist_coins.ticker,
artist_coins.mint,
artist_coins.decimals,
artist_coins.has_discord,
artist_coins.user_id,
artist_coins.logo_uri,
artist_coins.banner_image_url,
balances_by_mint.balance,
coin_prices.price
ORDER BY
-- Always show user's owned coins first, regardless of balance
(artist_coins.user_id = @user_id) DESC,
Expand All @@ -102,7 +126,7 @@ func (app *ApiServer) v1UsersCoins(c *fiber.Ctx) error {
return err
}

userCoins, err := pgx.CollectRows(rows, pgx.RowToStructByName[UserCoin])
userCoins, err := pgx.CollectRows(rows, pgx.RowToStructByName[UserCoinAccounts])
if err != nil {
return err
}
Expand Down
44 changes: 33 additions & 11 deletions api/v1_users_coins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,39 @@ func TestUserCoins(t *testing.T) {
assert.Equal(t, 200, status)

jsonAssert(t, body, map[string]any{
"data.#": 2,
"data.0.ticker": "AUDIO",
"data.0.mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM",
"data.0.decimals": 8,
"data.0.owner_id": trashid.MustEncodeHashID(1),
"data.0.balance": 1800000000, // 18 AUDIO
"data.0.balance_usd": 180.0, // Assuming $10 per AUDIO
"data.1.ticker": "USDC",
"data.1.mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"data.1.balance": 7000000, // 7 USDC
"data.1.balance_usd": 7.0,
"data.#": 2,
"data.0.ticker": "AUDIO",
"data.0.mint": "9LzCMqDgTKYz9Drzqnpgee3SGa89up3a247ypMj2xrqM",
"data.0.decimals": 8,
"data.0.owner_id": trashid.MustEncodeHashID(1),
"data.0.balance": 1800000000, // 18 AUDIO
"data.0.balance_usd": 180.0, // Assuming $10 per AUDIO
"data.0.accounts.#": 3,
"data.0.accounts.0.account": "associated",
"data.0.accounts.0.owner": "owner_wallet",
"data.0.accounts.0.balance": 1000000000, // 10 AUDIO
"data.0.accounts.0.balance_usd": 100.0,
"data.0.accounts.0.is_in_app_wallet": false,
"data.0.accounts.1.account": "associated3",
"data.0.accounts.1.owner": "owner_wallet3",
"data.0.accounts.1.balance": 500000000, // 5 AUDIO
"data.0.accounts.1.balance_usd": 50.0,
"data.0.accounts.1.is_in_app_wallet": false,
"data.0.accounts.2.account": "claimable",
"data.0.accounts.2.owner": "claimable_tokens_pda",
"data.0.accounts.2.balance": 300000000, // 3 AUDIO
"data.0.accounts.2.balance_usd": 30.0,
"data.0.accounts.2.is_in_app_wallet": true,
"data.1.ticker": "USDC",
"data.1.mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"data.1.balance": 7000000, // 7 USDC
"data.1.balance_usd": 7.0,
"data.1.accounts.#": 1,
"data.1.accounts.0.account": "associated2",
"data.1.accounts.0.owner": "owner_wallet2",
"data.1.accounts.0.balance": 7000000,
"data.1.accounts.0.balance_usd": 7.0,
"data.1.accounts.0.is_in_app_wallet": false,
})
}
}
Expand Down
Loading