agentic-x402 0.1.2 → 0.2.2
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.
- package/README.md +80 -16
- package/SKILL.md +67 -14
- package/bin/cli.ts +17 -15
- package/config/example.env +1 -4
- package/package.json +2 -1
- package/scripts/commands/create-link.ts +1 -9
- package/scripts/commands/setup.ts +296 -0
- package/scripts/core/config.ts +0 -2
package/README.md
CHANGED
|
@@ -27,20 +27,26 @@ x402 --version
|
|
|
27
27
|
|
|
28
28
|
### Configure
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Run the interactive setup to create a new wallet:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
33
|
+
x402 setup
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
This will:
|
|
37
|
+
1. Generate a new wallet (recommended) or accept an existing key
|
|
38
|
+
2. Save configuration to `~/.x402/.env`
|
|
39
|
+
3. Display your wallet address for funding
|
|
40
|
+
4. Show your private key for backup
|
|
41
|
+
|
|
42
|
+
**Back up your private key immediately!** See [Backup](#backup-your-private-key) below.
|
|
43
|
+
|
|
44
|
+
#### Manual Configuration
|
|
45
|
+
|
|
46
|
+
Alternatively, set the environment variable directly:
|
|
37
47
|
|
|
38
48
|
```bash
|
|
39
|
-
|
|
40
|
-
cat > ~/.x402/.env << 'EOF'
|
|
41
|
-
EVM_PRIVATE_KEY=0x...your_private_key...
|
|
42
|
-
X402_NETWORK=mainnet
|
|
43
|
-
EOF
|
|
49
|
+
export EVM_PRIVATE_KEY=0x...your_private_key...
|
|
44
50
|
```
|
|
45
51
|
|
|
46
52
|
### Development / Local Install
|
|
@@ -56,24 +62,38 @@ cp config/example.env .env
|
|
|
56
62
|
## Quick Start
|
|
57
63
|
|
|
58
64
|
```bash
|
|
59
|
-
# 1.
|
|
60
|
-
|
|
65
|
+
# 1. Create a wallet
|
|
66
|
+
x402 setup
|
|
61
67
|
|
|
62
|
-
# 2.
|
|
68
|
+
# 2. Fund your wallet with USDC on Base (send to the address shown)
|
|
69
|
+
|
|
70
|
+
# 3. Check balance
|
|
63
71
|
x402 balance
|
|
64
72
|
|
|
65
|
-
#
|
|
73
|
+
# 4. Pay for a resource
|
|
66
74
|
x402 pay https://api.example.com/paid-endpoint
|
|
67
75
|
|
|
68
|
-
#
|
|
76
|
+
# 5. Fetch with auto-payment
|
|
69
77
|
x402 fetch https://api.example.com/data --json
|
|
70
78
|
|
|
71
|
-
#
|
|
79
|
+
# 6. Create a payment link (requires x402-links-server)
|
|
72
80
|
x402 create-link --name "My API" --price 1.00 --url https://api.example.com/premium
|
|
73
81
|
```
|
|
74
82
|
|
|
75
83
|
## Commands
|
|
76
84
|
|
|
85
|
+
### setup — Create or Configure Wallet
|
|
86
|
+
|
|
87
|
+
Interactive setup that generates a new wallet or accepts an existing private key. Saves configuration to `~/.x402/.env`.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
x402 setup
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Security:** If you choose to use an existing private key, you'll see a warning. We recommend using a **dedicated wallet** with limited funds for automated agents.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
77
97
|
### balance — Check Wallet Balances
|
|
78
98
|
|
|
79
99
|
Show your wallet address, USDC balance (for payments), and ETH balance (for gas). Warns if either balance is low.
|
|
@@ -142,7 +162,7 @@ x402 fetch https://api.example.com/submit --method POST --body '{"key":"value"}'
|
|
|
142
162
|
|
|
143
163
|
### create-link — Create a Payment Link
|
|
144
164
|
|
|
145
|
-
Create a payment link via the [21.cash](https://21.cash) x402-links-server. You can gate a URL or text content behind a USDC payment. Requires `X402_LINKS_API_URL`
|
|
165
|
+
Create a payment link via the [21.cash](https://21.cash) x402-links-server. You can gate a URL or text content behind a USDC payment. Requires `X402_LINKS_API_URL` environment variable. Link creation costs $0.10 USDC, paid automatically via x402.
|
|
146
166
|
|
|
147
167
|
```bash
|
|
148
168
|
x402 create-link --name "Premium Guide" --price 5.00 --url https://mysite.com/guide.pdf
|
|
@@ -210,7 +230,6 @@ Config is loaded from these locations (in order of priority):
|
|
|
210
230
|
| Variable | Description |
|
|
211
231
|
|----------|-------------|
|
|
212
232
|
| `X402_LINKS_API_URL` | Base URL of x402-links-server (e.g., `https://21.cash`) |
|
|
213
|
-
| `X402_LINKS_API_KEY` | API key for programmatic link creation |
|
|
214
233
|
|
|
215
234
|
## Global Options
|
|
216
235
|
|
|
@@ -223,6 +242,7 @@ Config is loaded from these locations (in order of priority):
|
|
|
223
242
|
|
|
224
243
|
| Category | Command | Description |
|
|
225
244
|
|----------|---------|-------------|
|
|
245
|
+
| **Setup** | `setup` | Create or configure your x402 wallet |
|
|
226
246
|
| **Info** | `balance` | Check wallet USDC and ETH balances |
|
|
227
247
|
| **Payments** | `pay <url>` | Pay for an x402-gated resource (verbose output) |
|
|
228
248
|
| **Payments** | `fetch <url>` | Fetch with auto-payment (pipe-friendly `--json`/`--raw`) |
|
|
@@ -244,6 +264,50 @@ x402 fetch https://api.example.com/data --json
|
|
|
244
264
|
x402 fetch https://api.example.com/data --raw
|
|
245
265
|
```
|
|
246
266
|
|
|
267
|
+
## Backup Your Private Key
|
|
268
|
+
|
|
269
|
+
Your private key is stored in `~/.x402/.env`. **If lost, your funds cannot be recovered.**
|
|
270
|
+
|
|
271
|
+
### Recommended Backup Methods
|
|
272
|
+
|
|
273
|
+
1. **Password Manager** (Recommended)
|
|
274
|
+
- Store in 1Password, Bitwarden, or similar
|
|
275
|
+
- Create a secure note with your private key
|
|
276
|
+
|
|
277
|
+
2. **Encrypted File**
|
|
278
|
+
```bash
|
|
279
|
+
gpg -c ~/.x402/.env
|
|
280
|
+
# Store the encrypted .env.gpg file securely
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
3. **Paper Backup** (for larger amounts)
|
|
284
|
+
- Write down the private key
|
|
285
|
+
- Store in a safe or safety deposit box
|
|
286
|
+
|
|
287
|
+
### View Your Private Key
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
cat ~/.x402/.env | grep EVM_PRIVATE_KEY
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Recovery
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
mkdir -p ~/.x402
|
|
297
|
+
echo "EVM_PRIVATE_KEY=0x...your_backed_up_key..." > ~/.x402/.env
|
|
298
|
+
chmod 600 ~/.x402/.env
|
|
299
|
+
x402 balance # verify
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Security Best Practices
|
|
303
|
+
|
|
304
|
+
- **Use a dedicated wallet** — Never use your main wallet with automated agents
|
|
305
|
+
- **Limit funds** — Only transfer what you need for payments
|
|
306
|
+
- **Set payment limits** — Configure `X402_MAX_PAYMENT_USD` to cap exposure
|
|
307
|
+
- **Test first** — Use `X402_NETWORK=testnet` before mainnet
|
|
308
|
+
- **Protect the config** — Keep `~/.x402/.env` with 600 permissions
|
|
309
|
+
- **Never share** — Your private key gives full access to your wallet
|
|
310
|
+
|
|
247
311
|
## License
|
|
248
312
|
|
|
249
313
|
MIT
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: Requires Node.js 20+, network access to x402 facilitators and EVM chains
|
|
6
6
|
metadata:
|
|
7
7
|
author: monemetrics
|
|
8
|
-
version: "0.1
|
|
8
|
+
version: "0.2.1"
|
|
9
9
|
allowed-tools: Bash(x402:*) Bash(npm:*) Read
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -17,6 +17,7 @@ Pay for x402-gated APIs and content using USDC on Base. This skill enables agent
|
|
|
17
17
|
|
|
18
18
|
| Command | Description |
|
|
19
19
|
|---------|-------------|
|
|
20
|
+
| `x402 setup` | Create or configure wallet |
|
|
20
21
|
| `x402 balance` | Check USDC and ETH balances |
|
|
21
22
|
| `x402 pay <url>` | Pay for a gated resource |
|
|
22
23
|
| `x402 fetch <url>` | Fetch with auto-payment |
|
|
@@ -38,20 +39,33 @@ x402 --version
|
|
|
38
39
|
|
|
39
40
|
## Setup
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
Run the interactive setup to create a new wallet:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
x402 setup
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This will:
|
|
49
|
+
1. Generate a new wallet (recommended) or accept an existing key
|
|
50
|
+
2. Save configuration to `~/.x402/.env`
|
|
51
|
+
3. Display your wallet address for funding
|
|
52
|
+
|
|
53
|
+
**Important:** Back up your private key immediately after setup!
|
|
54
|
+
|
|
55
|
+
### Manual Configuration
|
|
56
|
+
|
|
57
|
+
Alternatively, set the environment variable directly:
|
|
42
58
|
|
|
43
59
|
```bash
|
|
44
60
|
export EVM_PRIVATE_KEY=0x...your_private_key...
|
|
45
61
|
```
|
|
46
62
|
|
|
47
|
-
Or create a
|
|
63
|
+
Or create a config file:
|
|
48
64
|
|
|
49
65
|
```bash
|
|
50
66
|
mkdir -p ~/.x402
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
X402_NETWORK=mainnet
|
|
54
|
-
EOF
|
|
67
|
+
echo "EVM_PRIVATE_KEY=0x..." > ~/.x402/.env
|
|
68
|
+
chmod 600 ~/.x402/.env
|
|
55
69
|
```
|
|
56
70
|
|
|
57
71
|
Verify setup:
|
|
@@ -116,7 +130,6 @@ Create payment links to monetize your own content using x402-links-server:
|
|
|
116
130
|
Add to `.env`:
|
|
117
131
|
```bash
|
|
118
132
|
X402_LINKS_API_URL=https://your-x402-links-server.com
|
|
119
|
-
X402_LINKS_API_KEY=your_api_key
|
|
120
133
|
```
|
|
121
134
|
|
|
122
135
|
### Create a link
|
|
@@ -241,7 +254,6 @@ x402 link-info <router-address> [--json]
|
|
|
241
254
|
| `X402_SLIPPAGE_BPS` | Slippage tolerance in basis points (100 bps = 1%) | `50` |
|
|
242
255
|
| `X402_VERBOSE` | Enable verbose logging (`1` = on, `0` = off) | `0` |
|
|
243
256
|
| `X402_LINKS_API_URL` | Base URL of x402-links-server (e.g., `https://21.cash`) | — |
|
|
244
|
-
| `X402_LINKS_API_KEY` | API key for programmatic link creation | — |
|
|
245
257
|
|
|
246
258
|
## Supported Networks
|
|
247
259
|
|
|
@@ -299,12 +311,53 @@ Ensure your wallet has funds on the correct network:
|
|
|
299
311
|
- `X402_NETWORK=mainnet` → Base mainnet
|
|
300
312
|
- `X402_NETWORK=testnet` → Base Sepolia
|
|
301
313
|
|
|
302
|
-
##
|
|
314
|
+
## Backup Your Private Key
|
|
315
|
+
|
|
316
|
+
Your private key is stored in `~/.x402/.env`. If lost, your funds cannot be recovered.
|
|
317
|
+
|
|
318
|
+
### Recommended Backup Methods
|
|
319
|
+
|
|
320
|
+
1. **Password Manager** (Recommended)
|
|
321
|
+
- Store in 1Password, Bitwarden, or similar
|
|
322
|
+
- Create a secure note with your private key
|
|
323
|
+
- Tag it for easy retrieval
|
|
324
|
+
|
|
325
|
+
2. **Encrypted File**
|
|
326
|
+
```bash
|
|
327
|
+
# Encrypt with GPG
|
|
328
|
+
gpg -c ~/.x402/.env
|
|
329
|
+
# Creates ~/.x402/.env.gpg - store this backup securely
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
3. **Paper Backup** (for larger amounts)
|
|
333
|
+
- Write down the private key
|
|
334
|
+
- Store in a safe or safety deposit box
|
|
335
|
+
- Never store digitally unencrypted
|
|
336
|
+
|
|
337
|
+
### View Your Private Key
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
cat ~/.x402/.env | grep EVM_PRIVATE_KEY
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Recovery
|
|
344
|
+
|
|
345
|
+
To restore from backup:
|
|
346
|
+
```bash
|
|
347
|
+
mkdir -p ~/.x402
|
|
348
|
+
echo "EVM_PRIVATE_KEY=0x...your_backed_up_key..." > ~/.x402/.env
|
|
349
|
+
chmod 600 ~/.x402/.env
|
|
350
|
+
x402 balance # verify
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Security Best Practices
|
|
303
354
|
|
|
304
|
-
- Never
|
|
305
|
-
-
|
|
306
|
-
- Set
|
|
307
|
-
-
|
|
355
|
+
- **Use a dedicated wallet** — Never use your main wallet with automated agents
|
|
356
|
+
- **Limit funds** — Only transfer what you need for payments
|
|
357
|
+
- **Set payment limits** — Configure `X402_MAX_PAYMENT_USD` to cap exposure
|
|
358
|
+
- **Test first** — Use `X402_NETWORK=testnet` with test tokens before mainnet
|
|
359
|
+
- **Protect the config** — `~/.x402/.env` has 600 permissions; keep it that way
|
|
360
|
+
- **Never share** — Your private key gives full access to your wallet
|
|
308
361
|
|
|
309
362
|
## Links
|
|
310
363
|
|
package/bin/cli.ts
CHANGED
|
@@ -11,10 +11,17 @@ const scriptsDir = resolve(__dirname, '../scripts');
|
|
|
11
11
|
interface Command {
|
|
12
12
|
script: string;
|
|
13
13
|
description: string;
|
|
14
|
-
category: 'info' | 'payments' | 'links';
|
|
14
|
+
category: 'setup' | 'info' | 'payments' | 'links';
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const commands: Record<string, Command> = {
|
|
18
|
+
// Setup commands
|
|
19
|
+
setup: {
|
|
20
|
+
script: 'commands/setup.ts',
|
|
21
|
+
description: 'Create or configure your x402 wallet',
|
|
22
|
+
category: 'setup',
|
|
23
|
+
},
|
|
24
|
+
|
|
18
25
|
// Info commands
|
|
19
26
|
balance: {
|
|
20
27
|
script: 'commands/balance.ts',
|
|
@@ -53,6 +60,9 @@ x402 - Agent skill for x402 payments
|
|
|
53
60
|
|
|
54
61
|
Usage: x402 <command> [arguments]
|
|
55
62
|
|
|
63
|
+
Setup:
|
|
64
|
+
setup Create or configure your x402 wallet
|
|
65
|
+
|
|
56
66
|
Info Commands:
|
|
57
67
|
balance Check wallet USDC and ETH balances
|
|
58
68
|
|
|
@@ -68,26 +78,18 @@ Options:
|
|
|
68
78
|
-h, --help Show this help
|
|
69
79
|
-v, --version Show version
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
X402_LINKS_API_URL x402-links-server URL (for link commands)
|
|
77
|
-
X402_LINKS_API_KEY API key for x402-links-server
|
|
78
|
-
|
|
79
|
-
Examples:
|
|
80
|
-
x402 balance
|
|
81
|
-
x402 pay https://api.example.com/data
|
|
82
|
-
x402 fetch https://api.example.com/resource --json
|
|
83
|
-
x402 create-link --name "Guide" --price 5.00 --url https://mysite.com/guide
|
|
81
|
+
Getting Started:
|
|
82
|
+
1. Run "x402 setup" to create a new wallet
|
|
83
|
+
2. Fund your wallet with USDC on Base
|
|
84
|
+
3. Run "x402 balance" to verify
|
|
85
|
+
4. Use "x402 pay <url>" to pay for resources
|
|
84
86
|
|
|
85
87
|
For command-specific help: x402 <command> --help
|
|
86
88
|
`);
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
function showVersion() {
|
|
90
|
-
console.log('agentic-x402 v0.1
|
|
92
|
+
console.log('agentic-x402 v0.2.1');
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
async function main() {
|
package/config/example.env
CHANGED
|
@@ -39,13 +39,10 @@ X402_MAX_PAYMENT_USD=10
|
|
|
39
39
|
# 21CASH INTEGRATION (Optional - for creating payment links)
|
|
40
40
|
# =============================================================================
|
|
41
41
|
|
|
42
|
-
# x402-links-server API URL
|
|
42
|
+
# x402-links-server API URL (link creation is gated by x402 payment)
|
|
43
43
|
# Example: https://your-21cash-instance.com or https://21.cash
|
|
44
44
|
# X402_LINKS_API_URL=https://your-server.com
|
|
45
45
|
|
|
46
|
-
# API key for programmatic link creation
|
|
47
|
-
# X402_LINKS_API_KEY=your_api_key_here
|
|
48
|
-
|
|
49
46
|
# =============================================================================
|
|
50
47
|
# DEBUG
|
|
51
48
|
# =============================================================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-x402",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Agent skill for x402 payments - pay for and sell gated content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"SKILL.md"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
+
"setup": "tsx scripts/commands/setup.ts",
|
|
17
18
|
"pay": "tsx scripts/commands/pay.ts",
|
|
18
19
|
"fetch": "tsx scripts/commands/fetch-paid.ts",
|
|
19
20
|
"balance": "tsx scripts/commands/balance.ts",
|
|
@@ -57,7 +57,6 @@ Options:
|
|
|
57
57
|
|
|
58
58
|
Environment:
|
|
59
59
|
X402_LINKS_API_URL Base URL of x402-links-server (required)
|
|
60
|
-
X402_LINKS_API_KEY API key for programmatic access (required)
|
|
61
60
|
|
|
62
61
|
Examples:
|
|
63
62
|
x402 create-link --name "Premium Guide" --price 5.00 --url https://mysite.com/guide.pdf
|
|
@@ -75,12 +74,6 @@ Examples:
|
|
|
75
74
|
process.exit(1);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
if (!config.x402LinksApiKey) {
|
|
79
|
-
console.error('Error: X402_LINKS_API_KEY environment variable is required');
|
|
80
|
-
console.error('This is the API key for programmatic access to x402-links-server');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
77
|
const name = flags.name as string;
|
|
85
78
|
const price = flags.price as string;
|
|
86
79
|
const gatedUrl = flags.url as string | undefined;
|
|
@@ -142,11 +135,10 @@ Examples:
|
|
|
142
135
|
|
|
143
136
|
const apiUrl = `${config.x402LinksApiUrl}/api/links/programmatic`;
|
|
144
137
|
|
|
145
|
-
const response = await
|
|
138
|
+
const response = await client.fetchWithPayment(apiUrl, {
|
|
146
139
|
method: 'POST',
|
|
147
140
|
headers: {
|
|
148
141
|
'Content-Type': 'application/json',
|
|
149
|
-
'X-API-Key': config.x402LinksApiKey,
|
|
150
142
|
},
|
|
151
143
|
body: JSON.stringify(requestBody),
|
|
152
144
|
});
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// x402 Agent Skill - Wallet Setup
|
|
3
|
+
// Creates a new wallet or configures an existing one
|
|
4
|
+
|
|
5
|
+
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as readline from 'readline';
|
|
9
|
+
import { homedir } from 'os';
|
|
10
|
+
|
|
11
|
+
// Global config directory: ~/.x402/
|
|
12
|
+
const CONFIG_DIR = path.join(homedir(), '.x402');
|
|
13
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, '.env');
|
|
14
|
+
|
|
15
|
+
interface SetupResult {
|
|
16
|
+
success: boolean;
|
|
17
|
+
walletAddress?: string;
|
|
18
|
+
privateKey?: string;
|
|
19
|
+
isNewWallet?: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createReadline(): readline.Interface {
|
|
24
|
+
return readline.createInterface({
|
|
25
|
+
input: process.stdin,
|
|
26
|
+
output: process.stdout,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function prompt(rl: readline.Interface, question: string): Promise<string> {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
rl.question(question, (answer) => {
|
|
33
|
+
resolve(answer.trim());
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isValidPrivateKey(key: string): boolean {
|
|
39
|
+
return /^0x[a-fA-F0-9]{64}$/.test(key);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ensureConfigDir(): void {
|
|
43
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
44
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function backupExistingConfig(): string | null {
|
|
49
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
54
|
+
const backupPath = path.join(CONFIG_DIR, `.env.backup.${timestamp}`);
|
|
55
|
+
|
|
56
|
+
fs.copyFileSync(CONFIG_PATH, backupPath);
|
|
57
|
+
fs.chmodSync(backupPath, 0o600);
|
|
58
|
+
|
|
59
|
+
return backupPath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function main(): Promise<SetupResult> {
|
|
63
|
+
console.log('x402 Agent Skill - Wallet Setup');
|
|
64
|
+
console.log('================================\n');
|
|
65
|
+
|
|
66
|
+
// Check if config already exists
|
|
67
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
68
|
+
console.log('Existing config found!');
|
|
69
|
+
console.log(`Location: ${CONFIG_PATH}\n`);
|
|
70
|
+
|
|
71
|
+
// Read existing config and show wallet address
|
|
72
|
+
const envContent = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
73
|
+
const keyMatch = envContent.match(/EVM_PRIVATE_KEY=0x([a-fA-F0-9]{64})/);
|
|
74
|
+
|
|
75
|
+
if (keyMatch) {
|
|
76
|
+
const existingKey = `0x${keyMatch[1]}` as `0x${string}`;
|
|
77
|
+
const account = privateKeyToAccount(existingKey);
|
|
78
|
+
|
|
79
|
+
console.log('Current Configuration');
|
|
80
|
+
console.log('---------------------');
|
|
81
|
+
console.log(`Wallet Address: ${account.address}`);
|
|
82
|
+
console.log(`Config File: ${CONFIG_PATH}`);
|
|
83
|
+
|
|
84
|
+
const rl = createReadline();
|
|
85
|
+
console.log('\nOptions:');
|
|
86
|
+
console.log(' 1) Keep existing config (exit)');
|
|
87
|
+
console.log(' 2) Create new wallet (backs up existing config)\n');
|
|
88
|
+
|
|
89
|
+
let choice = '';
|
|
90
|
+
while (choice !== '1' && choice !== '2') {
|
|
91
|
+
choice = await prompt(rl, 'Enter choice (1 or 2): ');
|
|
92
|
+
if (choice !== '1' && choice !== '2') {
|
|
93
|
+
console.log('Please enter 1 or 2');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (choice === '1') {
|
|
98
|
+
rl.close();
|
|
99
|
+
console.log('\nKeeping existing config.');
|
|
100
|
+
console.log(`\nTo check your balance:`);
|
|
101
|
+
console.log(` x402 balance`);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
walletAddress: account.address,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// User wants to reconfigure - backup first
|
|
110
|
+
rl.close();
|
|
111
|
+
const backupPath = backupExistingConfig();
|
|
112
|
+
console.log(`\nExisting config backed up to:`);
|
|
113
|
+
console.log(` ${backupPath}`);
|
|
114
|
+
console.log('');
|
|
115
|
+
|
|
116
|
+
// Continue with setup flow below...
|
|
117
|
+
} else {
|
|
118
|
+
// Config exists but is invalid - back it up and continue
|
|
119
|
+
const backupPath = backupExistingConfig();
|
|
120
|
+
console.log('Invalid config file found (missing private key).');
|
|
121
|
+
console.log(`Backed up to: ${backupPath}\n`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const rl = createReadline();
|
|
126
|
+
|
|
127
|
+
// Step 1: Wallet choice
|
|
128
|
+
console.log('Step 1/2: Wallet Setup');
|
|
129
|
+
console.log('----------------------');
|
|
130
|
+
console.log('Do you have an existing wallet private key?\n');
|
|
131
|
+
console.log(' 1) Generate a NEW wallet (Recommended for agents)');
|
|
132
|
+
console.log(' 2) Use an EXISTING private key\n');
|
|
133
|
+
|
|
134
|
+
let choice = '';
|
|
135
|
+
while (choice !== '1' && choice !== '2') {
|
|
136
|
+
choice = await prompt(rl, 'Enter choice (1 or 2): ');
|
|
137
|
+
if (choice !== '1' && choice !== '2') {
|
|
138
|
+
console.log('Please enter 1 or 2');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let privateKey: `0x${string}`;
|
|
143
|
+
let isNewWallet = false;
|
|
144
|
+
|
|
145
|
+
if (choice === '1') {
|
|
146
|
+
// Generate new wallet (recommended)
|
|
147
|
+
console.log('\nGenerating new wallet...');
|
|
148
|
+
privateKey = generatePrivateKey();
|
|
149
|
+
isNewWallet = true;
|
|
150
|
+
console.log('New wallet created!\n');
|
|
151
|
+
} else {
|
|
152
|
+
// User wants to use existing key - show warnings
|
|
153
|
+
console.log('\n' + '='.repeat(60));
|
|
154
|
+
console.log(' SECURITY WARNING');
|
|
155
|
+
console.log('='.repeat(60));
|
|
156
|
+
console.log('\nUsing your main wallet with automated agents is RISKY:');
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(' - Agents can sign transactions without your approval');
|
|
159
|
+
console.log(' - A bug or misconfiguration could drain funds');
|
|
160
|
+
console.log(' - Private keys stored in env files can be exposed');
|
|
161
|
+
console.log('');
|
|
162
|
+
console.log('RECOMMENDED: Use a DEDICATED wallet with limited funds.');
|
|
163
|
+
console.log('Only transfer what you need for payments to this wallet.');
|
|
164
|
+
console.log('='.repeat(60) + '\n');
|
|
165
|
+
|
|
166
|
+
const confirm = await prompt(rl, 'I understand the risks (type "yes" to continue): ');
|
|
167
|
+
|
|
168
|
+
if (confirm.toLowerCase() !== 'yes') {
|
|
169
|
+
console.log('\nSetup cancelled. Run "x402 setup" again to generate a new wallet.');
|
|
170
|
+
rl.close();
|
|
171
|
+
return { success: false, error: 'User cancelled setup' };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log('\nEnter your private key (0x... format):\n');
|
|
175
|
+
|
|
176
|
+
// Loop until we get a valid key
|
|
177
|
+
let inputKey = '';
|
|
178
|
+
while (!isValidPrivateKey(inputKey)) {
|
|
179
|
+
inputKey = await prompt(rl, 'Private key: ');
|
|
180
|
+
|
|
181
|
+
if (!isValidPrivateKey(inputKey)) {
|
|
182
|
+
console.log('Invalid private key format. Must be 0x followed by 64 hex characters.');
|
|
183
|
+
console.log('Example: 0x1234...abcd (66 characters total)\n');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
privateKey = inputKey as `0x${string}`;
|
|
188
|
+
console.log('\nPrivate key accepted');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
rl.close();
|
|
192
|
+
|
|
193
|
+
// Derive account from private key
|
|
194
|
+
const account = privateKeyToAccount(privateKey);
|
|
195
|
+
console.log(`Wallet Address: ${account.address}\n`);
|
|
196
|
+
|
|
197
|
+
// Step 2: Create config
|
|
198
|
+
console.log('Step 2/2: Saving configuration...');
|
|
199
|
+
ensureConfigDir();
|
|
200
|
+
|
|
201
|
+
const envContent = `# x402 Agent Skill Configuration
|
|
202
|
+
# Location: ~/.x402/.env
|
|
203
|
+
# Created: ${new Date().toISOString()}
|
|
204
|
+
#
|
|
205
|
+
# WARNING: Keep this file secret! Never share your private key!
|
|
206
|
+
# This wallet can sign payment transactions automatically.
|
|
207
|
+
|
|
208
|
+
# Your wallet private key (required)
|
|
209
|
+
EVM_PRIVATE_KEY=${privateKey}
|
|
210
|
+
|
|
211
|
+
# Network: mainnet or testnet
|
|
212
|
+
# mainnet = Base (chain 8453) - real USDC
|
|
213
|
+
# testnet = Base Sepolia (chain 84532) - test tokens
|
|
214
|
+
X402_NETWORK=mainnet
|
|
215
|
+
|
|
216
|
+
# Maximum payment limit in USD (safety feature)
|
|
217
|
+
# Payments exceeding this will be rejected
|
|
218
|
+
X402_MAX_PAYMENT_USD=10
|
|
219
|
+
|
|
220
|
+
# Verbose logging (0 = off, 1 = on)
|
|
221
|
+
X402_VERBOSE=0
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
fs.writeFileSync(CONFIG_PATH, envContent, { mode: 0o600 });
|
|
225
|
+
console.log(`Config saved to: ${CONFIG_PATH}\n`);
|
|
226
|
+
|
|
227
|
+
// Final summary
|
|
228
|
+
console.log('='.repeat(50));
|
|
229
|
+
console.log(' SETUP COMPLETE!');
|
|
230
|
+
console.log('='.repeat(50) + '\n');
|
|
231
|
+
|
|
232
|
+
console.log('Your x402 Payment Wallet');
|
|
233
|
+
console.log('------------------------');
|
|
234
|
+
console.log(`Address: ${account.address}`);
|
|
235
|
+
console.log(`Network: Base Mainnet (chain 8453)`);
|
|
236
|
+
console.log(`Config: ${CONFIG_PATH}`);
|
|
237
|
+
|
|
238
|
+
if (isNewWallet) {
|
|
239
|
+
console.log('\n' + '!'.repeat(50));
|
|
240
|
+
console.log(' BACKUP YOUR PRIVATE KEY NOW!');
|
|
241
|
+
console.log('!'.repeat(50));
|
|
242
|
+
console.log('\nYour private key (save this securely):');
|
|
243
|
+
console.log(`\n ${privateKey}\n`);
|
|
244
|
+
console.log('Store this in a password manager or secure location.');
|
|
245
|
+
console.log('If lost, your funds CANNOT be recovered!');
|
|
246
|
+
console.log('!'.repeat(50));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log('\nNext Steps');
|
|
250
|
+
console.log('----------');
|
|
251
|
+
console.log('1. Fund your wallet with USDC on Base:');
|
|
252
|
+
console.log(` Address: ${account.address}`);
|
|
253
|
+
console.log('');
|
|
254
|
+
console.log('2. Add a small amount of ETH for gas (optional):');
|
|
255
|
+
console.log(' ~0.001 ETH is enough for many transactions');
|
|
256
|
+
console.log('');
|
|
257
|
+
console.log('3. Check your balance:');
|
|
258
|
+
console.log(' x402 balance');
|
|
259
|
+
console.log('');
|
|
260
|
+
console.log('4. Try paying for an x402 resource:');
|
|
261
|
+
console.log(' x402 pay https://some-x402-api.com/endpoint');
|
|
262
|
+
|
|
263
|
+
console.log('\nBackup Reminder');
|
|
264
|
+
console.log('---------------');
|
|
265
|
+
console.log(`Your config is stored at: ${CONFIG_PATH}`);
|
|
266
|
+
console.log('Back up this file or your private key securely!');
|
|
267
|
+
console.log('Consider using a password manager like 1Password or Bitwarden.');
|
|
268
|
+
|
|
269
|
+
console.log('\nSecurity Tips');
|
|
270
|
+
console.log('-------------');
|
|
271
|
+
console.log('- Only fund this wallet with what you need');
|
|
272
|
+
console.log('- Set X402_MAX_PAYMENT_USD to limit exposure');
|
|
273
|
+
console.log('- Never share your private key or config file');
|
|
274
|
+
console.log('- Use testnet first to verify everything works');
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
walletAddress: account.address,
|
|
279
|
+
privateKey: privateKey,
|
|
280
|
+
isNewWallet,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Export for programmatic use
|
|
285
|
+
export { main as setup };
|
|
286
|
+
|
|
287
|
+
// Run if executed directly
|
|
288
|
+
main().then(result => {
|
|
289
|
+
if (!result.success) {
|
|
290
|
+
console.error(`\nSetup failed: ${result.error}`);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
}).catch(error => {
|
|
294
|
+
console.error('Setup error:', error);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
});
|
package/scripts/core/config.ts
CHANGED
|
@@ -36,7 +36,6 @@ export interface X402Config {
|
|
|
36
36
|
|
|
37
37
|
// 21cash integration (optional)
|
|
38
38
|
x402LinksApiUrl?: string;
|
|
39
|
-
x402LinksApiKey?: string;
|
|
40
39
|
|
|
41
40
|
// Defaults
|
|
42
41
|
maxPaymentUsd: number;
|
|
@@ -73,7 +72,6 @@ export function getConfig(): X402Config {
|
|
|
73
72
|
chainId,
|
|
74
73
|
facilitatorUrl: getOptionalEnv('X402_FACILITATOR_URL', defaultFacilitator),
|
|
75
74
|
x402LinksApiUrl: process.env.X402_LINKS_API_URL,
|
|
76
|
-
x402LinksApiKey: process.env.X402_LINKS_API_KEY,
|
|
77
75
|
maxPaymentUsd: parseFloat(getOptionalEnv('X402_MAX_PAYMENT_USD', '10')),
|
|
78
76
|
slippageBps: parseInt(getOptionalEnv('X402_SLIPPAGE_BPS', '50'), 10),
|
|
79
77
|
verbose: getOptionalEnv('X402_VERBOSE', '0') === '1',
|