clicks-crewai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clicks_crewai-0.1.0/PKG-INFO +237 -0
- clicks_crewai-0.1.0/README.md +212 -0
- clicks_crewai-0.1.0/clicks_crewai.egg-info/PKG-INFO +237 -0
- clicks_crewai-0.1.0/clicks_crewai.egg-info/SOURCES.txt +8 -0
- clicks_crewai-0.1.0/clicks_crewai.egg-info/dependency_links.txt +1 -0
- clicks_crewai-0.1.0/clicks_crewai.egg-info/requires.txt +3 -0
- clicks_crewai-0.1.0/clicks_crewai.egg-info/top_level.txt +1 -0
- clicks_crewai-0.1.0/clicks_crewai.py +422 -0
- clicks_crewai-0.1.0/pyproject.toml +39 -0
- clicks_crewai-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clicks-crewai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clicks Protocol yield management tools for CrewAI agent crews on Base
|
|
5
|
+
Author-email: Clicks Protocol <dev@clicksprotocol.xyz>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://clicksprotocol.xyz
|
|
8
|
+
Project-URL: Repository, https://github.com/clicks-protocol/clicks-crewai
|
|
9
|
+
Project-URL: Documentation, https://docs.clicksprotocol.xyz/integrations/crewai
|
|
10
|
+
Keywords: crewai,defi,yield,base,usdc,ai-agents,clicks-protocol
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: crewai>=0.28.0
|
|
23
|
+
Requires-Dist: crewai-tools>=0.4.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
|
|
26
|
+
# Clicks Protocol — CrewAI Integration
|
|
27
|
+
|
|
28
|
+
On-chain yield management tools for CrewAI agent crews on Base.
|
|
29
|
+
|
|
30
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
31
|
+
|
|
32
|
+
## What It Does
|
|
33
|
+
|
|
34
|
+
Gives your CrewAI agents the ability to manage idle USDC as a team. A Treasury Manager agent monitors balances, an Investment Analyst evaluates yield rates, and together they autonomously split funds 80/20: 80% liquid, 20% earning 4-8% APY on Base.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install clicks-crewai
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or from source:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/clicks-protocol/clicks-crewai
|
|
46
|
+
cd clicks-crewai
|
|
47
|
+
pip install -e .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
- Python >= 3.9
|
|
53
|
+
- Node.js >= 18 (for SDK bridge)
|
|
54
|
+
- `@clicks-protocol/sdk` installed: `npm install @clicks-protocol/sdk`
|
|
55
|
+
|
|
56
|
+
### Environment Variables
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export CLICKS_PRIVATE_KEY="your-agent-wallet-private-key"
|
|
60
|
+
export CLICKS_RPC_URL="https://mainnet.base.org" # optional
|
|
61
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from crewai import Agent, Task, Crew
|
|
68
|
+
from clicks_crewai import (
|
|
69
|
+
ClicksCheckBalanceTool,
|
|
70
|
+
ClicksActivateYieldTool,
|
|
71
|
+
ClicksGetAPYTool,
|
|
72
|
+
ClicksSimulateSplitTool,
|
|
73
|
+
ClicksWithdrawYieldTool,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
77
|
+
|
|
78
|
+
# Create tools
|
|
79
|
+
balance_tool = ClicksCheckBalanceTool()
|
|
80
|
+
yield_tool = ClicksActivateYieldTool()
|
|
81
|
+
apy_tool = ClicksGetAPYTool()
|
|
82
|
+
simulate_tool = ClicksSimulateSplitTool()
|
|
83
|
+
withdraw_tool = ClicksWithdrawYieldTool()
|
|
84
|
+
|
|
85
|
+
# Define agents
|
|
86
|
+
treasury_manager = Agent(
|
|
87
|
+
role="Treasury Manager",
|
|
88
|
+
goal="Maximize yield on idle USDC while maintaining operational liquidity",
|
|
89
|
+
backstory=(
|
|
90
|
+
"You are an autonomous treasury manager for an AI agent. "
|
|
91
|
+
"Your job is to ensure idle USDC earns yield via Clicks Protocol "
|
|
92
|
+
"while keeping enough liquid for operations. You never risk funds "
|
|
93
|
+
"needed for imminent transactions."
|
|
94
|
+
),
|
|
95
|
+
tools=[balance_tool, yield_tool, withdraw_tool, simulate_tool],
|
|
96
|
+
verbose=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
investment_analyst = Agent(
|
|
100
|
+
role="Investment Analyst",
|
|
101
|
+
goal="Monitor yield rates and recommend optimal treasury allocation",
|
|
102
|
+
backstory=(
|
|
103
|
+
"You analyze DeFi yield rates and market conditions to advise "
|
|
104
|
+
"the Treasury Manager. You track Clicks Protocol APY and recommend "
|
|
105
|
+
"when to activate, hold, or withdraw yield positions."
|
|
106
|
+
),
|
|
107
|
+
tools=[apy_tool, balance_tool, simulate_tool],
|
|
108
|
+
verbose=True,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Define tasks
|
|
112
|
+
analysis_task = Task(
|
|
113
|
+
description=(
|
|
114
|
+
f"Analyze the current treasury position for {AGENT_ADDRESS}. "
|
|
115
|
+
"Check the current APY rate, simulate a split for any idle funds, "
|
|
116
|
+
"and provide a recommendation on whether to activate yield."
|
|
117
|
+
),
|
|
118
|
+
expected_output=(
|
|
119
|
+
"A treasury analysis report with: current balances, APY rate, "
|
|
120
|
+
"simulated split breakdown, and a clear recommendation."
|
|
121
|
+
),
|
|
122
|
+
agent=investment_analyst,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
execution_task = Task(
|
|
126
|
+
description=(
|
|
127
|
+
f"Based on the analyst's recommendation, take action on {AGENT_ADDRESS}. "
|
|
128
|
+
"If yield activation is recommended and APY > 4%, activate yield on idle funds. "
|
|
129
|
+
"If withdrawal is recommended, withdraw. Always check balance first."
|
|
130
|
+
),
|
|
131
|
+
expected_output=(
|
|
132
|
+
"Confirmation of action taken with transaction details, or explanation "
|
|
133
|
+
"of why no action was taken."
|
|
134
|
+
),
|
|
135
|
+
agent=treasury_manager,
|
|
136
|
+
context=[analysis_task],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Run the crew
|
|
140
|
+
crew = Crew(
|
|
141
|
+
agents=[investment_analyst, treasury_manager],
|
|
142
|
+
tasks=[analysis_task, execution_task],
|
|
143
|
+
verbose=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
result = crew.kickoff()
|
|
147
|
+
print(result)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Available Tools
|
|
151
|
+
|
|
152
|
+
| Tool | Description | Best For |
|
|
153
|
+
|------|-------------|----------|
|
|
154
|
+
| `ClicksCheckBalanceTool` | View liquid vs. yield balances | Treasury Manager |
|
|
155
|
+
| `ClicksActivateYieldTool` | Split USDC 80/20 to earn yield | Treasury Manager |
|
|
156
|
+
| `ClicksWithdrawYieldTool` | Withdraw all yield + deposits | Treasury Manager |
|
|
157
|
+
| `ClicksGetAPYTool` | Get current APY rate | Investment Analyst |
|
|
158
|
+
| `ClicksSimulateSplitTool` | Preview split without moving funds | Both |
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from clicks_crewai import configure_sdk
|
|
164
|
+
|
|
165
|
+
# Configure before creating tools
|
|
166
|
+
configure_sdk(
|
|
167
|
+
rpc_url="https://mainnet.base.org",
|
|
168
|
+
private_key="your-key",
|
|
169
|
+
sdk_path="/path/to/sdk",
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Or use environment variables (recommended):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
export CLICKS_PRIVATE_KEY="..."
|
|
177
|
+
export CLICKS_RPC_URL="https://mainnet.base.org"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Example: Scheduled Treasury Optimization
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
import schedule
|
|
184
|
+
import time
|
|
185
|
+
from crewai import Agent, Task, Crew
|
|
186
|
+
from clicks_crewai import get_all_tools
|
|
187
|
+
|
|
188
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
189
|
+
|
|
190
|
+
def run_treasury_check():
|
|
191
|
+
tools = get_all_tools()
|
|
192
|
+
|
|
193
|
+
manager = Agent(
|
|
194
|
+
role="Treasury Manager",
|
|
195
|
+
goal="Optimize USDC yield allocation",
|
|
196
|
+
backstory="Autonomous treasury manager using Clicks Protocol on Base.",
|
|
197
|
+
tools=tools,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
task = Task(
|
|
201
|
+
description=(
|
|
202
|
+
f"Check treasury status for {AGENT_ADDRESS}. "
|
|
203
|
+
"If idle USDC > 500 and APY > 4%, activate yield. "
|
|
204
|
+
"Report current status."
|
|
205
|
+
),
|
|
206
|
+
expected_output="Treasury status and actions taken.",
|
|
207
|
+
agent=manager,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
crew = Crew(agents=[manager], tasks=[task])
|
|
211
|
+
return crew.kickoff()
|
|
212
|
+
|
|
213
|
+
# Run every 6 hours
|
|
214
|
+
schedule.every(6).hours.do(run_treasury_check)
|
|
215
|
+
|
|
216
|
+
while True:
|
|
217
|
+
schedule.run_pending()
|
|
218
|
+
time.sleep(60)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Contract Addresses (Base Mainnet)
|
|
222
|
+
|
|
223
|
+
| Contract | Address |
|
|
224
|
+
|----------|---------|
|
|
225
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
226
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
227
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
228
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
229
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Clicks Protocol — CrewAI Integration
|
|
2
|
+
|
|
3
|
+
On-chain yield management tools for CrewAI agent crews on Base.
|
|
4
|
+
|
|
5
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
Gives your CrewAI agents the ability to manage idle USDC as a team. A Treasury Manager agent monitors balances, an Investment Analyst evaluates yield rates, and together they autonomously split funds 80/20: 80% liquid, 20% earning 4-8% APY on Base.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install clicks-crewai
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or from source:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/clicks-protocol/clicks-crewai
|
|
21
|
+
cd clicks-crewai
|
|
22
|
+
pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Prerequisites
|
|
26
|
+
|
|
27
|
+
- Python >= 3.9
|
|
28
|
+
- Node.js >= 18 (for SDK bridge)
|
|
29
|
+
- `@clicks-protocol/sdk` installed: `npm install @clicks-protocol/sdk`
|
|
30
|
+
|
|
31
|
+
### Environment Variables
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export CLICKS_PRIVATE_KEY="your-agent-wallet-private-key"
|
|
35
|
+
export CLICKS_RPC_URL="https://mainnet.base.org" # optional
|
|
36
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from crewai import Agent, Task, Crew
|
|
43
|
+
from clicks_crewai import (
|
|
44
|
+
ClicksCheckBalanceTool,
|
|
45
|
+
ClicksActivateYieldTool,
|
|
46
|
+
ClicksGetAPYTool,
|
|
47
|
+
ClicksSimulateSplitTool,
|
|
48
|
+
ClicksWithdrawYieldTool,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
52
|
+
|
|
53
|
+
# Create tools
|
|
54
|
+
balance_tool = ClicksCheckBalanceTool()
|
|
55
|
+
yield_tool = ClicksActivateYieldTool()
|
|
56
|
+
apy_tool = ClicksGetAPYTool()
|
|
57
|
+
simulate_tool = ClicksSimulateSplitTool()
|
|
58
|
+
withdraw_tool = ClicksWithdrawYieldTool()
|
|
59
|
+
|
|
60
|
+
# Define agents
|
|
61
|
+
treasury_manager = Agent(
|
|
62
|
+
role="Treasury Manager",
|
|
63
|
+
goal="Maximize yield on idle USDC while maintaining operational liquidity",
|
|
64
|
+
backstory=(
|
|
65
|
+
"You are an autonomous treasury manager for an AI agent. "
|
|
66
|
+
"Your job is to ensure idle USDC earns yield via Clicks Protocol "
|
|
67
|
+
"while keeping enough liquid for operations. You never risk funds "
|
|
68
|
+
"needed for imminent transactions."
|
|
69
|
+
),
|
|
70
|
+
tools=[balance_tool, yield_tool, withdraw_tool, simulate_tool],
|
|
71
|
+
verbose=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
investment_analyst = Agent(
|
|
75
|
+
role="Investment Analyst",
|
|
76
|
+
goal="Monitor yield rates and recommend optimal treasury allocation",
|
|
77
|
+
backstory=(
|
|
78
|
+
"You analyze DeFi yield rates and market conditions to advise "
|
|
79
|
+
"the Treasury Manager. You track Clicks Protocol APY and recommend "
|
|
80
|
+
"when to activate, hold, or withdraw yield positions."
|
|
81
|
+
),
|
|
82
|
+
tools=[apy_tool, balance_tool, simulate_tool],
|
|
83
|
+
verbose=True,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Define tasks
|
|
87
|
+
analysis_task = Task(
|
|
88
|
+
description=(
|
|
89
|
+
f"Analyze the current treasury position for {AGENT_ADDRESS}. "
|
|
90
|
+
"Check the current APY rate, simulate a split for any idle funds, "
|
|
91
|
+
"and provide a recommendation on whether to activate yield."
|
|
92
|
+
),
|
|
93
|
+
expected_output=(
|
|
94
|
+
"A treasury analysis report with: current balances, APY rate, "
|
|
95
|
+
"simulated split breakdown, and a clear recommendation."
|
|
96
|
+
),
|
|
97
|
+
agent=investment_analyst,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
execution_task = Task(
|
|
101
|
+
description=(
|
|
102
|
+
f"Based on the analyst's recommendation, take action on {AGENT_ADDRESS}. "
|
|
103
|
+
"If yield activation is recommended and APY > 4%, activate yield on idle funds. "
|
|
104
|
+
"If withdrawal is recommended, withdraw. Always check balance first."
|
|
105
|
+
),
|
|
106
|
+
expected_output=(
|
|
107
|
+
"Confirmation of action taken with transaction details, or explanation "
|
|
108
|
+
"of why no action was taken."
|
|
109
|
+
),
|
|
110
|
+
agent=treasury_manager,
|
|
111
|
+
context=[analysis_task],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Run the crew
|
|
115
|
+
crew = Crew(
|
|
116
|
+
agents=[investment_analyst, treasury_manager],
|
|
117
|
+
tasks=[analysis_task, execution_task],
|
|
118
|
+
verbose=True,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
result = crew.kickoff()
|
|
122
|
+
print(result)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Available Tools
|
|
126
|
+
|
|
127
|
+
| Tool | Description | Best For |
|
|
128
|
+
|------|-------------|----------|
|
|
129
|
+
| `ClicksCheckBalanceTool` | View liquid vs. yield balances | Treasury Manager |
|
|
130
|
+
| `ClicksActivateYieldTool` | Split USDC 80/20 to earn yield | Treasury Manager |
|
|
131
|
+
| `ClicksWithdrawYieldTool` | Withdraw all yield + deposits | Treasury Manager |
|
|
132
|
+
| `ClicksGetAPYTool` | Get current APY rate | Investment Analyst |
|
|
133
|
+
| `ClicksSimulateSplitTool` | Preview split without moving funds | Both |
|
|
134
|
+
|
|
135
|
+
## Configuration
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from clicks_crewai import configure_sdk
|
|
139
|
+
|
|
140
|
+
# Configure before creating tools
|
|
141
|
+
configure_sdk(
|
|
142
|
+
rpc_url="https://mainnet.base.org",
|
|
143
|
+
private_key="your-key",
|
|
144
|
+
sdk_path="/path/to/sdk",
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Or use environment variables (recommended):
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
export CLICKS_PRIVATE_KEY="..."
|
|
152
|
+
export CLICKS_RPC_URL="https://mainnet.base.org"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Example: Scheduled Treasury Optimization
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
import schedule
|
|
159
|
+
import time
|
|
160
|
+
from crewai import Agent, Task, Crew
|
|
161
|
+
from clicks_crewai import get_all_tools
|
|
162
|
+
|
|
163
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
164
|
+
|
|
165
|
+
def run_treasury_check():
|
|
166
|
+
tools = get_all_tools()
|
|
167
|
+
|
|
168
|
+
manager = Agent(
|
|
169
|
+
role="Treasury Manager",
|
|
170
|
+
goal="Optimize USDC yield allocation",
|
|
171
|
+
backstory="Autonomous treasury manager using Clicks Protocol on Base.",
|
|
172
|
+
tools=tools,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
task = Task(
|
|
176
|
+
description=(
|
|
177
|
+
f"Check treasury status for {AGENT_ADDRESS}. "
|
|
178
|
+
"If idle USDC > 500 and APY > 4%, activate yield. "
|
|
179
|
+
"Report current status."
|
|
180
|
+
),
|
|
181
|
+
expected_output="Treasury status and actions taken.",
|
|
182
|
+
agent=manager,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
crew = Crew(agents=[manager], tasks=[task])
|
|
186
|
+
return crew.kickoff()
|
|
187
|
+
|
|
188
|
+
# Run every 6 hours
|
|
189
|
+
schedule.every(6).hours.do(run_treasury_check)
|
|
190
|
+
|
|
191
|
+
while True:
|
|
192
|
+
schedule.run_pending()
|
|
193
|
+
time.sleep(60)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Contract Addresses (Base Mainnet)
|
|
197
|
+
|
|
198
|
+
| Contract | Address |
|
|
199
|
+
|----------|---------|
|
|
200
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
201
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
202
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
203
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
204
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clicks-crewai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clicks Protocol yield management tools for CrewAI agent crews on Base
|
|
5
|
+
Author-email: Clicks Protocol <dev@clicksprotocol.xyz>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://clicksprotocol.xyz
|
|
8
|
+
Project-URL: Repository, https://github.com/clicks-protocol/clicks-crewai
|
|
9
|
+
Project-URL: Documentation, https://docs.clicksprotocol.xyz/integrations/crewai
|
|
10
|
+
Keywords: crewai,defi,yield,base,usdc,ai-agents,clicks-protocol
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: crewai>=0.28.0
|
|
23
|
+
Requires-Dist: crewai-tools>=0.4.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
|
|
26
|
+
# Clicks Protocol — CrewAI Integration
|
|
27
|
+
|
|
28
|
+
On-chain yield management tools for CrewAI agent crews on Base.
|
|
29
|
+
|
|
30
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
31
|
+
|
|
32
|
+
## What It Does
|
|
33
|
+
|
|
34
|
+
Gives your CrewAI agents the ability to manage idle USDC as a team. A Treasury Manager agent monitors balances, an Investment Analyst evaluates yield rates, and together they autonomously split funds 80/20: 80% liquid, 20% earning 4-8% APY on Base.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install clicks-crewai
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or from source:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/clicks-protocol/clicks-crewai
|
|
46
|
+
cd clicks-crewai
|
|
47
|
+
pip install -e .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Prerequisites
|
|
51
|
+
|
|
52
|
+
- Python >= 3.9
|
|
53
|
+
- Node.js >= 18 (for SDK bridge)
|
|
54
|
+
- `@clicks-protocol/sdk` installed: `npm install @clicks-protocol/sdk`
|
|
55
|
+
|
|
56
|
+
### Environment Variables
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
export CLICKS_PRIVATE_KEY="your-agent-wallet-private-key"
|
|
60
|
+
export CLICKS_RPC_URL="https://mainnet.base.org" # optional
|
|
61
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from crewai import Agent, Task, Crew
|
|
68
|
+
from clicks_crewai import (
|
|
69
|
+
ClicksCheckBalanceTool,
|
|
70
|
+
ClicksActivateYieldTool,
|
|
71
|
+
ClicksGetAPYTool,
|
|
72
|
+
ClicksSimulateSplitTool,
|
|
73
|
+
ClicksWithdrawYieldTool,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
77
|
+
|
|
78
|
+
# Create tools
|
|
79
|
+
balance_tool = ClicksCheckBalanceTool()
|
|
80
|
+
yield_tool = ClicksActivateYieldTool()
|
|
81
|
+
apy_tool = ClicksGetAPYTool()
|
|
82
|
+
simulate_tool = ClicksSimulateSplitTool()
|
|
83
|
+
withdraw_tool = ClicksWithdrawYieldTool()
|
|
84
|
+
|
|
85
|
+
# Define agents
|
|
86
|
+
treasury_manager = Agent(
|
|
87
|
+
role="Treasury Manager",
|
|
88
|
+
goal="Maximize yield on idle USDC while maintaining operational liquidity",
|
|
89
|
+
backstory=(
|
|
90
|
+
"You are an autonomous treasury manager for an AI agent. "
|
|
91
|
+
"Your job is to ensure idle USDC earns yield via Clicks Protocol "
|
|
92
|
+
"while keeping enough liquid for operations. You never risk funds "
|
|
93
|
+
"needed for imminent transactions."
|
|
94
|
+
),
|
|
95
|
+
tools=[balance_tool, yield_tool, withdraw_tool, simulate_tool],
|
|
96
|
+
verbose=True,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
investment_analyst = Agent(
|
|
100
|
+
role="Investment Analyst",
|
|
101
|
+
goal="Monitor yield rates and recommend optimal treasury allocation",
|
|
102
|
+
backstory=(
|
|
103
|
+
"You analyze DeFi yield rates and market conditions to advise "
|
|
104
|
+
"the Treasury Manager. You track Clicks Protocol APY and recommend "
|
|
105
|
+
"when to activate, hold, or withdraw yield positions."
|
|
106
|
+
),
|
|
107
|
+
tools=[apy_tool, balance_tool, simulate_tool],
|
|
108
|
+
verbose=True,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Define tasks
|
|
112
|
+
analysis_task = Task(
|
|
113
|
+
description=(
|
|
114
|
+
f"Analyze the current treasury position for {AGENT_ADDRESS}. "
|
|
115
|
+
"Check the current APY rate, simulate a split for any idle funds, "
|
|
116
|
+
"and provide a recommendation on whether to activate yield."
|
|
117
|
+
),
|
|
118
|
+
expected_output=(
|
|
119
|
+
"A treasury analysis report with: current balances, APY rate, "
|
|
120
|
+
"simulated split breakdown, and a clear recommendation."
|
|
121
|
+
),
|
|
122
|
+
agent=investment_analyst,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
execution_task = Task(
|
|
126
|
+
description=(
|
|
127
|
+
f"Based on the analyst's recommendation, take action on {AGENT_ADDRESS}. "
|
|
128
|
+
"If yield activation is recommended and APY > 4%, activate yield on idle funds. "
|
|
129
|
+
"If withdrawal is recommended, withdraw. Always check balance first."
|
|
130
|
+
),
|
|
131
|
+
expected_output=(
|
|
132
|
+
"Confirmation of action taken with transaction details, or explanation "
|
|
133
|
+
"of why no action was taken."
|
|
134
|
+
),
|
|
135
|
+
agent=treasury_manager,
|
|
136
|
+
context=[analysis_task],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Run the crew
|
|
140
|
+
crew = Crew(
|
|
141
|
+
agents=[investment_analyst, treasury_manager],
|
|
142
|
+
tasks=[analysis_task, execution_task],
|
|
143
|
+
verbose=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
result = crew.kickoff()
|
|
147
|
+
print(result)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Available Tools
|
|
151
|
+
|
|
152
|
+
| Tool | Description | Best For |
|
|
153
|
+
|------|-------------|----------|
|
|
154
|
+
| `ClicksCheckBalanceTool` | View liquid vs. yield balances | Treasury Manager |
|
|
155
|
+
| `ClicksActivateYieldTool` | Split USDC 80/20 to earn yield | Treasury Manager |
|
|
156
|
+
| `ClicksWithdrawYieldTool` | Withdraw all yield + deposits | Treasury Manager |
|
|
157
|
+
| `ClicksGetAPYTool` | Get current APY rate | Investment Analyst |
|
|
158
|
+
| `ClicksSimulateSplitTool` | Preview split without moving funds | Both |
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from clicks_crewai import configure_sdk
|
|
164
|
+
|
|
165
|
+
# Configure before creating tools
|
|
166
|
+
configure_sdk(
|
|
167
|
+
rpc_url="https://mainnet.base.org",
|
|
168
|
+
private_key="your-key",
|
|
169
|
+
sdk_path="/path/to/sdk",
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Or use environment variables (recommended):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
export CLICKS_PRIVATE_KEY="..."
|
|
177
|
+
export CLICKS_RPC_URL="https://mainnet.base.org"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Example: Scheduled Treasury Optimization
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
import schedule
|
|
184
|
+
import time
|
|
185
|
+
from crewai import Agent, Task, Crew
|
|
186
|
+
from clicks_crewai import get_all_tools
|
|
187
|
+
|
|
188
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
189
|
+
|
|
190
|
+
def run_treasury_check():
|
|
191
|
+
tools = get_all_tools()
|
|
192
|
+
|
|
193
|
+
manager = Agent(
|
|
194
|
+
role="Treasury Manager",
|
|
195
|
+
goal="Optimize USDC yield allocation",
|
|
196
|
+
backstory="Autonomous treasury manager using Clicks Protocol on Base.",
|
|
197
|
+
tools=tools,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
task = Task(
|
|
201
|
+
description=(
|
|
202
|
+
f"Check treasury status for {AGENT_ADDRESS}. "
|
|
203
|
+
"If idle USDC > 500 and APY > 4%, activate yield. "
|
|
204
|
+
"Report current status."
|
|
205
|
+
),
|
|
206
|
+
expected_output="Treasury status and actions taken.",
|
|
207
|
+
agent=manager,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
crew = Crew(agents=[manager], tasks=[task])
|
|
211
|
+
return crew.kickoff()
|
|
212
|
+
|
|
213
|
+
# Run every 6 hours
|
|
214
|
+
schedule.every(6).hours.do(run_treasury_check)
|
|
215
|
+
|
|
216
|
+
while True:
|
|
217
|
+
schedule.run_pending()
|
|
218
|
+
time.sleep(60)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Contract Addresses (Base Mainnet)
|
|
222
|
+
|
|
223
|
+
| Contract | Address |
|
|
224
|
+
|----------|---------|
|
|
225
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
226
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
227
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
228
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
229
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
clicks_crewai
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clicks Protocol CrewAI Tools
|
|
3
|
+
==============================
|
|
4
|
+
On-chain yield management tools for CrewAI agent crews on Base.
|
|
5
|
+
Powered by Clicks Protocol (clicksprotocol.xyz)
|
|
6
|
+
|
|
7
|
+
These tools enable CrewAI agents (e.g. Treasury Managers, Investment Analysts)
|
|
8
|
+
to autonomously manage idle USDC by splitting funds 80/20:
|
|
9
|
+
80% stays liquid for operations, 20% earns 4-8% APY on-chain.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
from clicks_crewai import ClicksCheckBalanceTool, ClicksActivateYieldTool
|
|
13
|
+
treasury_agent = Agent(
|
|
14
|
+
role="Treasury Manager",
|
|
15
|
+
tools=[ClicksCheckBalanceTool(), ClicksActivateYieldTool()],
|
|
16
|
+
)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import json
|
|
21
|
+
import subprocess
|
|
22
|
+
from typing import Optional, Type
|
|
23
|
+
from pydantic import BaseModel, Field
|
|
24
|
+
from crewai_tools import BaseTool
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Contract addresses (Base Mainnet)
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
CONTRACTS = {
|
|
31
|
+
"registry": "0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3",
|
|
32
|
+
"splitter": "0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D",
|
|
33
|
+
"yield_router": "0x053167a233d18E05Bc65a8d5F3F8808782a3EECD",
|
|
34
|
+
"fee": "0xc47B162D3c456B6C56a3cE6EE89A828CFd34E6bE",
|
|
35
|
+
"referral": "0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC",
|
|
36
|
+
"usdc": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ClicksSDKBridge:
|
|
41
|
+
"""
|
|
42
|
+
Bridge to the @clicks-protocol/sdk Node.js package.
|
|
43
|
+
|
|
44
|
+
Requires:
|
|
45
|
+
- Node.js >= 18
|
|
46
|
+
- @clicks-protocol/sdk installed
|
|
47
|
+
- CLICKS_RPC_URL env var (Base RPC endpoint)
|
|
48
|
+
- CLICKS_PRIVATE_KEY env var (agent wallet private key)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
rpc_url: Optional[str] = None,
|
|
54
|
+
private_key: Optional[str] = None,
|
|
55
|
+
sdk_path: Optional[str] = None,
|
|
56
|
+
):
|
|
57
|
+
self.rpc_url = rpc_url or os.environ.get("CLICKS_RPC_URL", "https://mainnet.base.org")
|
|
58
|
+
self.private_key = private_key or os.environ.get("CLICKS_PRIVATE_KEY", "")
|
|
59
|
+
self.sdk_path = sdk_path or os.environ.get("CLICKS_SDK_PATH", ".")
|
|
60
|
+
|
|
61
|
+
if not self.private_key:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
"No private key. Set CLICKS_PRIVATE_KEY env var or pass private_key."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def _call_sdk(self, method: str, **kwargs) -> dict:
|
|
67
|
+
"""Execute an SDK method via Node.js subprocess."""
|
|
68
|
+
args_str = ", ".join(
|
|
69
|
+
f"'{v}'" if isinstance(v, str) else str(v) for v in kwargs.values()
|
|
70
|
+
)
|
|
71
|
+
script = f"""
|
|
72
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
73
|
+
const {{ ethers }} = require('ethers');
|
|
74
|
+
(async () => {{
|
|
75
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
76
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
77
|
+
const sdk = new ClicksClient(wallet);
|
|
78
|
+
try {{
|
|
79
|
+
const result = await sdk.{method}({args_str});
|
|
80
|
+
console.log(JSON.stringify({{ success: true, data: result }}, (k, v) => typeof v === 'bigint' ? v.toString() : v));
|
|
81
|
+
}} catch (e) {{
|
|
82
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
83
|
+
}}
|
|
84
|
+
}})();
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
result = subprocess.run(
|
|
88
|
+
["node", "-e", script],
|
|
89
|
+
capture_output=True, text=True, timeout=60, cwd=self.sdk_path,
|
|
90
|
+
)
|
|
91
|
+
if result.returncode != 0:
|
|
92
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
93
|
+
return json.loads(result.stdout.strip())
|
|
94
|
+
except subprocess.TimeoutExpired:
|
|
95
|
+
return {"success": False, "error": "SDK call timed out (60s)"}
|
|
96
|
+
except json.JSONDecodeError:
|
|
97
|
+
return {"success": False, "error": f"Invalid response: {result.stdout[:200]}"}
|
|
98
|
+
|
|
99
|
+
def quick_start(self, amount: str, agent_address: str) -> dict:
|
|
100
|
+
return self._call_sdk("quickStart", amount=amount, agentAddress=agent_address)
|
|
101
|
+
|
|
102
|
+
def withdraw_yield(self, agent_address: str) -> dict:
|
|
103
|
+
return self._call_sdk("withdrawYield", agentAddress=agent_address)
|
|
104
|
+
|
|
105
|
+
def simulate_split(self, amount: str, agent_address: str) -> dict:
|
|
106
|
+
return self._call_sdk("simulateSplit", amount=amount, agentAddress=agent_address)
|
|
107
|
+
|
|
108
|
+
def get_agent_info(self, agent_address: str) -> dict:
|
|
109
|
+
"""Get agent registration info + yield balance combined."""
|
|
110
|
+
script = f"""
|
|
111
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
112
|
+
const {{ ethers }} = require('ethers');
|
|
113
|
+
(async () => {{
|
|
114
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
115
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
116
|
+
const sdk = new ClicksClient(wallet);
|
|
117
|
+
try {{
|
|
118
|
+
const [info, yieldBal, usdcBal] = await Promise.all([
|
|
119
|
+
sdk.getAgentInfo('{agent_address}'),
|
|
120
|
+
sdk.getAgentYieldBalance('{agent_address}'),
|
|
121
|
+
sdk.getUSDCBalance('{agent_address}'),
|
|
122
|
+
]);
|
|
123
|
+
console.log(JSON.stringify({{ success: true, data: {{
|
|
124
|
+
isRegistered: info.isRegistered,
|
|
125
|
+
operator: info.operator,
|
|
126
|
+
liquidBalance: usdcBal.toString(),
|
|
127
|
+
depositedBalance: yieldBal.deposited.toString(),
|
|
128
|
+
accruedYield: yieldBal.yieldEarned.toString(),
|
|
129
|
+
yieldPct: info.yieldPct.toString(),
|
|
130
|
+
}} }}));
|
|
131
|
+
}} catch (e) {{
|
|
132
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
133
|
+
}}
|
|
134
|
+
}})();
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
result = subprocess.run(
|
|
138
|
+
["node", "-e", script],
|
|
139
|
+
capture_output=True, text=True, timeout=60, cwd=self.sdk_path,
|
|
140
|
+
)
|
|
141
|
+
if result.returncode != 0:
|
|
142
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
143
|
+
return json.loads(result.stdout.strip())
|
|
144
|
+
except subprocess.TimeoutExpired:
|
|
145
|
+
return {"success": False, "error": "SDK call timed out (60s)"}
|
|
146
|
+
except json.JSONDecodeError:
|
|
147
|
+
return {"success": False, "error": f"Invalid response: {result.stdout[:200]}"}
|
|
148
|
+
|
|
149
|
+
def get_yield_info(self) -> dict:
|
|
150
|
+
return self._call_sdk("getYieldInfo")
|
|
151
|
+
|
|
152
|
+
def get_current_apy(self) -> dict:
|
|
153
|
+
"""Get current APY from the active yield protocol."""
|
|
154
|
+
script = f"""
|
|
155
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
156
|
+
const {{ ethers }} = require('ethers');
|
|
157
|
+
(async () => {{
|
|
158
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
159
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
160
|
+
const sdk = new ClicksClient(wallet);
|
|
161
|
+
try {{
|
|
162
|
+
const info = await sdk.getYieldInfo();
|
|
163
|
+
const apy = info.activeProtocol === 1 ? info.aaveAPY : info.morphoAPY;
|
|
164
|
+
const apyPct = (Number(apy) / 100).toFixed(2);
|
|
165
|
+
console.log(JSON.stringify({{ success: true, data: apyPct }}));
|
|
166
|
+
}} catch (e) {{
|
|
167
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
168
|
+
}}
|
|
169
|
+
}})();
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
result = subprocess.run(
|
|
173
|
+
["node", "-e", script],
|
|
174
|
+
capture_output=True, text=True, timeout=60, cwd=self.sdk_path,
|
|
175
|
+
)
|
|
176
|
+
if result.returncode != 0:
|
|
177
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
178
|
+
return json.loads(result.stdout.strip())
|
|
179
|
+
except subprocess.TimeoutExpired:
|
|
180
|
+
return {"success": False, "error": "SDK call timed out (60s)"}
|
|
181
|
+
except json.JSONDecodeError:
|
|
182
|
+
return {"success": False, "error": f"Invalid response: {result.stdout[:200]}"}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ---------------------------------------------------------------------------
|
|
186
|
+
# Shared SDK instance (lazy init)
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
_sdk_instance: Optional[ClicksSDKBridge] = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _get_sdk() -> ClicksSDKBridge:
|
|
192
|
+
global _sdk_instance
|
|
193
|
+
if _sdk_instance is None:
|
|
194
|
+
_sdk_instance = ClicksSDKBridge()
|
|
195
|
+
return _sdk_instance
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def configure_sdk(
|
|
199
|
+
rpc_url: Optional[str] = None,
|
|
200
|
+
private_key: Optional[str] = None,
|
|
201
|
+
sdk_path: Optional[str] = None,
|
|
202
|
+
):
|
|
203
|
+
"""Configure the shared SDK instance. Call before using tools."""
|
|
204
|
+
global _sdk_instance
|
|
205
|
+
_sdk_instance = ClicksSDKBridge(rpc_url, private_key, sdk_path)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# ---------------------------------------------------------------------------
|
|
209
|
+
# CrewAI Tools
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
class ClicksCheckBalanceTool(BaseTool):
|
|
213
|
+
"""Check how much USDC is liquid vs. earning yield on Clicks Protocol.
|
|
214
|
+
|
|
215
|
+
Use this tool when you need to understand the agent's treasury position
|
|
216
|
+
before making spending or allocation decisions. Returns liquid balance,
|
|
217
|
+
deposited amount, accrued yield, and registration status.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
name: str = "Check USDC Balance & Yield Status"
|
|
221
|
+
description: str = (
|
|
222
|
+
"Check the agent's USDC treasury on Clicks Protocol: liquid funds "
|
|
223
|
+
"(available immediately), deposited amount (earning yield), accrued "
|
|
224
|
+
"earnings, and current APY. Use BEFORE making any treasury decision."
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def _run(self, agent_address: str) -> str:
|
|
228
|
+
"""
|
|
229
|
+
Args:
|
|
230
|
+
agent_address: Ethereum address of the agent wallet on Base
|
|
231
|
+
"""
|
|
232
|
+
sdk = _get_sdk()
|
|
233
|
+
info = sdk.get_agent_info(agent_address)
|
|
234
|
+
apy = sdk.get_current_apy()
|
|
235
|
+
|
|
236
|
+
if not info.get("success"):
|
|
237
|
+
return f"Balance check failed: {info.get('error', 'Unknown error')}"
|
|
238
|
+
|
|
239
|
+
data = info.get("data", {})
|
|
240
|
+
apy_rate = apy.get("data", "N/A") if apy.get("success") else "N/A"
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
f"Treasury Status for {agent_address}:\n"
|
|
244
|
+
f" Liquid USDC: {data.get('liquidBalance', '0')} (available now)\n"
|
|
245
|
+
f" Deposited: {data.get('depositedBalance', '0')} (earning yield)\n"
|
|
246
|
+
f" Accrued: {data.get('accruedYield', '0')} (yield earned so far)\n"
|
|
247
|
+
f" Current APY: {apy_rate}%\n"
|
|
248
|
+
f" Registered: {data.get('isRegistered', False)}\n"
|
|
249
|
+
f" Protocol: Clicks Protocol on Base"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class ClicksActivateYieldTool(BaseTool):
|
|
254
|
+
"""Activate yield on idle USDC via Clicks Protocol.
|
|
255
|
+
|
|
256
|
+
Use this tool when the agent has USDC sitting idle that won't be needed
|
|
257
|
+
immediately. Splits funds 80/20: 80% stays liquid for operations,
|
|
258
|
+
20% earns 4-8% APY on-chain on Base.
|
|
259
|
+
|
|
260
|
+
DO NOT use when:
|
|
261
|
+
- All funds are needed for an imminent transaction
|
|
262
|
+
- The amount is very small (gas may exceed yield)
|
|
263
|
+
- The agent is not on Base network
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
name: str = "Activate USDC Yield"
|
|
267
|
+
description: str = (
|
|
268
|
+
"Split idle USDC 80/20 via Clicks Protocol. 80% stays liquid, "
|
|
269
|
+
"20% earns 4-8% APY on Base. Provide the USDC amount and agent address."
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def _run(self, amount: str, agent_address: str) -> str:
|
|
273
|
+
"""
|
|
274
|
+
Args:
|
|
275
|
+
amount: USDC amount to split (e.g. '5000')
|
|
276
|
+
agent_address: Ethereum address of the agent wallet on Base
|
|
277
|
+
"""
|
|
278
|
+
sdk = _get_sdk()
|
|
279
|
+
|
|
280
|
+
# Simulate first
|
|
281
|
+
sim = sdk.simulate_split(amount, agent_address)
|
|
282
|
+
if not sim.get("success"):
|
|
283
|
+
return f"Simulation failed: {sim.get('error')}. Aborting."
|
|
284
|
+
|
|
285
|
+
result = sdk.quick_start(amount, agent_address)
|
|
286
|
+
if result.get("success"):
|
|
287
|
+
data = result.get("data", {})
|
|
288
|
+
liquid = float(amount) * 0.8
|
|
289
|
+
yield_amt = float(amount) * 0.2
|
|
290
|
+
return (
|
|
291
|
+
f"Yield activated on {amount} USDC:\n"
|
|
292
|
+
f" Liquid (80%): {liquid:.2f} USDC — available immediately\n"
|
|
293
|
+
f" Yield (20%): {yield_amt:.2f} USDC — earning APY on Base\n"
|
|
294
|
+
f" TX: {data.get('txHash', 'pending')}\n"
|
|
295
|
+
f" Protocol: Clicks Protocol"
|
|
296
|
+
)
|
|
297
|
+
return f"Activation failed: {result.get('error', 'Unknown error')}"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class ClicksWithdrawYieldTool(BaseTool):
|
|
301
|
+
"""Withdraw earned yield and deposits from Clicks Protocol.
|
|
302
|
+
|
|
303
|
+
Use when the agent needs maximum liquidity, wants to consolidate funds,
|
|
304
|
+
or when APY has dropped below acceptable levels. All accrued yield and
|
|
305
|
+
the deposited 20% are returned to the agent's wallet.
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
name: str = "Withdraw Yield"
|
|
309
|
+
description: str = (
|
|
310
|
+
"Withdraw all earned yield and deposited funds from Clicks Protocol "
|
|
311
|
+
"back to the agent wallet. Use when maximum liquidity is needed."
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def _run(self, agent_address: str) -> str:
|
|
315
|
+
"""
|
|
316
|
+
Args:
|
|
317
|
+
agent_address: Ethereum address of the agent wallet on Base
|
|
318
|
+
"""
|
|
319
|
+
sdk = _get_sdk()
|
|
320
|
+
result = sdk.withdraw_yield(agent_address)
|
|
321
|
+
|
|
322
|
+
if result.get("success"):
|
|
323
|
+
data = result.get("data", {})
|
|
324
|
+
return (
|
|
325
|
+
f"Withdrawal successful:\n"
|
|
326
|
+
f" Amount returned: {data.get('amount', 'N/A')} USDC\n"
|
|
327
|
+
f" TX: {data.get('txHash', 'pending')}\n"
|
|
328
|
+
f" All funds are now liquid. No yield is being earned."
|
|
329
|
+
)
|
|
330
|
+
return f"Withdrawal failed: {result.get('error', 'Unknown error')}"
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class ClicksGetAPYTool(BaseTool):
|
|
334
|
+
"""Get the current APY rate from Clicks Protocol.
|
|
335
|
+
|
|
336
|
+
Use to evaluate whether activating yield is worthwhile, to compare
|
|
337
|
+
rates over time, or to report current earnings potential.
|
|
338
|
+
Typically returns 4-8% APY.
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
name: str = "Get Current APY"
|
|
342
|
+
description: str = (
|
|
343
|
+
"Get the current annual percentage yield (APY) from Clicks Protocol. "
|
|
344
|
+
"Returns the live rate (typically 4-8%). Use to evaluate yield potential."
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def _run(self) -> str:
|
|
348
|
+
sdk = _get_sdk()
|
|
349
|
+
result = sdk.get_current_apy()
|
|
350
|
+
|
|
351
|
+
if result.get("success"):
|
|
352
|
+
apy = result["data"]
|
|
353
|
+
assessment = ""
|
|
354
|
+
try:
|
|
355
|
+
apy_float = float(apy)
|
|
356
|
+
if apy_float >= 6:
|
|
357
|
+
assessment = " (strong yield, recommended to activate)"
|
|
358
|
+
elif apy_float >= 4:
|
|
359
|
+
assessment = " (healthy yield)"
|
|
360
|
+
else:
|
|
361
|
+
assessment = " (below average, consider waiting)"
|
|
362
|
+
except (ValueError, TypeError):
|
|
363
|
+
pass
|
|
364
|
+
return f"Current Clicks Protocol APY: {apy}%{assessment}"
|
|
365
|
+
return f"Could not fetch APY: {result.get('error', 'Unknown error')}"
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class ClicksSimulateSplitTool(BaseTool):
|
|
369
|
+
"""Preview how a USDC amount would be split before committing.
|
|
370
|
+
|
|
371
|
+
Use for planning and analysis. Shows the 80/20 breakdown and
|
|
372
|
+
estimated annual yield. No funds are moved. This is read-only.
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
name: str = "Simulate USDC Split"
|
|
376
|
+
description: str = (
|
|
377
|
+
"Preview how USDC would be split 80/20 without moving any funds. "
|
|
378
|
+
"Shows liquid amount, yield amount, and estimated earnings. "
|
|
379
|
+
"Use for planning before activating yield."
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def _run(self, amount: str, agent_address: str) -> str:
|
|
383
|
+
"""
|
|
384
|
+
Args:
|
|
385
|
+
amount: USDC amount to simulate (e.g. '10000')
|
|
386
|
+
agent_address: Ethereum address of the agent wallet on Base
|
|
387
|
+
"""
|
|
388
|
+
sdk = _get_sdk()
|
|
389
|
+
result = sdk.simulate_split(amount, agent_address)
|
|
390
|
+
|
|
391
|
+
if result.get("success"):
|
|
392
|
+
data = result.get("data", {})
|
|
393
|
+
liquid = float(amount) * 0.8
|
|
394
|
+
yield_amt = float(amount) * 0.2
|
|
395
|
+
return (
|
|
396
|
+
f"Split Simulation for {amount} USDC:\n"
|
|
397
|
+
f" Liquid (80%): {liquid:.2f} USDC — stays available\n"
|
|
398
|
+
f" Yield (20%): {yield_amt:.2f} USDC — earns APY\n"
|
|
399
|
+
f" Est. Annual: {data.get('estimatedYield', 'N/A')} USDC\n"
|
|
400
|
+
f" No funds moved. Use 'Activate USDC Yield' to proceed."
|
|
401
|
+
)
|
|
402
|
+
return f"Simulation failed: {result.get('error', 'Unknown error')}"
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def get_all_tools() -> list:
|
|
406
|
+
"""
|
|
407
|
+
Get all Clicks Protocol tools for a CrewAI crew.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
List of CrewAI tools for treasury management
|
|
411
|
+
|
|
412
|
+
Example:
|
|
413
|
+
from clicks_crewai import get_all_tools
|
|
414
|
+
tools = get_all_tools()
|
|
415
|
+
"""
|
|
416
|
+
return [
|
|
417
|
+
ClicksCheckBalanceTool(),
|
|
418
|
+
ClicksActivateYieldTool(),
|
|
419
|
+
ClicksWithdrawYieldTool(),
|
|
420
|
+
ClicksGetAPYTool(),
|
|
421
|
+
ClicksSimulateSplitTool(),
|
|
422
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "clicks-crewai"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Clicks Protocol yield management tools for CrewAI agent crews on Base"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Clicks Protocol", email = "dev@clicksprotocol.xyz"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["crewai", "defi", "yield", "base", "usdc", "ai-agents", "clicks-protocol"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Software Development :: Libraries",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"crewai>=0.28.0",
|
|
29
|
+
"crewai-tools>=0.4.0",
|
|
30
|
+
"pydantic>=2.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://clicksprotocol.xyz"
|
|
35
|
+
Repository = "https://github.com/clicks-protocol/clicks-crewai"
|
|
36
|
+
Documentation = "https://docs.clicksprotocol.xyz/integrations/crewai"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
py-modules = ["clicks_crewai"]
|