| | """ |
| | BlockchainToken Explorer β HuggingFace Space |
| | Search 303,330 Dogeparty & Counterparty blockchain assets. |
| | Data served from blockchaintoken.com API. |
| | """ |
| | import gradio as gr |
| | import requests |
| | import pandas as pd |
| |
|
| | API_BASE = "https://blockchaintoken.com/api.php" |
| |
|
| | def get_stats(network): |
| | r = requests.get(API_BASE, params={"action": "stats", "network": network}, timeout=10) |
| | return r.json() |
| |
|
| | def search_assets(query, network, asset_type, page): |
| | params = {"action": "search", "network": network, "q": query, "page": int(page)} |
| | if asset_type and asset_type != "all": |
| | params["type"] = asset_type |
| | r = requests.get(API_BASE, params=params, timeout=10) |
| | data = r.json() |
| | |
| | if not data.get("results"): |
| | return pd.DataFrame(), f"No results found for '{query}'" |
| | |
| | rows = [] |
| | for a in data["results"]: |
| | rows.append({ |
| | "Asset": a.get("asset_longname") or a.get("asset", ""), |
| | "Type": a.get("type", ""), |
| | "Supply": f"{a.get('supply', 0):,.0f}", |
| | "Divisible": "Yes" if a.get("divisible") else "No", |
| | "Locked": "π" if a.get("locked") else "", |
| | "Description": (a.get("description") or "")[:100], |
| | "Issuer": (a.get("issuer") or "")[:20] + "..." if len(a.get("issuer","")) > 20 else a.get("issuer",""), |
| | }) |
| | |
| | df = pd.DataFrame(rows) |
| | info = f"Page {data.get('page',1)} of {data.get('pages',1)} β {data.get('total',0):,} total results" |
| | return df, info |
| |
|
| | def get_asset_detail(asset_name, network): |
| | if not asset_name.strip(): |
| | return "Enter an asset name" |
| | r = requests.get(API_BASE, params={"action": "asset", "network": network, "asset": asset_name.strip()}, timeout=10) |
| | data = r.json() |
| | if "error" in data: |
| | return f"β {data['error']}" |
| | |
| | lines = [ |
| | f"# {data.get('asset_longname') or data.get('asset', 'Unknown')}", |
| | "", |
| | f"**Network:** {network.title()}", |
| | f"**Type:** {data.get('type', 'unknown')}", |
| | f"**Supply:** {data.get('supply', 0):,.8f}" if data.get('divisible') else f"**Supply:** {data.get('supply', 0):,.0f}", |
| | f"**Divisible:** {'Yes' if data.get('divisible') else 'No'}", |
| | f"**Locked:** {'Yes π' if data.get('locked') else 'No'}", |
| | f"**Issuer:** `{data.get('issuer', 'unknown')}`", |
| | f"**Owner:** `{data.get('owner', 'unknown')}`", |
| | ] |
| | if data.get("description"): |
| | lines.append(f"**Description:** {data['description']}") |
| | |
| | return "\n".join(lines) |
| |
|
| | def get_top_issuers(network): |
| | r = requests.get(API_BASE, params={"action": "issuers", "network": network}, timeout=10) |
| | data = r.json() |
| | if not data.get("issuers"): |
| | return pd.DataFrame() |
| | rows = [] |
| | for iss in data["issuers"][:20]: |
| | rows.append({ |
| | "Address": iss.get("issuer","")[:16] + "..." if len(iss.get("issuer","")) > 16 else iss.get("issuer",""), |
| | "Full Address": iss.get("issuer",""), |
| | "Assets Created": iss.get("count", 0), |
| | }) |
| | return pd.DataFrame(rows) |
| |
|
| | def load_stats(network): |
| | try: |
| | s = get_stats(network) |
| | return ( |
| | f"### {network.title()} Network\n\n" |
| | f"| Metric | Count |\n|--------|-------|\n" |
| | f"| **Total Assets** | **{s['total']:,}** |\n" |
| | f"| Named | {s['named']:,} |\n" |
| | f"| Numeric | {s['numeric']:,} |\n" |
| | f"| Subassets | {s['sub']:,} |\n" |
| | f"| Locked | {s['locked']:,} |" |
| | ) |
| | except Exception as e: |
| | return f"Error loading stats: {e}" |
| |
|
| |
|
| | |
| | theme = gr.themes.Base( |
| | primary_hue="amber", |
| | neutral_hue="gray", |
| | font=gr.themes.GoogleFont("Space Grotesk"), |
| | font_mono=gr.themes.GoogleFont("JetBrains Mono"), |
| | ).set( |
| | body_background_fill="#ffffff", |
| | body_background_fill_dark="#1a1a2e", |
| | block_background_fill="#f8f8f8", |
| | block_background_fill_dark="#22223a", |
| | body_text_color="#1a1a1a", |
| | body_text_color_dark="#e8e6e3", |
| | block_label_text_color="#1a1a1a", |
| | block_label_text_color_dark="#b0b0bc", |
| | block_title_text_color="#1a1a1a", |
| | block_title_text_color_dark="#b0b0bc", |
| | button_primary_background_fill="#b8941e", |
| | button_primary_background_fill_dark="#c4a035", |
| | button_primary_text_color="#ffffff", |
| | button_primary_text_color_dark="#0a0a0f", |
| | border_color_primary="#b8941e", |
| | border_color_primary_dark="#c4a035", |
| | input_background_fill="#ffffff", |
| | input_background_fill_dark="#2a2a42", |
| | input_border_color="#cccccc", |
| | input_border_color_dark="#444466", |
| | ) |
| |
|
| | css = """ |
| | .gradio-container { max-width: 1200px !important; } |
| | .main-header { text-align: center; margin-bottom: 1rem; } |
| | .main-header h1 { font-family: 'JetBrains Mono', monospace; color: #b8941e !important; } |
| | .main-header p { color: #555 !important; } |
| | footer { display: none !important; } |
| | /* Ensure all text is readable */ |
| | .gradio-dataframe td, .gradio-dataframe th { color: #1a1a1a !important; } |
| | .dark .gradio-dataframe td, .dark .gradio-dataframe th { color: #e8e6e3 !important; } |
| | /* Radio buttons, checkboxes, labels */ |
| | .gradio-radio label span, .gradio-radio .group_label, |
| | input[type="radio"] + label, .radio-group label, |
| | label.svelte-1qxcj04, span.svelte-1qxcj04, |
| | .block label span, .wrap label span { color: #1a1a1a !important; } |
| | .dark .block label span, .dark .wrap label span { color: #e8e6e3 !important; } |
| | /* All form labels and text - force black */ |
| | .label-wrap span, .block .label-wrap, .gradio-group label, |
| | label, .label-wrap, .block label, span[data-testid], |
| | .gradio-container label, .gradio-container .label-wrap span, |
| | .block .wrap .label-wrap span, h3, h4 { color: #1a1a1a !important; } |
| | .dark .label-wrap span, .dark .block .label-wrap { color: #e8e6e3 !important; } |
| | /* Input text */ |
| | input, textarea, select, .input-text { color: #1a1a1a !important; } |
| | .dark input, .dark textarea, .dark select { color: #e8e6e3 !important; } |
| | /* Dropdown and textbox */ |
| | .secondary-text, .token-text { color: #555 !important; } |
| | """ |
| |
|
| | with gr.Blocks(theme=theme, css=css, title="BlockchainToken Explorer") as demo: |
| | gr.HTML(""" |
| | <div class="main-header"> |
| | <h1>Γ BLOCKCHAIN<span style="color:#888;font-weight:400">TOKEN</span></h1> |
| | <p style="color:#555;font-size:0.9rem;"> |
| | The most comprehensive Dogeparty & Counterparty asset database β 303,330 assets indexed. |
| | <br>Powered by <a href="https://blockchaintoken.com" style="color:#c4a035">blockchaintoken.com</a> |
| | </p> |
| | </div> |
| | """) |
| | |
| | with gr.Row(): |
| | network = gr.Radio( |
| | choices=["dogeparty", "counterparty"], |
| | value="dogeparty", |
| | label="Network", |
| | scale=1, |
| | ) |
| | stats_display = gr.Markdown(label="Stats") |
| | |
| | |
| | network.change(fn=load_stats, inputs=network, outputs=stats_display) |
| | demo.load(fn=load_stats, inputs=network, outputs=stats_display) |
| | |
| | with gr.Tabs(): |
| | with gr.Tab("π Search Assets"): |
| | with gr.Row(): |
| | search_query = gr.Textbox(label="Search", placeholder="Enter asset name...", scale=3) |
| | search_type = gr.Dropdown( |
| | choices=["all", "named", "numeric", "subasset"], |
| | value="all", label="Type", scale=1 |
| | ) |
| | search_page = gr.Number(value=1, label="Page", minimum=1, scale=1) |
| | search_btn = gr.Button("Search", variant="primary", scale=1) |
| | |
| | search_info = gr.Textbox(label="Results", interactive=False) |
| | search_results = gr.Dataframe( |
| | label="Assets", |
| | headers=["Asset", "Type", "Supply", "Divisible", "Locked", "Description", "Issuer"], |
| | interactive=False, |
| | ) |
| | search_btn.click( |
| | fn=search_assets, |
| | inputs=[search_query, network, search_type, search_page], |
| | outputs=[search_results, search_info] |
| | ) |
| | search_query.submit( |
| | fn=search_assets, |
| | inputs=[search_query, network, search_type, search_page], |
| | outputs=[search_results, search_info] |
| | ) |
| | |
| | with gr.Tab("π Asset Detail"): |
| | with gr.Row(): |
| | asset_input = gr.Textbox(label="Asset Name", placeholder="e.g. PEPECASH, XDP, FDCARD", scale=3) |
| | detail_btn = gr.Button("Look Up", variant="primary", scale=1) |
| | asset_detail = gr.Markdown(label="Asset Information") |
| | detail_btn.click(fn=get_asset_detail, inputs=[asset_input, network], outputs=asset_detail) |
| | asset_input.submit(fn=get_asset_detail, inputs=[asset_input, network], outputs=asset_detail) |
| | |
| | with gr.Tab("π Top Issuers"): |
| | issuer_table = gr.Dataframe( |
| | label="Top 20 Asset Creators", |
| | headers=["Address", "Full Address", "Assets Created"], |
| | interactive=False, |
| | ) |
| | |
| | network.change(fn=get_top_issuers, inputs=network, outputs=issuer_table) |
| | demo.load(fn=get_top_issuers, inputs=network, outputs=issuer_table) |
| | |
| | gr.HTML(""" |
| | <div style="text-align:center;margin-top:2rem;padding:1rem;border-top:1px solid #ddd;"> |
| | <p style="font-family:'JetBrains Mono',monospace;font-size:0.7rem;color:#666;"> |
| | DATA: <a href="https://blockchaintoken.com" style="color:#b8941e">blockchaintoken.com</a> Β· |
| | API: <a href="https://blockchaintoken.com/api.php?action=stats" style="color:#b8941e">Free JSON API</a> Β· |
| | <a href="https://dogeparty.net" style="color:#b8941e">Dogeparty</a> Β· |
| | <a href="https://counterparty.io" style="color:#b8941e">Counterparty</a> |
| | </p> |
| | </div> |
| | """) |
| |
|
| | if __name__ == "__main__": |
| | demo.launch() |
| |
|