package rest import ( "context" "database/sql" "encoding/json" "fmt" "net/http" "time" ) // handleGetAddress handles GET /api/v1/addresses/{chain_id}/{address} func (s *Server) handleGetAddress(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { writeMethodNotAllowed(w) return } if !s.requireDB(w) { return } // Parse address from URL address := normalizeAddress(r.URL.Query().Get("address")) if address == "" { writeValidationError(w, fmt.Errorf("address required")) return } // Validate address format if !isValidAddress(address) { writeValidationError(w, ErrInvalidAddress) return } // Add query timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Get transaction count var txCount int64 err := s.db.QueryRow(ctx, `SELECT COUNT(*) FROM transactions WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)`, s.chainID, address, ).Scan(&txCount) if err != nil { writeInternalError(w, "Database error") return } // Get token count var tokenCount int err = s.db.QueryRow(ctx, `SELECT COUNT(DISTINCT token_contract) FROM token_transfers WHERE chain_id = $1 AND (LOWER(from_address) = $2 OR LOWER(to_address) = $2)`, s.chainID, address, ).Scan(&tokenCount) if err != nil { tokenCount = 0 } // Get label var label sql.NullString s.db.QueryRow(ctx, `SELECT label FROM address_labels WHERE chain_id = $1 AND LOWER(address) = $2 AND label_type = 'public' LIMIT 1`, s.chainID, address, ).Scan(&label) // Get tags rows, err := s.db.Query(ctx, `SELECT tag FROM address_tags WHERE chain_id = $1 AND LOWER(address) = $2`, s.chainID, address, ) tags := []string{} if err == nil { defer rows.Close() for rows.Next() { var tag string if err := rows.Scan(&tag); err == nil { tags = append(tags, tag) } } } // Check if contract var isContract bool s.db.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM contracts WHERE chain_id = $1 AND LOWER(address) = $2)`, s.chainID, address, ).Scan(&isContract) response := map[string]interface{}{ "address": address, "chain_id": s.chainID, "balance": nil, "balance_unavailable": true, "transaction_count": txCount, "token_count": tokenCount, "is_contract": isContract, "tags": tags, } if label.Valid { response["label"] = label.String } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "data": response, }) }