File size: 8,284 Bytes
c4d0c88 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | import gradio as gr
import requests
import json
MCP_URL = "https://mcp.clicksprotocol.xyz/mcp"
def call_mcp(method, params=None):
"""Call the Clicks Protocol MCP server."""
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": method,
"arguments": params or {}
}
}
try:
r = requests.post(MCP_URL, json=payload, timeout=15)
data = r.json()
if "result" in data:
content = data["result"].get("content", [])
for c in content:
if c.get("type") == "text":
try:
return json.loads(c["text"])
except json.JSONDecodeError:
return c["text"]
if "error" in data:
return {"error": data["error"]}
return data
except Exception as e:
return {"error": str(e)}
def get_yield_info():
"""Fetch current yield rates from Aave V3 and Morpho on Base."""
result = call_mcp("clicks_get_yield_info")
if isinstance(result, dict) and "error" not in result:
lines = []
lines.append(f"Active Protocol: {result.get('activeProtocol', 'N/A')}")
lines.append(f"Aave V3 APY: {result.get('aaveAPY', 'N/A')}")
lines.append(f"Morpho APY: {result.get('morphoAPY', 'N/A')}")
lines.append(f"Total Deposits: {result.get('totalDeposits', 'N/A')} USDC")
lines.append(f"Pending Fees: {result.get('pendingFees', 'N/A')} USDC")
return "\n".join(lines)
return json.dumps(result, indent=2)
def simulate_split(amount, address):
"""Simulate how a USDC payment would be split."""
if not amount:
return "Enter an amount in USDC."
params = {"amount": str(amount)}
if address and address.startswith("0x"):
params["agentAddress"] = address
result = call_mcp("clicks_simulate_split", params)
if isinstance(result, dict) and "error" not in result:
lines = []
lines.append(f"Payment: {result.get('totalAmount', amount)} USDC")
lines.append(f"Liquid (agent wallet): {result.get('liquidAmount', 'N/A')} USDC")
lines.append(f"Yield (DeFi): {result.get('yieldAmount', 'N/A')} USDC")
lines.append(f"Yield %: {result.get('yieldPercentage', '20')}%")
return "\n".join(lines)
return json.dumps(result, indent=2)
def get_agent_info(address):
"""Look up an agent's treasury status."""
if not address or not address.startswith("0x"):
return "Enter a valid Ethereum address (0x...)."
result = call_mcp("clicks_get_agent_info", {"agentAddress": address})
if isinstance(result, dict) and "error" not in result:
lines = []
lines.append(f"Registered: {result.get('registered', 'N/A')}")
lines.append(f"Operator: {result.get('operator', 'N/A')}")
lines.append(f"Deposited: {result.get('deposited', '0')} USDC")
lines.append(f"Yield %: {result.get('yieldPercentage', 'N/A')}%")
lines.append(f"Wallet Balance: {result.get('walletBalance', '0')} USDC")
return "\n".join(lines)
return json.dumps(result, indent=2)
def get_referral_stats(address):
"""Check referral network stats."""
if not address or not address.startswith("0x"):
return "Enter a valid Ethereum address (0x...)."
result = call_mcp("clicks_get_referral_stats", {"agentAddress": address})
if isinstance(result, dict) and "error" not in result:
lines = []
lines.append(f"Direct Referrals: {result.get('directReferrals', '0')}")
lines.append(f"Total Earned: {result.get('totalEarned', '0')} USDC")
lines.append(f"Claimable: {result.get('claimable', '0')} USDC")
lines.append(f"Team Tier: {result.get('teamTier', 'N/A')}")
return "\n".join(lines)
return json.dumps(result, indent=2)
def yield_calculator(amount, months, apy):
"""Calculate projected yield over time."""
try:
amt = float(amount)
m = int(months)
rate = float(apy) / 100
except (ValueError, TypeError):
return "Enter valid numbers."
yield_portion = amt * 0.2 # 20% goes to yield
liquid = amt * 0.8
monthly_rate = rate / 12
total_yield = yield_portion * ((1 + monthly_rate) ** m - 1)
fee = total_yield * 0.02
net_yield = total_yield - fee
lines = []
lines.append(f"Initial Payment: {amt:.2f} USDC")
lines.append(f"Liquid (immediate): {liquid:.2f} USDC")
lines.append(f"Yield Deposit: {yield_portion:.2f} USDC")
lines.append(f"")
lines.append(f"After {m} months at {rate*100:.1f}% APY:")
lines.append(f" Gross Yield: {total_yield:.2f} USDC")
lines.append(f" Protocol Fee (2%): {fee:.2f} USDC")
lines.append(f" Net Yield: {net_yield:.2f} USDC")
lines.append(f" Total Value: {yield_portion + net_yield:.2f} USDC")
lines.append(f"")
lines.append(f"Total Agent Value: {liquid + yield_portion + net_yield:.2f} USDC")
return "\n".join(lines)
# Build the Gradio UI
with gr.Blocks(
title="Clicks Protocol Treasury Simulator",
theme=gr.themes.Base(primary_hue="blue", neutral_hue="slate"),
) as demo:
gr.Markdown("""
# Clicks Protocol
### Autonomous USDC Yield for AI Agents on Base
80% liquid. 20% earning. No lockup. Non-custodial.
[Website](https://clicksprotocol.xyz) | [GitHub](https://github.com/clicks-protocol) | [SDK](https://www.npmjs.com/package/@clicks-protocol/sdk) | [MCP Server](https://www.npmjs.com/package/@clicks-protocol/mcp-server)
""")
with gr.Tab("Live Yield Rates"):
gr.Markdown("Current DeFi yields on Base, auto-routed by the YieldRouter.")
yield_btn = gr.Button("Fetch Live Rates", variant="primary")
yield_output = gr.Textbox(label="Current Rates", lines=6)
yield_btn.click(fn=get_yield_info, outputs=yield_output)
with gr.Tab("Payment Simulator"):
gr.Markdown("See how a USDC payment gets split between liquid funds and yield.")
with gr.Row():
sim_amount = gr.Number(label="USDC Amount", value=1000)
sim_address = gr.Textbox(label="Agent Address (optional)", placeholder="0x...")
sim_btn = gr.Button("Simulate Split", variant="primary")
sim_output = gr.Textbox(label="Split Result", lines=5)
sim_btn.click(fn=simulate_split, inputs=[sim_amount, sim_address], outputs=sim_output)
with gr.Tab("Yield Calculator"):
gr.Markdown("Project your yield earnings over time.")
with gr.Row():
calc_amount = gr.Number(label="USDC Amount", value=10000)
calc_months = gr.Slider(label="Months", minimum=1, maximum=36, value=12, step=1)
calc_apy = gr.Slider(label="APY %", minimum=1, maximum=20, value=8, step=0.5)
calc_btn = gr.Button("Calculate", variant="primary")
calc_output = gr.Textbox(label="Projection", lines=10)
calc_btn.click(fn=yield_calculator, inputs=[calc_amount, calc_months, calc_apy], outputs=calc_output)
with gr.Tab("Agent Lookup"):
gr.Markdown("Check any agent's treasury status on-chain.")
agent_address = gr.Textbox(label="Agent Address", placeholder="0x...")
agent_btn = gr.Button("Look Up Agent", variant="primary")
agent_output = gr.Textbox(label="Agent Info", lines=6)
agent_btn.click(fn=get_agent_info, inputs=agent_address, outputs=agent_output)
with gr.Tab("Referral Stats"):
gr.Markdown("Check referral network earnings and team status.")
ref_address = gr.Textbox(label="Agent Address", placeholder="0x...")
ref_btn = gr.Button("Check Referrals", variant="primary")
ref_output = gr.Textbox(label="Referral Stats", lines=5)
ref_btn.click(fn=get_referral_stats, inputs=ref_address, outputs=ref_output)
gr.Markdown("""
---
**How it works:** Agent receives USDC → Clicks splits 80/20 → 20% earns yield via Aave V3 or Morpho → Withdraw anytime → 2% fee on yield only.
**SDK:** `npm install @clicks-protocol/sdk` | **MCP:** `npx @clicks-protocol/mcp-server`
Contracts verified on [Basescan](https://basescan.org/address/0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3). Built on Base L2.
""")
demo.launch()
|