Files
explorer-monorepo/backend/analytics/token_distribution.go

105 lines
2.6 KiB
Go

package analytics
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
)
// TokenDistribution provides token distribution analytics
type TokenDistribution struct {
db *pgxpool.Pool
chainID int
}
// NewTokenDistribution creates a new token distribution analyzer
func NewTokenDistribution(db *pgxpool.Pool, chainID int) *TokenDistribution {
return &TokenDistribution{
db: db,
chainID: chainID,
}
}
// DistributionStats represents token distribution statistics
type DistributionStats struct {
Contract string
Symbol string
TotalSupply string
Holders int
Distribution map[string]string
TopHolders []HolderInfo
}
// HolderInfo represents holder information
type HolderInfo struct {
Address string
Balance string
Percentage string
}
// GetTokenDistribution gets token distribution for a contract
func (td *TokenDistribution) GetTokenDistribution(ctx context.Context, contract string, topN int) (*DistributionStats, error) {
// Refresh materialized view
_, err := td.db.Exec(ctx, `REFRESH MATERIALIZED VIEW CONCURRENTLY token_distribution`)
if err != nil {
// Ignore error if view doesn't exist yet
}
// Get distribution from materialized view
query := `
SELECT holder_count, total_balance
FROM token_distribution
WHERE token_contract = $1 AND chain_id = $2
`
var holders int
var totalSupply string
err = td.db.QueryRow(ctx, query, contract, td.chainID).Scan(&holders, &totalSupply)
if err != nil {
return nil, fmt.Errorf("failed to get distribution: %w", err)
}
// Get top holders
topHoldersQuery := `
SELECT address, balance
FROM token_balances
WHERE token_contract = $1 AND chain_id = $2 AND balance > 0
ORDER BY balance DESC
LIMIT $3
`
rows, err := td.db.Query(ctx, topHoldersQuery, contract, td.chainID, topN)
if err != nil {
return nil, fmt.Errorf("failed to get top holders: %w", err)
}
defer rows.Close()
topHolders := []HolderInfo{}
for rows.Next() {
var holder HolderInfo
if err := rows.Scan(&holder.Address, &holder.Balance); err != nil {
continue
}
// Calculate percentage (simplified)
holder.Percentage = "0.0" // TODO: Calculate from total supply
topHolders = append(topHolders, holder)
}
stats := &DistributionStats{
Contract: contract,
Holders: holders,
TotalSupply: totalSupply,
Distribution: make(map[string]string),
TopHolders: topHolders,
}
// Calculate distribution metrics
stats.Distribution["top_10_percent"] = "0.0" // TODO: Calculate
stats.Distribution["top_1_percent"] = "0.0" // TODO: Calculate
stats.Distribution["gini_coefficient"] = "0.0" // TODO: Calculate
return stats, nil
}