| """ |
| 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() |
|
|