package analytics import ( "context" "fmt" "github.com/jackc/pgx/v5/pgxpool" ) // Calculator calculates network analytics type Calculator struct { db *pgxpool.Pool chainID int } // NewCalculator creates a new analytics calculator func NewCalculator(db *pgxpool.Pool, chainID int) *Calculator { return &Calculator{ db: db, chainID: chainID, } } // NetworkStats represents network statistics type NetworkStats struct { CurrentBlock int64 `json:"current_block"` TPS float64 `json:"tps"` GPS float64 `json:"gps"` AvgGasPrice int64 `json:"avg_gas_price"` PendingTransactions int `json:"pending_transactions"` BlockTime float64 `json:"block_time_seconds"` } // CalculateNetworkStats calculates current network statistics func (c *Calculator) CalculateNetworkStats(ctx context.Context) (*NetworkStats, error) { // Get current block var currentBlock int64 err := c.db.QueryRow(ctx, `SELECT MAX(number) FROM blocks WHERE chain_id = $1`, c.chainID, ).Scan(¤tBlock) if err != nil { return nil, fmt.Errorf("failed to get current block: %w", err) } // Get transactions in last 10 blocks var txCount int var totalGas int64 var blockTimeSum float64 query := ` SELECT COUNT(*) as tx_count, SUM(gas_used) as total_gas, EXTRACT(EPOCH FROM (MAX(timestamp) - MIN(timestamp))) / COUNT(DISTINCT block_number) as avg_block_time FROM transactions WHERE chain_id = $1 AND block_number > $2 ` err = c.db.QueryRow(ctx, query, c.chainID, currentBlock-10).Scan(&txCount, &totalGas, &blockTimeSum) if err != nil { txCount = 0 totalGas = 0 blockTimeSum = 0 } // Calculate TPS and GPS tps := float64(txCount) / (blockTimeSum * 10) gps := float64(totalGas) / (blockTimeSum * 10) // Get average gas price var avgGasPrice int64 c.db.QueryRow(ctx, `SELECT AVG(gas_price) FROM transactions WHERE chain_id = $1 AND block_number > $2 AND gas_price IS NOT NULL`, c.chainID, currentBlock-100, ).Scan(&avgGasPrice) // Get pending transactions var pendingTx int c.db.QueryRow(ctx, `SELECT COUNT(*) FROM mempool_transactions WHERE chain_id = $1 AND status = 'pending'`, c.chainID, ).Scan(&pendingTx) return &NetworkStats{ CurrentBlock: currentBlock, TPS: tps, GPS: gps, AvgGasPrice: avgGasPrice, PendingTransactions: pendingTx, BlockTime: blockTimeSum, }, nil } // TopContracts gets top contracts by transaction count func (c *Calculator) TopContracts(ctx context.Context, limit int) ([]ContractStats, error) { query := ` SELECT to_address as contract_address, COUNT(*) as tx_count, SUM(value) as total_value FROM transactions WHERE chain_id = $1 AND to_address IS NOT NULL GROUP BY to_address ORDER BY tx_count DESC LIMIT $2 ` rows, err := c.db.Query(ctx, query, c.chainID, limit) if err != nil { return nil, fmt.Errorf("failed to query top contracts: %w", err) } defer rows.Close() var contracts []ContractStats for rows.Next() { var contract ContractStats if err := rows.Scan(&contract.Address, &contract.TransactionCount, &contract.TotalValue); err != nil { continue } contracts = append(contracts, contract) } return contracts, nil } // ContractStats represents contract statistics type ContractStats struct { Address string TransactionCount int64 TotalValue string }