x402-proxy 0.4.1 → 0.5.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/CHANGELOG.md ADDED
@@ -0,0 +1,153 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.0] - 2026-03-16
11
+
12
+ ### Changed
13
+ - MCP proxy graduated from alpha to stable - removed alpha warning and label
14
+ - MCP proxy uses low-level `Server` class instead of `McpServer` to proxy raw JSON schemas verbatim without Zod conversion
15
+ - MCP proxy now forwards blob resource contents (previously only text was proxied)
16
+ - MCP content type widened to pass through all MCP content types (image, audio, resource_link) not just text
17
+
18
+ ### Added
19
+ - MCP proxy forwards `notifications/tools/list_changed` and `notifications/resources/list_changed` from remote servers so local clients stay in sync with dynamic tool/resource updates
20
+
21
+ ## [0.4.2] - 2026-03-16
22
+
23
+ ### Fixed
24
+ - Twitter endpoint URLs updated from `/user/` to `/users/` to match spec change
25
+
26
+ ### Added
27
+ - CHANGELOG.md included in npm package metadata
28
+ - `skills/` directory with SKILL.md and library reference included in npm package
29
+
30
+ ## [0.4.1] - 2026-03-13
31
+
32
+ ### Changed
33
+ - Extracted `createNetworkFilter`, `createNetworkPreference`, `networkToCaipPrefix` as exported functions for testability
34
+ - Publish workflow auto-creates GitHub releases from CHANGELOG.md (no more manual `gh release create`)
35
+ - Release docs updated in CLAUDE.md with `act` dry-run instructions for CI workflows
36
+
37
+ ### Added
38
+ - Tests for network filter, network preference selector, Solana address derivation, and wallet resolution (14 new tests)
39
+
40
+ ## [0.4.0] - 2026-03-13
41
+
42
+ ### Added
43
+ - `--network` flag for `fetch` and `mcp` commands - hard filter that requires a specific network (base, solana, or CAIP-2 ID), fails with clear error if unavailable
44
+ - Human-readable network names in payment output ("Base", "Solana" instead of "eip155:8453")
45
+ - `displayNetwork()` exported from library for mapping CAIP-2 IDs to display names
46
+
47
+ ### Fixed
48
+ - Wildcard scheme registration (`eip155:*`, `solana:*`) via SDK helpers - payment signing now works for any EVM chain a server requests, not just Base
49
+ - Solana address derivation for `--solana-key` flag and `X402_PROXY_WALLET_SOLANA_KEY` env var - balance detection, wallet display, and history recording were broken without it
50
+ - MCP command now auto-detects preferred network based on USDC balance (same fix previously applied to `fetch`)
51
+ - MCP payment history records now include `amount`, `to`, and correct `network` (removed fragile type cast)
52
+ - Removed debug prefix stripping from payment amounts in handler
53
+ - USDC balance display now shows 4 decimal places (was 2)
54
+
55
+ ## [0.3.2] - 2026-03-13
56
+
57
+ ### Added
58
+ - Auto-setup: running `npx x402-proxy <url>` without a wallet launches the setup wizard, then continues with the request
59
+ - 402 error handling parses the endpoint's `PAYMENT-REQUIRED` header to show actual accepted networks and costs
60
+ - CI pipeline (GitHub Actions: check, build, test on push/PR)
61
+ - Automated npm publishing with OIDC provenance on tag push
62
+ - Tests for wallet derivation and transaction history (25 tests)
63
+ - Funding hint in `wallet` when USDC balance is zero
64
+
65
+ ### Changed
66
+ - Version injected at build time from package.json (no more stale hardcoded strings)
67
+ - `wallet fund` command removed (addresses and hint shown in `wallet` directly)
68
+ - All command references use `$ npx x402-proxy` format
69
+
70
+ ## [0.3.1] - 2026-03-12
71
+
72
+ ### Added
73
+ - `wallet export-key mnemonic` - export BIP-39 mnemonic to stdout (pipe-safe, with confirmation prompt)
74
+
75
+ ## [0.3.0] - 2026-03-12
76
+
77
+ ### Added
78
+ - Live wallet balances in `status` and `wallet` commands (USDC + ETH/SOL via RPC)
79
+ - Recent transactions shown in `status` (last 5) and `wallet` (last 10)
80
+ - Network preference (`defaultNetwork` config) - prefers configured chain when endpoint accepts multiple
81
+ - Spend limits (`spendLimitDaily`, `spendLimitPerTx` config) enforced via x402Client policy
82
+ - `--verbose` flag on `wallet` command to show transaction IDs
83
+ - Confirmation prompt on `wallet export-key` when stdout is a terminal
84
+ - Help text with usage examples for `fetch --help` and `mcp --help`
85
+ - Full command reference, "try:" suggestion, and repo link in no-args output
86
+ - Network indicator on transaction lines (base/sol)
87
+ - Setup outro with "try your first request" using real endpoint
88
+
89
+ ### Changed
90
+ - "EVM" label replaced with "Base" throughout (wallet, status, setup)
91
+ - Config directory displayed as `~/.config/...` instead of absolute path
92
+ - Error messages prefixed with `✗`, success with `✓` for accessibility
93
+ - No-args output redesigned: identity header, wallet summary, commands, try suggestion
94
+ - Example URLs use path-based format (`/user/cascade_fyi`) to avoid zsh glob issues
95
+ - `@solana/kit`, `ethers`, `viem` moved from peerDependencies to dependencies (fixes npx ERESOLVE warnings)
96
+ - Wallet subcommand hints shown at bottom of `wallet` output
97
+
98
+ ### Fixed
99
+ - RPC balance failures show "(network error)" instead of silent omission
100
+
101
+ ## [0.2.1] - 2026-03-12
102
+
103
+ ### Changed
104
+ - Package description and keywords aligned with "curl for x402 paid APIs" positioning
105
+ - README rewritten: real endpoint in Quick Start, MCP Proxy elevated above HTTP Requests
106
+ - Stricli commands use explicit generic types (fixes TS 5.9 type inference)
107
+ - `displayStatus()` extracted as callable function from status command
108
+ - `PaymentRequirements.amount` used instead of removed `maxAmountRequired`
109
+
110
+ ### Fixed
111
+ - All `tsc --noEmit` type errors resolved (previously passing only at build time)
112
+ - Biome schema version bumped to match CLI 2.4.6
113
+
114
+ ## [0.2.0] - 2026-03-12
115
+
116
+ ### Added
117
+ - CLI binary accessible via `npx x402-proxy`
118
+ - `fetch` command (default) - curl-like HTTP client with automatic x402 payment
119
+ - `mcp` command - MCP stdio proxy with auto-payment for AI agents
120
+ - `setup` command - interactive onboarding wizard with @clack/prompts
121
+ - `status` command - config, wallet, and spend summary
122
+ - `wallet` subcommand with `info`, `history`, `fund`, `export-key`
123
+ - BIP-39 mnemonic wallet derivation (Solana SLIP-10 + EVM BIP-32 from single seed)
124
+ - XDG-compliant config storage (`~/.config/x402-proxy/`)
125
+ - Wallet resolution cascade: flags > env vars > mnemonic env > wallet.json
126
+ - JSONL payment history with auto-truncation
127
+ - Env var overrides with `X402_PROXY_WALLET_*` prefix
128
+
129
+ ### Changed
130
+ - Package now ships both CLI binary and library
131
+ - Dual tsdown build entries (bin/cli with shebang + index with dts)
132
+
133
+ ## [0.1.0] - 2026-03-10
134
+
135
+ ### Added
136
+ - Initial release (library only)
137
+ - `createX402ProxyHandler` - wraps fetch with automatic x402 payment
138
+ - `extractTxSignature` - extracts TX signature from payment response headers
139
+ - `loadSvmWallet` / `loadEvmWallet` - wallet loading utilities
140
+ - `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
141
+ - Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
142
+
143
+ [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.0...HEAD
144
+ [0.5.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.2...v0.5.0
145
+ [0.4.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.1...v0.4.2
146
+ [0.4.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.4.0...v0.4.1
147
+ [0.4.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.2...v0.4.0
148
+ [0.3.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.1...v0.3.2
149
+ [0.3.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.3.0...v0.3.1
150
+ [0.3.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.2.1...v0.3.0
151
+ [0.2.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.2.0...v0.2.1
152
+ [0.2.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.1.0...v0.2.0
153
+ [0.1.0]: https://github.com/cascade-protocol/x402-proxy/releases/tag/v0.1.0
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx x402-proxy https://twitter.surf.cascade.fyi/user/cascade_fyi
8
+ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
9
9
  ```
10
10
 
11
11
  That's it. The endpoint returns 402, x402-proxy pays and streams the response.
@@ -38,7 +38,7 @@ Works like curl. Response body streams to stdout, payment info goes to stderr.
38
38
 
39
39
  ```bash
40
40
  # GET request
41
- $ npx x402-proxy https://twitter.surf.cascade.fyi/user/cascade_fyi
41
+ $ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
42
42
 
43
43
  # POST with body and headers
44
44
  $ npx x402-proxy --method POST \
package/dist/bin/cli.js CHANGED
@@ -330,7 +330,7 @@ Examples:
330
330
  //#region src/commands/mcp.ts
331
331
  const mcpCommand = buildCommand({
332
332
  docs: {
333
- brief: "Start MCP stdio proxy with x402 payment (alpha)",
333
+ brief: "Start MCP stdio proxy with x402 payment",
334
334
  fullDescription: `Start an MCP stdio proxy with automatic x402 payment for AI agents.
335
335
 
336
336
  Add to your MCP client config (Claude, Cursor, etc.):
@@ -376,7 +376,6 @@ Add to your MCP client config (Claude, Cursor, etc.):
376
376
  error("No wallet configured.\nRun:\n $ npx x402-proxy setup\n\nOr set X402_PROXY_WALLET_MNEMONIC");
377
377
  process.exit(1);
378
378
  }
379
- warn("Note: MCP proxy is alpha - please report issues.");
380
379
  dim(`x402-proxy MCP proxy -> ${remoteUrl}`);
381
380
  if (wallet.evmAddress) dim(` EVM: ${wallet.evmAddress}`);
382
381
  if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
@@ -399,13 +398,14 @@ Add to your MCP client config (Claude, Cursor, etc.):
399
398
  const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
400
399
  const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
401
400
  const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
402
- const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
401
+ const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
403
402
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
404
403
  const { x402MCPClient } = await import("@x402/mcp");
405
- const x402Mcp = new x402MCPClient(new Client({
404
+ const remoteClient = new Client({
406
405
  name: "x402-proxy",
407
- version: "0.4.1"
408
- }), x402PaymentClient, {
406
+ version: "0.5.0"
407
+ });
408
+ const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
409
409
  autoPayment: true,
410
410
  onPaymentRequested: (ctx) => {
411
411
  const accept = ctx.paymentRequired.accepts?.[0];
@@ -447,31 +447,58 @@ Add to your MCP client config (Claude, Cursor, etc.):
447
447
  error(`Failed to connect to ${remoteUrl}: ${err instanceof Error ? err.message : String(err)}`);
448
448
  process.exit(1);
449
449
  }
450
- const { tools } = await x402Mcp.listTools();
450
+ let { tools } = await x402Mcp.listTools();
451
451
  dim(` ${tools.length} tools available`);
452
- const localServer = new McpServer({
452
+ let remoteResources = [];
453
+ try {
454
+ remoteResources = (await x402Mcp.listResources()).resources;
455
+ if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
456
+ } catch {
457
+ dim(" Resources not available from remote");
458
+ }
459
+ const localServer = new Server({
453
460
  name: "x402-proxy",
454
- version: "0.4.1"
455
- });
456
- for (const tool of tools) localServer.tool(tool.name, tool.description ?? "", tool.inputSchema?.properties ? Object.fromEntries(Object.entries(tool.inputSchema.properties).map(([k, v]) => [k, v])) : {}, async (args) => {
457
- const result = await x402Mcp.callTool(tool.name, args);
461
+ version: "0.5.0"
462
+ }, { capabilities: {
463
+ tools: tools.length > 0 ? {} : void 0,
464
+ resources: remoteResources.length > 0 ? {} : void 0
465
+ } });
466
+ const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema } = await import("@modelcontextprotocol/sdk/types.js");
467
+ localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => ({
468
+ name: t.name,
469
+ description: t.description,
470
+ inputSchema: t.inputSchema,
471
+ annotations: t.annotations
472
+ })) }));
473
+ localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
474
+ const { name, arguments: args } = request.params;
475
+ const result = await x402Mcp.callTool(name, args ?? {});
458
476
  return {
459
477
  content: result.content,
460
478
  isError: result.isError
461
479
  };
462
480
  });
463
- try {
464
- const { resources } = await x402Mcp.listResources();
465
- if (resources.length > 0) {
466
- dim(` ${resources.length} resources available`);
467
- for (const resource of resources) localServer.resource(resource.name, resource.uri, resource.description ? { description: resource.description } : {}, async (uri) => {
468
- return { contents: (await x402Mcp.readResource({ uri: uri.href })).contents.map((c) => ({
469
- uri: c.uri,
470
- text: "text" in c ? c.text : ""
471
- })) };
472
- });
473
- }
474
- } catch {}
481
+ if (remoteResources.length > 0) {
482
+ localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => ({
483
+ name: r.name,
484
+ uri: r.uri,
485
+ description: r.description,
486
+ mimeType: r.mimeType
487
+ })) }));
488
+ localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
489
+ return { contents: (await x402Mcp.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
490
+ });
491
+ }
492
+ remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
493
+ tools = (await x402Mcp.listTools()).tools;
494
+ dim(` Tools updated: ${tools.length} available`);
495
+ await localServer.notification({ method: "notifications/tools/list_changed" });
496
+ });
497
+ if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
498
+ remoteResources = (await x402Mcp.listResources()).resources;
499
+ dim(` Resources updated: ${remoteResources.length} available`);
500
+ await localServer.notification({ method: "notifications/resources/list_changed" });
501
+ });
475
502
  const stdioTransport = new StdioServerTransport();
476
503
  await localServer.connect(stdioTransport);
477
504
  dim(" MCP proxy running (stdio)");
@@ -608,7 +635,7 @@ const routes = buildRouteMap({
608
635
  });
609
636
  const app = buildApplication(routes, {
610
637
  name: "x402-proxy",
611
- versionInfo: { currentVersion: "0.4.1" },
638
+ versionInfo: { currentVersion: "0.5.0" },
612
639
  scanner: { caseStyle: "allow-kebab-for-camel" }
613
640
  });
614
641
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "curl for x402 paid APIs. Auto-pays any endpoint on Base and Solana.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -47,6 +47,8 @@
47
47
  "files": [
48
48
  "dist/**",
49
49
  "README.md",
50
+ "CHANGELOG.md",
51
+ "skills/**",
50
52
  "LICENSE"
51
53
  ],
52
54
  "keywords": [
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: x402-proxy
3
+ description: Use x402-proxy CLI for consuming and debugging x402 paid APIs. Use this skill when testing x402 endpoints, configuring MCP payment proxies for AI agents, managing x402 wallets, or scripting paid HTTP requests. Triggers on x402-proxy, npx x402-proxy, x402 endpoint testing, paid API debugging, MCP payment proxy, x402 wallet management, or any mention of auto-paying HTTP 402 responses.
4
+ ---
5
+
6
+ # x402-proxy
7
+
8
+ `curl` for x402 paid APIs. Auto-pays HTTP 402 responses with USDC on Base and Solana.
9
+
10
+ ## Quick start
11
+
12
+ ```bash
13
+ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
14
+ ```
15
+
16
+ First run auto-creates a wallet. No setup needed.
17
+
18
+ ## HTTP requests
19
+
20
+ ```bash
21
+ # GET - body to stdout, payment info to stderr
22
+ npx x402-proxy https://api.example.com/resource
23
+
24
+ # POST with body and headers
25
+ npx x402-proxy --method POST \
26
+ --header "Content-Type: application/json" \
27
+ --body '{"query":"example"}' \
28
+ https://api.example.com/search
29
+
30
+ # Force a specific chain
31
+ npx x402-proxy --network solana https://api.example.com/data
32
+
33
+ # Pipe-safe - only response body on stdout
34
+ npx x402-proxy https://api.example.com/data | jq '.results'
35
+
36
+ # Save response to file
37
+ npx x402-proxy https://api.example.com/data > response.json
38
+ ```
39
+
40
+ ## Commands
41
+
42
+ ```
43
+ x402-proxy <url> # paid HTTP request (default)
44
+ x402-proxy mcp <url> # MCP stdio proxy for AI agents
45
+ x402-proxy setup # wallet onboarding wizard
46
+ x402-proxy setup --force # re-run setup (overwrite existing wallet)
47
+ x402-proxy status # config + wallet + daily spend summary
48
+ x402-proxy wallet # show addresses and USDC balances
49
+ x402-proxy wallet history # payment log
50
+ x402-proxy wallet history --limit 5 # last 5 payments
51
+ x402-proxy wallet history --json # machine-readable output
52
+ x402-proxy wallet export-key evm # bare EVM private key to stdout
53
+ x402-proxy wallet export-key solana # bare Solana private key to stdout
54
+ x402-proxy wallet export-key mnemonic # bare mnemonic to stdout
55
+ ```
56
+
57
+ ## Fetch flags
58
+
59
+ ```
60
+ --method, -X <METHOD> HTTP method (default: GET)
61
+ --header, -H <KEY:VALUE> Add request header (repeatable)
62
+ --body, -d <DATA> Request body (string or @file)
63
+ --network <base|solana> Force payment on this chain
64
+ ```
65
+
66
+ ## MCP proxy for AI agents
67
+
68
+ Drop into Claude, Cursor, or any MCP client config:
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "paid-service": {
74
+ "command": "npx",
75
+ "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
76
+ "env": {
77
+ "X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ The proxy intercepts 402 responses, pays automatically, forwards the result. Supports StreamableHTTP and SSE.
85
+
86
+ ## Wallet & env vars
87
+
88
+ One BIP-39 mnemonic derives both Solana and EVM keypairs. Auto-detects which chain based on USDC balance.
89
+
90
+ ```
91
+ X402_PROXY_WALLET_MNEMONIC # BIP-39 mnemonic (derives both chains)
92
+ X402_PROXY_WALLET_EVM_KEY # EVM private key (hex, 0x optional)
93
+ X402_PROXY_WALLET_SOLANA_KEY # Solana private key (base58 or JSON array)
94
+ ```
95
+
96
+ Resolution: flags > env vars > mnemonic env > `~/.config/x402-proxy/wallet.json`
97
+
98
+ Pipe-safe export for scripting:
99
+
100
+ ```bash
101
+ MY_KEY=$(npx x402-proxy wallet export-key evm)
102
+ MY_MNEMONIC=$(npx x402-proxy wallet export-key mnemonic)
103
+ ```
104
+
105
+ ## Config
106
+
107
+ Lives at `~/.config/x402-proxy/` (or `$XDG_CONFIG_HOME/x402-proxy/`):
108
+
109
+ ```yaml
110
+ # config.yaml
111
+ defaultNetwork: base # or "solana"
112
+ spendLimitDaily: 10 # USDC daily cap
113
+ spendLimitPerTx: 1 # USDC per-request cap
114
+ ```
115
+
116
+ Also supports JSONC and JSON config files. Wallet stored in `wallet.json` (mode 0600), payments logged to `history.jsonl`.
117
+
118
+ ## Testing & debugging x402 services
119
+
120
+ ```bash
121
+ # Smoke test an endpoint
122
+ npx x402-proxy https://your-service.com/paid-route
123
+
124
+ # Test both chains
125
+ npx x402-proxy --network base https://your-service.com/route
126
+ npx x402-proxy --network solana https://your-service.com/route
127
+
128
+ # Batch test
129
+ for route in /users/test /tweets/search /v1/crawl; do
130
+ echo "--- $route ---"
131
+ npx x402-proxy "https://your-service.com$route" 2>/dev/null | head -c 200
132
+ echo
133
+ done
134
+
135
+ # Check what you spent
136
+ npx x402-proxy wallet history --limit 5
137
+ npx x402-proxy status
138
+ ```
139
+
140
+ stdout = response body, stderr = payment info. Pipes, redirects, and `jq` all work cleanly.
141
+
142
+ ## Library API
143
+
144
+ For programmatic use in Node.js apps, read `references/library.md`.
@@ -0,0 +1,85 @@
1
+ # x402-proxy Library API
2
+
3
+ ```bash
4
+ npm install x402-proxy
5
+ ```
6
+
7
+ ## Exports
8
+
9
+ ```typescript
10
+ // Core
11
+ import { createX402ProxyHandler, extractTxSignature } from "x402-proxy";
12
+ import { x402Client } from "x402-proxy";
13
+
14
+ // Chain schemes
15
+ import { ExactEvmScheme, toClientEvmSigner } from "x402-proxy";
16
+ import { ExactSvmScheme } from "x402-proxy";
17
+
18
+ // Wallet loaders
19
+ import { loadEvmWallet, loadSvmWallet } from "x402-proxy";
20
+
21
+ // History
22
+ import { appendHistory, readHistory, calcSpend, explorerUrl, formatTxLine } from "x402-proxy";
23
+ ```
24
+
25
+ ## EVM (Base) setup
26
+
27
+ ```typescript
28
+ import { createX402ProxyHandler, x402Client, ExactEvmScheme, toClientEvmSigner } from "x402-proxy";
29
+ import { createWalletClient, http } from "viem";
30
+ import { privateKeyToAccount } from "viem/accounts";
31
+ import { base } from "viem/chains";
32
+
33
+ const account = privateKeyToAccount(process.env.EVM_KEY as `0x${string}`);
34
+ const wallet = createWalletClient({ account, chain: base, transport: http() });
35
+
36
+ const client = x402Client(fetch)
37
+ .register(new ExactEvmScheme(toClientEvmSigner(wallet)));
38
+
39
+ const { x402Fetch, shiftPayment } = createX402ProxyHandler({ x402Client: client });
40
+
41
+ const res = await x402Fetch("https://api.example.com/data");
42
+ const body = await res.json();
43
+ const payment = shiftPayment(); // { network, payTo, amount, asset }
44
+ ```
45
+
46
+ ## Solana setup
47
+
48
+ ```typescript
49
+ import { createX402ProxyHandler, x402Client, ExactSvmScheme, loadSvmWallet } from "x402-proxy";
50
+
51
+ const signer = loadSvmWallet(process.env.MNEMONIC!);
52
+ const client = x402Client(fetch).register(new ExactSvmScheme(signer));
53
+ const { x402Fetch } = createX402ProxyHandler({ x402Client: client });
54
+ ```
55
+
56
+ ## Payment history
57
+
58
+ ```typescript
59
+ import { appendHistory, readHistory, calcSpend } from "x402-proxy";
60
+
61
+ // Read and summarize
62
+ const records = await readHistory("./history.jsonl");
63
+ const { dailyUsd, totalUsd, txCount } = calcSpend(records);
64
+
65
+ // Append a record
66
+ await appendHistory("./history.jsonl", {
67
+ ts: Date.now(),
68
+ status: 200,
69
+ network: "eip155:8453",
70
+ payTo: "0x...",
71
+ amount: "0.001",
72
+ asset: "USDC",
73
+ txId: "0xabc...",
74
+ host: "api.example.com",
75
+ });
76
+ ```
77
+
78
+ ## Types
79
+
80
+ ```typescript
81
+ type PaymentInfo = { network: string; payTo: string; amount: string; asset: string };
82
+ type X402ProxyOptions = { x402Client: ReturnType<typeof x402Client> };
83
+ type X402ProxyHandler = { x402Fetch: typeof fetch; shiftPayment: () => PaymentInfo | undefined };
84
+ type TxRecord = { ts: number; status: number; network: string; payTo: string; amount: string; asset: string; txId?: string; host: string };
85
+ ```