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