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 }