x402-proxy 0.10.7 → 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.
- package/CHANGELOG.md +23 -1
- package/README.md +7 -1
- package/dist/Credential-COZQnr1-.js +2055 -0
- package/dist/Mcp-CrCEqLqO.js +10 -0
- package/dist/Sse-ChldYgU7.js +9742 -0
- package/dist/Sse-kCB38G56.js +16482 -0
- package/dist/accounts-DsuvWwph.js +232 -0
- package/dist/accounts-DzvAlQRn.js +5 -0
- package/dist/accounts-IG-Cmrwy.js +229 -0
- package/dist/api-CUzmQvTQ.js +2802 -0
- package/dist/auth-DTzQmnZ_.js +1196 -0
- package/dist/bin/cli.js +585 -242
- package/dist/ccip-Bx-zoUCJ.js +240 -0
- package/dist/ccip-C2k1DD1T.js +153 -0
- package/dist/ccip-C6CQOJYv.js +152 -0
- package/dist/ccip-RZzsZ5Mv.js +156 -0
- package/dist/chain-CafcHffR.js +1997 -0
- package/dist/chain-DwfP5RGZ.js +1968 -0
- package/dist/chunk-DBEY4PJZ.js +16 -0
- package/dist/chunk-DjEMn6fM.js +36 -0
- package/dist/client-Blw2V7LF.js +657 -0
- package/dist/client-C37gWJOZ.js +102 -0
- package/dist/client-CEc4NYAA.js +6388 -0
- package/dist/client-CVDTUY0l.js +5152 -0
- package/dist/config-BUQsit4s.js +3 -0
- package/dist/config-DR1Fs_wL.js +6600 -0
- package/dist/{config-D9wIR3xc.js → config-rvKA3SYT.js} +10 -5
- package/dist/decodeFunctionData-DuFcwhC_.js +4510 -0
- package/dist/decodeFunctionData-JPOUdvil.js +4394 -0
- package/dist/derive-DNUl8LU9.js +9109 -0
- package/dist/dist-C2YO6HSQ.js +6581 -0
- package/dist/dist-DM5_F3r5.js +4 -0
- package/dist/dist-DxJCYyL5.js +1388 -0
- package/dist/hashTypedData-BHmP9dBd.js +859 -0
- package/dist/hashTypedData-CtEdfx4y.js +846 -0
- package/dist/helpers-CuUSw-tH.js +7125 -0
- package/dist/hmac-59IlS_by.js +648 -0
- package/dist/http-BAtucMbS.js +2060 -0
- package/dist/index.d.ts +1903 -9
- package/dist/index.js +18006 -50
- package/dist/index.node-CxkL0OFh.js +3592 -0
- package/dist/index.node-DvmeuZBj.js +3 -0
- package/dist/isAddressEqual-BLrd1Hg1.js +9 -0
- package/dist/isAddressEqual-DsAqfQOD.js +10 -0
- package/dist/localBatchGatewayRequest-C-RPJyDO.js +6260 -0
- package/dist/localBatchGatewayRequest-DOdQ9bR7.js +93 -0
- package/dist/localBatchGatewayRequest-DQkbZaSy.js +6261 -0
- package/dist/parseUnits-CApwcKSD.js +49 -0
- package/dist/parseUnits-cMO2udMe.js +48 -0
- package/dist/schemas-BxMFYNbH.js +1270 -0
- package/dist/secp256k1-BZpiyffY.js +2525 -0
- package/dist/secp256k1-BjenrLl5.js +1877 -0
- package/dist/secp256k1-CLPUX17u.js +3 -0
- package/dist/sendRawTransactionSync-DvSkhZtW.js +3612 -0
- package/dist/server-CSq0IuUq.js +565 -0
- package/dist/setup-BY4J49Lv.js +1110 -0
- package/dist/setup-wMOAgrsN.js +3 -0
- package/dist/sha256-FAs0qeni.js +17 -0
- package/dist/sha3-CYkWM8Xa.js +195 -0
- package/dist/sha3-DbMJRJ3C.js +194 -0
- package/dist/sse-B4LLqBQm.js +408 -0
- package/dist/status-Bu23RjW6.js +3 -0
- package/dist/{status-DihAcUSC.js → status-X21VnGUO.js} +16 -15
- package/dist/stdio-BADqxZdZ.js +85 -0
- package/dist/streamableHttp-BHkJypcI.js +358 -0
- package/dist/tempo-3nttrxgQ.js +17 -0
- package/dist/tempo-DER0P-ul.js +18 -0
- package/dist/types-BEKUz-Mf.js +1240 -0
- package/dist/types-DatK5vR5.js +3 -0
- package/dist/utils-BYjkXZDF.js +444 -0
- package/dist/utils-SeGHMW9O.js +445 -0
- package/dist/wallet-DKVlrR1S.js +3 -0
- package/dist/wallet-DSyht15_.js +17759 -0
- package/package.json +18 -71
- package/dist/config-B_upkJeK.js +0 -66
- package/dist/config-Be35NM5s.js +0 -3
- package/dist/config-J1m-CWXT.js +0 -27
- package/dist/derive-CL6e8K0Z.js +0 -81
- package/dist/openclaw/plugin.d.ts +0 -15
- package/dist/openclaw/plugin.js +0 -2067
- package/dist/openclaw.plugin.json +0 -93
- package/dist/setup-CNyMLnM-.js +0 -197
- package/dist/setup-DTIxPe58.js +0 -3
- package/dist/status-DZlJ4pS7.js +0 -3
- package/dist/wallet-B0S-rma9.js +0 -544
- package/dist/wallet-DBrVZJqe.js +0 -3
- package/openclaw.plugin.json +0 -93
- package/skills/SKILL.md +0 -183
- package/skills/references/library.md +0 -85
- package/skills/references/openclaw-plugin.md +0 -145
package/dist/wallet-B0S-rma9.js
DELETED
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { a as getHistoryPath, u as loadWalletFile } from "./config-D9wIR3xc.js";
|
|
3
|
-
import { n as deriveSolanaKeypair, t as deriveEvmKeypair } from "./derive-CL6e8K0Z.js";
|
|
4
|
-
import { dirname } from "node:path";
|
|
5
|
-
import { buildCommand } from "@stricli/core";
|
|
6
|
-
import pc from "picocolors";
|
|
7
|
-
import { x402Client } from "@x402/fetch";
|
|
8
|
-
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { ed25519 } from "@noble/curves/ed25519.js";
|
|
10
|
-
import { base58 } from "@scure/base";
|
|
11
|
-
import { toClientEvmSigner } from "@x402/evm";
|
|
12
|
-
import { registerExactEvmScheme } from "@x402/evm/exact/client";
|
|
13
|
-
import { createPublicClient, http } from "viem";
|
|
14
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
15
|
-
import { base } from "viem/chains";
|
|
16
|
-
import { SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR, address, appendTransactionMessageInstructions, createDefaultRpcTransport, createSolanaRpcFromTransport, createTransactionMessage, getAddressEncoder, getBase64EncodedWireTransaction, getProgramDerivedAddress, isSolanaError, mainnet, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageComputeUnitLimit, setTransactionMessageComputeUnitPrice, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash } from "@solana/kit";
|
|
17
|
-
import { TOKEN_PROGRAM_ADDRESS, findAssociatedTokenPda, getTransferCheckedInstruction } from "@solana-program/token";
|
|
18
|
-
//#region src/lib/output.ts
|
|
19
|
-
function isTTY() {
|
|
20
|
-
return !!process.stderr.isTTY;
|
|
21
|
-
}
|
|
22
|
-
function info(msg) {
|
|
23
|
-
process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
|
|
24
|
-
}
|
|
25
|
-
function warn(msg) {
|
|
26
|
-
process.stderr.write(`${isTTY() ? pc.yellow(msg) : msg}\n`);
|
|
27
|
-
}
|
|
28
|
-
function error(msg) {
|
|
29
|
-
process.stderr.write(`${isTTY() ? pc.red(`✗ ${msg}`) : `✗ ${msg}`}\n`);
|
|
30
|
-
}
|
|
31
|
-
function dim(msg) {
|
|
32
|
-
process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
|
|
33
|
-
}
|
|
34
|
-
function success(msg) {
|
|
35
|
-
process.stderr.write(`${isTTY() ? pc.green(`✓ ${msg}`) : `✓ ${msg}`}\n`);
|
|
36
|
-
}
|
|
37
|
-
function isMeaningfulInferenceRecord(record) {
|
|
38
|
-
if (record.kind !== "x402_inference") return true;
|
|
39
|
-
if (!record.ok) return true;
|
|
40
|
-
return record.amount != null || record.model != null || record.inputTokens != null || record.outputTokens != null || record.tx != null;
|
|
41
|
-
}
|
|
42
|
-
function appendHistory(historyPath, record) {
|
|
43
|
-
try {
|
|
44
|
-
mkdirSync(dirname(historyPath), { recursive: true });
|
|
45
|
-
appendFileSync(historyPath, `${JSON.stringify(record)}\n`);
|
|
46
|
-
if (existsSync(historyPath)) {
|
|
47
|
-
if (statSync(historyPath).size > 1e3 * 200) {
|
|
48
|
-
const lines = readFileSync(historyPath, "utf-8").trimEnd().split("\n");
|
|
49
|
-
if (lines.length > 1e3) writeFileSync(historyPath, `${lines.slice(-500).join("\n")}\n`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch {}
|
|
53
|
-
}
|
|
54
|
-
function readHistory(historyPath) {
|
|
55
|
-
try {
|
|
56
|
-
if (!existsSync(historyPath)) return [];
|
|
57
|
-
const content = readFileSync(historyPath, "utf-8").trimEnd();
|
|
58
|
-
if (!content) return [];
|
|
59
|
-
return content.split("\n").flatMap((line) => {
|
|
60
|
-
try {
|
|
61
|
-
const parsed = JSON.parse(line);
|
|
62
|
-
if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
|
|
63
|
-
const record = parsed;
|
|
64
|
-
return isMeaningfulInferenceRecord(record) ? [record] : [];
|
|
65
|
-
} catch {
|
|
66
|
-
return [];
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
} catch {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function calcSpend(records) {
|
|
74
|
-
const todayStart = /* @__PURE__ */ new Date();
|
|
75
|
-
todayStart.setUTCHours(0, 0, 0, 0);
|
|
76
|
-
const todayMs = todayStart.getTime();
|
|
77
|
-
let today = 0;
|
|
78
|
-
let total = 0;
|
|
79
|
-
let count = 0;
|
|
80
|
-
for (const r of records) {
|
|
81
|
-
if (!r.ok || r.amount == null) continue;
|
|
82
|
-
if (r.token !== "USDC") continue;
|
|
83
|
-
total += r.amount;
|
|
84
|
-
count++;
|
|
85
|
-
if (r.t >= todayMs) today += r.amount;
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
today,
|
|
89
|
-
total,
|
|
90
|
-
count
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function formatUsdcValue(amount) {
|
|
94
|
-
return new Intl.NumberFormat("en-US", {
|
|
95
|
-
useGrouping: false,
|
|
96
|
-
minimumFractionDigits: 0,
|
|
97
|
-
maximumFractionDigits: 12
|
|
98
|
-
}).format(amount);
|
|
99
|
-
}
|
|
100
|
-
function formatAmount(amount, token) {
|
|
101
|
-
if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
|
|
102
|
-
if (token === "SOL") return `${amount} SOL`;
|
|
103
|
-
return `${amount} ${token}`;
|
|
104
|
-
}
|
|
105
|
-
const KIND_LABELS = {
|
|
106
|
-
x402_inference: "inference",
|
|
107
|
-
x402_payment: "payment",
|
|
108
|
-
mpp_payment: "mpp payment",
|
|
109
|
-
transfer: "transfer",
|
|
110
|
-
buy: "buy",
|
|
111
|
-
sell: "sell",
|
|
112
|
-
mint: "mint",
|
|
113
|
-
swap: "swap"
|
|
114
|
-
};
|
|
115
|
-
function explorerUrl(net, tx) {
|
|
116
|
-
if (net.startsWith("eip155:")) {
|
|
117
|
-
const chainId = net.split(":")[1];
|
|
118
|
-
if (chainId === "4217") return `https://explore.mainnet.tempo.xyz/tx/${tx}`;
|
|
119
|
-
if (chainId === "8453") return `https://basescan.org/tx/${tx}`;
|
|
120
|
-
return `https://basescan.org/tx/${tx}`;
|
|
121
|
-
}
|
|
122
|
-
return `https://solscan.io/tx/${tx}`;
|
|
123
|
-
}
|
|
124
|
-
/** Strip provider prefix and OpenRouter date suffixes from model IDs.
|
|
125
|
-
* e.g. "minimax/minimax-m2.5-20260211" -> "minimax-m2.5"
|
|
126
|
-
* "moonshotai/kimi-k2.5-0127" -> "kimi-k2.5" */
|
|
127
|
-
function shortModel(model) {
|
|
128
|
-
const parts = model.split("/");
|
|
129
|
-
return parts[parts.length - 1].replace(/-\d{6,8}$/, "").replace(/-\d{4}$/, "");
|
|
130
|
-
}
|
|
131
|
-
function displayNetwork(net) {
|
|
132
|
-
if (net === "eip155:8453") return "Base";
|
|
133
|
-
if (net === "eip155:4217") return "Tempo";
|
|
134
|
-
if (net.startsWith("eip155:")) return `EVM (${net.split(":")[1]})`;
|
|
135
|
-
if (net.startsWith("solana:")) return "Solana";
|
|
136
|
-
return net;
|
|
137
|
-
}
|
|
138
|
-
function shortNetwork(net) {
|
|
139
|
-
if (net === "eip155:8453") return "base";
|
|
140
|
-
if (net === "eip155:4217") return "tempo";
|
|
141
|
-
if (net.startsWith("eip155:")) return `evm:${net.split(":")[1]}`;
|
|
142
|
-
if (net.startsWith("solana:")) return "sol";
|
|
143
|
-
return net;
|
|
144
|
-
}
|
|
145
|
-
function formatTxLine(r, opts) {
|
|
146
|
-
const time = new Date(r.t).toLocaleTimeString("en-US", {
|
|
147
|
-
hour: "2-digit",
|
|
148
|
-
minute: "2-digit",
|
|
149
|
-
hour12: false,
|
|
150
|
-
timeZone: "UTC"
|
|
151
|
-
});
|
|
152
|
-
const timeStr = r.tx ? `[${time}](${explorerUrl(r.net, r.tx)})` : time;
|
|
153
|
-
const parts = [r.kind === "x402_inference" && r.model ? shortModel(r.model) : KIND_LABELS[r.kind] ?? r.kind];
|
|
154
|
-
if (r.label) parts.push(r.label);
|
|
155
|
-
if (r.ok && r.amount != null && r.token) parts.push(formatAmount(r.amount, r.token));
|
|
156
|
-
else if (r.ok && r.kind === "sell" && r.meta?.pct != null) parts.push(`${r.meta.pct}%`);
|
|
157
|
-
parts.push(shortNetwork(r.net));
|
|
158
|
-
if (opts?.verbose && r.tx) {
|
|
159
|
-
const short = r.tx.length > 20 ? `${r.tx.slice(0, 10)}...${r.tx.slice(-6)}` : r.tx;
|
|
160
|
-
parts.push(short);
|
|
161
|
-
}
|
|
162
|
-
return ` ${timeStr} ${r.ok ? "" : "✗ "}${parts.join(" · ")}`;
|
|
163
|
-
}
|
|
164
|
-
//#endregion
|
|
165
|
-
//#region src/lib/optimized-svm-scheme.ts
|
|
166
|
-
const MEMO_PROGRAM_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
|
|
167
|
-
const COMPUTE_UNIT_LIMIT = 2e4;
|
|
168
|
-
const COMPUTE_UNIT_PRICE_MICROLAMPORTS = 1n;
|
|
169
|
-
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
170
|
-
const USDC_DECIMALS = 6;
|
|
171
|
-
const MAINNET_RPC_URLS = ["https://api.mainnet.solana.com", "https://public.rpc.solanavibestation.com"];
|
|
172
|
-
/**
|
|
173
|
-
* Create a failover transport that tries each RPC in order.
|
|
174
|
-
* On 429 from one endpoint, immediately tries the next instead of waiting.
|
|
175
|
-
* Each transport gets its own coalescing via createDefaultRpcTransport.
|
|
176
|
-
*/
|
|
177
|
-
function createFailoverTransport(urls) {
|
|
178
|
-
const transports = urls.map((url) => createDefaultRpcTransport({ url }));
|
|
179
|
-
const failover = (async (config) => {
|
|
180
|
-
let lastError;
|
|
181
|
-
for (const transport of transports) try {
|
|
182
|
-
return await transport(config);
|
|
183
|
-
} catch (e) {
|
|
184
|
-
lastError = e;
|
|
185
|
-
if (isSolanaError(e, SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR) && e.context.statusCode === 429) continue;
|
|
186
|
-
throw e;
|
|
187
|
-
}
|
|
188
|
-
throw lastError;
|
|
189
|
-
});
|
|
190
|
-
return failover;
|
|
191
|
-
}
|
|
192
|
-
function createRpcClient(customRpcUrl) {
|
|
193
|
-
return createSolanaRpcFromTransport(createFailoverTransport((customRpcUrl ? [customRpcUrl, ...MAINNET_RPC_URLS] : MAINNET_RPC_URLS).map((u) => mainnet(u))));
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Optimized ExactSvmScheme that replaces upstream @x402/svm to prevent
|
|
197
|
-
* RPC rate-limit failures on parallel payments.
|
|
198
|
-
*
|
|
199
|
-
* Two optimizations over upstream:
|
|
200
|
-
* 1. Shared RPC client - @solana/kit's built-in request coalescing
|
|
201
|
-
* merges identical getLatestBlockhash calls in the same tick into 1.
|
|
202
|
-
* 2. Hardcoded USDC - skips fetchMint RPC call for USDC (immutable data).
|
|
203
|
-
*/
|
|
204
|
-
var OptimizedSvmScheme = class {
|
|
205
|
-
scheme = "exact";
|
|
206
|
-
rpc;
|
|
207
|
-
constructor(signer, config) {
|
|
208
|
-
this.signer = signer;
|
|
209
|
-
this.rpc = createRpcClient(config?.rpcUrl);
|
|
210
|
-
}
|
|
211
|
-
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
212
|
-
const rpc = this.rpc;
|
|
213
|
-
const asset = paymentRequirements.asset;
|
|
214
|
-
if (asset !== USDC_MINT) throw new Error(`Unsupported asset: ${asset}. Only USDC is supported.`);
|
|
215
|
-
const [[sourceATA], [destinationATA]] = await Promise.all([findAssociatedTokenPda({
|
|
216
|
-
mint: asset,
|
|
217
|
-
owner: this.signer.address,
|
|
218
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS
|
|
219
|
-
}), findAssociatedTokenPda({
|
|
220
|
-
mint: asset,
|
|
221
|
-
owner: paymentRequirements.payTo,
|
|
222
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS
|
|
223
|
-
})]);
|
|
224
|
-
const transferIx = getTransferCheckedInstruction({
|
|
225
|
-
source: sourceATA,
|
|
226
|
-
mint: asset,
|
|
227
|
-
destination: destinationATA,
|
|
228
|
-
authority: this.signer,
|
|
229
|
-
amount: BigInt(paymentRequirements.amount),
|
|
230
|
-
decimals: USDC_DECIMALS
|
|
231
|
-
});
|
|
232
|
-
const feePayer = paymentRequirements.extra?.feePayer;
|
|
233
|
-
if (!feePayer) throw new Error("feePayer is required in paymentRequirements.extra for SVM transactions");
|
|
234
|
-
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
235
|
-
const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
236
|
-
const memoIx = {
|
|
237
|
-
programAddress: MEMO_PROGRAM_ADDRESS,
|
|
238
|
-
accounts: [],
|
|
239
|
-
data: new TextEncoder().encode(Array.from(nonce).map((b) => b.toString(16).padStart(2, "0")).join(""))
|
|
240
|
-
};
|
|
241
|
-
return {
|
|
242
|
-
x402Version,
|
|
243
|
-
payload: { transaction: getBase64EncodedWireTransaction(await partiallySignTransactionMessageWithSigners(pipe(createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageComputeUnitPrice(COMPUTE_UNIT_PRICE_MICROLAMPORTS, tx), (tx) => setTransactionMessageComputeUnitLimit(COMPUTE_UNIT_LIMIT, tx), (tx) => setTransactionMessageFeePayer(feePayer, tx), (tx) => appendTransactionMessageInstructions([transferIx, memoIx], tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx)))) }
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
//#endregion
|
|
248
|
-
//#region src/lib/resolve-wallet.ts
|
|
249
|
-
/**
|
|
250
|
-
* Resolve wallet keys following the priority cascade:
|
|
251
|
-
* 1. Flags (--evm-key / --solana-key as raw key strings)
|
|
252
|
-
* 2. X402_PROXY_WALLET_EVM_KEY / X402_PROXY_WALLET_SOLANA_KEY env vars
|
|
253
|
-
* 3. X402_PROXY_WALLET_MNEMONIC env var (derives both)
|
|
254
|
-
* 4. ~/.config/x402-proxy/wallet.json (mnemonic file)
|
|
255
|
-
*/
|
|
256
|
-
function resolveWallet(opts) {
|
|
257
|
-
if (opts?.evmKey || opts?.solanaKey) {
|
|
258
|
-
const result = { source: "flag" };
|
|
259
|
-
if (opts.evmKey) {
|
|
260
|
-
const hex = opts.evmKey.startsWith("0x") ? opts.evmKey : `0x${opts.evmKey}`;
|
|
261
|
-
result.evmKey = hex;
|
|
262
|
-
result.evmAddress = privateKeyToAccount(hex).address;
|
|
263
|
-
}
|
|
264
|
-
if (opts.solanaKey) {
|
|
265
|
-
result.solanaKey = parsesolanaKey(opts.solanaKey);
|
|
266
|
-
result.solanaAddress = solanaAddressFromKey(result.solanaKey);
|
|
267
|
-
}
|
|
268
|
-
return result;
|
|
269
|
-
}
|
|
270
|
-
const envEvm = process.env.X402_PROXY_WALLET_EVM_KEY;
|
|
271
|
-
const envSol = process.env.X402_PROXY_WALLET_SOLANA_KEY;
|
|
272
|
-
if (envEvm || envSol) {
|
|
273
|
-
const result = { source: "env" };
|
|
274
|
-
if (envEvm) {
|
|
275
|
-
const hex = envEvm.startsWith("0x") ? envEvm : `0x${envEvm}`;
|
|
276
|
-
result.evmKey = hex;
|
|
277
|
-
result.evmAddress = privateKeyToAccount(hex).address;
|
|
278
|
-
}
|
|
279
|
-
if (envSol) {
|
|
280
|
-
result.solanaKey = parsesolanaKey(envSol);
|
|
281
|
-
result.solanaAddress = solanaAddressFromKey(result.solanaKey);
|
|
282
|
-
}
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
const envMnemonic = process.env.X402_PROXY_WALLET_MNEMONIC;
|
|
286
|
-
if (envMnemonic) return resolveFromMnemonic(envMnemonic, "mnemonic-env");
|
|
287
|
-
const walletFile = loadWalletFile();
|
|
288
|
-
if (walletFile) return resolveFromMnemonic(walletFile.mnemonic, "wallet-file");
|
|
289
|
-
return { source: "none" };
|
|
290
|
-
}
|
|
291
|
-
function resolveFromMnemonic(mnemonic, source) {
|
|
292
|
-
const evm = deriveEvmKeypair(mnemonic);
|
|
293
|
-
const sol = deriveSolanaKeypair(mnemonic);
|
|
294
|
-
const solanaKey = new Uint8Array(64);
|
|
295
|
-
solanaKey.set(sol.secretKey, 0);
|
|
296
|
-
solanaKey.set(sol.publicKey, 32);
|
|
297
|
-
return {
|
|
298
|
-
evmKey: evm.privateKey,
|
|
299
|
-
evmAddress: evm.address,
|
|
300
|
-
solanaKey,
|
|
301
|
-
solanaAddress: sol.address,
|
|
302
|
-
source
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
function parsesolanaKey(input) {
|
|
306
|
-
const trimmed = input.trim();
|
|
307
|
-
if (trimmed.startsWith("[")) {
|
|
308
|
-
const arr = JSON.parse(trimmed);
|
|
309
|
-
return new Uint8Array(arr);
|
|
310
|
-
}
|
|
311
|
-
return base58.decode(trimmed);
|
|
312
|
-
}
|
|
313
|
-
function solanaAddressFromKey(keyBytes) {
|
|
314
|
-
if (keyBytes.length >= 64) return base58.encode(keyBytes.slice(32));
|
|
315
|
-
return base58.encode(ed25519.getPublicKey(keyBytes));
|
|
316
|
-
}
|
|
317
|
-
function networkToCaipPrefix(name) {
|
|
318
|
-
switch (name.toLowerCase()) {
|
|
319
|
-
case "base": return "eip155:8453";
|
|
320
|
-
case "tempo": return "eip155:4217";
|
|
321
|
-
case "solana": return "solana:";
|
|
322
|
-
default: return name;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Validate that payTo addresses match the network format.
|
|
327
|
-
* Filters out malformed entries (e.g. EVM hex address on a Solana network).
|
|
328
|
-
*/
|
|
329
|
-
function createAddressValidationPolicy() {
|
|
330
|
-
return (_version, reqs) => {
|
|
331
|
-
const malformed = [];
|
|
332
|
-
const valid = reqs.filter((r) => {
|
|
333
|
-
if (r.network.startsWith("solana:") && r.payTo.startsWith("0x")) {
|
|
334
|
-
malformed.push(`Solana option has EVM-format payTo (${r.payTo})`);
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
if (r.network.startsWith("eip155:") && !r.payTo.startsWith("0x")) {
|
|
338
|
-
malformed.push(`EVM option has non-EVM payTo (${r.payTo})`);
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
return true;
|
|
342
|
-
});
|
|
343
|
-
if (valid.length === 0 && malformed.length > 0) throw new Error(`Server returned only malformed payment options:\n ${malformed.join("\n ")}\nThe server's payTo addresses don't match the advertised networks.`);
|
|
344
|
-
return valid;
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
function createNetworkFilter(network) {
|
|
348
|
-
const prefix = networkToCaipPrefix(network);
|
|
349
|
-
return (_version, reqs) => {
|
|
350
|
-
const filtered = reqs.filter((r) => r.network.startsWith(prefix));
|
|
351
|
-
if (filtered.length === 0) {
|
|
352
|
-
const available = [...new Set(reqs.map((r) => displayNetwork(r.network)))].join(", ");
|
|
353
|
-
throw new Error(`Network '${network}' not accepted. Available: ${available}`);
|
|
354
|
-
}
|
|
355
|
-
return filtered;
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function createNetworkPreference(network) {
|
|
359
|
-
const prefix = networkToCaipPrefix(network);
|
|
360
|
-
return (_version, accepts) => {
|
|
361
|
-
return accepts.find((r) => r.network.startsWith(prefix)) || accepts[0];
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Build a configured x402Client from resolved wallet keys.
|
|
366
|
-
*/
|
|
367
|
-
async function buildX402Client(wallet, opts) {
|
|
368
|
-
const client = new x402Client(opts?.preferredNetwork ? createNetworkPreference(opts.preferredNetwork) : void 0);
|
|
369
|
-
if (wallet.evmKey) {
|
|
370
|
-
const hex = wallet.evmKey;
|
|
371
|
-
registerExactEvmScheme(client, { signer: toClientEvmSigner(privateKeyToAccount(hex), createPublicClient({
|
|
372
|
-
chain: base,
|
|
373
|
-
transport: http()
|
|
374
|
-
})) });
|
|
375
|
-
}
|
|
376
|
-
if (wallet.solanaKey) {
|
|
377
|
-
const { createKeyPairSignerFromBytes } = await import("@solana/kit");
|
|
378
|
-
const signer = await createKeyPairSignerFromBytes(wallet.solanaKey);
|
|
379
|
-
client.register("solana:*", new OptimizedSvmScheme(signer));
|
|
380
|
-
}
|
|
381
|
-
client.registerPolicy(createAddressValidationPolicy());
|
|
382
|
-
if (opts?.network) client.registerPolicy(createNetworkFilter(opts.network));
|
|
383
|
-
const daily = opts?.spendLimitDaily;
|
|
384
|
-
const perTx = opts?.spendLimitPerTx;
|
|
385
|
-
if (daily || perTx) client.registerPolicy((_version, reqs) => {
|
|
386
|
-
if (daily) {
|
|
387
|
-
const spend = calcSpend(readHistory(getHistoryPath()));
|
|
388
|
-
if (spend.today >= daily) throw new Error(`Daily spend limit reached (${formatUsdcValue(spend.today)}/${daily} USDC)`);
|
|
389
|
-
const remaining = daily - spend.today;
|
|
390
|
-
reqs = reqs.filter((r) => Number(r.amount) / 1e6 <= remaining);
|
|
391
|
-
if (reqs.length === 0) throw new Error(`Daily spend limit of ${daily} USDC would be exceeded (${formatUsdcValue(spend.today)} spent today)`);
|
|
392
|
-
}
|
|
393
|
-
if (perTx) {
|
|
394
|
-
const before = reqs.length;
|
|
395
|
-
reqs = reqs.filter((r) => Number(r.amount) / 1e6 <= perTx);
|
|
396
|
-
if (reqs.length === 0 && before > 0) throw new Error(`Payment exceeds per-transaction limit of ${perTx} USDC`);
|
|
397
|
-
}
|
|
398
|
-
return reqs;
|
|
399
|
-
});
|
|
400
|
-
return client;
|
|
401
|
-
}
|
|
402
|
-
//#endregion
|
|
403
|
-
//#region src/commands/wallet.ts
|
|
404
|
-
const BASE_RPC = "https://mainnet.base.org";
|
|
405
|
-
const SOLANA_RPC = "https://api.mainnet-beta.solana.com";
|
|
406
|
-
const TEMPO_RPC = "https://rpc.presto.tempo.xyz";
|
|
407
|
-
const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
408
|
-
const USDC_TEMPO = "0x20C000000000000000000000b9537d11c60E8b50";
|
|
409
|
-
const USDC_SOLANA_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
410
|
-
const TOKEN_PROGRAM = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
|
411
|
-
const ATA_PROGRAM = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
|
412
|
-
async function rpcCall(url, method, params) {
|
|
413
|
-
const res = await fetch(url, {
|
|
414
|
-
method: "POST",
|
|
415
|
-
headers: { "Content-Type": "application/json" },
|
|
416
|
-
body: JSON.stringify({
|
|
417
|
-
jsonrpc: "2.0",
|
|
418
|
-
method,
|
|
419
|
-
params,
|
|
420
|
-
id: 1
|
|
421
|
-
})
|
|
422
|
-
});
|
|
423
|
-
if (!res.ok) throw new Error(`RPC ${method} failed: ${res.status} ${res.statusText}`);
|
|
424
|
-
return res.json();
|
|
425
|
-
}
|
|
426
|
-
async function fetchEvmBalances(address) {
|
|
427
|
-
const usdcData = `0x70a08231${address.slice(2).padStart(64, "0")}`;
|
|
428
|
-
const [ethRes, usdcRes] = await Promise.all([rpcCall(BASE_RPC, "eth_getBalance", [address, "latest"]), rpcCall(BASE_RPC, "eth_call", [{
|
|
429
|
-
to: USDC_BASE,
|
|
430
|
-
data: usdcData
|
|
431
|
-
}, "latest"])]);
|
|
432
|
-
return {
|
|
433
|
-
eth: ethRes.result ? (Number(BigInt(ethRes.result)) / 0xde0b6b3a7640000).toFixed(6) : "?",
|
|
434
|
-
usdc: usdcRes.result ? formatUsdcValue(Number(BigInt(usdcRes.result)) / 1e6) : "?"
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
async function fetchTempoBalances(address) {
|
|
438
|
-
const res = await rpcCall(TEMPO_RPC, "eth_call", [{
|
|
439
|
-
to: USDC_TEMPO,
|
|
440
|
-
data: `0x70a08231${address.slice(2).padStart(64, "0")}`
|
|
441
|
-
}, "latest"]);
|
|
442
|
-
return { usdc: res.result ? formatUsdcValue(Number(BigInt(res.result)) / 1e6) : "?" };
|
|
443
|
-
}
|
|
444
|
-
async function getUsdcAta(owner) {
|
|
445
|
-
const encoder = getAddressEncoder();
|
|
446
|
-
const [ata] = await getProgramDerivedAddress({
|
|
447
|
-
programAddress: address(ATA_PROGRAM),
|
|
448
|
-
seeds: [
|
|
449
|
-
encoder.encode(address(owner)),
|
|
450
|
-
encoder.encode(address(TOKEN_PROGRAM)),
|
|
451
|
-
encoder.encode(address(USDC_SOLANA_MINT))
|
|
452
|
-
]
|
|
453
|
-
});
|
|
454
|
-
return ata;
|
|
455
|
-
}
|
|
456
|
-
async function fetchSolanaBalances(ownerAddress) {
|
|
457
|
-
const ata = await getUsdcAta(ownerAddress);
|
|
458
|
-
const [solRes, usdcRes] = await Promise.all([rpcCall(SOLANA_RPC, "getBalance", [ownerAddress]), rpcCall(SOLANA_RPC, "getTokenAccountBalance", [ata])]);
|
|
459
|
-
const sol = solRes.result?.value != null ? (solRes.result.value / 1e9).toFixed(6) : "?";
|
|
460
|
-
const usdcVal = usdcRes.result?.value;
|
|
461
|
-
return {
|
|
462
|
-
sol,
|
|
463
|
-
usdc: usdcVal ? formatUsdcValue(Number(usdcVal.uiAmountString)) : "0"
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function balanceLine(usdc, native, nativeSymbol) {
|
|
467
|
-
return pc.dim(` (${usdc} USDC, ${native} ${nativeSymbol})`);
|
|
468
|
-
}
|
|
469
|
-
async function fetchAllBalances(evmAddress, solanaAddress) {
|
|
470
|
-
const [evmResult, solResult, tempoResult] = await Promise.allSettled([
|
|
471
|
-
evmAddress ? fetchEvmBalances(evmAddress) : Promise.resolve(null),
|
|
472
|
-
solanaAddress ? fetchSolanaBalances(solanaAddress) : Promise.resolve(null),
|
|
473
|
-
evmAddress ? fetchTempoBalances(evmAddress) : Promise.resolve(null)
|
|
474
|
-
]);
|
|
475
|
-
return {
|
|
476
|
-
evm: evmResult.status === "fulfilled" ? evmResult.value : null,
|
|
477
|
-
sol: solResult.status === "fulfilled" ? solResult.value : null,
|
|
478
|
-
tempo: tempoResult.status === "fulfilled" ? tempoResult.value : null
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
const walletInfoCommand = buildCommand({
|
|
482
|
-
docs: { brief: "Show wallet addresses and balances" },
|
|
483
|
-
parameters: {
|
|
484
|
-
flags: { verbose: {
|
|
485
|
-
kind: "boolean",
|
|
486
|
-
brief: "Show transaction IDs",
|
|
487
|
-
default: false
|
|
488
|
-
} },
|
|
489
|
-
positional: {
|
|
490
|
-
kind: "tuple",
|
|
491
|
-
parameters: []
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
async func(flags) {
|
|
495
|
-
const wallet = resolveWallet();
|
|
496
|
-
if (wallet.source === "none") {
|
|
497
|
-
console.log(pc.yellow("No wallet configured."));
|
|
498
|
-
console.log(pc.dim(`\nRun:\n ${pc.cyan("$ npx x402-proxy setup")}\n\nOr set ${pc.cyan("X402_PROXY_WALLET_MNEMONIC")} environment variable.`));
|
|
499
|
-
process.exit(1);
|
|
500
|
-
}
|
|
501
|
-
console.log();
|
|
502
|
-
info("Wallet");
|
|
503
|
-
console.log();
|
|
504
|
-
console.log(pc.dim(` Source: ${wallet.source}`));
|
|
505
|
-
const { evm, sol, tempo } = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
506
|
-
if (wallet.evmAddress) {
|
|
507
|
-
const bal = evm ? balanceLine(evm.usdc, evm.eth, "ETH") : pc.dim(" (network error)");
|
|
508
|
-
console.log(` Base: ${pc.green(wallet.evmAddress)}${bal}`);
|
|
509
|
-
}
|
|
510
|
-
if (wallet.evmAddress) {
|
|
511
|
-
const bal = tempo ? pc.dim(` (${tempo.usdc} USDC)`) : pc.dim(" (network error)");
|
|
512
|
-
console.log(` Tempo: ${pc.green(wallet.evmAddress)}${bal}`);
|
|
513
|
-
}
|
|
514
|
-
if (wallet.solanaAddress) {
|
|
515
|
-
const bal = sol ? balanceLine(sol.usdc, sol.sol, "SOL") : pc.dim(" (network error)");
|
|
516
|
-
console.log(` Solana: ${pc.green(wallet.solanaAddress)}${bal}`);
|
|
517
|
-
}
|
|
518
|
-
const evmEmpty = !evm || Number(evm.usdc) === 0;
|
|
519
|
-
const solEmpty = !sol || Number(sol.usdc) === 0;
|
|
520
|
-
const tempoEmpty = !tempo || Number(tempo.usdc) === 0;
|
|
521
|
-
if (evmEmpty && solEmpty && tempoEmpty) {
|
|
522
|
-
console.log();
|
|
523
|
-
dim(" Send USDC to any address above to start using paid APIs.");
|
|
524
|
-
}
|
|
525
|
-
console.log();
|
|
526
|
-
const records = readHistory(getHistoryPath());
|
|
527
|
-
if (records.length > 0) {
|
|
528
|
-
const spend = calcSpend(records);
|
|
529
|
-
const recent = records.slice(-10);
|
|
530
|
-
dim(" Recent transactions:");
|
|
531
|
-
for (const r of recent) {
|
|
532
|
-
const line = formatTxLine(r, { verbose: flags.verbose }).replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
533
|
-
console.log(line);
|
|
534
|
-
}
|
|
535
|
-
console.log();
|
|
536
|
-
console.log(pc.dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} tx`));
|
|
537
|
-
} else dim(" No transactions yet.");
|
|
538
|
-
console.log();
|
|
539
|
-
console.log(pc.dim(" See also: wallet history, wallet export-key"));
|
|
540
|
-
console.log();
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
//#endregion
|
|
544
|
-
export { error as _, fetchTempoBalances as a, success as b, resolveWallet as c, displayNetwork as d, formatAmount as f, dim as g, readHistory as h, fetchSolanaBalances as i, appendHistory as l, formatUsdcValue as m, fetchAllBalances as n, walletInfoCommand as o, formatTxLine as p, fetchEvmBalances as r, buildX402Client as s, balanceLine as t, calcSpend as u, info as v, warn as x, isTTY as y };
|
package/dist/wallet-DBrVZJqe.js
DELETED
package/openclaw.plugin.json
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
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": "1.0.0",
|
|
6
|
-
"providers": ["surf"],
|
|
7
|
-
"autoEnableWhenConfiguredProviders": ["surf"],
|
|
8
|
-
"skills": ["skills"],
|
|
9
|
-
"contracts": {
|
|
10
|
-
"tools": ["x_wallet", "x_balance", "x_request", "x_payment"]
|
|
11
|
-
},
|
|
12
|
-
"configSchema": {
|
|
13
|
-
"type": "object",
|
|
14
|
-
"additionalProperties": false,
|
|
15
|
-
"properties": {
|
|
16
|
-
"providers": {
|
|
17
|
-
"type": "object",
|
|
18
|
-
"description": "Provider catalog keyed by name. Defaults to the built-in surf provider when omitted.",
|
|
19
|
-
"additionalProperties": {
|
|
20
|
-
"type": "object",
|
|
21
|
-
"additionalProperties": false,
|
|
22
|
-
"properties": {
|
|
23
|
-
"baseUrl": {
|
|
24
|
-
"type": "string",
|
|
25
|
-
"description": "Gateway base path for this provider, typically under /x402-proxy"
|
|
26
|
-
},
|
|
27
|
-
"upstreamUrl": {
|
|
28
|
-
"type": "string",
|
|
29
|
-
"description": "Upstream inference origin to proxy to"
|
|
30
|
-
},
|
|
31
|
-
"protocol": {
|
|
32
|
-
"type": "string",
|
|
33
|
-
"enum": ["x402", "mpp", "auto"],
|
|
34
|
-
"description": "Payment protocol for this provider. Defaults to mpp."
|
|
35
|
-
},
|
|
36
|
-
"mppSessionBudget": {
|
|
37
|
-
"type": "string",
|
|
38
|
-
"description": "Maximum USDC budget to lock for MPP sessions for this provider (default: 0.5)"
|
|
39
|
-
},
|
|
40
|
-
"models": {
|
|
41
|
-
"type": "array",
|
|
42
|
-
"description": "Static OpenClaw model catalog entries for this provider"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"protocol": {
|
|
48
|
-
"type": "string",
|
|
49
|
-
"enum": ["x402", "mpp", "auto"],
|
|
50
|
-
"description": "Default payment protocol fallback for providers that do not override it."
|
|
51
|
-
},
|
|
52
|
-
"mppSessionBudget": {
|
|
53
|
-
"type": "string",
|
|
54
|
-
"description": "Default maximum USDC budget to lock for MPP sessions (default: 0.5)"
|
|
55
|
-
},
|
|
56
|
-
"keypairPath": {
|
|
57
|
-
"type": "string",
|
|
58
|
-
"description": "Optional path to Solana keypair JSON file (overrides x402-proxy wallet resolution for Solana/x402 surfaces)"
|
|
59
|
-
},
|
|
60
|
-
"rpcUrl": {
|
|
61
|
-
"type": "string",
|
|
62
|
-
"description": "Solana RPC URL"
|
|
63
|
-
},
|
|
64
|
-
"dashboardUrl": {
|
|
65
|
-
"type": "string",
|
|
66
|
-
"description": "URL to link from /x_wallet dashboard"
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"required": []
|
|
70
|
-
},
|
|
71
|
-
"uiHints": {
|
|
72
|
-
"protocol": {
|
|
73
|
-
"label": "Default protocol",
|
|
74
|
-
"help": "Used only when a provider entry does not set its own protocol."
|
|
75
|
-
},
|
|
76
|
-
"mppSessionBudget": {
|
|
77
|
-
"label": "Default MPP budget",
|
|
78
|
-
"placeholder": "0.5"
|
|
79
|
-
},
|
|
80
|
-
"keypairPath": {
|
|
81
|
-
"label": "Solana keypair path",
|
|
82
|
-
"advanced": true
|
|
83
|
-
},
|
|
84
|
-
"rpcUrl": {
|
|
85
|
-
"label": "Solana RPC URL",
|
|
86
|
-
"advanced": true
|
|
87
|
-
},
|
|
88
|
-
"dashboardUrl": {
|
|
89
|
-
"label": "Dashboard URL",
|
|
90
|
-
"advanced": true
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|