x402-proxy-openclaw 0.10.9

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.
@@ -0,0 +1,13 @@
1
+ import pc from "picocolors";
2
+ //#region packages/x402-proxy/src/lib/output.ts
3
+ function isTTY() {
4
+ return !!process.stderr.isTTY;
5
+ }
6
+ function info(msg) {
7
+ process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
8
+ }
9
+ function dim(msg) {
10
+ process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
11
+ }
12
+ //#endregion
13
+ export { dim, info };
@@ -0,0 +1,76 @@
1
+ import { loadWalletFile } from "./config.js";
2
+ import { deriveEvmKeypair, deriveSolanaKeypair } from "./derive.js";
3
+ import { ed25519 } from "@noble/curves/ed25519.js";
4
+ import { base58 } from "@scure/base";
5
+ import { privateKeyToAccount } from "viem/accounts";
6
+ //#region packages/x402-proxy/src/lib/wallet-resolution.ts
7
+ /**
8
+ * Resolve wallet keys following the priority cascade:
9
+ * 1. Flags (--evm-key / --solana-key as raw key strings)
10
+ * 2. X402_PROXY_WALLET_EVM_KEY / X402_PROXY_WALLET_SOLANA_KEY env vars
11
+ * 3. X402_PROXY_WALLET_MNEMONIC env var (derives both)
12
+ * 4. ~/.config/x402-proxy/wallet.json (mnemonic file)
13
+ */
14
+ function resolveWallet(opts) {
15
+ if (opts?.evmKey || opts?.solanaKey) {
16
+ const result = { source: "flag" };
17
+ if (opts.evmKey) {
18
+ const hex = opts.evmKey.startsWith("0x") ? opts.evmKey : `0x${opts.evmKey}`;
19
+ result.evmKey = hex;
20
+ result.evmAddress = privateKeyToAccount(hex).address;
21
+ }
22
+ if (opts.solanaKey) {
23
+ result.solanaKey = parseSolanaKey(opts.solanaKey);
24
+ result.solanaAddress = solanaAddressFromKey(result.solanaKey);
25
+ }
26
+ return result;
27
+ }
28
+ const envEvm = process.env.X402_PROXY_WALLET_EVM_KEY;
29
+ const envSol = process.env.X402_PROXY_WALLET_SOLANA_KEY;
30
+ if (envEvm || envSol) {
31
+ const result = { source: "env" };
32
+ if (envEvm) {
33
+ const hex = envEvm.startsWith("0x") ? envEvm : `0x${envEvm}`;
34
+ result.evmKey = hex;
35
+ result.evmAddress = privateKeyToAccount(hex).address;
36
+ }
37
+ if (envSol) {
38
+ result.solanaKey = parseSolanaKey(envSol);
39
+ result.solanaAddress = solanaAddressFromKey(result.solanaKey);
40
+ }
41
+ return result;
42
+ }
43
+ const envMnemonic = process.env.X402_PROXY_WALLET_MNEMONIC;
44
+ if (envMnemonic) return resolveFromMnemonic(envMnemonic, "mnemonic-env");
45
+ const walletFile = loadWalletFile();
46
+ if (walletFile) return resolveFromMnemonic(walletFile.mnemonic, "wallet-file");
47
+ return { source: "none" };
48
+ }
49
+ function resolveFromMnemonic(mnemonic, source) {
50
+ const evm = deriveEvmKeypair(mnemonic);
51
+ const sol = deriveSolanaKeypair(mnemonic);
52
+ const solanaKey = new Uint8Array(64);
53
+ solanaKey.set(sol.secretKey, 0);
54
+ solanaKey.set(sol.publicKey, 32);
55
+ return {
56
+ evmKey: evm.privateKey,
57
+ evmAddress: evm.address,
58
+ solanaKey,
59
+ solanaAddress: sol.address,
60
+ source
61
+ };
62
+ }
63
+ function parseSolanaKey(input) {
64
+ const trimmed = input.trim();
65
+ if (trimmed.startsWith("[")) {
66
+ const arr = JSON.parse(trimmed);
67
+ return new Uint8Array(arr);
68
+ }
69
+ return base58.decode(trimmed);
70
+ }
71
+ function solanaAddressFromKey(keyBytes) {
72
+ if (keyBytes.length >= 64) return base58.encode(keyBytes.slice(32));
73
+ return base58.encode(ed25519.getPublicKey(keyBytes));
74
+ }
75
+ //#endregion
76
+ export { resolveWallet };
@@ -0,0 +1,15 @@
1
+ import { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
2
+ import * as openclaw_plugin_sdk0 from "openclaw/plugin-sdk";
3
+ import * as openclaw_plugin_sdk_core0 from "openclaw/plugin-sdk/core";
4
+
5
+ //#region packages/x402-proxy/src/openclaw/plugin.d.ts
6
+ declare function register(api: OpenClawPluginApi): void;
7
+ declare const _default: {
8
+ id: string;
9
+ name: string;
10
+ description: string;
11
+ configSchema: openclaw_plugin_sdk0.OpenClawPluginConfigSchema;
12
+ register: NonNullable<openclaw_plugin_sdk_core0.OpenClawPluginDefinition["register"]>;
13
+ } & Pick<openclaw_plugin_sdk_core0.OpenClawPluginDefinition, "kind">;
14
+ //#endregion
15
+ export { _default as default, register };
@@ -0,0 +1,156 @@
1
+ import { createMppProxyHandler, createX402ProxyHandler } from "../handler.js";
2
+ import { getHistoryPath } from "../lib/config.js";
3
+ import { OptimizedSvmScheme } from "../lib/optimized-svm-scheme.js";
4
+ import { resolveWallet } from "../lib/wallet-resolution.js";
5
+ import { loadSvmWallet } from "../wallet.js";
6
+ import { SOL_MAINNET, addressForNetwork, createRequestTool, createWalletTool } from "../tools.js";
7
+ import { createSendCommand, createWalletCommand } from "../commands.js";
8
+ import { resolveProviders, routePrefixForBaseUrl } from "../defaults.js";
9
+ import { createInferenceProxyRouteHandler } from "../route.js";
10
+ import { homedir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { createKeyPairSignerFromBytes } from "@solana/kit";
13
+ import { x402Client } from "@x402/fetch";
14
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
15
+ //#region packages/x402-proxy/src/openclaw/plugin.ts
16
+ function register(api) {
17
+ const config = api.pluginConfig ?? {};
18
+ const explicitKeypairPath = config.keypairPath;
19
+ const rpcUrl = config.rpcUrl || "https://api.mainnet-beta.solana.com";
20
+ const dashboardUrl = config.dashboardUrl || "";
21
+ const { providers, models: allModels } = resolveProviders(config);
22
+ const defaultProvider = providers[0];
23
+ for (const provider of providers) api.registerProvider({
24
+ id: provider.id,
25
+ label: provider.id,
26
+ auth: [],
27
+ catalog: {
28
+ order: "simple",
29
+ run: async () => ({ provider: {
30
+ baseUrl: provider.baseUrl,
31
+ api: "openai-completions",
32
+ authHeader: false,
33
+ models: provider.models
34
+ } })
35
+ }
36
+ });
37
+ api.logger.info(`x402-proxy: ${providers.map((provider) => `${provider.id}:${provider.protocol}`).join(", ")} - ${allModels.length} models`);
38
+ let solanaWalletAddress = null;
39
+ let evmWalletAddress = null;
40
+ let signerRef = null;
41
+ let proxyRef = null;
42
+ let mppHandlerRef = null;
43
+ let evmKeyRef = null;
44
+ let walletLoadPromise = null;
45
+ const historyPath = getHistoryPath();
46
+ const routePrefixes = [...new Set(providers.map((provider) => routePrefixForBaseUrl(provider.baseUrl)))];
47
+ const handler = createInferenceProxyRouteHandler({
48
+ providers,
49
+ getX402Proxy: () => proxyRef,
50
+ getMppHandler: () => mppHandlerRef,
51
+ getWalletAddress: () => solanaWalletAddress ?? evmWalletAddress,
52
+ getWalletAddressForNetwork: (network) => addressForNetwork(evmWalletAddress, solanaWalletAddress, network),
53
+ historyPath,
54
+ allModels,
55
+ logger: api.logger
56
+ });
57
+ for (const path of routePrefixes) api.registerHttpRoute({
58
+ path,
59
+ match: "prefix",
60
+ auth: "plugin",
61
+ handler
62
+ });
63
+ api.logger.info(`proxy: HTTP routes ${routePrefixes.join(", ")} registered for ${providers.map((provider) => provider.upstreamUrl).join(", ")}`);
64
+ async function ensureWalletLoaded() {
65
+ if (walletLoadPromise) {
66
+ await walletLoadPromise;
67
+ return;
68
+ }
69
+ walletLoadPromise = (async () => {
70
+ try {
71
+ const resolution = resolveWallet();
72
+ evmKeyRef = resolution.evmKey ?? null;
73
+ evmWalletAddress = resolution.evmAddress ?? null;
74
+ if (explicitKeypairPath) {
75
+ signerRef = await loadSvmWallet(explicitKeypairPath.startsWith("~/") ? join(homedir(), explicitKeypairPath.slice(2)) : explicitKeypairPath);
76
+ solanaWalletAddress = signerRef.address;
77
+ } else if (resolution.solanaKey && resolution.solanaAddress) {
78
+ signerRef = await createKeyPairSignerFromBytes(resolution.solanaKey);
79
+ solanaWalletAddress = resolution.solanaAddress;
80
+ } else {
81
+ signerRef = null;
82
+ solanaWalletAddress = null;
83
+ }
84
+ if (!solanaWalletAddress && !evmWalletAddress) {
85
+ api.logger.error("x402-proxy: no wallet found. Run `x402-proxy setup` to create one, or set X402_PROXY_WALLET_MNEMONIC / X402_PROXY_WALLET_EVM_KEY.");
86
+ return;
87
+ }
88
+ if (signerRef) {
89
+ const client = new x402Client();
90
+ client.register(SOL_MAINNET, new OptimizedSvmScheme(signerRef, { rpcUrl }));
91
+ proxyRef = createX402ProxyHandler({ client });
92
+ } else proxyRef = null;
93
+ if (evmKeyRef) {
94
+ const maxBudget = Math.max(...providers.map((p) => Number(p.mppSessionBudget) || .5)).toString();
95
+ mppHandlerRef = await createMppProxyHandler({
96
+ evmKey: evmKeyRef,
97
+ maxDeposit: maxBudget
98
+ });
99
+ } else mppHandlerRef = null;
100
+ api.logger.info(`wallets: solana=${solanaWalletAddress ?? "missing"} evm=${evmWalletAddress ?? "missing"}`);
101
+ } catch (err) {
102
+ api.logger.error(`wallet load failed: ${err}`);
103
+ }
104
+ })();
105
+ await walletLoadPromise;
106
+ }
107
+ ensureWalletLoaded();
108
+ api.registerService({
109
+ id: "x402-wallet",
110
+ async start() {
111
+ await ensureWalletLoaded();
112
+ },
113
+ async stop() {
114
+ if (mppHandlerRef) try {
115
+ await mppHandlerRef.close();
116
+ } catch {}
117
+ }
118
+ });
119
+ const toolCtx = {
120
+ ensureReady: ensureWalletLoaded,
121
+ getSolanaWalletAddress: () => solanaWalletAddress,
122
+ getEvmWalletAddress: () => evmWalletAddress,
123
+ getSigner: () => signerRef,
124
+ getX402Proxy: () => proxyRef,
125
+ getEvmKey: () => evmKeyRef,
126
+ getDefaultRequestProtocol: () => defaultProvider?.protocol ?? "mpp",
127
+ getDefaultMppSessionBudget: () => defaultProvider?.mppSessionBudget ?? "0.5",
128
+ rpcUrl,
129
+ historyPath,
130
+ allModels
131
+ };
132
+ api.registerTool(createWalletTool(toolCtx), { names: ["x_balance"] });
133
+ api.registerTool(createRequestTool(toolCtx), { names: ["x_payment"] });
134
+ const cmdCtx = {
135
+ ensureReady: ensureWalletLoaded,
136
+ getSolanaWalletAddress: () => solanaWalletAddress,
137
+ getEvmWalletAddress: () => evmWalletAddress,
138
+ getSigner: () => signerRef,
139
+ getDefaultRequestProtocol: () => defaultProvider?.protocol ?? "mpp",
140
+ getDefaultMppSessionBudget: () => defaultProvider?.mppSessionBudget ?? "0.5",
141
+ rpcUrl,
142
+ dashboardUrl,
143
+ historyPath,
144
+ allModels
145
+ };
146
+ api.registerCommand(createWalletCommand(cmdCtx));
147
+ api.registerCommand(createSendCommand(cmdCtx));
148
+ }
149
+ var plugin_default = definePluginEntry({
150
+ id: "x402-proxy",
151
+ name: "mpp/x402 Payments Proxy",
152
+ description: "x402 and MPP payments, wallet tools, and paid inference proxying",
153
+ register
154
+ });
155
+ //#endregion
156
+ export { plugin_default as default, register };
@@ -0,0 +1,112 @@
1
+ {
2
+ "id": "x402-proxy",
3
+ "name": "mpp/x402 Payments Proxy",
4
+ "description": "x402 and MPP payments, wallet tools, and paid inference proxying",
5
+ "version": "0.10.9",
6
+ "providers": [
7
+ "surf"
8
+ ],
9
+ "autoEnableWhenConfiguredProviders": [
10
+ "surf"
11
+ ],
12
+ "skills": [
13
+ "skills"
14
+ ],
15
+ "contracts": {
16
+ "tools": [
17
+ "x_wallet",
18
+ "x_balance",
19
+ "x_request",
20
+ "x_payment"
21
+ ]
22
+ },
23
+ "configSchema": {
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "properties": {
27
+ "providers": {
28
+ "type": "object",
29
+ "description": "Provider catalog keyed by name. Defaults to the built-in surf provider when omitted.",
30
+ "additionalProperties": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "baseUrl": {
35
+ "type": "string",
36
+ "description": "Gateway base path for this provider, typically under /x402-proxy"
37
+ },
38
+ "upstreamUrl": {
39
+ "type": "string",
40
+ "description": "Upstream inference origin to proxy to"
41
+ },
42
+ "protocol": {
43
+ "type": "string",
44
+ "enum": [
45
+ "x402",
46
+ "mpp",
47
+ "auto"
48
+ ],
49
+ "description": "Payment protocol for this provider. Defaults to mpp."
50
+ },
51
+ "mppSessionBudget": {
52
+ "type": "string",
53
+ "description": "Maximum USDC budget to lock for MPP sessions for this provider (default: 0.5)"
54
+ },
55
+ "models": {
56
+ "type": "array",
57
+ "description": "Static OpenClaw model catalog entries for this provider"
58
+ }
59
+ }
60
+ }
61
+ },
62
+ "protocol": {
63
+ "type": "string",
64
+ "enum": [
65
+ "x402",
66
+ "mpp",
67
+ "auto"
68
+ ],
69
+ "description": "Default payment protocol fallback for providers that do not override it."
70
+ },
71
+ "mppSessionBudget": {
72
+ "type": "string",
73
+ "description": "Default maximum USDC budget to lock for MPP sessions (default: 0.5)"
74
+ },
75
+ "keypairPath": {
76
+ "type": "string",
77
+ "description": "Optional path to Solana keypair JSON file (overrides x402-proxy wallet resolution for Solana/x402 surfaces)"
78
+ },
79
+ "rpcUrl": {
80
+ "type": "string",
81
+ "description": "Solana RPC URL"
82
+ },
83
+ "dashboardUrl": {
84
+ "type": "string",
85
+ "description": "URL to link from /x_wallet dashboard"
86
+ }
87
+ },
88
+ "required": []
89
+ },
90
+ "uiHints": {
91
+ "protocol": {
92
+ "label": "Default protocol",
93
+ "help": "Used only when a provider entry does not set its own protocol."
94
+ },
95
+ "mppSessionBudget": {
96
+ "label": "Default MPP budget",
97
+ "placeholder": "0.5"
98
+ },
99
+ "keypairPath": {
100
+ "label": "Solana keypair path",
101
+ "advanced": true
102
+ },
103
+ "rpcUrl": {
104
+ "label": "Solana RPC URL",
105
+ "advanced": true
106
+ },
107
+ "dashboardUrl": {
108
+ "label": "Dashboard URL",
109
+ "advanced": true
110
+ }
111
+ }
112
+ }