import gradio as gr import pandas as pd import bittensor as bt import requests from apscheduler.schedulers.background import BackgroundScheduler # 1. Define data functions first def get_validator_data() -> pd.DataFrame: if subtensor is None or metagraph is None: return pd.DataFrame(columns=['Name', 'UID', 'Axon', 'API', 'Step', 'Recent Bits', 'Updated', 'VTrust']) try: validator_ids = list(set([i for i in range(len(metagraph.validator_permit)) if metagraph.validator_permit[i] and metagraph.active[i] and str(metagraph.axons[i].ip) != "0.0.0.0"])) except Exception as e: print(f"Error getting validator IDs: {e}") validator_ids = [] current_block = subtensor.block results = [] for uid in validator_ids: validator_info = { 'Name': 'unavailable', 'UID': uid, 'Axon': 'unavailable', 'Step': 0, 'Recent Bits': 0, 'Updated': 0, 'VTrust': 0, 'API': '❌' } try: # Get validator name try: identity = subtensor.substrate.query('SubtensorModule', 'Identities', [metagraph.coldkeys[uid]]) validator_info['Name'] = identity.value["name"] if identity != None else 'unnamed' except Exception as e: print(f"Error getting Name for UID {uid}: {str(e)}") validator_info['Axon'] = f"{metagraph.axons[uid].ip}:{metagraph.axons[uid].port}" # Get Step and Range from endpoints try: axon_endpoint = f"http://{validator_info['Axon']}" step_response = requests.get(f"{axon_endpoint}/step", timeout=(2, 3)) step_response.raise_for_status() validator_info['Step'] = step_response.json() bits_response = requests.get( f"{axon_endpoint}/bits", headers={"range": "bytes=-1"}, timeout=(2, 3) ) bits_response.raise_for_status() binary_string = ''.join(format(byte, '08b') for byte in bits_response.content) validator_info['Recent Bits'] = binary_string[-8:] validator_info['API'] = '' if bits_response.ok else '' except requests.Timeout: validator_info['API'] = '' except Exception as e: if not isinstance(e, requests.Timeout): print(f"Error connecting to {axon_endpoint}: {str(e)}") validator_info['API'] = '' try: last_update = int(metagraph.last_update[uid]) validator_info['Updated'] = current_block - last_update except Exception as e: print(f"Error getting Updated for UID {uid}: {str(e)}") try: validator_info['VTrust'] = float(metagraph.validator_trust[uid]) except Exception as e: print(f"Error getting VTrust for UID {uid}: {str(e)}") except Exception as e: print(f"Error getting Axon for UID {uid}: {str(e)}") results.append(validator_info) df = pd.DataFrame(results) df['VTrust'] = df['VTrust'].round(4) return df.sort_values('Step', ascending=False)[['Name', 'UID', 'Axon', 'API', 'Step', 'Recent Bits', 'Updated', 'VTrust']] # 2. Initialize bittensor and load data try: subtensor = bt.subtensor() metagraph = bt.metagraph(netuid=36) initial_df = get_validator_data() initial_timestamp = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S UTC") print("✅ Data loaded successfully") except Exception as e: print(f"❌ Failed to initialize: {e}") initial_df = pd.DataFrame() initial_timestamp = "Failed to load initial data" # 3. Then CSS and UI components background_url = "https://cdn-lfs.hf.co/repos/a4/b4/a4b48e51a6a5ebd9414fc6798da9acf09a6a9425ea160334a1a81c4ad3fdb801/7f89926e2018d54403ac1dc8b0d6fe2401a6489d7da11df27259a07cde7acf87?response-content-disposition=inline%3B+filename*%3DUTF-8%27%27background2.png%3B+filename%3D%22background2.png%22%3B&response-content-type=image%2Fpng&Expires=1731722075&Policy=eyJTdGF0ZW1lbnQiOlt7IkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTczMTcyMjA3NX19LCJSZXNvdXJjZSI6Imh0dHBzOi8vY2RuLWxmcy5oZi5jby9yZXBvcy9hNC9iNC9hNGI0OGU1MWE2YTVlYmQ5NDE0ZmM2Nzk4ZGE5YWNmMDlhNmE5NDI1ZWExNjAzMzRhMWE4MWM0YWQzZmRiODAxLzdmODk5MjZlMjAxOGQ1NDQwM2FjMWRjOGIwZDZmZTI0MDFhNjQ4OWQ3ZGExMWRmMjcyNTlhMDdjZGU3YWNmODc%7EcmVzcG9uc2UtY29udGVudC1kaXNwb3NpdGlvbj0qJnJlc3BvbnNlLWNvbnRlbnQtdHlwZT0qIn1dfQ__&Signature=Y9KvhLMB7xHfQUnq2RC4rMDegTBzbqoMiHAIJzVKy%7E7ajPw2p-AQ19KCYCaXPNbElSV7PZowowMOS%7EtK98A7SM4ndHDePx3OcD%7EDNP8w150rLj58XeCZuVSZ32ayv%7Eb6nZEwYjUbrtrFboN5T9HG8xezW7BgmcXzV3iHppgSNu%7EnwKwJvorVr%7EyddXC6AMsAjsYKYOl1AxnkiMiIKeoD7Rd4ZaAlQsbqAC31BfnbSkBfPq4g0HlCiQYvYsxoofyLsGslnx9X5yIPaYRIRz3uJibPwDZg6GCEYViOfKWwiWV74iA1ptt2DeWLSxBRkjRfnv-HMAs25GmMjqyF85o8vA__&Key-Pair-Id=K3RPWS32NSSJCE" custom_css = """ #component-0 { max-width: 100% !important; padding: 0 !important; margin: 0 !important; } .gradio-container { max-width: 100% !important; padding: 0 !important; margin: 0 !important; background-image: url('""" + background_url + """') !important; background-size: cover !important; background-position: center !important; background-repeat: no-repeat !important; min-height: 100vh !important; } .header-box { text-align: center; max-width: 100%; margin: 20px auto; padding: 1rem; background-color: rgba(17, 24, 39, 0.95); /* Darker, more opaque background */ border-radius: 1rem; } .header-box h1 { color: white; margin: 0; font-size: 2rem; } .header-box p { color: white; /* Changed to white instead of gray */ margin-top: 0.5rem; } """ # Data functions first def fetch_data(): # your data fetching logic pass def create_leaderboard(): try: data = fetch_data() if not data: return pd.DataFrame() return data except Exception as e: print(f"Error creating leaderboard: {e}") return pd.DataFrame() def update_data(): try: data = create_leaderboard() error_msg = "" success = True except Exception as e: data = pd.DataFrame() error_msg = f"Error loading data: {str(e)}" success = False return data, error_msg, not success # Add this before creating the app header_html = """

SN36 Validator Leaderboard

Real-time validator status monitoring

""" # UI components last # Create the Gradio interface with custom theme app = gr.Blocks( title="SN36 Validator Leaderboard", css=custom_css, theme=gr.themes.Soft().set( body_background_fill="rgba(17, 24, 39, 0.95)", background_fill_secondary="rgba(17, 24, 39, 0.95)", ) ) with app: gr.HTML(header_html) with gr.Tabs(elem_id="main-tabs"): with gr.Tab("📊 Leaderboard", elem_id="leaderboard-tab"): # Initialize with preloaded data leaderboard = gr.DataFrame( value=initial_df, headers=["Name", "UID", "Axon", "API", "Step", "Recent Bits", "Updated", "VTrust"], datatype=["str", "number", "str", "html", "number", "str", "number", "number"], elem_id="leaderboard-table", render=True ) # Initialize with preloaded timestamp status_message = gr.Markdown( value=f"Last updated: {initial_timestamp}", elem_classes=["status-msg"] ) with gr.Row(equal_height=True): refresh_button = gr.Button("🔄 Refresh Data", variant="primary", elem_classes=["refresh-btn"]) auto_refresh = gr.Checkbox( label="Auto-refresh (5 min)", value=True, interactive=True ) with gr.Tab("ℹ️ About"): gr.Markdown( """
## About this Leaderboard This dashboard shows real-time information about validators on the network: - **Name**: Validator's registered name on the network - **UID**: Unique identifier of the validator - **Axon**: Validator's Axon address (IP:port) - **API**: API status (✅ online, ❌ offline) - **Step**: Current step count (0 if unavailable) - **Range**: Validator's bit range (0 if unavailable) - **Updated**: Blocks since last update (0 if unavailable) - **VTrust**: Validator's trust score (0 if unavailable) Data is automatically refreshed every 5 minutes, or you can manually refresh using the button.
""" ) def update_leaderboard(): df = get_validator_data() timestamp = pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S UTC") return df, f"Last updated: {timestamp}" refresh_button.click( fn=update_leaderboard, outputs=[leaderboard, status_message], queue=False ) # Auto-refresh logic def setup_auto_refresh(): app.scheduler = BackgroundScheduler() app.scheduler.add_job( lambda: app.queue(update_leaderboard), 'interval', minutes=5 ) app.scheduler.start() # Initial data load app.load( fn=update_leaderboard, outputs=[leaderboard, status_message] ) setup_auto_refresh() # Launch the interface with file serving enabled app.launch( share=False )