Skip to content

Commit aac9404

Browse files
committed
cleanup
1 parent 3c69e97 commit aac9404

16 files changed

+598
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"walletId":"be628710-a033-4013-8991-2d64207b83fd","seed":"c21cf2a7f5211087c56f3363defce5b2d1b200a69948b2d693b6b2cf29ad91f7","networkId":"base-mainnet"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2dfdc4d26d3a94d03e9ea80a75421f04
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
b50e010733089021415e207255e55856
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
wallet_data/
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# coinbase-agentkit
2+
3+
A DeFi payment agent built using CDP AgentKit that operates over the XMTP messaging protocol.
4+
5+
## Features
6+
7+
- Process payments on the blockchain using natural language commands
8+
- User-specific wallet management with flexible storage options (Redis or local file)
9+
- XMTP messaging integration for chat-based interactions
10+
- Powered by Coinbase AgentKit for blockchain operations
11+
12+
## Prerequisites
13+
14+
- Node.js (v20+)
15+
- [OpenAI](https://platform.openai.com/) API key
16+
- [Coinbase Developer Platform](https://portal.cdp.coinbase.com) (CDP) API credentials
17+
18+
## Quick Start Guide
19+
20+
Follow these steps to get your payment agent up and running quickly:
21+
22+
1. **Clone the repository**:
23+
24+
```bash
25+
git clone https://github.com/ephemeraHQ/xmtp-agent-examples.git
26+
cd integrations/coinbase-agentkit
27+
```
28+
29+
2. **Install dependencies**:
30+
31+
```bash
32+
yarn install
33+
```
34+
35+
3. **Generate XMTP keys**:
36+
37+
```bash
38+
yarn gen:keys
39+
```
40+
41+
This will generate random wallet and encryption keys for your agent and output them to the console. Copy these values to your `.env` file.
42+
43+
4. **Set up your environment variables**:
44+
Create a `.env` file with the following variables:
45+
46+
```
47+
# Required: OpenAI API Key
48+
OPENAI_API_KEY=your_openai_api_key_here
49+
50+
# Required: Coinbase Developer Platform credentials
51+
CDP_API_KEY_NAME=your_cdp_api_key_name_here
52+
CDP_API_KEY_PRIVATE_KEY=your_cdp_api_key_private_key_here
53+
54+
# Required: XMTP wallet and encryption keys (from step 3)
55+
WALLET_KEY=your_wallet_private_key_here
56+
ENCRYPTION_KEY=your_encryption_key_here
57+
58+
# Optional: Network ID (defaults to base-sepolia if not specified)
59+
NETWORK_ID=base-sepolia
60+
61+
# Optional: Redis for persistent storage (if not provided, local file storage will be used)
62+
REDIS_URL=redis://localhost:6379
63+
```
64+
65+
5. **Start the agent**:
66+
67+
```bash
68+
yarn dev
69+
```
70+
71+
6. **Interact with your agent**:
72+
Once running, you'll see a URL in the console like:
73+
```
74+
Send a message on http://xmtp.chat/dm/YOUR_AGENT_ADDRESS?env=dev
75+
```
76+
Open this URL in your browser to start chatting with your payment agent!
77+
78+
## Usage Examples
79+
80+
Once the agent is running, you can interact with it using natural language commands:
81+
82+
### Basic prompts
83+
84+
- "Send 0.01 ETH to 0x1234..."
85+
- "Check my wallet balance"
86+
- "Transfer 10 USDC to vitalik.eth"
87+
88+
## How It Works
89+
90+
This payment agent combines several technologies:
91+
92+
1. **XMTP Protocol**: For decentralized messaging and chat interface
93+
2. **Coinbase AgentKit**: AI agent framework
94+
3. **Storage Options**: Redis or local file storage for wallet data
95+
4. **LLM Integration**: For natural language processing
96+
97+
For each user who interacts with the agent:
98+
99+
1. A unique CDP MPC wallet is created and stored
100+
2. Natural language prompts are processed
101+
3. Onchain transactions are executed
102+
4. Transaction status is communicated back to the user
103+
104+
## Troubleshooting
105+
106+
Common issues and solutions:
107+
108+
### Connection Issues
109+
110+
- If Redis connection fails, the agent will automatically fall back to local file storage
111+
- Ensure your CDP API credentials are correct
112+
- Verify your OpenAI API key is valid
113+
114+
### Transaction Failures
115+
116+
- Check that you're on the correct network (default is base-sepolia)
117+
- Ensure the wallet has sufficient funds for the transaction
118+
- For testnet operations, request funds from a faucet
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "coinbase-agentkit",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"build": "tsc",
8+
"dev": "tsx --env-file=.env src/index.ts",
9+
"gen:keys": "tsx ../../scripts/generateKeys.ts",
10+
"start": "tsx --env-file=.env src/index.ts"
11+
},
12+
"dependencies": {
13+
"@coinbase/agentkit": "^0.2.0",
14+
"@coinbase/agentkit-langchain": "^0.2.0",
15+
"@langchain/core": "^0.3.20",
16+
"@langchain/langgraph": "^0.2.24",
17+
"@langchain/openai": "^0.3.14",
18+
"@redis/client": "^1.6.0",
19+
"@xmtp/node-sdk": "^1.0.0-rc2",
20+
"dotenv": "^16.4.7",
21+
"redis": "^4.7.0",
22+
"tsx": "^4.19.2",
23+
"typescript": "^5.7.3",
24+
"uint8arrays": "^5.1.0",
25+
"viem": "^2.22.17"
26+
}
27+
}
+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import {
2+
AgentKit,
3+
cdpApiActionProvider,
4+
cdpWalletActionProvider,
5+
CdpWalletProvider,
6+
erc20ActionProvider,
7+
walletActionProvider,
8+
} from "@coinbase/agentkit";
9+
import { getLangChainTools } from "@coinbase/agentkit-langchain";
10+
import { HumanMessage } from "@langchain/core/messages";
11+
import { MemorySaver } from "@langchain/langgraph";
12+
import { createReactAgent } from "@langchain/langgraph/prebuilt";
13+
import { ChatOpenAI } from "@langchain/openai";
14+
import { getWalletData, saveWalletData } from "./storage";
15+
16+
// Define types for agent and configuration
17+
interface AgentConfig {
18+
configurable: {
19+
thread_id: string;
20+
};
21+
}
22+
23+
// Type for the agent
24+
type Agent = ReturnType<typeof createReactAgent>;
25+
26+
/**
27+
* Get or create wallet for a user
28+
*/
29+
async function getOrCreateWalletForUser(userId: string) {
30+
const walletDataStr = await getWalletData(userId);
31+
32+
let walletProvider;
33+
34+
// Configure CDP Wallet Provider
35+
const config = {
36+
apiKeyName: process.env.CDP_API_KEY_NAME as string,
37+
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(
38+
/\\n/g,
39+
"\n",
40+
),
41+
cdpWalletData: walletDataStr || undefined,
42+
networkId: process.env.NETWORK_ID || "base-mainnet",
43+
};
44+
45+
// Create a new wallet if one doesn't exist
46+
walletProvider = await CdpWalletProvider.configureWithWallet(config);
47+
48+
if (!walletDataStr) {
49+
// Export wallet data and save
50+
const exportedWallet = await walletProvider.exportWallet();
51+
await saveWalletData(userId, JSON.stringify(exportedWallet));
52+
}
53+
54+
return { walletProvider, config };
55+
}
56+
57+
/**
58+
* Initialize the agent with CDP Agentkit
59+
* @param userId - The user's identifier (XMTP address)
60+
* @returns Agent executor and config
61+
*/
62+
export async function initializeAgent(
63+
userId: string,
64+
): Promise<{ agent: Agent; config: AgentConfig }> {
65+
try {
66+
// Initialize LLM
67+
const llm = new ChatOpenAI({
68+
model: "gpt-4o-mini",
69+
});
70+
71+
// Get or create wallet provider for the user
72+
const { walletProvider } = await getOrCreateWalletForUser(userId);
73+
74+
// Initialize AgentKit with payment-focused action providers
75+
const agentkit = await AgentKit.from({
76+
walletProvider,
77+
actionProviders: [
78+
walletActionProvider(),
79+
erc20ActionProvider(),
80+
cdpApiActionProvider({
81+
apiKeyName: process.env.CDP_API_KEY_NAME as string,
82+
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(
83+
/\\n/g,
84+
"\n",
85+
),
86+
}),
87+
cdpWalletActionProvider({
88+
apiKeyName: process.env.CDP_API_KEY_NAME as string,
89+
apiKeyPrivateKey: process.env.CDP_API_KEY_PRIVATE_KEY?.replace(
90+
/\\n/g,
91+
"\n",
92+
),
93+
}),
94+
],
95+
});
96+
97+
const tools = await getLangChainTools(agentkit);
98+
99+
// Store buffered conversation history in memory
100+
const memory = new MemorySaver();
101+
const agentConfig: AgentConfig = {
102+
configurable: { thread_id: `Payment Agent for ${userId}` },
103+
};
104+
105+
// Create React Agent using the LLM and CDP AgentKit tools
106+
const agent = createReactAgent({
107+
llm,
108+
tools,
109+
checkpointSaver: memory,
110+
messageModifier: `
111+
You are a DeFi Payment Agent that assists users with sending payments to any wallet address using natural language instructions.
112+
113+
When a user asks you to make a payment:
114+
1. Provide clear information about network fees (if any) and transaction status.
115+
2. Notify users of successful transactions with relevant details.
116+
117+
You can only perform payment-related tasks. For other requests, politely explain that you're
118+
specialized in processing payments and can't assist with other tasks.
119+
120+
Your default currency is USDC and the token address is 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. It's gasless on base-mainnet.
121+
122+
If you encounter an error, provide clear troubleshooting advice and offer to retry the transaction.
123+
124+
Before executing your first action, get the wallet balance to see how much funds you have.
125+
If you don't have enough funds, ask the user to deposit more funds into your wallet and provide them your wallet address.
126+
127+
If there is a 5XX (internal) HTTP error, ask the user to try again later.
128+
129+
Be concise, helpful, and security-focused in all your interactions.
130+
`,
131+
});
132+
133+
// Export and save updated wallet data
134+
const exportedWallet = await walletProvider.exportWallet();
135+
await saveWalletData(userId, JSON.stringify(exportedWallet));
136+
137+
return { agent, config: agentConfig };
138+
} catch (error) {
139+
console.error("Failed to initialize agent:", error);
140+
throw error;
141+
}
142+
}
143+
144+
/**
145+
* Process a message with the agent
146+
* @param agent - The agent executor
147+
* @param config - Agent configuration
148+
* @param message - The user message
149+
* @returns The agent's response
150+
*/
151+
export async function processMessage(
152+
agent: Agent,
153+
config: AgentConfig,
154+
message: string,
155+
): Promise<string> {
156+
let response = "";
157+
158+
try {
159+
const stream = await agent.stream(
160+
{ messages: [new HumanMessage(message)] },
161+
config,
162+
);
163+
164+
for await (const chunk of stream) {
165+
if ("agent" in chunk) {
166+
response += (chunk.agent.messages[0].content as string) + "\n";
167+
} else if ("tools" in chunk) {
168+
// Tool execution messages can be added to the response or handled separately
169+
// For now, we'll add them to provide transparency
170+
response += (chunk.tools.messages[0].content as string) + "\n";
171+
}
172+
}
173+
174+
return response.trim();
175+
} catch (error) {
176+
console.error("Error processing message:", error);
177+
return "Sorry, I encountered an error while processing your request. Please try again later.";
178+
}
179+
}

0 commit comments

Comments
 (0)