Skip to content

Commit fb754f2

Browse files
committed
improve token balances endpoint performance
1 parent c028d57 commit fb754f2

File tree

4 files changed

+91
-25
lines changed

4 files changed

+91
-25
lines changed

internal/handlers/token_handlers.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ import (
88
"github.com/gin-gonic/gin"
99
"github.com/rs/zerolog/log"
1010
"github.com/thirdweb-dev/indexer/api"
11+
"github.com/thirdweb-dev/indexer/internal/common"
1112
"github.com/thirdweb-dev/indexer/internal/storage"
1213
)
1314

1415
// BalanceModel return type for Swagger documentation
1516
type BalanceModel struct {
16-
ChainId string `json:"chain_id" ch:"chain_id"`
17-
TokenType string `json:"token_type" ch:"token_type"`
1817
TokenAddress string `json:"token_address" ch:"address"`
19-
Owner string `json:"owner" ch:"owner"`
2018
TokenId string `json:"token_id" ch:"token_id"`
2119
Balance *big.Int `json:"balance" ch:"balance"`
2220
}
@@ -37,7 +35,7 @@ type BalanceModel struct {
3735
// @Failure 400 {object} api.Error
3836
// @Failure 401 {object} api.Error
3937
// @Failure 500 {object} api.Error
40-
// @Router /{chainId}/events [get]
38+
// @Router /{chainId}/balances/{owner}/{type} [get]
4139
func GetTokenBalancesByType(c *gin.Context) {
4240
chainId, err := api.GetChainId(c)
4341
if err != nil {
@@ -60,12 +58,21 @@ func GetTokenBalancesByType(c *gin.Context) {
6058
return
6159
}
6260
hideZeroBalances := c.Query("hide_zero_balances") != "false"
61+
62+
columns := []string{"address", "sum(balance) as balance"}
63+
groupBy := []string{"address"}
64+
if tokenType != "erc20" {
65+
columns = []string{"address", "token_id", "sum(balance) as balance"}
66+
groupBy = []string{"address", "token_id"}
67+
}
68+
6369
qf := storage.BalancesQueryFilter{
6470
ChainId: chainId,
6571
Owner: owner,
6672
TokenType: tokenType,
6773
TokenAddress: tokenAddress,
6874
ZeroBalance: hideZeroBalances,
75+
GroupBy: groupBy,
6976
SortBy: c.Query("sort_by"),
7077
SortOrder: c.Query("sort_order"),
7178
Page: api.ParseIntQueryParam(c.Query("page"), 0),
@@ -87,13 +94,34 @@ func GetTokenBalancesByType(c *gin.Context) {
8794
return
8895
}
8996

90-
balancesResult, err := mainStorage.GetTokenBalances(qf)
97+
balancesResult, err := mainStorage.GetTokenBalances(qf, columns...)
9198
if err != nil {
9299
log.Error().Err(err).Msg("Error querying balances")
93100
// TODO: might want to choose BadRequestError if it's due to not-allowed functions
94101
api.InternalErrorHandler(c)
95102
return
96103
}
97-
queryResult.Data = balancesResult.Data
104+
queryResult.Data = serializeBalances(balancesResult.Data)
98105
sendJSONResponse(c, queryResult)
99106
}
107+
108+
func serializeBalances(balances []common.TokenBalance) []BalanceModel {
109+
balanceModels := make([]BalanceModel, len(balances))
110+
for i, balance := range balances {
111+
balanceModels[i] = serializeBalance(balance)
112+
}
113+
return balanceModels
114+
}
115+
116+
func serializeBalance(balance common.TokenBalance) BalanceModel {
117+
return BalanceModel{
118+
TokenAddress: balance.TokenAddress,
119+
Balance: balance.Balance,
120+
TokenId: func() string {
121+
if balance.TokenId != nil {
122+
return balance.TokenId.String()
123+
}
124+
return ""
125+
}(),
126+
}
127+
}

internal/storage/clickhouse.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,16 +1373,38 @@ func (c *ClickHouseConnector) getTableName(chainId *big.Int, defaultTable string
13731373
return defaultTable
13741374
}
13751375

1376-
func (c *ClickHouseConnector) GetTokenBalances(qf BalancesQueryFilter) (QueryResult[common.TokenBalance], error) {
1376+
func (c *ClickHouseConnector) GetTokenBalances(qf BalancesQueryFilter, fields ...string) (QueryResult[common.TokenBalance], error) {
13771377
columns := "chain_id, token_type, address, owner, token_id, balance"
1378+
if len(fields) > 0 {
1379+
columns = strings.Join(fields, ", ")
1380+
}
1381+
query := fmt.Sprintf("SELECT %s FROM %s.token_balances WHERE chain_id = ? AND token_type = ? AND owner = ?", columns, c.cfg.Database)
1382+
1383+
if qf.TokenAddress != "" {
1384+
query += fmt.Sprintf(" AND address = '%s'", qf.TokenAddress)
1385+
}
1386+
1387+
isBalanceAggregated := false
1388+
for _, field := range fields {
1389+
if strings.Contains(field, "balance") && strings.TrimSpace(field) != "balance" {
1390+
isBalanceAggregated = true
1391+
break
1392+
}
1393+
}
13781394
balanceCondition := ">="
13791395
if qf.ZeroBalance {
13801396
balanceCondition = ">"
13811397
}
1382-
query := fmt.Sprintf("SELECT %s FROM %s.token_balances FINAL WHERE chain_id = ? AND token_type = ? AND owner = ? AND balance %s 0", columns, c.cfg.Database, balanceCondition)
1398+
if !isBalanceAggregated {
1399+
query += fmt.Sprintf(" AND balance %s 0", balanceCondition)
1400+
}
13831401

1384-
if qf.TokenAddress != "" {
1385-
query += fmt.Sprintf(" AND address = '%s'", qf.TokenAddress)
1402+
if len(qf.GroupBy) > 0 {
1403+
query += fmt.Sprintf(" GROUP BY %s", strings.Join(qf.GroupBy, ", "))
1404+
1405+
if isBalanceAggregated {
1406+
query += fmt.Sprintf(" HAVING balance %s 0", balanceCondition)
1407+
}
13861408
}
13871409

13881410
// Add ORDER BY clause

internal/storage/connector.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type BalancesQueryFilter struct {
3131
TokenAddress string
3232
Owner string
3333
ZeroBalance bool
34+
GroupBy []string
3435
SortBy string
3536
SortOrder string
3637
Page int
@@ -80,7 +81,7 @@ type IMainStorage interface {
8081
GetBlockHeadersDescending(chainId *big.Int, from *big.Int, to *big.Int) (blockHeaders []common.BlockHeader, err error)
8182
DeleteBlockData(chainId *big.Int, blockNumbers []*big.Int) error
8283

83-
GetTokenBalances(qf BalancesQueryFilter) (QueryResult[common.TokenBalance], error)
84+
GetTokenBalances(qf BalancesQueryFilter, fields ...string) (QueryResult[common.TokenBalance], error)
8485
}
8586

8687
func NewStorageConnector(cfg *config.StorageConfig) (IStorage, error) {

test/mocks/MockIMainStorage.go

Lines changed: 29 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)