agentic-x402 0.2.33 → 0.3.1

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 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. `.env` file in current directory
209
- 3. `~/.x402/.env` (global config)
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
- name: agentic-x402
3
- description: Make x402 payments to access gated APIs and content. Fetch paid resources, check wallet balance, and create payment links. Use when encountering 402 Payment Required responses or when the user wants to pay for web resources with crypto.
2
+ name: x402
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
- metadata:
7
- author: monemetrics
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.2.33",
3
+ "version": "0.3.1",
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
+ });
@@ -54,7 +54,9 @@ Examples:
54
54
  }
55
55
 
56
56
  try {
57
- const headers: Record<string, string> = {};
57
+ const headers: Record<string, string> = {
58
+ 'Accept': 'application/json',
59
+ };
58
60
 
59
61
  if (body) {
60
62
  headers['Content-Type'] = 'application/json';
@@ -59,7 +59,10 @@ Examples:
59
59
  const initialResponse = await fetch(url, {
60
60
  method,
61
61
  body: body ? body : undefined,
62
- headers: body ? { 'Content-Type': 'application/json' } : undefined,
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: body ? { 'Content-Type': 'application/json' } : undefined,
149
+ headers: {
150
+ 'Accept': 'application/json',
151
+ ...(body ? { 'Content-Type': 'application/json' } : {}),
152
+ },
147
153
  });
148
154
 
149
155
  if (!response.ok) {