clicks-langchain 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_langchain-0.1.0/PKG-INFO +175 -0
- clicks_langchain-0.1.0/README.md +151 -0
- clicks_langchain-0.1.0/clicks_langchain.egg-info/PKG-INFO +175 -0
- clicks_langchain-0.1.0/clicks_langchain.egg-info/SOURCES.txt +8 -0
- clicks_langchain-0.1.0/clicks_langchain.egg-info/dependency_links.txt +1 -0
- clicks_langchain-0.1.0/clicks_langchain.egg-info/requires.txt +2 -0
- clicks_langchain-0.1.0/clicks_langchain.egg-info/top_level.txt +1 -0
- clicks_langchain-0.1.0/clicks_langchain.py +505 -0
- clicks_langchain-0.1.0/pyproject.toml +38 -0
- clicks_langchain-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clicks-langchain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clicks Protocol yield management tools for LangChain agents 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-langchain
|
|
9
|
+
Project-URL: Documentation, https://docs.clicksprotocol.xyz/integrations/langchain
|
|
10
|
+
Keywords: langchain,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: langchain>=0.1.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0
|
|
24
|
+
|
|
25
|
+
# Clicks Protocol — LangChain Integration
|
|
26
|
+
|
|
27
|
+
On-chain yield management tools for LangChain agents on Base.
|
|
28
|
+
|
|
29
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
30
|
+
|
|
31
|
+
## What It Does
|
|
32
|
+
|
|
33
|
+
Gives your LangChain agent the ability to earn yield on idle USDC. The agent can autonomously split funds 80/20: 80% stays liquid for operations, 20% earns 4-8% APY on-chain via Clicks Protocol on Base.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install clicks-langchain
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/clicks-protocol/clicks-langchain
|
|
45
|
+
cd clicks-langchain
|
|
46
|
+
pip install -e .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Prerequisites
|
|
50
|
+
|
|
51
|
+
- Python >= 3.9
|
|
52
|
+
- Node.js >= 18 (for SDK bridge)
|
|
53
|
+
- `@clicks-protocol/sdk` installed: `npm install @clicks-protocol/sdk`
|
|
54
|
+
|
|
55
|
+
### Environment Variables
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export CLICKS_PRIVATE_KEY="your-agent-wallet-private-key"
|
|
59
|
+
export CLICKS_RPC_URL="https://mainnet.base.org" # optional, defaults to Base mainnet
|
|
60
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional, defaults to cwd
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from langchain.chat_models import ChatOpenAI
|
|
67
|
+
from langchain.agents import initialize_agent, AgentType
|
|
68
|
+
from clicks_langchain import get_clicks_tools
|
|
69
|
+
|
|
70
|
+
# Get all Clicks Protocol tools
|
|
71
|
+
tools = get_clicks_tools()
|
|
72
|
+
|
|
73
|
+
# Create an agent with yield management capabilities
|
|
74
|
+
llm = ChatOpenAI(model="gpt-4", temperature=0)
|
|
75
|
+
agent = initialize_agent(
|
|
76
|
+
tools,
|
|
77
|
+
llm,
|
|
78
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
79
|
+
verbose=True,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# The agent can now manage its own treasury
|
|
83
|
+
agent.run("Check my USDC balance and yield status for address 0xYourAgent...")
|
|
84
|
+
agent.run("I have 5000 idle USDC. Activate yield on it.")
|
|
85
|
+
agent.run("What's the current APY on Clicks Protocol?")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Available Tools
|
|
89
|
+
|
|
90
|
+
| Tool | Description | When to Use |
|
|
91
|
+
|------|-------------|-------------|
|
|
92
|
+
| `clicks_activate_yield` | Split USDC 80/20 to earn yield | Agent has idle USDC not needed immediately |
|
|
93
|
+
| `clicks_withdraw_yield` | Withdraw all yield + deposits | Agent needs maximum liquidity |
|
|
94
|
+
| `clicks_check_balance` | View liquid vs. yield balances | Before spending decisions |
|
|
95
|
+
| `clicks_get_apy` | Get current APY rate | Evaluating whether to activate yield |
|
|
96
|
+
| `clicks_simulate_split` | Preview split without moving funds | Planning treasury allocation |
|
|
97
|
+
|
|
98
|
+
## Example: Treasury Management Agent
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from langchain.chat_models import ChatOpenAI
|
|
102
|
+
from langchain.agents import initialize_agent, AgentType
|
|
103
|
+
from langchain.memory import ConversationBufferMemory
|
|
104
|
+
from clicks_langchain import get_clicks_tools
|
|
105
|
+
|
|
106
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
107
|
+
|
|
108
|
+
SYSTEM_PROMPT = """You are a treasury management agent. Your job is to maximize
|
|
109
|
+
yield on idle USDC while keeping enough liquid for operations.
|
|
110
|
+
|
|
111
|
+
Rules:
|
|
112
|
+
1. Always check balance before activating yield
|
|
113
|
+
2. Keep at least 1000 USDC liquid at all times
|
|
114
|
+
3. Activate yield on any idle amount above 1000 USDC
|
|
115
|
+
4. Check APY weekly and report changes
|
|
116
|
+
5. Withdraw yield if APY drops below 2%
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
tools = get_clicks_tools()
|
|
120
|
+
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
|
|
121
|
+
|
|
122
|
+
agent = initialize_agent(
|
|
123
|
+
tools,
|
|
124
|
+
ChatOpenAI(model="gpt-4", temperature=0),
|
|
125
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
126
|
+
memory=memory,
|
|
127
|
+
system_message=SYSTEM_PROMPT,
|
|
128
|
+
verbose=True,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Run the agent
|
|
132
|
+
agent.run(f"Check the treasury status for {AGENT_ADDRESS} and optimize yield allocation.")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Using Individual Tools
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from clicks_langchain import (
|
|
139
|
+
ClicksActivateYieldTool,
|
|
140
|
+
ClicksCheckBalanceTool,
|
|
141
|
+
ClicksGetAPYTool,
|
|
142
|
+
ClicksSDKBridge,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Create SDK bridge with explicit config
|
|
146
|
+
sdk = ClicksSDKBridge(
|
|
147
|
+
rpc_url="https://mainnet.base.org",
|
|
148
|
+
private_key="your-key",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Use individual tools
|
|
152
|
+
balance_tool = ClicksCheckBalanceTool(sdk=sdk)
|
|
153
|
+
print(balance_tool.run({"agent_address": "0xYourAgent"}))
|
|
154
|
+
|
|
155
|
+
apy_tool = ClicksGetAPYTool(sdk=sdk)
|
|
156
|
+
print(apy_tool.run({}))
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Contract Addresses (Base Mainnet)
|
|
160
|
+
|
|
161
|
+
| Contract | Address |
|
|
162
|
+
|----------|---------|
|
|
163
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
164
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
165
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
166
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
167
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Clicks Protocol — LangChain Integration
|
|
2
|
+
|
|
3
|
+
On-chain yield management tools for LangChain agents on Base.
|
|
4
|
+
|
|
5
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
Gives your LangChain agent the ability to earn yield on idle USDC. The agent can autonomously split funds 80/20: 80% stays liquid for operations, 20% earns 4-8% APY on-chain via Clicks Protocol on Base.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install clicks-langchain
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or from source:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/clicks-protocol/clicks-langchain
|
|
21
|
+
cd clicks-langchain
|
|
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, defaults to Base mainnet
|
|
36
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional, defaults to cwd
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from langchain.chat_models import ChatOpenAI
|
|
43
|
+
from langchain.agents import initialize_agent, AgentType
|
|
44
|
+
from clicks_langchain import get_clicks_tools
|
|
45
|
+
|
|
46
|
+
# Get all Clicks Protocol tools
|
|
47
|
+
tools = get_clicks_tools()
|
|
48
|
+
|
|
49
|
+
# Create an agent with yield management capabilities
|
|
50
|
+
llm = ChatOpenAI(model="gpt-4", temperature=0)
|
|
51
|
+
agent = initialize_agent(
|
|
52
|
+
tools,
|
|
53
|
+
llm,
|
|
54
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
55
|
+
verbose=True,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# The agent can now manage its own treasury
|
|
59
|
+
agent.run("Check my USDC balance and yield status for address 0xYourAgent...")
|
|
60
|
+
agent.run("I have 5000 idle USDC. Activate yield on it.")
|
|
61
|
+
agent.run("What's the current APY on Clicks Protocol?")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Available Tools
|
|
65
|
+
|
|
66
|
+
| Tool | Description | When to Use |
|
|
67
|
+
|------|-------------|-------------|
|
|
68
|
+
| `clicks_activate_yield` | Split USDC 80/20 to earn yield | Agent has idle USDC not needed immediately |
|
|
69
|
+
| `clicks_withdraw_yield` | Withdraw all yield + deposits | Agent needs maximum liquidity |
|
|
70
|
+
| `clicks_check_balance` | View liquid vs. yield balances | Before spending decisions |
|
|
71
|
+
| `clicks_get_apy` | Get current APY rate | Evaluating whether to activate yield |
|
|
72
|
+
| `clicks_simulate_split` | Preview split without moving funds | Planning treasury allocation |
|
|
73
|
+
|
|
74
|
+
## Example: Treasury Management Agent
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from langchain.chat_models import ChatOpenAI
|
|
78
|
+
from langchain.agents import initialize_agent, AgentType
|
|
79
|
+
from langchain.memory import ConversationBufferMemory
|
|
80
|
+
from clicks_langchain import get_clicks_tools
|
|
81
|
+
|
|
82
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
83
|
+
|
|
84
|
+
SYSTEM_PROMPT = """You are a treasury management agent. Your job is to maximize
|
|
85
|
+
yield on idle USDC while keeping enough liquid for operations.
|
|
86
|
+
|
|
87
|
+
Rules:
|
|
88
|
+
1. Always check balance before activating yield
|
|
89
|
+
2. Keep at least 1000 USDC liquid at all times
|
|
90
|
+
3. Activate yield on any idle amount above 1000 USDC
|
|
91
|
+
4. Check APY weekly and report changes
|
|
92
|
+
5. Withdraw yield if APY drops below 2%
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
tools = get_clicks_tools()
|
|
96
|
+
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
|
|
97
|
+
|
|
98
|
+
agent = initialize_agent(
|
|
99
|
+
tools,
|
|
100
|
+
ChatOpenAI(model="gpt-4", temperature=0),
|
|
101
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
102
|
+
memory=memory,
|
|
103
|
+
system_message=SYSTEM_PROMPT,
|
|
104
|
+
verbose=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Run the agent
|
|
108
|
+
agent.run(f"Check the treasury status for {AGENT_ADDRESS} and optimize yield allocation.")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Using Individual Tools
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from clicks_langchain import (
|
|
115
|
+
ClicksActivateYieldTool,
|
|
116
|
+
ClicksCheckBalanceTool,
|
|
117
|
+
ClicksGetAPYTool,
|
|
118
|
+
ClicksSDKBridge,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Create SDK bridge with explicit config
|
|
122
|
+
sdk = ClicksSDKBridge(
|
|
123
|
+
rpc_url="https://mainnet.base.org",
|
|
124
|
+
private_key="your-key",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Use individual tools
|
|
128
|
+
balance_tool = ClicksCheckBalanceTool(sdk=sdk)
|
|
129
|
+
print(balance_tool.run({"agent_address": "0xYourAgent"}))
|
|
130
|
+
|
|
131
|
+
apy_tool = ClicksGetAPYTool(sdk=sdk)
|
|
132
|
+
print(apy_tool.run({}))
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Contract Addresses (Base Mainnet)
|
|
136
|
+
|
|
137
|
+
| Contract | Address |
|
|
138
|
+
|----------|---------|
|
|
139
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
140
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
141
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
142
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
143
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clicks-langchain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clicks Protocol yield management tools for LangChain agents 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-langchain
|
|
9
|
+
Project-URL: Documentation, https://docs.clicksprotocol.xyz/integrations/langchain
|
|
10
|
+
Keywords: langchain,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: langchain>=0.1.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0
|
|
24
|
+
|
|
25
|
+
# Clicks Protocol — LangChain Integration
|
|
26
|
+
|
|
27
|
+
On-chain yield management tools for LangChain agents on Base.
|
|
28
|
+
|
|
29
|
+
Powered by [Clicks Protocol](https://clicksprotocol.xyz)
|
|
30
|
+
|
|
31
|
+
## What It Does
|
|
32
|
+
|
|
33
|
+
Gives your LangChain agent the ability to earn yield on idle USDC. The agent can autonomously split funds 80/20: 80% stays liquid for operations, 20% earns 4-8% APY on-chain via Clicks Protocol on Base.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install clicks-langchain
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or from source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/clicks-protocol/clicks-langchain
|
|
45
|
+
cd clicks-langchain
|
|
46
|
+
pip install -e .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Prerequisites
|
|
50
|
+
|
|
51
|
+
- Python >= 3.9
|
|
52
|
+
- Node.js >= 18 (for SDK bridge)
|
|
53
|
+
- `@clicks-protocol/sdk` installed: `npm install @clicks-protocol/sdk`
|
|
54
|
+
|
|
55
|
+
### Environment Variables
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export CLICKS_PRIVATE_KEY="your-agent-wallet-private-key"
|
|
59
|
+
export CLICKS_RPC_URL="https://mainnet.base.org" # optional, defaults to Base mainnet
|
|
60
|
+
export CLICKS_SDK_PATH="/path/to/sdk" # optional, defaults to cwd
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from langchain.chat_models import ChatOpenAI
|
|
67
|
+
from langchain.agents import initialize_agent, AgentType
|
|
68
|
+
from clicks_langchain import get_clicks_tools
|
|
69
|
+
|
|
70
|
+
# Get all Clicks Protocol tools
|
|
71
|
+
tools = get_clicks_tools()
|
|
72
|
+
|
|
73
|
+
# Create an agent with yield management capabilities
|
|
74
|
+
llm = ChatOpenAI(model="gpt-4", temperature=0)
|
|
75
|
+
agent = initialize_agent(
|
|
76
|
+
tools,
|
|
77
|
+
llm,
|
|
78
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
79
|
+
verbose=True,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# The agent can now manage its own treasury
|
|
83
|
+
agent.run("Check my USDC balance and yield status for address 0xYourAgent...")
|
|
84
|
+
agent.run("I have 5000 idle USDC. Activate yield on it.")
|
|
85
|
+
agent.run("What's the current APY on Clicks Protocol?")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Available Tools
|
|
89
|
+
|
|
90
|
+
| Tool | Description | When to Use |
|
|
91
|
+
|------|-------------|-------------|
|
|
92
|
+
| `clicks_activate_yield` | Split USDC 80/20 to earn yield | Agent has idle USDC not needed immediately |
|
|
93
|
+
| `clicks_withdraw_yield` | Withdraw all yield + deposits | Agent needs maximum liquidity |
|
|
94
|
+
| `clicks_check_balance` | View liquid vs. yield balances | Before spending decisions |
|
|
95
|
+
| `clicks_get_apy` | Get current APY rate | Evaluating whether to activate yield |
|
|
96
|
+
| `clicks_simulate_split` | Preview split without moving funds | Planning treasury allocation |
|
|
97
|
+
|
|
98
|
+
## Example: Treasury Management Agent
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from langchain.chat_models import ChatOpenAI
|
|
102
|
+
from langchain.agents import initialize_agent, AgentType
|
|
103
|
+
from langchain.memory import ConversationBufferMemory
|
|
104
|
+
from clicks_langchain import get_clicks_tools
|
|
105
|
+
|
|
106
|
+
AGENT_ADDRESS = "0xYourAgentAddress"
|
|
107
|
+
|
|
108
|
+
SYSTEM_PROMPT = """You are a treasury management agent. Your job is to maximize
|
|
109
|
+
yield on idle USDC while keeping enough liquid for operations.
|
|
110
|
+
|
|
111
|
+
Rules:
|
|
112
|
+
1. Always check balance before activating yield
|
|
113
|
+
2. Keep at least 1000 USDC liquid at all times
|
|
114
|
+
3. Activate yield on any idle amount above 1000 USDC
|
|
115
|
+
4. Check APY weekly and report changes
|
|
116
|
+
5. Withdraw yield if APY drops below 2%
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
tools = get_clicks_tools()
|
|
120
|
+
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
|
|
121
|
+
|
|
122
|
+
agent = initialize_agent(
|
|
123
|
+
tools,
|
|
124
|
+
ChatOpenAI(model="gpt-4", temperature=0),
|
|
125
|
+
agent=AgentType.OPENAI_FUNCTIONS,
|
|
126
|
+
memory=memory,
|
|
127
|
+
system_message=SYSTEM_PROMPT,
|
|
128
|
+
verbose=True,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Run the agent
|
|
132
|
+
agent.run(f"Check the treasury status for {AGENT_ADDRESS} and optimize yield allocation.")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Using Individual Tools
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from clicks_langchain import (
|
|
139
|
+
ClicksActivateYieldTool,
|
|
140
|
+
ClicksCheckBalanceTool,
|
|
141
|
+
ClicksGetAPYTool,
|
|
142
|
+
ClicksSDKBridge,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Create SDK bridge with explicit config
|
|
146
|
+
sdk = ClicksSDKBridge(
|
|
147
|
+
rpc_url="https://mainnet.base.org",
|
|
148
|
+
private_key="your-key",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Use individual tools
|
|
152
|
+
balance_tool = ClicksCheckBalanceTool(sdk=sdk)
|
|
153
|
+
print(balance_tool.run({"agent_address": "0xYourAgent"}))
|
|
154
|
+
|
|
155
|
+
apy_tool = ClicksGetAPYTool(sdk=sdk)
|
|
156
|
+
print(apy_tool.run({}))
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Contract Addresses (Base Mainnet)
|
|
160
|
+
|
|
161
|
+
| Contract | Address |
|
|
162
|
+
|----------|---------|
|
|
163
|
+
| Registry | `0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3` |
|
|
164
|
+
| Splitter | `0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D` |
|
|
165
|
+
| YieldRouter | `0x053167a233d18E05Bc65a8d5F3F8808782a3EECD` |
|
|
166
|
+
| ClicksReferral | `0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC` |
|
|
167
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
Built for autonomous AI agents. Powered by [Clicks Protocol](https://clicksprotocol.xyz).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
clicks_langchain
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Clicks Protocol LangChain Tools
|
|
3
|
+
================================
|
|
4
|
+
On-chain yield management for AI agents on Base.
|
|
5
|
+
Powered by Clicks Protocol (clicksprotocol.xyz)
|
|
6
|
+
|
|
7
|
+
These tools let LangChain agents autonomously manage idle USDC by splitting
|
|
8
|
+
funds 80/20: 80% stays liquid for operations, 20% earns 4-8% APY on-chain.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from clicks_langchain import ClicksYieldTool, ClicksBalanceTool
|
|
12
|
+
tools = [ClicksYieldTool(), ClicksBalanceTool()]
|
|
13
|
+
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import json
|
|
18
|
+
import subprocess
|
|
19
|
+
from typing import Optional, Type
|
|
20
|
+
from pydantic import BaseModel, Field
|
|
21
|
+
from langchain.tools import BaseTool
|
|
22
|
+
from langchain.callbacks.manager import CallbackManagerForToolRun
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Contract addresses (Base Mainnet)
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
CONTRACTS = {
|
|
29
|
+
"registry": "0x23bb0Ea69b2BD2e527D5DbA6093155A6E1D0C0a3",
|
|
30
|
+
"splitter": "0xc96C1a566a8ed7A39040a34927fEe952bAB8Ad1D",
|
|
31
|
+
"yield_router": "0x053167a233d18E05Bc65a8d5F3F8808782a3EECD",
|
|
32
|
+
"fee": "0xc47B162D3c456B6C56a3cE6EE89A828CFd34E6bE",
|
|
33
|
+
"referral": "0x1E5Ab896D3b3A542C5E91852e221b2D849944ccC",
|
|
34
|
+
"usdc": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Default split: 80% liquid, 20% yield
|
|
38
|
+
DEFAULT_LIQUID_PERCENT = 80
|
|
39
|
+
DEFAULT_YIELD_PERCENT = 20
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ClicksSDKBridge:
|
|
43
|
+
"""
|
|
44
|
+
Bridge to the @clicks-protocol/sdk Node.js package.
|
|
45
|
+
Executes SDK methods via a subprocess call to a thin JS wrapper.
|
|
46
|
+
|
|
47
|
+
Requires:
|
|
48
|
+
- Node.js >= 18
|
|
49
|
+
- @clicks-protocol/sdk installed (npm install @clicks-protocol/sdk)
|
|
50
|
+
- CLICKS_RPC_URL env var (Base RPC endpoint)
|
|
51
|
+
- CLICKS_PRIVATE_KEY env var (agent wallet private key)
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
rpc_url: Optional[str] = None,
|
|
57
|
+
private_key: Optional[str] = None,
|
|
58
|
+
sdk_path: Optional[str] = None,
|
|
59
|
+
):
|
|
60
|
+
self.rpc_url = rpc_url or os.environ.get("CLICKS_RPC_URL", "https://mainnet.base.org")
|
|
61
|
+
self.private_key = private_key or os.environ.get("CLICKS_PRIVATE_KEY", "")
|
|
62
|
+
self.sdk_path = sdk_path or os.environ.get("CLICKS_SDK_PATH", ".")
|
|
63
|
+
|
|
64
|
+
if not self.private_key:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"No private key provided. Set CLICKS_PRIVATE_KEY env var "
|
|
67
|
+
"or pass private_key to the constructor."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _call_sdk(self, method: str, **kwargs) -> dict:
|
|
71
|
+
"""Execute an SDK method via the Node.js bridge script."""
|
|
72
|
+
script = f"""
|
|
73
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
74
|
+
const {{ ethers }} = require('ethers');
|
|
75
|
+
|
|
76
|
+
(async () => {{
|
|
77
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
78
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
79
|
+
const sdk = new ClicksClient(wallet);
|
|
80
|
+
|
|
81
|
+
try {{
|
|
82
|
+
const result = await sdk.{method}({json.dumps(kwargs).strip('{}')});
|
|
83
|
+
console.log(JSON.stringify({{ success: true, data: result }}, (k, v) => typeof v === 'bigint' ? v.toString() : v));
|
|
84
|
+
}} catch (e) {{
|
|
85
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
86
|
+
}}
|
|
87
|
+
}})();
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
result = subprocess.run(
|
|
91
|
+
["node", "-e", script],
|
|
92
|
+
capture_output=True,
|
|
93
|
+
text=True,
|
|
94
|
+
timeout=60,
|
|
95
|
+
cwd=self.sdk_path,
|
|
96
|
+
)
|
|
97
|
+
if result.returncode != 0:
|
|
98
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
99
|
+
return json.loads(result.stdout.strip())
|
|
100
|
+
except subprocess.TimeoutExpired:
|
|
101
|
+
return {"success": False, "error": "SDK call timed out after 60s"}
|
|
102
|
+
except json.JSONDecodeError:
|
|
103
|
+
return {"success": False, "error": f"Invalid SDK response: {result.stdout[:200]}"}
|
|
104
|
+
|
|
105
|
+
def quick_start(self, amount: str, agent_address: str) -> dict:
|
|
106
|
+
return self._call_sdk("quickStart", amount=amount, agentAddress=agent_address)
|
|
107
|
+
|
|
108
|
+
def withdraw_yield(self, agent_address: str) -> dict:
|
|
109
|
+
return self._call_sdk("withdrawYield", agentAddress=agent_address)
|
|
110
|
+
|
|
111
|
+
def simulate_split(self, amount: str, agent_address: str) -> dict:
|
|
112
|
+
return self._call_sdk("simulateSplit", amount=amount, agentAddress=agent_address)
|
|
113
|
+
|
|
114
|
+
def get_agent_info(self, agent_address: str) -> dict:
|
|
115
|
+
"""Get agent registration info + yield balance combined."""
|
|
116
|
+
script = f"""
|
|
117
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
118
|
+
const {{ ethers }} = require('ethers');
|
|
119
|
+
(async () => {{
|
|
120
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
121
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
122
|
+
const sdk = new ClicksClient(wallet);
|
|
123
|
+
try {{
|
|
124
|
+
const [info, yieldBal, usdcBal] = await Promise.all([
|
|
125
|
+
sdk.getAgentInfo('{agent_address}'),
|
|
126
|
+
sdk.getAgentYieldBalance('{agent_address}'),
|
|
127
|
+
sdk.getUSDCBalance('{agent_address}'),
|
|
128
|
+
]);
|
|
129
|
+
console.log(JSON.stringify({{ success: true, data: {{
|
|
130
|
+
isRegistered: info.isRegistered,
|
|
131
|
+
operator: info.operator,
|
|
132
|
+
liquidBalance: usdcBal.toString(),
|
|
133
|
+
depositedBalance: yieldBal.deposited.toString(),
|
|
134
|
+
accruedYield: yieldBal.yieldEarned.toString(),
|
|
135
|
+
yieldPct: info.yieldPct.toString(),
|
|
136
|
+
}} }}));
|
|
137
|
+
}} catch (e) {{
|
|
138
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
139
|
+
}}
|
|
140
|
+
}})();
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
result = subprocess.run(
|
|
144
|
+
["node", "-e", script],
|
|
145
|
+
capture_output=True, text=True, timeout=60, cwd=self.sdk_path,
|
|
146
|
+
)
|
|
147
|
+
if result.returncode != 0:
|
|
148
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
149
|
+
return json.loads(result.stdout.strip())
|
|
150
|
+
except subprocess.TimeoutExpired:
|
|
151
|
+
return {"success": False, "error": "SDK call timed out after 60s"}
|
|
152
|
+
except json.JSONDecodeError:
|
|
153
|
+
return {"success": False, "error": f"Invalid SDK response: {result.stdout[:200]}"}
|
|
154
|
+
|
|
155
|
+
def get_yield_info(self) -> dict:
|
|
156
|
+
return self._call_sdk("getYieldInfo")
|
|
157
|
+
|
|
158
|
+
def get_current_apy(self) -> dict:
|
|
159
|
+
"""Get current APY from the active yield protocol."""
|
|
160
|
+
script = f"""
|
|
161
|
+
const {{ ClicksClient }} = require('@clicks-protocol/sdk');
|
|
162
|
+
const {{ ethers }} = require('ethers');
|
|
163
|
+
(async () => {{
|
|
164
|
+
const provider = new ethers.JsonRpcProvider('{self.rpc_url}');
|
|
165
|
+
const wallet = new ethers.Wallet('{self.private_key}', provider);
|
|
166
|
+
const sdk = new ClicksClient(wallet);
|
|
167
|
+
try {{
|
|
168
|
+
const info = await sdk.getYieldInfo();
|
|
169
|
+
const apy = info.activeProtocol === 1 ? info.aaveAPY : info.morphoAPY;
|
|
170
|
+
const apyPct = (Number(apy) / 100).toFixed(2);
|
|
171
|
+
console.log(JSON.stringify({{ success: true, data: apyPct }}));
|
|
172
|
+
}} catch (e) {{
|
|
173
|
+
console.log(JSON.stringify({{ success: false, error: e.message }}));
|
|
174
|
+
}}
|
|
175
|
+
}})();
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
result = subprocess.run(
|
|
179
|
+
["node", "-e", script],
|
|
180
|
+
capture_output=True, text=True, timeout=60, cwd=self.sdk_path,
|
|
181
|
+
)
|
|
182
|
+
if result.returncode != 0:
|
|
183
|
+
return {"success": False, "error": result.stderr.strip()}
|
|
184
|
+
return json.loads(result.stdout.strip())
|
|
185
|
+
except subprocess.TimeoutExpired:
|
|
186
|
+
return {"success": False, "error": "SDK call timed out after 60s"}
|
|
187
|
+
except json.JSONDecodeError:
|
|
188
|
+
return {"success": False, "error": f"Invalid SDK response: {result.stdout[:200]}"}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
# Input schemas
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
class ActivateYieldInput(BaseModel):
|
|
196
|
+
amount: str = Field(
|
|
197
|
+
description="Amount of USDC to split (in human-readable units, e.g. '1000' for 1000 USDC)"
|
|
198
|
+
)
|
|
199
|
+
agent_address: str = Field(
|
|
200
|
+
description="The Ethereum address of the agent wallet on Base"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class WithdrawYieldInput(BaseModel):
|
|
205
|
+
agent_address: str = Field(
|
|
206
|
+
description="The Ethereum address of the agent wallet on Base"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class CheckBalanceInput(BaseModel):
|
|
211
|
+
agent_address: str = Field(
|
|
212
|
+
description="The Ethereum address of the agent wallet on Base"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class SimulateSplitInput(BaseModel):
|
|
217
|
+
amount: str = Field(
|
|
218
|
+
description="Amount of USDC to simulate splitting"
|
|
219
|
+
)
|
|
220
|
+
agent_address: str = Field(
|
|
221
|
+
description="The Ethereum address of the agent wallet on Base"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
# LangChain Tools
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
class ClicksActivateYieldTool(BaseTool):
|
|
230
|
+
"""
|
|
231
|
+
Activate yield on idle USDC via Clicks Protocol.
|
|
232
|
+
|
|
233
|
+
USE THIS TOOL WHEN:
|
|
234
|
+
- The agent has idle USDC sitting in its wallet doing nothing
|
|
235
|
+
- The agent wants to earn passive yield (4-8% APY) on unused funds
|
|
236
|
+
- After receiving a payment that won't be spent immediately
|
|
237
|
+
|
|
238
|
+
HOW IT WORKS:
|
|
239
|
+
- Splits the specified USDC amount 80/20
|
|
240
|
+
- 80% stays liquid in the agent's wallet (available immediately)
|
|
241
|
+
- 20% is deposited into an on-chain yield strategy on Base
|
|
242
|
+
- The yield portion earns 4-8% APY automatically
|
|
243
|
+
|
|
244
|
+
DO NOT USE WHEN:
|
|
245
|
+
- The agent needs 100% of its USDC for an imminent transaction
|
|
246
|
+
- The amount is very small (gas costs may exceed yield)
|
|
247
|
+
- The agent is on a network other than Base
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
name: str = "clicks_activate_yield"
|
|
251
|
+
description: str = (
|
|
252
|
+
"Split idle USDC 80/20 to earn yield on the idle portion. "
|
|
253
|
+
"80% stays liquid, 20% earns 4-8% APY on Base via Clicks Protocol. "
|
|
254
|
+
"Input: amount (USDC as string) and agent_address (Ethereum address)."
|
|
255
|
+
)
|
|
256
|
+
args_schema: Type[BaseModel] = ActivateYieldInput
|
|
257
|
+
sdk: Optional[ClicksSDKBridge] = None
|
|
258
|
+
|
|
259
|
+
class Config:
|
|
260
|
+
arbitrary_types_allowed = True
|
|
261
|
+
|
|
262
|
+
def __init__(self, sdk: Optional[ClicksSDKBridge] = None, **kwargs):
|
|
263
|
+
super().__init__(**kwargs)
|
|
264
|
+
self.sdk = sdk or ClicksSDKBridge()
|
|
265
|
+
|
|
266
|
+
def _run(
|
|
267
|
+
self,
|
|
268
|
+
amount: str,
|
|
269
|
+
agent_address: str,
|
|
270
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
271
|
+
) -> str:
|
|
272
|
+
# First simulate to show the user what will happen
|
|
273
|
+
sim = self.sdk.simulate_split(amount, agent_address)
|
|
274
|
+
if not sim.get("success"):
|
|
275
|
+
return f"Simulation failed: {sim.get('error', 'Unknown error')}"
|
|
276
|
+
|
|
277
|
+
result = self.sdk.quick_start(amount, agent_address)
|
|
278
|
+
if result.get("success"):
|
|
279
|
+
data = result.get("data", {})
|
|
280
|
+
return (
|
|
281
|
+
f"Yield activated successfully.\n"
|
|
282
|
+
f"Total: {amount} USDC\n"
|
|
283
|
+
f"Liquid (80%): {float(amount) * 0.8:.2f} USDC (available now)\n"
|
|
284
|
+
f"Yield (20%): {float(amount) * 0.2:.2f} USDC (earning APY)\n"
|
|
285
|
+
f"Transaction: {data.get('txHash', 'N/A')}"
|
|
286
|
+
)
|
|
287
|
+
return f"Failed to activate yield: {result.get('error', 'Unknown error')}"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class ClicksWithdrawYieldTool(BaseTool):
|
|
291
|
+
"""
|
|
292
|
+
Withdraw earned yield from Clicks Protocol.
|
|
293
|
+
|
|
294
|
+
USE THIS TOOL WHEN:
|
|
295
|
+
- The agent needs to access its yield earnings
|
|
296
|
+
- The agent wants to consolidate all funds back to liquid
|
|
297
|
+
- Before a large purchase that requires maximum available funds
|
|
298
|
+
|
|
299
|
+
WHAT HAPPENS:
|
|
300
|
+
- All accrued yield is withdrawn to the agent's wallet
|
|
301
|
+
- The previously deposited 20% is returned to the wallet
|
|
302
|
+
- After withdrawal, no more yield is earned until re-activated
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
name: str = "clicks_withdraw_yield"
|
|
306
|
+
description: str = (
|
|
307
|
+
"Withdraw all earned yield and deposited funds from Clicks Protocol "
|
|
308
|
+
"back to the agent's wallet. Use when the agent needs maximum liquidity."
|
|
309
|
+
)
|
|
310
|
+
args_schema: Type[BaseModel] = WithdrawYieldInput
|
|
311
|
+
sdk: Optional[ClicksSDKBridge] = None
|
|
312
|
+
|
|
313
|
+
class Config:
|
|
314
|
+
arbitrary_types_allowed = True
|
|
315
|
+
|
|
316
|
+
def __init__(self, sdk: Optional[ClicksSDKBridge] = None, **kwargs):
|
|
317
|
+
super().__init__(**kwargs)
|
|
318
|
+
self.sdk = sdk or ClicksSDKBridge()
|
|
319
|
+
|
|
320
|
+
def _run(
|
|
321
|
+
self,
|
|
322
|
+
agent_address: str,
|
|
323
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
324
|
+
) -> str:
|
|
325
|
+
result = self.sdk.withdraw_yield(agent_address)
|
|
326
|
+
if result.get("success"):
|
|
327
|
+
data = result.get("data", {})
|
|
328
|
+
return (
|
|
329
|
+
f"Yield withdrawn successfully.\n"
|
|
330
|
+
f"Amount returned: {data.get('amount', 'N/A')} USDC\n"
|
|
331
|
+
f"Transaction: {data.get('txHash', 'N/A')}"
|
|
332
|
+
)
|
|
333
|
+
return f"Withdrawal failed: {result.get('error', 'Unknown error')}"
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class ClicksCheckBalanceTool(BaseTool):
|
|
337
|
+
"""
|
|
338
|
+
Check agent balance and yield status on Clicks Protocol.
|
|
339
|
+
|
|
340
|
+
USE THIS TOOL WHEN:
|
|
341
|
+
- The agent needs to know how much USDC is liquid vs. earning yield
|
|
342
|
+
- Before making spending decisions to understand available funds
|
|
343
|
+
- To monitor yield performance and accrued earnings
|
|
344
|
+
- As a periodic health check on treasury status
|
|
345
|
+
|
|
346
|
+
RETURNS:
|
|
347
|
+
- Total USDC balance (liquid + yield)
|
|
348
|
+
- Liquid amount (immediately available)
|
|
349
|
+
- Deposited amount (earning yield)
|
|
350
|
+
- Accrued yield (earnings so far)
|
|
351
|
+
- Current APY rate
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
name: str = "clicks_check_balance"
|
|
355
|
+
description: str = (
|
|
356
|
+
"Check the agent's USDC balance split: liquid funds, deposited yield amount, "
|
|
357
|
+
"accrued earnings, and current APY. Use to understand treasury status before "
|
|
358
|
+
"spending decisions."
|
|
359
|
+
)
|
|
360
|
+
args_schema: Type[BaseModel] = CheckBalanceInput
|
|
361
|
+
sdk: Optional[ClicksSDKBridge] = None
|
|
362
|
+
|
|
363
|
+
class Config:
|
|
364
|
+
arbitrary_types_allowed = True
|
|
365
|
+
|
|
366
|
+
def __init__(self, sdk: Optional[ClicksSDKBridge] = None, **kwargs):
|
|
367
|
+
super().__init__(**kwargs)
|
|
368
|
+
self.sdk = sdk or ClicksSDKBridge()
|
|
369
|
+
|
|
370
|
+
def _run(
|
|
371
|
+
self,
|
|
372
|
+
agent_address: str,
|
|
373
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
374
|
+
) -> str:
|
|
375
|
+
info = self.sdk.get_agent_info(agent_address)
|
|
376
|
+
apy = self.sdk.get_current_apy()
|
|
377
|
+
|
|
378
|
+
if not info.get("success"):
|
|
379
|
+
return f"Balance check failed: {info.get('error', 'Unknown error')}"
|
|
380
|
+
|
|
381
|
+
data = info.get("data", {})
|
|
382
|
+
apy_rate = apy.get("data", "N/A") if apy.get("success") else "N/A"
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
f"Agent Treasury Status:\n"
|
|
386
|
+
f" Liquid USDC: {data.get('liquidBalance', '0')} (available now)\n"
|
|
387
|
+
f" Deposited: {data.get('depositedBalance', '0')} (earning yield)\n"
|
|
388
|
+
f" Accrued: {data.get('accruedYield', '0')} (yield earned)\n"
|
|
389
|
+
f" Current APY: {apy_rate}%\n"
|
|
390
|
+
f" Registered: {data.get('isRegistered', False)}"
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
class ClicksGetAPYTool(BaseTool):
|
|
395
|
+
"""
|
|
396
|
+
Get the current APY rate from Clicks Protocol.
|
|
397
|
+
|
|
398
|
+
USE THIS TOOL WHEN:
|
|
399
|
+
- The agent wants to evaluate whether activating yield is worthwhile
|
|
400
|
+
- Comparing yield rates before making treasury decisions
|
|
401
|
+
- Reporting current earnings potential to the user
|
|
402
|
+
|
|
403
|
+
Typically returns 4-8% APY depending on market conditions.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
name: str = "clicks_get_apy"
|
|
407
|
+
description: str = (
|
|
408
|
+
"Get the current annual percentage yield (APY) from Clicks Protocol. "
|
|
409
|
+
"Returns the current rate (typically 4-8%). Use to evaluate yield potential."
|
|
410
|
+
)
|
|
411
|
+
sdk: Optional[ClicksSDKBridge] = None
|
|
412
|
+
|
|
413
|
+
class Config:
|
|
414
|
+
arbitrary_types_allowed = True
|
|
415
|
+
|
|
416
|
+
def __init__(self, sdk: Optional[ClicksSDKBridge] = None, **kwargs):
|
|
417
|
+
super().__init__(**kwargs)
|
|
418
|
+
self.sdk = sdk or ClicksSDKBridge()
|
|
419
|
+
|
|
420
|
+
def _run(
|
|
421
|
+
self,
|
|
422
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
423
|
+
) -> str:
|
|
424
|
+
result = self.sdk.get_current_apy()
|
|
425
|
+
if result.get("success"):
|
|
426
|
+
return f"Current Clicks Protocol APY: {result['data']}%"
|
|
427
|
+
return f"Could not fetch APY: {result.get('error', 'Unknown error')}"
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class ClicksSimulateSplitTool(BaseTool):
|
|
431
|
+
"""
|
|
432
|
+
Preview how a USDC amount would be split before committing.
|
|
433
|
+
|
|
434
|
+
USE THIS TOOL WHEN:
|
|
435
|
+
- The agent wants to see the 80/20 breakdown before activating
|
|
436
|
+
- Planning treasury allocation
|
|
437
|
+
- The user asks "what would happen if I deposited X?"
|
|
438
|
+
|
|
439
|
+
This is a read-only operation. No funds are moved.
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
name: str = "clicks_simulate_split"
|
|
443
|
+
description: str = (
|
|
444
|
+
"Preview how USDC would be split 80/20 without moving funds. "
|
|
445
|
+
"Shows liquid amount, yield amount, and estimated earnings. "
|
|
446
|
+
"Use before clicks_activate_yield to preview the outcome."
|
|
447
|
+
)
|
|
448
|
+
args_schema: Type[BaseModel] = SimulateSplitInput
|
|
449
|
+
sdk: Optional[ClicksSDKBridge] = None
|
|
450
|
+
|
|
451
|
+
class Config:
|
|
452
|
+
arbitrary_types_allowed = True
|
|
453
|
+
|
|
454
|
+
def __init__(self, sdk: Optional[ClicksSDKBridge] = None, **kwargs):
|
|
455
|
+
super().__init__(**kwargs)
|
|
456
|
+
self.sdk = sdk or ClicksSDKBridge()
|
|
457
|
+
|
|
458
|
+
def _run(
|
|
459
|
+
self,
|
|
460
|
+
amount: str,
|
|
461
|
+
agent_address: str,
|
|
462
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
463
|
+
) -> str:
|
|
464
|
+
result = self.sdk.simulate_split(amount, agent_address)
|
|
465
|
+
if result.get("success"):
|
|
466
|
+
data = result.get("data", {})
|
|
467
|
+
return (
|
|
468
|
+
f"Split Preview for {amount} USDC:\n"
|
|
469
|
+
f" Liquid (80%): {data.get('liquidAmount', float(amount) * 0.8):.2f} USDC\n"
|
|
470
|
+
f" Yield (20%): {data.get('yieldAmount', float(amount) * 0.2):.2f} USDC\n"
|
|
471
|
+
f" Est. Annual: {data.get('estimatedYield', 'N/A')} USDC\n"
|
|
472
|
+
f" No funds moved. Use clicks_activate_yield to proceed."
|
|
473
|
+
)
|
|
474
|
+
return f"Simulation failed: {result.get('error', 'Unknown error')}"
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def get_clicks_tools(
|
|
478
|
+
rpc_url: Optional[str] = None,
|
|
479
|
+
private_key: Optional[str] = None,
|
|
480
|
+
sdk_path: Optional[str] = None,
|
|
481
|
+
) -> list:
|
|
482
|
+
"""
|
|
483
|
+
Get all Clicks Protocol tools for a LangChain agent.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
rpc_url: Base RPC URL (default: CLICKS_RPC_URL env var)
|
|
487
|
+
private_key: Agent wallet private key (default: CLICKS_PRIVATE_KEY env var)
|
|
488
|
+
sdk_path: Path to installed @clicks-protocol/sdk (default: CLICKS_SDK_PATH env var)
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
List of LangChain tools for yield management
|
|
492
|
+
|
|
493
|
+
Example:
|
|
494
|
+
from clicks_langchain import get_clicks_tools
|
|
495
|
+
tools = get_clicks_tools()
|
|
496
|
+
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS)
|
|
497
|
+
"""
|
|
498
|
+
sdk = ClicksSDKBridge(rpc_url=rpc_url, private_key=private_key, sdk_path=sdk_path)
|
|
499
|
+
return [
|
|
500
|
+
ClicksActivateYieldTool(sdk=sdk),
|
|
501
|
+
ClicksWithdrawYieldTool(sdk=sdk),
|
|
502
|
+
ClicksCheckBalanceTool(sdk=sdk),
|
|
503
|
+
ClicksGetAPYTool(sdk=sdk),
|
|
504
|
+
ClicksSimulateSplitTool(sdk=sdk),
|
|
505
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "clicks-langchain"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Clicks Protocol yield management tools for LangChain agents 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 = ["langchain", "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
|
+
"langchain>=0.1.0",
|
|
29
|
+
"pydantic>=2.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://clicksprotocol.xyz"
|
|
34
|
+
Repository = "https://github.com/clicks-protocol/clicks-langchain"
|
|
35
|
+
Documentation = "https://docs.clicksprotocol.xyz/integrations/langchain"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools]
|
|
38
|
+
py-modules = ["clicks_langchain"]
|