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
1 change: 1 addition & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ func NewApiServer(config config.Config) *ApiServer {
g.Get("/users/:userId/now-playing", app.v1UsersNowPlaying)
g.Get("/users/:userId/coins", app.v1UsersCoins)
g.Get("/users/:userId/coins/:mint", app.v1UsersCoin)
g.Get("/wallet/:walletId/coins", app.v1WalletCoins)
g.Get("/users/:userId/authorized_apps", app.v1UsersAuthorizedApps)
g.Get("/users/:userId/authorized-apps", app.v1UsersAuthorizedApps)
g.Get("/users/:userId/developer_apps", app.v1UsersDeveloperApps)
Expand Down
37 changes: 37 additions & 0 deletions api/swagger/swagger-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2209,6 +2209,43 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/user_coin_response'
/wallet/{walletId}/coins:
get:
tags:
- wallet
description: 'Gets a list of the coins held by a wallet address and their balances'
parameters:
- name: walletId
in: path
description: A Solana wallet address
required: true
schema:
type: string
example: "Dez1g5f3h4j5k6l7m8n9o0p1q2r3s4t5u6v7w8x9y0z"
- name: offset
in: query
description: The number of items to skip. Useful for pagination (page number
* limit)
schema:
type: integer
default: 0
minimum: 0
- name: limit
in: query
description: The number of items to fetch
schema:
type: integer
default: 50
minimum: 1
maximum: 100
operationId: Get Wallet Coins
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/user_coins_response'
/users/{id}/collectibles:
get:
tags:
Expand Down
78 changes: 78 additions & 0 deletions api/v1_wallet_coins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package api

import (
"github.com/gofiber/fiber/v2"
"github.com/jackc/pgx/v5"
)

type GetWalletCoinsQueryParams struct {
Limit int `query:"limit" default:"50" validate:"min=1,max=100"`
Offset int `query:"offset" default:"0" validate:"min=0"`
}

type WalletCoinsRouteParams struct {
WalletId string `params:"walletId"`
}

func (app *ApiServer) v1WalletCoins(c *fiber.Ctx) error {
params := WalletCoinsRouteParams{}
if err := c.ParamsParser(&params); err != nil {
return err
}

queryParams := GetWalletCoinsQueryParams{}
if err := app.ParseAndValidateQueryParams(c, &queryParams); err != nil {
return err
}

sql := `
WITH balances_by_mint AS (
SELECT
balances.mint,
SUM(balances.balance) AS balance
FROM sol_token_account_balances AS balances
WHERE balances.owner = @wallet_address
GROUP BY balances.mint
)
SELECT
artist_coins.ticker,
artist_coins.mint,
artist_coins.decimals,
artist_coins.has_discord,
artist_coins.user_id AS owner_id,
COALESCE(balances_by_mint.balance, 0) AS balance,
(COALESCE(balances_by_mint.balance, 0) * COALESCE(stats.price, pools.price_usd)) / POWER(10, artist_coins.decimals) AS balance_usd
FROM artist_coins
LEFT JOIN balances_by_mint ON balances_by_mint.mint = artist_coins.mint
LEFT JOIN artist_coin_stats stats ON stats.mint = artist_coins.mint
LEFT JOIN artist_coin_pools pools ON pools.base_mint = artist_coins.mint
WHERE balance > 0 -- Show coins with positive balance
ORDER BY
-- Prioritize AUDIO
artist_coins.ticker = 'AUDIO' DESC,
-- Then by number of coins (balance)
balance DESC,
-- Finally by mint for consistent ordering
artist_coins.mint ASC
LIMIT @limit
OFFSET @offset
;`

rows, err := app.pool.Query(c.Context(), sql, pgx.NamedArgs{
"wallet_address": params.WalletId,
"limit": queryParams.Limit,
"offset": queryParams.Offset,
})
if err != nil {
return err
}

userCoins, err := pgx.CollectRows(rows, pgx.RowToStructByName[UserCoin])
if err != nil {
return err
}

return c.JSON(fiber.Map{
"data": userCoins,
})
}
Loading
Loading