agentic-x402 0.2.33 → 0.3.0
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 +143 -2
- package/SKILL.md +123 -4
- package/bin/cli.ts +12 -0
- package/openclaw.plugin.json +73 -0
- package/package.json +6 -1
- package/scripts/commands/distribute.ts +201 -0
- package/scripts/commands/fetch-paid.ts +3 -1
- package/scripts/commands/pay.ts +8 -2
- package/scripts/commands/routers.ts +183 -0
- package/scripts/plugin/cli.ts +91 -0
- package/scripts/plugin/config-bridge.ts +31 -0
- package/scripts/plugin/index.ts +49 -0
- package/scripts/plugin/tools.ts +453 -0
- package/scripts/plugin/types.ts +107 -0
- package/scripts/plugin/watcher.ts +235 -0
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ This skill gives AI agents the ability to:
|
|
|
11
11
|
- **Fetch** content with automatic payment handling
|
|
12
12
|
- **Create** payment links to sell content (via 21.cash)
|
|
13
13
|
- **Manage** wallet balances (USDC + ETH for gas)
|
|
14
|
+
- **Monitor** payment routers for incoming payments (background watcher)
|
|
15
|
+
- **Distribute** accumulated funds from routers
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -78,6 +80,99 @@ x402 fetch https://api.example.com/data --json
|
|
|
78
80
|
|
|
79
81
|
# 6. Create a payment link (requires x402-links-server)
|
|
80
82
|
x402 create-link --name "My API" --price 1.00 --url https://api.example.com/premium
|
|
83
|
+
|
|
84
|
+
# 7. List your routers and check balances
|
|
85
|
+
x402 routers --with-balance
|
|
86
|
+
|
|
87
|
+
# 8. Withdraw accumulated funds
|
|
88
|
+
x402 distribute 0x1234...5678
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## OpenClaw Plugin
|
|
92
|
+
|
|
93
|
+
agentic-x402 works as an [OpenClaw](https://openclaw.dev) plugin, providing **8 agent tools** and a **background payment watcher** with zero CLI required.
|
|
94
|
+
|
|
95
|
+
### Install as Plugin
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
openclaw plugins install agentic-x402
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
All configuration is optional — the plugin picks up your existing `~/.x402/.env` automatically.
|
|
102
|
+
|
|
103
|
+
### Agent Tools
|
|
104
|
+
|
|
105
|
+
When running inside OpenClaw, the agent can call these tools directly:
|
|
106
|
+
|
|
107
|
+
| Tool | Description |
|
|
108
|
+
|------|-------------|
|
|
109
|
+
| `x402_balance` | Check wallet USDC + ETH balances |
|
|
110
|
+
| `x402_pay` | Pay for x402-gated resource (supports dry-run) |
|
|
111
|
+
| `x402_fetch` | Fetch URL with automatic payment |
|
|
112
|
+
| `x402_create_link` | Create payment link via 21.cash |
|
|
113
|
+
| `x402_link_info` | Get link details by router address |
|
|
114
|
+
| `x402_routers` | List beneficiary routers (optional balances) |
|
|
115
|
+
| `x402_distribute` | Distribute USDC from a router |
|
|
116
|
+
| `x402_watcher_status` | Get watcher state (tracked routers, payments detected) |
|
|
117
|
+
|
|
118
|
+
All tools return structured JSON. Parameters use camelCase (e.g., `routerAddress`, `maxPaymentUsd`, `withBalances`).
|
|
119
|
+
|
|
120
|
+
### Background Payment Watcher
|
|
121
|
+
|
|
122
|
+
The plugin includes a background service that monitors your payment routers for incoming USDC:
|
|
123
|
+
|
|
124
|
+
- Fetches your routers from the 21.cash API
|
|
125
|
+
- Polls USDC `balanceOf` for each router onchain (free view call, no gas)
|
|
126
|
+
- Detects balance increases and triggers a hook at `/hooks/agent` with payment details
|
|
127
|
+
- Configurable poll interval (default 30s)
|
|
128
|
+
- First poll seeds state without false positives on restart
|
|
129
|
+
|
|
130
|
+
When a payment is detected, the watcher POSTs to `http://127.0.0.1:<port>/hooks/agent`:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"name": "x402-payment",
|
|
135
|
+
"wakeMode": "now",
|
|
136
|
+
"data": {
|
|
137
|
+
"routerAddress": "0x...",
|
|
138
|
+
"routerName": "My Link",
|
|
139
|
+
"previousBalance": "10.00",
|
|
140
|
+
"newBalance": "15.00",
|
|
141
|
+
"increase": "5.00",
|
|
142
|
+
"detectedAt": "2025-01-15T12:00:00.000Z"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
This eliminates the need to expose your gateway externally or use a tunnel for webhook delivery.
|
|
148
|
+
|
|
149
|
+
### Plugin Configuration
|
|
150
|
+
|
|
151
|
+
All config is optional. Config priority:
|
|
152
|
+
|
|
153
|
+
1. Environment variables (highest)
|
|
154
|
+
2. Plugin config (from OpenClaw)
|
|
155
|
+
3. `~/.x402/.env` (dotenv)
|
|
156
|
+
4. Hardcoded defaults
|
|
157
|
+
|
|
158
|
+
Plugin config keys (set in OpenClaw):
|
|
159
|
+
|
|
160
|
+
| Key | Description | Default |
|
|
161
|
+
|-----|-------------|---------|
|
|
162
|
+
| `evmPrivateKey` | EVM private key (0x-prefixed) | from env |
|
|
163
|
+
| `network` | `mainnet` or `testnet` | `mainnet` |
|
|
164
|
+
| `maxPaymentUsd` | Max payment limit in USD | `10` |
|
|
165
|
+
| `x402LinksApiUrl` | 21.cash API URL | `https://21.cash` |
|
|
166
|
+
| `watcher.enabled` | Enable background payment watcher | `true` |
|
|
167
|
+
| `watcher.pollIntervalMs` | Poll interval in ms | `30000` |
|
|
168
|
+
| `watcher.notifyOnPayment` | Send hook on payment detection | `true` |
|
|
169
|
+
|
|
170
|
+
### Plugin CLI Commands
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
openclaw x402 watch # Start watcher in foreground (debugging)
|
|
174
|
+
openclaw x402 watch --interval 10000 # Custom poll interval
|
|
175
|
+
openclaw x402 status # Show watcher state and payment count
|
|
81
176
|
```
|
|
82
177
|
|
|
83
178
|
## Commands
|
|
@@ -200,13 +295,52 @@ x402 link-info https://21.cash/pay/0x1234...5678
|
|
|
200
295
|
| `--json` | Output as JSON | — |
|
|
201
296
|
| `-h, --help` | Show help | — |
|
|
202
297
|
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### routers — List Your Payment Routers
|
|
301
|
+
|
|
302
|
+
See all payment routers where your wallet is a beneficiary. Optionally fetch on-chain USDC balances.
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
x402 routers
|
|
306
|
+
x402 routers --with-balance
|
|
307
|
+
x402 routers --json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
| Flag | Description | Default |
|
|
311
|
+
|------|-------------|---------|
|
|
312
|
+
| `--with-balance` | Fetch on-chain USDC balance for each router | — |
|
|
313
|
+
| `--json` | Output as JSON | — |
|
|
314
|
+
| `-h, --help` | Show help | — |
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### distribute — Withdraw Funds from a Router
|
|
319
|
+
|
|
320
|
+
Distribute accumulated USDC from a PaymentRouter contract. Calls the router's `distribute()` function on-chain.
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
x402 distribute 0x1234...5678
|
|
324
|
+
x402 distribute 0x1234...5678 --amount 5.00
|
|
325
|
+
x402 distribute 0x1234...5678 --force --json
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
| Flag | Description | Default |
|
|
329
|
+
|------|-------------|---------|
|
|
330
|
+
| `<router-address>` | PaymentRouter contract address (positional) | **required** |
|
|
331
|
+
| `--amount` | Specific USDC amount to distribute (defaults to full balance) | full balance |
|
|
332
|
+
| `--force` | Skip gas balance warning | — |
|
|
333
|
+
| `--json` | Output as JSON | — |
|
|
334
|
+
| `-h, --help` | Show help | — |
|
|
335
|
+
|
|
203
336
|
## Configuration
|
|
204
337
|
|
|
205
338
|
Config is loaded from these locations (in order of priority):
|
|
206
339
|
|
|
207
340
|
1. Environment variables
|
|
208
|
-
2.
|
|
209
|
-
3.
|
|
341
|
+
2. Plugin config (when running as OpenClaw plugin)
|
|
342
|
+
3. `.env` file in current directory
|
|
343
|
+
4. `~/.x402/.env` (global config)
|
|
210
344
|
|
|
211
345
|
### Environment Variables
|
|
212
346
|
|
|
@@ -248,6 +382,8 @@ Config is loaded from these locations (in order of priority):
|
|
|
248
382
|
| **Payments** | `fetch <url>` | Fetch with auto-payment (pipe-friendly `--json`/`--raw`) |
|
|
249
383
|
| **Links** | `create-link` | Create a payment link to sell content (21.cash) |
|
|
250
384
|
| **Links** | `link-info <addr>` | Get info about a payment link |
|
|
385
|
+
| **Routers** | `routers` | List routers where your wallet is a beneficiary |
|
|
386
|
+
| **Routers** | `distribute <addr>` | Distribute USDC from a PaymentRouter |
|
|
251
387
|
|
|
252
388
|
## For Agents
|
|
253
389
|
|
|
@@ -262,8 +398,13 @@ x402 fetch https://api.example.com/data --json
|
|
|
262
398
|
|
|
263
399
|
# Raw output for further processing
|
|
264
400
|
x402 fetch https://api.example.com/data --raw
|
|
401
|
+
|
|
402
|
+
# Router balances as JSON
|
|
403
|
+
x402 routers --with-balance --json
|
|
265
404
|
```
|
|
266
405
|
|
|
406
|
+
When installed as an OpenClaw plugin, agents get native tool access (`x402_balance`, `x402_pay`, etc.) without needing shell commands at all.
|
|
407
|
+
|
|
267
408
|
## Backup Your Private Key
|
|
268
409
|
|
|
269
410
|
Your private key is stored in `~/.x402/.env`. **If lost, your funds cannot be recovered.**
|
package/SKILL.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentic-x402
|
|
3
|
-
description: Make x402 payments to access gated APIs and content. Fetch paid resources, check wallet balance,
|
|
3
|
+
description: Make x402 payments to access gated APIs and content. Fetch paid resources, check wallet balance, create payment links, monitor routers for incoming payments, and distribute funds. Use when encountering 402 Payment Required responses or when the user wants to pay for web resources with crypto. Also available as an OpenClaw plugin with background payment watching and 8 agent tools.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Node.js 20+, network access to x402 facilitators and EVM chains
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
version: "0.2.1"
|
|
6
|
+
homepage: https://www.npmjs.com/package/agentic-x402
|
|
7
|
+
metadata: {"author": "monemetrics", "version": "0.3.0", "openclaw": {"requires": {"bins": ["x402"], "env": ["EVM_PRIVATE_KEY"]}, "primaryEnv": "EVM_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "agentic-x402", "bins": ["x402"], "label": "Install agentic-x402 (npm)"}], "plugin": true}}
|
|
9
8
|
allowed-tools: Bash(x402:*) Bash(npm:*) Read
|
|
10
9
|
---
|
|
11
10
|
|
|
@@ -23,6 +22,90 @@ Pay for x402-gated APIs and content using USDC on Base. This skill enables agent
|
|
|
23
22
|
| `x402 fetch <url>` | Fetch with auto-payment |
|
|
24
23
|
| `x402 create-link` | Create payment link (seller) |
|
|
25
24
|
| `x402 link-info <addr>` | Get payment link details |
|
|
25
|
+
| `x402 routers` | List routers where your wallet is a beneficiary |
|
|
26
|
+
| `x402 distribute <addr>` | Distribute USDC from a PaymentRouter |
|
|
27
|
+
|
|
28
|
+
## OpenClaw Plugin
|
|
29
|
+
|
|
30
|
+
When installed as an OpenClaw plugin, agentic-x402 provides **8 agent tools** and a **background payment watcher** — no CLI required.
|
|
31
|
+
|
|
32
|
+
### Install as Plugin
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
openclaw plugins install agentic-x402
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Agent Tools
|
|
39
|
+
|
|
40
|
+
When running inside OpenClaw, the agent can call these tools directly (no shell commands needed):
|
|
41
|
+
|
|
42
|
+
| Tool | Description |
|
|
43
|
+
|------|-------------|
|
|
44
|
+
| `x402_balance` | Check wallet USDC + ETH balances |
|
|
45
|
+
| `x402_pay` | Pay for x402-gated resource (supports dry-run) |
|
|
46
|
+
| `x402_fetch` | Fetch URL with automatic payment |
|
|
47
|
+
| `x402_create_link` | Create payment link via 21.cash |
|
|
48
|
+
| `x402_link_info` | Get link details by router address |
|
|
49
|
+
| `x402_routers` | List beneficiary routers (optional balances) |
|
|
50
|
+
| `x402_distribute` | Distribute USDC from a router |
|
|
51
|
+
| `x402_watcher_status` | Get watcher state (tracked routers, payments detected) |
|
|
52
|
+
|
|
53
|
+
All tools return structured JSON. Parameters use camelCase (e.g., `routerAddress`, `maxPaymentUsd`, `withBalances`).
|
|
54
|
+
|
|
55
|
+
### Background Payment Watcher
|
|
56
|
+
|
|
57
|
+
The plugin includes a background service that monitors your payment routers for incoming USDC:
|
|
58
|
+
|
|
59
|
+
- Fetches your routers from the 21.cash API
|
|
60
|
+
- Polls USDC `balanceOf` for each router onchain (free view call, no gas)
|
|
61
|
+
- Detects balance increases and triggers a hook at `/hooks/agent` with payment details
|
|
62
|
+
- Configurable poll interval (default 30s)
|
|
63
|
+
- First poll seeds state without triggering notifications (no false positives on restart)
|
|
64
|
+
|
|
65
|
+
When a payment is detected, the watcher sends a hook with:
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"name": "x402-payment",
|
|
69
|
+
"wakeMode": "now",
|
|
70
|
+
"data": {
|
|
71
|
+
"routerAddress": "0x...",
|
|
72
|
+
"routerName": "My Link",
|
|
73
|
+
"previousBalance": "10.00",
|
|
74
|
+
"newBalance": "15.00",
|
|
75
|
+
"increase": "5.00",
|
|
76
|
+
"detectedAt": "2025-01-15T12:00:00.000Z"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Plugin Configuration
|
|
82
|
+
|
|
83
|
+
All config is optional — zero-config if `~/.x402/.env` exists. Config priority:
|
|
84
|
+
|
|
85
|
+
1. Environment variables (highest)
|
|
86
|
+
2. Plugin config (from OpenClaw)
|
|
87
|
+
3. `~/.x402/.env` (dotenv)
|
|
88
|
+
4. Hardcoded defaults
|
|
89
|
+
|
|
90
|
+
Plugin config keys (set in OpenClaw):
|
|
91
|
+
|
|
92
|
+
| Key | Description | Default |
|
|
93
|
+
|-----|-------------|---------|
|
|
94
|
+
| `evmPrivateKey` | EVM private key (0x-prefixed) | from env |
|
|
95
|
+
| `network` | `mainnet` or `testnet` | `mainnet` |
|
|
96
|
+
| `maxPaymentUsd` | Max payment limit in USD | `10` |
|
|
97
|
+
| `x402LinksApiUrl` | 21.cash API URL | `https://21.cash` |
|
|
98
|
+
| `watcher.enabled` | Enable background payment watcher | `true` |
|
|
99
|
+
| `watcher.pollIntervalMs` | Poll interval in ms | `30000` |
|
|
100
|
+
| `watcher.notifyOnPayment` | Send hook on payment detection | `true` |
|
|
101
|
+
|
|
102
|
+
### Plugin CLI Commands
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
openclaw x402 watch # Start watcher in foreground (debugging)
|
|
106
|
+
openclaw x402 watch --interval 10000 # Custom poll interval
|
|
107
|
+
openclaw x402 status # Show watcher state, tracked routers, payment count
|
|
108
|
+
```
|
|
26
109
|
|
|
27
110
|
## Installation
|
|
28
111
|
|
|
@@ -156,6 +239,42 @@ x402 link-info 0x1234...5678
|
|
|
156
239
|
x402 link-info https://21.cash/pay/0x1234...5678
|
|
157
240
|
```
|
|
158
241
|
|
|
242
|
+
## Managing Routers
|
|
243
|
+
|
|
244
|
+
### List your routers
|
|
245
|
+
|
|
246
|
+
See all payment routers where your wallet is a beneficiary:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
x402 routers
|
|
250
|
+
x402 routers --with-balance # Include on-chain USDC balances
|
|
251
|
+
x402 routers --json
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| Flag | Description | Default |
|
|
255
|
+
|------|-------------|---------|
|
|
256
|
+
| `--with-balance` | Fetch on-chain USDC balance for each router | — |
|
|
257
|
+
| `--json` | Output as JSON | — |
|
|
258
|
+
| `-h, --help` | Show help | — |
|
|
259
|
+
|
|
260
|
+
### Distribute funds
|
|
261
|
+
|
|
262
|
+
Withdraw accumulated USDC from a PaymentRouter contract:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
x402 distribute 0x1234...5678
|
|
266
|
+
x402 distribute 0x1234...5678 --amount 5.00
|
|
267
|
+
x402 distribute 0x1234...5678 --force --json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
| Flag | Description | Default |
|
|
271
|
+
|------|-------------|---------|
|
|
272
|
+
| `<router-address>` | PaymentRouter contract address (positional) | **required** |
|
|
273
|
+
| `--amount` | Specific USDC amount to distribute (defaults to full balance) | full balance |
|
|
274
|
+
| `--force` | Skip gas balance warning | — |
|
|
275
|
+
| `--json` | Output as JSON | — |
|
|
276
|
+
| `-h, --help` | Show help | — |
|
|
277
|
+
|
|
159
278
|
## Command Reference
|
|
160
279
|
|
|
161
280
|
### x402 balance
|
package/bin/cli.ts
CHANGED
|
@@ -52,6 +52,16 @@ const commands: Record<string, Command> = {
|
|
|
52
52
|
description: 'Get info about a payment link',
|
|
53
53
|
category: 'links',
|
|
54
54
|
},
|
|
55
|
+
routers: {
|
|
56
|
+
script: 'commands/routers.ts',
|
|
57
|
+
description: 'List routers where your wallet is a beneficiary',
|
|
58
|
+
category: 'links',
|
|
59
|
+
},
|
|
60
|
+
distribute: {
|
|
61
|
+
script: 'commands/distribute.ts',
|
|
62
|
+
description: 'Distribute USDC from a PaymentRouter',
|
|
63
|
+
category: 'links',
|
|
64
|
+
},
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
function showHelp() {
|
|
@@ -73,6 +83,8 @@ Payment Commands:
|
|
|
73
83
|
Link Commands (21cash integration):
|
|
74
84
|
create-link Create a payment link to sell content
|
|
75
85
|
link-info <addr> Get info about a payment link
|
|
86
|
+
routers List routers where your wallet is a beneficiary
|
|
87
|
+
distribute <addr> Distribute USDC from a PaymentRouter
|
|
76
88
|
|
|
77
89
|
Options:
|
|
78
90
|
-h, --help Show this help
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agentic-x402",
|
|
3
|
+
"name": "x402 Payments",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"description": "Agent skill for x402 payments — pay for and sell gated content with background payment watching",
|
|
6
|
+
"skills": ["./skills/x402"],
|
|
7
|
+
"configSchema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"evmPrivateKey": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "EVM private key (0x-prefixed). Falls back to EVM_PRIVATE_KEY env var or ~/.x402/.env"
|
|
13
|
+
},
|
|
14
|
+
"network": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"enum": ["mainnet", "testnet"],
|
|
17
|
+
"default": "mainnet",
|
|
18
|
+
"description": "Network to use: mainnet (Base 8453) or testnet (Base Sepolia 84532)"
|
|
19
|
+
},
|
|
20
|
+
"maxPaymentUsd": {
|
|
21
|
+
"type": "number",
|
|
22
|
+
"default": 10,
|
|
23
|
+
"description": "Maximum payment amount in USD before rejection"
|
|
24
|
+
},
|
|
25
|
+
"x402LinksApiUrl": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"default": "https://21.cash",
|
|
28
|
+
"description": "Base URL of the x402 links API (21.cash)"
|
|
29
|
+
},
|
|
30
|
+
"watcher": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"enabled": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"default": true,
|
|
36
|
+
"description": "Enable background payment watcher"
|
|
37
|
+
},
|
|
38
|
+
"pollIntervalMs": {
|
|
39
|
+
"type": "number",
|
|
40
|
+
"default": 30000,
|
|
41
|
+
"description": "Poll interval in milliseconds for checking router balances"
|
|
42
|
+
},
|
|
43
|
+
"notifyOnPayment": {
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"default": true,
|
|
46
|
+
"description": "Send hook notification when a payment is detected"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"additionalProperties": false
|
|
53
|
+
},
|
|
54
|
+
"uiHints": {
|
|
55
|
+
"evmPrivateKey": {
|
|
56
|
+
"label": "EVM Private Key",
|
|
57
|
+
"sensitive": true,
|
|
58
|
+
"placeholder": "0x..."
|
|
59
|
+
},
|
|
60
|
+
"network": {
|
|
61
|
+
"label": "Network",
|
|
62
|
+
"placeholder": "mainnet"
|
|
63
|
+
},
|
|
64
|
+
"maxPaymentUsd": {
|
|
65
|
+
"label": "Max Payment (USD)",
|
|
66
|
+
"placeholder": "10"
|
|
67
|
+
},
|
|
68
|
+
"x402LinksApiUrl": {
|
|
69
|
+
"label": "21.cash API URL",
|
|
70
|
+
"placeholder": "https://21.cash"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-x402",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agent skill for x402 payments - pay for and sell gated content",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"x402": "./bin/x402.js"
|
|
8
8
|
},
|
|
9
|
+
"openclaw": {
|
|
10
|
+
"extensions": ["./scripts/plugin/index.ts"]
|
|
11
|
+
},
|
|
9
12
|
"files": [
|
|
10
13
|
"bin/",
|
|
11
14
|
"scripts/",
|
|
15
|
+
"skills/",
|
|
12
16
|
"config/example.env",
|
|
17
|
+
"openclaw.plugin.json",
|
|
13
18
|
"README.md",
|
|
14
19
|
"SKILL.md"
|
|
15
20
|
],
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// Distribute accumulated USDC from a PaymentRouter
|
|
3
|
+
|
|
4
|
+
import { getClient, getWalletAddress, getEthBalance } from '../core/client.js';
|
|
5
|
+
import { parseArgs, formatCrypto, truncateAddress, formatError } from '../core/utils.js';
|
|
6
|
+
import { getConfig, getUsdcAddress } from '../core/config.js';
|
|
7
|
+
import { formatUnits, parseUnits } from 'viem';
|
|
8
|
+
|
|
9
|
+
const ERC20_BALANCE_ABI = [{
|
|
10
|
+
name: 'balanceOf',
|
|
11
|
+
type: 'function',
|
|
12
|
+
stateMutability: 'view',
|
|
13
|
+
inputs: [{ name: 'account', type: 'address' }],
|
|
14
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
15
|
+
}] as const;
|
|
16
|
+
|
|
17
|
+
const PAYMENT_ROUTER_ABI = [{
|
|
18
|
+
name: 'distribute',
|
|
19
|
+
type: 'function',
|
|
20
|
+
stateMutability: 'nonpayable',
|
|
21
|
+
inputs: [
|
|
22
|
+
{ name: 'token', type: 'address' },
|
|
23
|
+
{ name: 'amount', type: 'uint256' },
|
|
24
|
+
],
|
|
25
|
+
outputs: [],
|
|
26
|
+
}] as const;
|
|
27
|
+
|
|
28
|
+
const MIN_ETH_FOR_GAS = 0.0001; // Minimum ETH required for gas
|
|
29
|
+
|
|
30
|
+
async function main() {
|
|
31
|
+
const { positional, flags } = parseArgs(process.argv.slice(2));
|
|
32
|
+
|
|
33
|
+
if (positional.length === 0 || flags.help || flags.h) {
|
|
34
|
+
console.log(`
|
|
35
|
+
x402 distribute - Distribute USDC from a PaymentRouter
|
|
36
|
+
|
|
37
|
+
Usage: x402 distribute <router-address> [options]
|
|
38
|
+
|
|
39
|
+
Arguments:
|
|
40
|
+
router-address The PaymentRouter contract address
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--amount <amt> Distribute a specific USDC amount (e.g., "10.00"). Defaults to full balance.
|
|
44
|
+
--force Skip gas balance warning
|
|
45
|
+
--json Output as JSON
|
|
46
|
+
-h, --help Show this help
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
x402 distribute 0x1234...5678
|
|
50
|
+
x402 distribute 0x1234...5678 --amount 5.00
|
|
51
|
+
x402 distribute 0x1234...5678 --force --json
|
|
52
|
+
`);
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const config = getConfig();
|
|
57
|
+
const jsonOutput = flags.json === true;
|
|
58
|
+
const force = flags.force === true;
|
|
59
|
+
const specifiedAmount = flags.amount as string | undefined;
|
|
60
|
+
|
|
61
|
+
// Validate router address
|
|
62
|
+
const routerAddress = positional[0];
|
|
63
|
+
if (!routerAddress.startsWith('0x') || routerAddress.length !== 42) {
|
|
64
|
+
const msg = 'Invalid router address. Must be a 0x-prefixed 40-character hex string.';
|
|
65
|
+
if (jsonOutput) {
|
|
66
|
+
console.log(JSON.stringify({ success: false, error: msg }));
|
|
67
|
+
} else {
|
|
68
|
+
console.error(`Error: ${msg}`);
|
|
69
|
+
}
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const client = getClient();
|
|
75
|
+
const walletAddress = getWalletAddress();
|
|
76
|
+
const usdcAddress = getUsdcAddress(config.chainId);
|
|
77
|
+
const routerAddr = routerAddress as `0x${string}`;
|
|
78
|
+
|
|
79
|
+
// Read router's USDC balance
|
|
80
|
+
const routerBalance = await client.publicClient.readContract({
|
|
81
|
+
address: usdcAddress,
|
|
82
|
+
abi: ERC20_BALANCE_ABI,
|
|
83
|
+
functionName: 'balanceOf',
|
|
84
|
+
args: [routerAddr],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const routerBalanceFormatted = formatUnits(routerBalance, 6);
|
|
88
|
+
|
|
89
|
+
if (routerBalance === 0n) {
|
|
90
|
+
if (jsonOutput) {
|
|
91
|
+
console.log(JSON.stringify({ success: false, error: 'Router has no USDC balance to distribute' }));
|
|
92
|
+
} else {
|
|
93
|
+
console.log('Router has no USDC balance to distribute.');
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Determine amount to distribute
|
|
99
|
+
let distributeAmount: bigint;
|
|
100
|
+
if (specifiedAmount) {
|
|
101
|
+
distributeAmount = parseUnits(specifiedAmount, 6);
|
|
102
|
+
if (distributeAmount > routerBalance) {
|
|
103
|
+
const msg = `Requested amount (${specifiedAmount} USDC) exceeds router balance (${routerBalanceFormatted} USDC)`;
|
|
104
|
+
if (jsonOutput) {
|
|
105
|
+
console.log(JSON.stringify({ success: false, error: msg }));
|
|
106
|
+
} else {
|
|
107
|
+
console.error(`Error: ${msg}`);
|
|
108
|
+
}
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
distributeAmount = routerBalance;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check ETH for gas
|
|
116
|
+
if (!force) {
|
|
117
|
+
const eth = await getEthBalance();
|
|
118
|
+
const ethNum = parseFloat(eth.formatted);
|
|
119
|
+
if (ethNum < MIN_ETH_FOR_GAS) {
|
|
120
|
+
const msg = `Low ETH balance (${formatCrypto(eth.formatted, 'ETH', 6)}). Need at least ${MIN_ETH_FOR_GAS} ETH for gas. Use --force to skip this check.`;
|
|
121
|
+
if (jsonOutput) {
|
|
122
|
+
console.log(JSON.stringify({ success: false, error: msg }));
|
|
123
|
+
} else {
|
|
124
|
+
console.error(`Warning: ${msg}`);
|
|
125
|
+
}
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const distributeFormatted = formatUnits(distributeAmount, 6);
|
|
131
|
+
|
|
132
|
+
if (!jsonOutput) {
|
|
133
|
+
console.log('Distributing USDC from PaymentRouter');
|
|
134
|
+
console.log('====================================');
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(`Router: ${routerAddress}`);
|
|
137
|
+
console.log(`Balance: ${formatCrypto(routerBalanceFormatted, 'USDC', 2)}`);
|
|
138
|
+
console.log(`Amount: ${formatCrypto(distributeFormatted, 'USDC', 2)}`);
|
|
139
|
+
console.log(`Caller: ${truncateAddress(walletAddress)}`);
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log('Submitting transaction...');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Call distribute(token, amount)
|
|
145
|
+
const txHash = await client.walletClient.writeContract({
|
|
146
|
+
address: routerAddr,
|
|
147
|
+
abi: PAYMENT_ROUTER_ABI,
|
|
148
|
+
functionName: 'distribute',
|
|
149
|
+
args: [usdcAddress, distributeAmount],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!jsonOutput) {
|
|
153
|
+
console.log(`TX submitted: ${txHash}`);
|
|
154
|
+
console.log('Waiting for confirmation...');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Wait for receipt
|
|
158
|
+
const receipt = await client.publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
159
|
+
|
|
160
|
+
const success = receipt.status === 'success';
|
|
161
|
+
|
|
162
|
+
if (jsonOutput) {
|
|
163
|
+
console.log(JSON.stringify({
|
|
164
|
+
success,
|
|
165
|
+
routerAddress,
|
|
166
|
+
amount: distributeFormatted,
|
|
167
|
+
amountRaw: distributeAmount.toString(),
|
|
168
|
+
transactionHash: txHash,
|
|
169
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
170
|
+
status: receipt.status,
|
|
171
|
+
}));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (success) {
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log('Distribution successful!');
|
|
178
|
+
console.log(`TX: ${txHash}`);
|
|
179
|
+
console.log(`Block: ${receipt.blockNumber}`);
|
|
180
|
+
console.log(`Distributed: ${formatCrypto(distributeFormatted, 'USDC', 2)}`);
|
|
181
|
+
} else {
|
|
182
|
+
console.error('');
|
|
183
|
+
console.error('Transaction reverted.');
|
|
184
|
+
console.error(`TX: ${txHash}`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (jsonOutput) {
|
|
190
|
+
console.log(JSON.stringify({ success: false, error: formatError(error) }));
|
|
191
|
+
} else {
|
|
192
|
+
console.error('Error:', formatError(error));
|
|
193
|
+
}
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
main().catch(err => {
|
|
199
|
+
console.error('Error:', err.message);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
package/scripts/commands/pay.ts
CHANGED
|
@@ -59,7 +59,10 @@ Examples:
|
|
|
59
59
|
const initialResponse = await fetch(url, {
|
|
60
60
|
method,
|
|
61
61
|
body: body ? body : undefined,
|
|
62
|
-
headers:
|
|
62
|
+
headers: {
|
|
63
|
+
'Accept': 'application/json',
|
|
64
|
+
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
65
|
+
},
|
|
63
66
|
});
|
|
64
67
|
|
|
65
68
|
if (initialResponse.status !== 402) {
|
|
@@ -143,7 +146,10 @@ Examples:
|
|
|
143
146
|
const response = await client.fetchWithPayment(url, {
|
|
144
147
|
method,
|
|
145
148
|
body: body ? body : undefined,
|
|
146
|
-
headers:
|
|
149
|
+
headers: {
|
|
150
|
+
'Accept': 'application/json',
|
|
151
|
+
...(body ? { 'Content-Type': 'application/json' } : {}),
|
|
152
|
+
},
|
|
147
153
|
});
|
|
148
154
|
|
|
149
155
|
if (!response.ok) {
|