agentwallet-mcp 1.10.0 → 1.10.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 +326 -316
- package/build/index.js +82 -90
- package/build/ssrf-guard.d.ts +38 -0
- package/build/ssrf-guard.js +178 -0
- package/package.json +68 -68
package/README.md
CHANGED
|
@@ -1,316 +1,326 @@
|
|
|
1
|
-
# AgentWallet MCP Server
|
|
2
|
-
|
|
3
|
-
Permissionless wallet infrastructure for AI agents. Create wallets, sign transactions, and broadcast on-chain, on any EVM chain and Solana. Built-in guards. No KYC. The only AI agent wallet that accepts crypto for its own API fees.
|
|
4
|
-
|
|
5
|
-
**Your keys can stay on your machine.** Set one environment variable and every signature happens in your own process. No server sees the key, no company can freeze the wallet, and you can verify that claim by reading `src/local-wallet.ts` or by running the server with the API pointed at a closed port.
|
|
6
|
-
|
|
7
|
-
**No KYC. No KYT. No approval process. No transaction monitoring. No one can block your wallet. Pay with USDC on-chain, no credit card required.**
|
|
8
|
-
|
|
9
|
-
## Two modes
|
|
10
|
-
|
|
11
|
-
| | Local (self-custody) | Hosted (custodial) |
|
|
12
|
-
|---|---|---|
|
|
13
|
-
| Who holds the key | You, in your own process | Encrypted on AgentWallet servers |
|
|
14
|
-
| Set up | One env var, no account needed | API key |
|
|
15
|
-
| Chains | EVM + Solana | EVM + Solana |
|
|
16
|
-
| Can anyone freeze it | No | Yes, that is what pause is for |
|
|
17
|
-
| Spend guards | `AGENTWALLET_MAX_TX_NATIVE`, `AGENTWALLET_MAX_TX_SOL`, `AGENTWALLET_MAX_AUTOPAY` | Server-side limits, pause, rate limits |
|
|
18
|
-
| Paywalls, usage, billing | Needs an API key too | Included |
|
|
19
|
-
|
|
20
|
-
Run `wallet_mode` at any time and the server will tell you which one you are in, and which address it controls.
|
|
21
|
-
|
|
22
|
-
### Local signing in one variable
|
|
23
|
-
|
|
24
|
-
```json
|
|
25
|
-
{
|
|
26
|
-
"mcpServers": {
|
|
27
|
-
"agentwallet": {
|
|
28
|
-
"command": "npx",
|
|
29
|
-
"args": ["-y", "agentwallet-mcp"],
|
|
30
|
-
"env": {
|
|
31
|
-
"AGENTWALLET_PRIVATE_KEY": "0xyour_key",
|
|
32
|
-
"AGENTWALLET_RPC_8453": "https://your-own-rpc",
|
|
33
|
-
"AGENTWALLET_MAX_TX_NATIVE": "0.05"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Use `AGENTWALLET_KEYFILE=/path/to/key` instead if you would rather keep the key out of your shell config. The key is read once, never written to disk, never logged, and never included in an error message.
|
|
41
|
-
|
|
42
|
-
`AGENTWALLET_RPC_<chainId>` (or `AGENTWALLET_RPC_URL` for all chains) points at an endpoint you trust. Without it a public RPC is used, and a public RPC can see which addresses you ask about.
|
|
43
|
-
|
|
44
|
-
`AGENTWALLET_MAX_TX_NATIVE` is a per-transaction ceiling in native units. In hosted mode the server enforces limits; in local mode there is no server, so this guard is the only one there is. Set it.
|
|
45
|
-
|
|
46
|
-
### Solana local signing
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
"env": {
|
|
50
|
-
"AGENTWALLET_SOLANA_KEY": "[12,34...]",
|
|
51
|
-
"AGENTWALLET_SOLANA_RPC": "https://your-own-rpc",
|
|
52
|
-
"AGENTWALLET_MAX_TX_SOL": "1"
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Accepts whichever format you already have: a `solana-keygen` id.json array, a base58 secret key as exported by Phantom, or base64. Use `AGENTWALLET_SOLANA_KEYFILE` to point at a file instead. Native SOL and SPL token transfers are both signed locally, and a missing associated token account is created for the recipient automatically.
|
|
57
|
-
|
|
58
|
-
Set either key, or both. They are independent: run EVM locally and Solana hosted, or the reverse.
|
|
59
|
-
|
|
60
|
-
**An operation with no matching local key is refused, never silently routed to the hosted signer.** If you have an EVM key configured and ask for a Solana transfer with no Solana key, the server stops and tells you which variable is missing. Quietly moving funds onto a key you do not hold, while you believe you are in self-custody, is the worst thing this server could do.
|
|
61
|
-
|
|
62
|
-
### About the dependency tree
|
|
63
|
-
|
|
64
|
-
Local signing uses [viem](https://viem.sh) for EVM and `@solana/web3.js` for Solana, plus `bs58` for key parsing. SPL instructions are built by hand rather than with `@solana/spl-token`, because that package pulls in `bigint-buffer`, which carries a high severity buffer overflow advisory. A wallet has no business shipping that to save a dozen lines of instruction encoding.
|
|
65
|
-
|
|
66
|
-
`npm audit` currently reports issues inside `@modelcontextprotocol/sdk`'s HTTP transport dependencies. This server speaks stdio, so that code never loads, and the SDK is not something this package can patch. Run the audit yourself. Publishing a tree you can inspect is the point.
|
|
67
|
-
|
|
68
|
-
<p align="center">
|
|
69
|
-
<img src="assets/demo.svg" alt="AgentWallet demo, AI agent pays x402 invoice automatically" width="800">
|
|
70
|
-
</p>
|
|
71
|
-
|
|
72
|
-
## Features
|
|
73
|
-
|
|
74
|
-
- **31 MCP tools**: create wallets, send transactions, approve tokens, wrap ETH, transfer SPL tokens, pay and accept x402 payments, verify custody mode, and more
|
|
75
|
-
- **EVM + Solana**: Ethereum, Base, Polygon, BSC, Arbitrum, Optimism, Avalanche, Zora, PulseChain, Solana, and any other EVM-compatible chain
|
|
76
|
-
- **SOL + SPL tokens**: native SOL transfers and SPL token transfers (USDC, USDT, etc.) with automatic account creation
|
|
77
|
-
- **Built-in guards**: daily spending limits, gas price protection, emergency pause, rate limiting, replay protection, and on-chain verification, all active by default
|
|
78
|
-
- **x402 payments**: pay for x402-enabled APIs automatically, or accept x402 payments on your own endpoints (EVM and Solana)
|
|
79
|
-
- **Self-custody option**: run local mode and the key never leaves your machine. In hosted mode, keys are encrypted at rest, decrypted only during signing, and zeroed from memory immediately after. Either way you can export and walk away.
|
|
80
|
-
- **Permissionless**: No KYC. No KYT. No identity verification. No approval process. No compliance gatekeeping. Sign up, get an API key, and start transacting immediately.
|
|
81
|
-
- **30-second setup**: three lines of config. No SDK to install. No dependencies to manage.
|
|
82
|
-
|
|
83
|
-
## Pricing
|
|
84
|
-
|
|
85
|
-
- **$0.00345 per operation**
|
|
86
|
-
- **6,000 free operations/month**
|
|
87
|
-
- **$0.0005 per x402 verification**
|
|
88
|
-
- **1,000 free x402 verifications/month**
|
|
89
|
-
- **Pay with USDC on-chain** via x402, no credit card required
|
|
90
|
-
- No monthly fee, no tiers, just pay as you go
|
|
91
|
-
|
|
92
|
-
Competitor comparisons are kept at [hifriendbot.com/wallet/#pricing](https://hifriendbot.com/wallet/#pricing) with the date they were last verified. They live there rather than here because a published npm README cannot be corrected when someone else changes their prices.
|
|
93
|
-
|
|
94
|
-
## Quick Start
|
|
95
|
-
|
|
96
|
-
Get your free API key at [hifriendbot.com/wallet](https://hifriendbot.com/wallet), no credit card required, no KYC, no approval wait.
|
|
97
|
-
|
|
98
|
-
### Claude Desktop / OpenClaw
|
|
99
|
-
|
|
100
|
-
Add to your config:
|
|
101
|
-
|
|
102
|
-
```json
|
|
103
|
-
{
|
|
104
|
-
"mcpServers": {
|
|
105
|
-
"agentwallet": {
|
|
106
|
-
"command": "npx",
|
|
107
|
-
"args": ["-y", "agentwallet-mcp"],
|
|
108
|
-
"env": {
|
|
109
|
-
"AGENTWALLET_USER": "your_username",
|
|
110
|
-
"AGENTWALLET_PASS": "your_api_key",
|
|
111
|
-
"AGENTWALLET_WALLET_ID": "1"
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
> `AGENTWALLET_WALLET_ID` is optional. Set it to enable x402 auto-pay: when you exceed the free tier without a credit card, the MCP server automatically pays for operations with USDC from this wallet.
|
|
119
|
-
>
|
|
120
|
-
> **Auto-pay safety cap.** `AGENTWALLET_MAX_AUTOPAY` (optional, default `1`) is the maximum amount, in human-readable units of the asset, that x402 auto-pay will authorize for a single payment. Any 402 requirement above this cap is rejected instead of paid, so a malformed or tampered payment requirement cannot drain the wallet. Raise it only if you genuinely need larger automatic payments (for example `"5"` to allow up to 5 USDC per call).
|
|
121
|
-
|
|
122
|
-
### Claude Code
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
claude mcp add agentwallet \
|
|
126
|
-
-e AGENTWALLET_USER=your_username \
|
|
127
|
-
-e AGENTWALLET_PASS=your_api_key \
|
|
128
|
-
-e AGENTWALLET_WALLET_ID=1 \
|
|
129
|
-
-- npx -y agentwallet-mcp
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### VS Code
|
|
133
|
-
|
|
134
|
-
Add to your settings:
|
|
135
|
-
|
|
136
|
-
```json
|
|
137
|
-
{
|
|
138
|
-
"mcp": {
|
|
139
|
-
"servers": {
|
|
140
|
-
"agentwallet": {
|
|
141
|
-
"command": "npx",
|
|
142
|
-
"args": ["-y", "agentwallet-mcp"],
|
|
143
|
-
"env": {
|
|
144
|
-
"AGENTWALLET_USER": "your_username",
|
|
145
|
-
"AGENTWALLET_PASS": "your_api_key",
|
|
146
|
-
"AGENTWALLET_WALLET_ID": "1"
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Tools
|
|
155
|
-
|
|
156
|
-
| Tool | Description |
|
|
157
|
-
|------|-------------|
|
|
158
|
-
| `create_wallet` | Create a new EVM or Solana wallet |
|
|
159
|
-
| `list_wallets` | List all your wallets |
|
|
160
|
-
| `get_wallet` | Get wallet details by ID |
|
|
161
|
-
| `get_balance` | Check native token balance on any chain |
|
|
162
|
-
| `get_token_balance` | Check ERC-20 or SPL token balance |
|
|
163
|
-
| `get_token_info` | Get ERC-20 token name, symbol, and decimals |
|
|
164
|
-
| `transfer` | Send native tokens (ETH, SOL, POL, BNB, etc.) |
|
|
165
|
-
| `transfer_token` | Send ERC-20 or SPL tokens (USDC, USDT, etc.) |
|
|
166
|
-
| `send_transaction` | Sign and broadcast a raw transaction |
|
|
167
|
-
| `sign_transaction` | Sign a transaction without broadcasting |
|
|
168
|
-
| `call_contract` | Read-only contract call (eth_call) |
|
|
169
|
-
| `approve_token` | Approve ERC-20 token spending for DeFi |
|
|
170
|
-
| `get_allowance` | Check ERC-20 token allowance |
|
|
171
|
-
| `wrap_eth` | Wrap native tokens to WETH/WAVAX/etc. |
|
|
172
|
-
| `unwrap_eth` | Unwrap WETH back to native tokens |
|
|
173
|
-
| `pay_x402` | Pay x402 invoices automatically (fetch, pay, retry) |
|
|
174
|
-
| `create_paywall` | Create an x402 paywall to charge for a resource |
|
|
175
|
-
| `list_paywalls` | List all your x402 paywalls |
|
|
176
|
-
| `get_paywall` | Get paywall details by ID |
|
|
177
|
-
| `update_paywall` | Update paywall pricing, resource, or status |
|
|
178
|
-
| `delete_paywall` | Delete a paywall |
|
|
179
|
-
| `get_paywall_payments` | View payment history for a paywall |
|
|
180
|
-
| `get_x402_revenue` | Aggregate revenue stats across all paywalls |
|
|
181
|
-
| `wallet_mode` | Report whether signing is local (self-custody) or hosted, and which address is in use |
|
|
182
|
-
| `export_wallet_key` | How to export a hosted wallet key and move to self-custody |
|
|
183
|
-
| `buy_verification_credits` | Buy x402 verification credits with USDC on-chain |
|
|
184
|
-
| `get_usage` | Check your monthly usage and billing |
|
|
185
|
-
| `get_chains` | List all supported chains |
|
|
186
|
-
| `pause_wallet` | Emergency pause a wallet |
|
|
187
|
-
| `unpause_wallet` | Resume a paused wallet |
|
|
188
|
-
| `delete_wallet` | Delete a wallet |
|
|
189
|
-
|
|
190
|
-
## Supported Chains
|
|
191
|
-
|
|
192
|
-
| Chain | ID | Native Token | Stablecoin |
|
|
193
|
-
|-------|-----|-------------|------------|
|
|
194
|
-
| Ethereum | 1 | ETH | USDC |
|
|
195
|
-
| Base | 8453 | ETH | USDC |
|
|
196
|
-
| Polygon | 137 | POL | USDC |
|
|
197
|
-
| BSC | 56 | BNB | USDT |
|
|
198
|
-
| Arbitrum | 42161 | ETH | USDC |
|
|
199
|
-
| Optimism | 10 | ETH | USDC |
|
|
200
|
-
| Avalanche | 43114 | AVAX | USDC |
|
|
201
|
-
| Zora | 7777777 | ETH | USDC |
|
|
202
|
-
| PulseChain | 369 | PLS | USDC |
|
|
203
|
-
| Solana | 900 | SOL | USDC |
|
|
204
|
-
| Solana Devnet | 901 | SOL | USDC |
|
|
205
|
-
|
|
206
|
-
## Use Case: GuessMarket
|
|
207
|
-
|
|
208
|
-
Pair with [guessmarket-mcp](https://www.npmjs.com/package/guessmarket-mcp) to let your AI agent trade prediction markets:
|
|
209
|
-
|
|
210
|
-
1. Create a wallet on Base
|
|
211
|
-
2. Approve USDC spending
|
|
212
|
-
3. Buy YES/NO shares on prediction markets
|
|
213
|
-
4. Provide liquidity and earn trading fees
|
|
214
|
-
5. Claim winnings
|
|
215
|
-
|
|
216
|
-
All on-chain. All through MCP. No frontend needed.
|
|
217
|
-
|
|
218
|
-
## x402 Payments
|
|
219
|
-
|
|
220
|
-
AgentWallet natively supports the [x402 open payment standard](https://x402.org). When your Ai agent encounters an API that returns HTTP 402 Payment Required, the `pay_x402` tool handles the entire flow automatically:
|
|
221
|
-
|
|
222
|
-
1. Fetches the URL and detects the 402 response
|
|
223
|
-
2. Parses the payment requirements (amount, token, chain)
|
|
224
|
-
3. Executes the on-chain payment from your wallet
|
|
225
|
-
4. Retries the request with proof of payment
|
|
226
|
-
5. Returns the final response
|
|
227
|
-
|
|
228
|
-
**Set `max_payment` to control spending:**
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
pay_x402(
|
|
232
|
-
url="https://api.example.com/premium-data",
|
|
233
|
-
wallet_id=1,
|
|
234
|
-
max_payment="1.00"
|
|
235
|
-
)
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
`max_payment` is enforced as a hard per-payment cap. If you omit it, `pay_x402` falls back to `AGENTWALLET_MAX_AUTOPAY` (default `1`), the same cap the auto-pay path uses, so a malicious or compromised 402 endpoint can never authorize an unbounded payment. Set `max_payment` explicitly (or raise `AGENTWALLET_MAX_AUTOPAY`) to allow a larger single payment.
|
|
239
|
-
|
|
240
|
-
Supports ERC-20 tokens, SPL tokens, and native tokens on EVM and Solana. Compatible with x402 V1 and V2 (CAIP-2 chain identifiers), and reads the token address from the standard x402 `asset` field (falling back to `extra.token`).
|
|
241
|
-
|
|
242
|
-
## x402 Acceptance
|
|
243
|
-
|
|
244
|
-
AgentWallet also lets you **accept** x402 payments. Create a paywall, point it at any resource, and get a public URL that charges agents automatically:
|
|
245
|
-
|
|
246
|
-
```
|
|
247
|
-
create_paywall(
|
|
248
|
-
wallet_id=1,
|
|
249
|
-
name="Premium API",
|
|
250
|
-
amount="0.01",
|
|
251
|
-
token_name="USDC",
|
|
252
|
-
token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
253
|
-
chain_id=8453,
|
|
254
|
-
resource_url="https://your-api.com/data"
|
|
255
|
-
)
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
When an agent hits the paywall URL:
|
|
259
|
-
1. Gets back HTTP 402 with payment requirements
|
|
260
|
-
2. Pays on-chain using `pay_x402` (or any x402-compatible client)
|
|
261
|
-
3. Retries with proof of payment
|
|
262
|
-
4. Receives the protected content
|
|
263
|
-
|
|
264
|
-
On-chain verification ensures every payment is real. Replay protection prevents double-spending. Revenue tracking shows you who paid, how much, and when. 1,000 free verifications/month, then $0.0005 each. See the [pricing comparison](https://hifriendbot.com/wallet/#pricing) for how that stacks up.
|
|
265
|
-
|
|
266
|
-
## How We Compare
|
|
267
|
-
|
|
268
|
-
| Feature | Coinbase CDP | AgentWallet |
|
|
269
|
-
|---------|-------------|-------------|
|
|
270
|
-
| Setup Time | Install SDK + configure | **3 lines of config** |
|
|
271
|
-
| Approval Process | Identity verification required | **None, instant access** |
|
|
272
|
-
| KYC Required | Yes | **No** |
|
|
273
|
-
| KYT / Transaction Monitoring | Yes | **No** |
|
|
274
|
-
| Can Block Your Wallet | Yes | **No** |
|
|
275
|
-
| Built-in Guards | Yes (requires setup) | **Yes (active by default)** |
|
|
276
|
-
| Free Operations / Month | 5,000 | **6,000** |
|
|
277
|
-
| Cost Per Operation | $0.005 | **$0.00345** |
|
|
278
|
-
| x402 Verification Cost | $0.001 | **$0.0005** |
|
|
279
|
-
| Free x402 Verifications / Month | 1,000 | **1,000** |
|
|
280
|
-
| x402 Acceptance (Paywalls) | No | **Yes** |
|
|
281
|
-
| Pay for API Fees with Crypto | No (credit card only) | **Yes (USDC via x402)** |
|
|
282
|
-
| Supported Chains | 8 EVM + Solana | **Any EVM + Solana** |
|
|
283
|
-
| Token Tools | Yes | **ERC-20 + SPL (29 tools)** |
|
|
284
|
-
| MCP Server | Yes | **Yes** |
|
|
285
|
-
|
|
286
|
-
## Pay with Crypto, No Credit Card Required
|
|
287
|
-
|
|
288
|
-
AgentWallet is the only AI agent wallet infrastructure that accepts crypto for its own API fees. Every competitor, Coinbase CDP, Circle, MoonPay, Crossmint, Turnkey, requires a credit card or monthly invoice. With AgentWallet, your agent can pay for operations with USDC on-chain via the x402 protocol. No credit card, no invoice, no billing portal. Just on-chain payments.
|
|
289
|
-
|
|
290
|
-
When your agent exceeds the free tier (6,000 ops/month) without a credit card configured, the API returns HTTP 402 with USDC payment instructions. Your agent pays on-chain, retries with proof of payment, and the operation executes. Fully automated via the MCP server.
|
|
291
|
-
|
|
292
|
-
You can also pre-purchase x402 verification credits with USDC using the `buy_verification_credits` tool, keeping your paywalls running beyond the free 1,000 verifications/month without needing a credit card.
|
|
293
|
-
|
|
294
|
-
## Built-in Guards
|
|
295
|
-
|
|
296
|
-
All guards are active by default, no configuration required.
|
|
297
|
-
|
|
298
|
-
- **Encrypted at rest**: private keys encrypted before storage and never leave the server
|
|
299
|
-
- **Memory zeroing**: keys wiped from memory immediately after every signing operation
|
|
300
|
-
- **Daily spending limits**: set a per-wallet daily cap in USD, enforced automatically on every transaction
|
|
301
|
-
- **Gas price protection**: transactions blocked when gas prices spike above safe thresholds
|
|
302
|
-
- **Emergency pause**: instantly freeze any wallet or all wallets with one click
|
|
303
|
-
- **Rate limiting**: API requests capped per minute to prevent abuse and brute force attacks
|
|
304
|
-
- **Replay protection**: every x402 payment verified on-chain with unique transaction tracking
|
|
305
|
-
- **On-chain verification**: x402 payments verified directly on the blockchain with finalized commitment
|
|
306
|
-
- Bug bounty program: $50,$500 for responsible disclosure ([details](https://hifriendbot.com/wallet))
|
|
307
|
-
|
|
308
|
-
## Links
|
|
309
|
-
|
|
310
|
-
- **Website:** [hifriendbot.com/wallet](https://hifriendbot.com/wallet)
|
|
311
|
-
- **npm:** [agentwallet-mcp](https://www.npmjs.com/package/agentwallet-mcp)
|
|
312
|
-
- **Security:** [security@hifriendbot.com](mailto:security@hifriendbot.com)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
1
|
+
# AgentWallet MCP Server
|
|
2
|
+
|
|
3
|
+
Permissionless wallet infrastructure for AI agents. Create wallets, sign transactions, and broadcast on-chain, on any EVM chain and Solana. Built-in guards. No KYC. The only AI agent wallet that accepts crypto for its own API fees.
|
|
4
|
+
|
|
5
|
+
**Your keys can stay on your machine.** Set one environment variable and every signature happens in your own process. No server sees the key, no company can freeze the wallet, and you can verify that claim by reading `src/local-wallet.ts` or by running the server with the API pointed at a closed port.
|
|
6
|
+
|
|
7
|
+
**No KYC. No KYT. No approval process. No transaction monitoring. No one can block your wallet. Pay with USDC on-chain, no credit card required.**
|
|
8
|
+
|
|
9
|
+
## Two modes
|
|
10
|
+
|
|
11
|
+
| | Local (self-custody) | Hosted (custodial) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Who holds the key | You, in your own process | Encrypted on AgentWallet servers |
|
|
14
|
+
| Set up | One env var, no account needed | API key |
|
|
15
|
+
| Chains | EVM + Solana | EVM + Solana |
|
|
16
|
+
| Can anyone freeze it | No | Yes, that is what pause is for |
|
|
17
|
+
| Spend guards | `AGENTWALLET_MAX_TX_NATIVE`, `AGENTWALLET_MAX_TX_SOL`, `AGENTWALLET_MAX_AUTOPAY` | Server-side limits, pause, rate limits |
|
|
18
|
+
| Paywalls, usage, billing | Needs an API key too | Included |
|
|
19
|
+
|
|
20
|
+
Run `wallet_mode` at any time and the server will tell you which one you are in, and which address it controls.
|
|
21
|
+
|
|
22
|
+
### Local signing in one variable
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"agentwallet": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "agentwallet-mcp"],
|
|
30
|
+
"env": {
|
|
31
|
+
"AGENTWALLET_PRIVATE_KEY": "0xyour_key",
|
|
32
|
+
"AGENTWALLET_RPC_8453": "https://your-own-rpc",
|
|
33
|
+
"AGENTWALLET_MAX_TX_NATIVE": "0.05"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `AGENTWALLET_KEYFILE=/path/to/key` instead if you would rather keep the key out of your shell config. The key is read once, never written to disk, never logged, and never included in an error message.
|
|
41
|
+
|
|
42
|
+
`AGENTWALLET_RPC_<chainId>` (or `AGENTWALLET_RPC_URL` for all chains) points at an endpoint you trust. Without it a public RPC is used, and a public RPC can see which addresses you ask about.
|
|
43
|
+
|
|
44
|
+
`AGENTWALLET_MAX_TX_NATIVE` is a per-transaction ceiling in native units. In hosted mode the server enforces limits; in local mode there is no server, so this guard is the only one there is. Set it.
|
|
45
|
+
|
|
46
|
+
### Solana local signing
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
"env": {
|
|
50
|
+
"AGENTWALLET_SOLANA_KEY": "[12,34...]",
|
|
51
|
+
"AGENTWALLET_SOLANA_RPC": "https://your-own-rpc",
|
|
52
|
+
"AGENTWALLET_MAX_TX_SOL": "1"
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Accepts whichever format you already have: a `solana-keygen` id.json array, a base58 secret key as exported by Phantom, or base64. Use `AGENTWALLET_SOLANA_KEYFILE` to point at a file instead. Native SOL and SPL token transfers are both signed locally, and a missing associated token account is created for the recipient automatically.
|
|
57
|
+
|
|
58
|
+
Set either key, or both. They are independent: run EVM locally and Solana hosted, or the reverse.
|
|
59
|
+
|
|
60
|
+
**An operation with no matching local key is refused, never silently routed to the hosted signer.** If you have an EVM key configured and ask for a Solana transfer with no Solana key, the server stops and tells you which variable is missing. Quietly moving funds onto a key you do not hold, while you believe you are in self-custody, is the worst thing this server could do.
|
|
61
|
+
|
|
62
|
+
### About the dependency tree
|
|
63
|
+
|
|
64
|
+
Local signing uses [viem](https://viem.sh) for EVM and `@solana/web3.js` for Solana, plus `bs58` for key parsing. SPL instructions are built by hand rather than with `@solana/spl-token`, because that package pulls in `bigint-buffer`, which carries a high severity buffer overflow advisory. A wallet has no business shipping that to save a dozen lines of instruction encoding.
|
|
65
|
+
|
|
66
|
+
`npm audit` currently reports issues inside `@modelcontextprotocol/sdk`'s HTTP transport dependencies. This server speaks stdio, so that code never loads, and the SDK is not something this package can patch. Run the audit yourself. Publishing a tree you can inspect is the point.
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<img src="assets/demo.svg" alt="AgentWallet demo, AI agent pays x402 invoice automatically" width="800">
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
- **31 MCP tools**: create wallets, send transactions, approve tokens, wrap ETH, transfer SPL tokens, pay and accept x402 payments, verify custody mode, and more
|
|
75
|
+
- **EVM + Solana**: Ethereum, Base, Polygon, BSC, Arbitrum, Optimism, Avalanche, Zora, PulseChain, Solana, and any other EVM-compatible chain
|
|
76
|
+
- **SOL + SPL tokens**: native SOL transfers and SPL token transfers (USDC, USDT, etc.) with automatic account creation
|
|
77
|
+
- **Built-in guards**: daily spending limits, gas price protection, emergency pause, rate limiting, replay protection, and on-chain verification, all active by default
|
|
78
|
+
- **x402 payments**: pay for x402-enabled APIs automatically, or accept x402 payments on your own endpoints (EVM and Solana)
|
|
79
|
+
- **Self-custody option**: run local mode and the key never leaves your machine. In hosted mode, keys are encrypted at rest, decrypted only during signing, and zeroed from memory immediately after. Either way you can export and walk away.
|
|
80
|
+
- **Permissionless**: No KYC. No KYT. No identity verification. No approval process. No compliance gatekeeping. Sign up, get an API key, and start transacting immediately.
|
|
81
|
+
- **30-second setup**: three lines of config. No SDK to install. No dependencies to manage.
|
|
82
|
+
|
|
83
|
+
## Pricing
|
|
84
|
+
|
|
85
|
+
- **$0.00345 per operation**
|
|
86
|
+
- **6,000 free operations/month**
|
|
87
|
+
- **$0.0005 per x402 verification**
|
|
88
|
+
- **1,000 free x402 verifications/month**
|
|
89
|
+
- **Pay with USDC on-chain** via x402, no credit card required
|
|
90
|
+
- No monthly fee, no tiers, just pay as you go
|
|
91
|
+
|
|
92
|
+
Competitor comparisons are kept at [hifriendbot.com/wallet/#pricing](https://hifriendbot.com/wallet/#pricing) with the date they were last verified. They live there rather than here because a published npm README cannot be corrected when someone else changes their prices.
|
|
93
|
+
|
|
94
|
+
## Quick Start
|
|
95
|
+
|
|
96
|
+
Get your free API key at [hifriendbot.com/wallet](https://hifriendbot.com/wallet), no credit card required, no KYC, no approval wait.
|
|
97
|
+
|
|
98
|
+
### Claude Desktop / OpenClaw
|
|
99
|
+
|
|
100
|
+
Add to your config:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"agentwallet": {
|
|
106
|
+
"command": "npx",
|
|
107
|
+
"args": ["-y", "agentwallet-mcp"],
|
|
108
|
+
"env": {
|
|
109
|
+
"AGENTWALLET_USER": "your_username",
|
|
110
|
+
"AGENTWALLET_PASS": "your_api_key",
|
|
111
|
+
"AGENTWALLET_WALLET_ID": "1"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> `AGENTWALLET_WALLET_ID` is optional. Set it to enable x402 auto-pay: when you exceed the free tier without a credit card, the MCP server automatically pays for operations with USDC from this wallet.
|
|
119
|
+
>
|
|
120
|
+
> **Auto-pay safety cap.** `AGENTWALLET_MAX_AUTOPAY` (optional, default `1`) is the maximum amount, in human-readable units of the asset, that x402 auto-pay will authorize for a single payment. Any 402 requirement above this cap is rejected instead of paid, so a malformed or tampered payment requirement cannot drain the wallet. Raise it only if you genuinely need larger automatic payments (for example `"5"` to allow up to 5 USDC per call).
|
|
121
|
+
|
|
122
|
+
### Claude Code
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
claude mcp add agentwallet \
|
|
126
|
+
-e AGENTWALLET_USER=your_username \
|
|
127
|
+
-e AGENTWALLET_PASS=your_api_key \
|
|
128
|
+
-e AGENTWALLET_WALLET_ID=1 \
|
|
129
|
+
-- npx -y agentwallet-mcp
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### VS Code
|
|
133
|
+
|
|
134
|
+
Add to your settings:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"mcp": {
|
|
139
|
+
"servers": {
|
|
140
|
+
"agentwallet": {
|
|
141
|
+
"command": "npx",
|
|
142
|
+
"args": ["-y", "agentwallet-mcp"],
|
|
143
|
+
"env": {
|
|
144
|
+
"AGENTWALLET_USER": "your_username",
|
|
145
|
+
"AGENTWALLET_PASS": "your_api_key",
|
|
146
|
+
"AGENTWALLET_WALLET_ID": "1"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Tools
|
|
155
|
+
|
|
156
|
+
| Tool | Description |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `create_wallet` | Create a new EVM or Solana wallet |
|
|
159
|
+
| `list_wallets` | List all your wallets |
|
|
160
|
+
| `get_wallet` | Get wallet details by ID |
|
|
161
|
+
| `get_balance` | Check native token balance on any chain |
|
|
162
|
+
| `get_token_balance` | Check ERC-20 or SPL token balance |
|
|
163
|
+
| `get_token_info` | Get ERC-20 token name, symbol, and decimals |
|
|
164
|
+
| `transfer` | Send native tokens (ETH, SOL, POL, BNB, etc.) |
|
|
165
|
+
| `transfer_token` | Send ERC-20 or SPL tokens (USDC, USDT, etc.) |
|
|
166
|
+
| `send_transaction` | Sign and broadcast a raw transaction |
|
|
167
|
+
| `sign_transaction` | Sign a transaction without broadcasting |
|
|
168
|
+
| `call_contract` | Read-only contract call (eth_call) |
|
|
169
|
+
| `approve_token` | Approve ERC-20 token spending for DeFi |
|
|
170
|
+
| `get_allowance` | Check ERC-20 token allowance |
|
|
171
|
+
| `wrap_eth` | Wrap native tokens to WETH/WAVAX/etc. |
|
|
172
|
+
| `unwrap_eth` | Unwrap WETH back to native tokens |
|
|
173
|
+
| `pay_x402` | Pay x402 invoices automatically (fetch, pay, retry) |
|
|
174
|
+
| `create_paywall` | Create an x402 paywall to charge for a resource |
|
|
175
|
+
| `list_paywalls` | List all your x402 paywalls |
|
|
176
|
+
| `get_paywall` | Get paywall details by ID |
|
|
177
|
+
| `update_paywall` | Update paywall pricing, resource, or status |
|
|
178
|
+
| `delete_paywall` | Delete a paywall |
|
|
179
|
+
| `get_paywall_payments` | View payment history for a paywall |
|
|
180
|
+
| `get_x402_revenue` | Aggregate revenue stats across all paywalls |
|
|
181
|
+
| `wallet_mode` | Report whether signing is local (self-custody) or hosted, and which address is in use |
|
|
182
|
+
| `export_wallet_key` | How to export a hosted wallet key and move to self-custody |
|
|
183
|
+
| `buy_verification_credits` | Buy x402 verification credits with USDC on-chain |
|
|
184
|
+
| `get_usage` | Check your monthly usage and billing |
|
|
185
|
+
| `get_chains` | List all supported chains |
|
|
186
|
+
| `pause_wallet` | Emergency pause a wallet |
|
|
187
|
+
| `unpause_wallet` | Resume a paused wallet |
|
|
188
|
+
| `delete_wallet` | Delete a wallet |
|
|
189
|
+
|
|
190
|
+
## Supported Chains
|
|
191
|
+
|
|
192
|
+
| Chain | ID | Native Token | Stablecoin |
|
|
193
|
+
|-------|-----|-------------|------------|
|
|
194
|
+
| Ethereum | 1 | ETH | USDC |
|
|
195
|
+
| Base | 8453 | ETH | USDC |
|
|
196
|
+
| Polygon | 137 | POL | USDC |
|
|
197
|
+
| BSC | 56 | BNB | USDT |
|
|
198
|
+
| Arbitrum | 42161 | ETH | USDC |
|
|
199
|
+
| Optimism | 10 | ETH | USDC |
|
|
200
|
+
| Avalanche | 43114 | AVAX | USDC |
|
|
201
|
+
| Zora | 7777777 | ETH | USDC |
|
|
202
|
+
| PulseChain | 369 | PLS | USDC |
|
|
203
|
+
| Solana | 900 | SOL | USDC |
|
|
204
|
+
| Solana Devnet | 901 | SOL | USDC |
|
|
205
|
+
|
|
206
|
+
## Use Case: GuessMarket
|
|
207
|
+
|
|
208
|
+
Pair with [guessmarket-mcp](https://www.npmjs.com/package/guessmarket-mcp) to let your AI agent trade prediction markets:
|
|
209
|
+
|
|
210
|
+
1. Create a wallet on Base
|
|
211
|
+
2. Approve USDC spending
|
|
212
|
+
3. Buy YES/NO shares on prediction markets
|
|
213
|
+
4. Provide liquidity and earn trading fees
|
|
214
|
+
5. Claim winnings
|
|
215
|
+
|
|
216
|
+
All on-chain. All through MCP. No frontend needed.
|
|
217
|
+
|
|
218
|
+
## x402 Payments
|
|
219
|
+
|
|
220
|
+
AgentWallet natively supports the [x402 open payment standard](https://x402.org). When your Ai agent encounters an API that returns HTTP 402 Payment Required, the `pay_x402` tool handles the entire flow automatically:
|
|
221
|
+
|
|
222
|
+
1. Fetches the URL and detects the 402 response
|
|
223
|
+
2. Parses the payment requirements (amount, token, chain)
|
|
224
|
+
3. Executes the on-chain payment from your wallet
|
|
225
|
+
4. Retries the request with proof of payment
|
|
226
|
+
5. Returns the final response
|
|
227
|
+
|
|
228
|
+
**Set `max_payment` to control spending:**
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
pay_x402(
|
|
232
|
+
url="https://api.example.com/premium-data",
|
|
233
|
+
wallet_id=1,
|
|
234
|
+
max_payment="1.00"
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`max_payment` is enforced as a hard per-payment cap. If you omit it, `pay_x402` falls back to `AGENTWALLET_MAX_AUTOPAY` (default `1`), the same cap the auto-pay path uses, so a malicious or compromised 402 endpoint can never authorize an unbounded payment. Set `max_payment` explicitly (or raise `AGENTWALLET_MAX_AUTOPAY`) to allow a larger single payment.
|
|
239
|
+
|
|
240
|
+
Supports ERC-20 tokens, SPL tokens, and native tokens on EVM and Solana. Compatible with x402 V1 and V2 (CAIP-2 chain identifiers), and reads the token address from the standard x402 `asset` field (falling back to `extra.token`).
|
|
241
|
+
|
|
242
|
+
## x402 Acceptance
|
|
243
|
+
|
|
244
|
+
AgentWallet also lets you **accept** x402 payments. Create a paywall, point it at any resource, and get a public URL that charges agents automatically:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
create_paywall(
|
|
248
|
+
wallet_id=1,
|
|
249
|
+
name="Premium API",
|
|
250
|
+
amount="0.01",
|
|
251
|
+
token_name="USDC",
|
|
252
|
+
token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
253
|
+
chain_id=8453,
|
|
254
|
+
resource_url="https://your-api.com/data"
|
|
255
|
+
)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
When an agent hits the paywall URL:
|
|
259
|
+
1. Gets back HTTP 402 with payment requirements
|
|
260
|
+
2. Pays on-chain using `pay_x402` (or any x402-compatible client)
|
|
261
|
+
3. Retries with proof of payment
|
|
262
|
+
4. Receives the protected content
|
|
263
|
+
|
|
264
|
+
On-chain verification ensures every payment is real. Replay protection prevents double-spending. Revenue tracking shows you who paid, how much, and when. 1,000 free verifications/month, then $0.0005 each. See the [pricing comparison](https://hifriendbot.com/wallet/#pricing) for how that stacks up.
|
|
265
|
+
|
|
266
|
+
## How We Compare
|
|
267
|
+
|
|
268
|
+
| Feature | Coinbase CDP | AgentWallet |
|
|
269
|
+
|---------|-------------|-------------|
|
|
270
|
+
| Setup Time | Install SDK + configure | **3 lines of config** |
|
|
271
|
+
| Approval Process | Identity verification required | **None, instant access** |
|
|
272
|
+
| KYC Required | Yes | **No** |
|
|
273
|
+
| KYT / Transaction Monitoring | Yes | **No** |
|
|
274
|
+
| Can Block Your Wallet | Yes | **No** |
|
|
275
|
+
| Built-in Guards | Yes (requires setup) | **Yes (active by default)** |
|
|
276
|
+
| Free Operations / Month | 5,000 | **6,000** |
|
|
277
|
+
| Cost Per Operation | $0.005 | **$0.00345** |
|
|
278
|
+
| x402 Verification Cost | $0.001 | **$0.0005** |
|
|
279
|
+
| Free x402 Verifications / Month | 1,000 | **1,000** |
|
|
280
|
+
| x402 Acceptance (Paywalls) | No | **Yes** |
|
|
281
|
+
| Pay for API Fees with Crypto | No (credit card only) | **Yes (USDC via x402)** |
|
|
282
|
+
| Supported Chains | 8 EVM + Solana | **Any EVM + Solana** |
|
|
283
|
+
| Token Tools | Yes | **ERC-20 + SPL (29 tools)** |
|
|
284
|
+
| MCP Server | Yes | **Yes** |
|
|
285
|
+
|
|
286
|
+
## Pay with Crypto, No Credit Card Required
|
|
287
|
+
|
|
288
|
+
AgentWallet is the only AI agent wallet infrastructure that accepts crypto for its own API fees. Every competitor, Coinbase CDP, Circle, MoonPay, Crossmint, Turnkey, requires a credit card or monthly invoice. With AgentWallet, your agent can pay for operations with USDC on-chain via the x402 protocol. No credit card, no invoice, no billing portal. Just on-chain payments.
|
|
289
|
+
|
|
290
|
+
When your agent exceeds the free tier (6,000 ops/month) without a credit card configured, the API returns HTTP 402 with USDC payment instructions. Your agent pays on-chain, retries with proof of payment, and the operation executes. Fully automated via the MCP server.
|
|
291
|
+
|
|
292
|
+
You can also pre-purchase x402 verification credits with USDC using the `buy_verification_credits` tool, keeping your paywalls running beyond the free 1,000 verifications/month without needing a credit card.
|
|
293
|
+
|
|
294
|
+
## Built-in Guards
|
|
295
|
+
|
|
296
|
+
All guards are active by default, no configuration required.
|
|
297
|
+
|
|
298
|
+
- **Encrypted at rest**: private keys encrypted before storage and never leave the server
|
|
299
|
+
- **Memory zeroing**: keys wiped from memory immediately after every signing operation
|
|
300
|
+
- **Daily spending limits**: set a per-wallet daily cap in USD, enforced automatically on every transaction
|
|
301
|
+
- **Gas price protection**: transactions blocked when gas prices spike above safe thresholds
|
|
302
|
+
- **Emergency pause**: instantly freeze any wallet or all wallets with one click
|
|
303
|
+
- **Rate limiting**: API requests capped per minute to prevent abuse and brute force attacks
|
|
304
|
+
- **Replay protection**: every x402 payment verified on-chain with unique transaction tracking
|
|
305
|
+
- **On-chain verification**: x402 payments verified directly on the blockchain with finalized commitment
|
|
306
|
+
- Bug bounty program: $50,$500 for responsible disclosure ([details](https://hifriendbot.com/wallet))
|
|
307
|
+
|
|
308
|
+
## Links
|
|
309
|
+
|
|
310
|
+
- **Website:** [hifriendbot.com/wallet](https://hifriendbot.com/wallet)
|
|
311
|
+
- **npm:** [agentwallet-mcp](https://www.npmjs.com/package/agentwallet-mcp)
|
|
312
|
+
- **Security:** [security@hifriendbot.com](mailto:security@hifriendbot.com)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
## Security
|
|
316
|
+
|
|
317
|
+
`pay_x402` validates the target URL before every outbound request and again on
|
|
318
|
+
each redirect hop. IP literals are canonicalized (including IPv4-mapped IPv6
|
|
319
|
+
such as `[::ffff:127.0.0.1]`) and hostnames are resolved, with loopback,
|
|
320
|
+
private, link-local, carrier-grade NAT, multicast and cloud-metadata
|
|
321
|
+
destinations refused. Report security issues privately to
|
|
322
|
+
security@hifriendbot.com.
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT
|
package/build/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
+
import { assertPublicUrl, safeFetch } from './ssrf-guard.js';
|
|
15
16
|
import { deriveX402Payment, isWithinCap } from './x402-payment.js';
|
|
16
17
|
import { isLocalMode, getLocalAddress, localWalletRecord, localSend, localNativeBalance, localTokenBalance, localSignMessage, resolveRpcUrl, } from './local-wallet.js';
|
|
17
18
|
import { isSolanaLocalMode, getSolanaAddress, solanaWalletRecord, localSolBalance, localSplBalance, localSolTransfer, localSplTransfer, resolveSolanaRpc, } from './local-solana.js';
|
|
@@ -360,82 +361,82 @@ function resolveChainId(network) {
|
|
|
360
361
|
// ─── Server ──────────────────────────────────────────────────────
|
|
361
362
|
const server = new McpServer({
|
|
362
363
|
name: 'agentwallet',
|
|
363
|
-
version: '1.
|
|
364
|
+
version: '1.10.1',
|
|
364
365
|
}, {
|
|
365
|
-
instructions: `AgentWallet gives AI agents their own blockchain wallets. Private keys are encrypted server-side and never exposed — agents sign and broadcast transactions without ever touching raw keys.
|
|
366
|
-
|
|
367
|
-
## Getting Started
|
|
368
|
-
1. Call list_wallets to see existing wallets, or create_wallet to make a new one.
|
|
369
|
-
2. Fund the wallet by sending tokens to its address from an external source.
|
|
370
|
-
3. Use transfer (native tokens) or transfer_token (ERC-20/SPL tokens) to send funds.
|
|
371
|
-
|
|
372
|
-
## Supported Chains
|
|
373
|
-
- **EVM chains:** Ethereum (1), Base (8453), Arbitrum (42161), Optimism (10), Polygon (137), Avalanche (43114), BSC (56), Zora (7777777), PulseChain (369)
|
|
374
|
-
- **Solana:** Mainnet (900), Devnet (901)
|
|
375
|
-
- Call get_chains for the full list with native tokens and stablecoin contract addresses.
|
|
376
|
-
|
|
377
|
-
## EVM vs Solana
|
|
378
|
-
- **Addresses:** EVM uses 0x-prefixed hex (42 chars). Solana uses Base58 (32-44 chars).
|
|
379
|
-
- **Native tokens:** EVM = ETH/POL/BNB/etc (18 decimals, wei). Solana = SOL (9 decimals, lamports).
|
|
380
|
-
- **Token standards:** EVM uses ERC-20. Solana uses SPL tokens.
|
|
381
|
-
- **EVM-only tools:** approve_token, get_allowance, wrap_eth, unwrap_eth, call_contract, get_token_info. These do not work on Solana.
|
|
382
|
-
- **Works on both:** create_wallet, transfer, transfer_token, send_transaction, sign_transaction, get_balance, get_token_balance.
|
|
383
|
-
|
|
384
|
-
## Common Workflows
|
|
385
|
-
|
|
386
|
-
### Send native tokens (ETH, SOL, etc.)
|
|
387
|
-
Use the transfer tool with a human-readable amount (e.g. "0.1" ETH). It auto-converts to wei/lamports.
|
|
388
|
-
|
|
389
|
-
### Send ERC-20 or SPL tokens
|
|
390
|
-
Use transfer_token. You need: wallet_id, token contract address, recipient, amount, chain_id, and decimals (6 for USDC, 18 for most ERC-20s). Call get_chains to find stablecoin addresses.
|
|
391
|
-
|
|
392
|
-
### Interact with DeFi (EVM only)
|
|
393
|
-
1. Check allowance with get_allowance.
|
|
394
|
-
2. If needed, call approve_token to let the DeFi contract spend your tokens.
|
|
395
|
-
3. Use send_transaction with the contract's calldata to execute the interaction.
|
|
396
|
-
|
|
397
|
-
### Wrap/unwrap native tokens (EVM only)
|
|
398
|
-
Use wrap_eth to convert ETH → WETH (required by many DeFi protocols). Use unwrap_eth to convert back.
|
|
399
|
-
|
|
400
|
-
### Read on-chain data (EVM only)
|
|
401
|
-
Use call_contract for gas-free read-only calls (eth_call). Provide ABI-encoded calldata.
|
|
402
|
-
|
|
403
|
-
## x402 Payments — Paying for Resources
|
|
404
|
-
The pay_x402 tool handles the full x402 payment flow automatically:
|
|
405
|
-
1. Fetches the URL.
|
|
406
|
-
2. If the server returns HTTP 402, parses the payment requirements.
|
|
407
|
-
3. Pays on-chain using your wallet.
|
|
408
|
-
4. Retries the request with proof of payment.
|
|
409
|
-
Always set max_payment to prevent overspending (e.g. "1.00" for max 1 USDC).
|
|
410
|
-
|
|
411
|
-
## x402 Paywalls — Accepting Payments
|
|
412
|
-
Create paywalls to charge other agents for accessing your resources:
|
|
413
|
-
1. create_paywall — set price, token, chain, and the resource URL to protect.
|
|
414
|
-
2. Share the access URL. Clients that pay on-chain get the resource; others get HTTP 402.
|
|
415
|
-
3. list_paywalls and get_paywall_payments to track revenue.
|
|
416
|
-
4. get_x402_revenue for aggregate stats across all paywalls.
|
|
417
|
-
|
|
418
|
-
## Wallet Security
|
|
419
|
-
- Use pause_wallet to immediately freeze a wallet if compromised. No transactions can be signed while paused.
|
|
420
|
-
- Use unpause_wallet to resume operations.
|
|
421
|
-
- Use delete_wallet to permanently disable a wallet.
|
|
422
|
-
- Wallets have server-enforced spending limits.
|
|
423
|
-
|
|
424
|
-
## Tool Selection Guide
|
|
425
|
-
| Goal | Tool |
|
|
426
|
-
|------|------|
|
|
427
|
-
| Send ETH/SOL/native tokens | transfer |
|
|
428
|
-
| Send USDC/ERC-20/SPL tokens | transfer_token |
|
|
429
|
-
| Check native balance | get_balance |
|
|
430
|
-
| Check token balance | get_token_balance |
|
|
431
|
-
| Look up token details | get_token_info (EVM only) |
|
|
432
|
-
| Approve DeFi spending | approve_token (EVM only) |
|
|
433
|
-
| Check approval amount | get_allowance (EVM only) |
|
|
434
|
-
| Read smart contract | call_contract (EVM only) |
|
|
435
|
-
| Custom transaction | send_transaction |
|
|
436
|
-
| Sign without broadcasting | sign_transaction |
|
|
437
|
-
| Pay for x402 resource | pay_x402 |
|
|
438
|
-
| Charge for your resource | create_paywall |
|
|
366
|
+
instructions: `AgentWallet gives AI agents their own blockchain wallets. Private keys are encrypted server-side and never exposed — agents sign and broadcast transactions without ever touching raw keys.
|
|
367
|
+
|
|
368
|
+
## Getting Started
|
|
369
|
+
1. Call list_wallets to see existing wallets, or create_wallet to make a new one.
|
|
370
|
+
2. Fund the wallet by sending tokens to its address from an external source.
|
|
371
|
+
3. Use transfer (native tokens) or transfer_token (ERC-20/SPL tokens) to send funds.
|
|
372
|
+
|
|
373
|
+
## Supported Chains
|
|
374
|
+
- **EVM chains:** Ethereum (1), Base (8453), Arbitrum (42161), Optimism (10), Polygon (137), Avalanche (43114), BSC (56), Zora (7777777), PulseChain (369)
|
|
375
|
+
- **Solana:** Mainnet (900), Devnet (901)
|
|
376
|
+
- Call get_chains for the full list with native tokens and stablecoin contract addresses.
|
|
377
|
+
|
|
378
|
+
## EVM vs Solana
|
|
379
|
+
- **Addresses:** EVM uses 0x-prefixed hex (42 chars). Solana uses Base58 (32-44 chars).
|
|
380
|
+
- **Native tokens:** EVM = ETH/POL/BNB/etc (18 decimals, wei). Solana = SOL (9 decimals, lamports).
|
|
381
|
+
- **Token standards:** EVM uses ERC-20. Solana uses SPL tokens.
|
|
382
|
+
- **EVM-only tools:** approve_token, get_allowance, wrap_eth, unwrap_eth, call_contract, get_token_info. These do not work on Solana.
|
|
383
|
+
- **Works on both:** create_wallet, transfer, transfer_token, send_transaction, sign_transaction, get_balance, get_token_balance.
|
|
384
|
+
|
|
385
|
+
## Common Workflows
|
|
386
|
+
|
|
387
|
+
### Send native tokens (ETH, SOL, etc.)
|
|
388
|
+
Use the transfer tool with a human-readable amount (e.g. "0.1" ETH). It auto-converts to wei/lamports.
|
|
389
|
+
|
|
390
|
+
### Send ERC-20 or SPL tokens
|
|
391
|
+
Use transfer_token. You need: wallet_id, token contract address, recipient, amount, chain_id, and decimals (6 for USDC, 18 for most ERC-20s). Call get_chains to find stablecoin addresses.
|
|
392
|
+
|
|
393
|
+
### Interact with DeFi (EVM only)
|
|
394
|
+
1. Check allowance with get_allowance.
|
|
395
|
+
2. If needed, call approve_token to let the DeFi contract spend your tokens.
|
|
396
|
+
3. Use send_transaction with the contract's calldata to execute the interaction.
|
|
397
|
+
|
|
398
|
+
### Wrap/unwrap native tokens (EVM only)
|
|
399
|
+
Use wrap_eth to convert ETH → WETH (required by many DeFi protocols). Use unwrap_eth to convert back.
|
|
400
|
+
|
|
401
|
+
### Read on-chain data (EVM only)
|
|
402
|
+
Use call_contract for gas-free read-only calls (eth_call). Provide ABI-encoded calldata.
|
|
403
|
+
|
|
404
|
+
## x402 Payments — Paying for Resources
|
|
405
|
+
The pay_x402 tool handles the full x402 payment flow automatically:
|
|
406
|
+
1. Fetches the URL.
|
|
407
|
+
2. If the server returns HTTP 402, parses the payment requirements.
|
|
408
|
+
3. Pays on-chain using your wallet.
|
|
409
|
+
4. Retries the request with proof of payment.
|
|
410
|
+
Always set max_payment to prevent overspending (e.g. "1.00" for max 1 USDC).
|
|
411
|
+
|
|
412
|
+
## x402 Paywalls — Accepting Payments
|
|
413
|
+
Create paywalls to charge other agents for accessing your resources:
|
|
414
|
+
1. create_paywall — set price, token, chain, and the resource URL to protect.
|
|
415
|
+
2. Share the access URL. Clients that pay on-chain get the resource; others get HTTP 402.
|
|
416
|
+
3. list_paywalls and get_paywall_payments to track revenue.
|
|
417
|
+
4. get_x402_revenue for aggregate stats across all paywalls.
|
|
418
|
+
|
|
419
|
+
## Wallet Security
|
|
420
|
+
- Use pause_wallet to immediately freeze a wallet if compromised. No transactions can be signed while paused.
|
|
421
|
+
- Use unpause_wallet to resume operations.
|
|
422
|
+
- Use delete_wallet to permanently disable a wallet.
|
|
423
|
+
- Wallets have server-enforced spending limits.
|
|
424
|
+
|
|
425
|
+
## Tool Selection Guide
|
|
426
|
+
| Goal | Tool |
|
|
427
|
+
|------|------|
|
|
428
|
+
| Send ETH/SOL/native tokens | transfer |
|
|
429
|
+
| Send USDC/ERC-20/SPL tokens | transfer_token |
|
|
430
|
+
| Check native balance | get_balance |
|
|
431
|
+
| Check token balance | get_token_balance |
|
|
432
|
+
| Look up token details | get_token_info (EVM only) |
|
|
433
|
+
| Approve DeFi spending | approve_token (EVM only) |
|
|
434
|
+
| Check approval amount | get_allowance (EVM only) |
|
|
435
|
+
| Read smart contract | call_contract (EVM only) |
|
|
436
|
+
| Custom transaction | send_transaction |
|
|
437
|
+
| Sign without broadcasting | sign_transaction |
|
|
438
|
+
| Pay for x402 resource | pay_x402 |
|
|
439
|
+
| Charge for your resource | create_paywall |
|
|
439
440
|
| Check usage/billing | get_usage |`,
|
|
440
441
|
});
|
|
441
442
|
// ─── Tool: create_wallet ─────────────────────────────────────────
|
|
@@ -900,19 +901,10 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
|
|
|
900
901
|
throw new Error('Invalid headers JSON. Must be a JSON object (e.g. {"Authorization": "Bearer ..."}).');
|
|
901
902
|
}
|
|
902
903
|
}
|
|
903
|
-
// Validate URL
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const blockedPrefixes = ['10.', '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', '172.21.',
|
|
908
|
-
'172.22.', '172.23.', '172.24.', '172.25.', '172.26.', '172.27.', '172.28.', '172.29.',
|
|
909
|
-
'172.30.', '172.31.', '192.168.'];
|
|
910
|
-
if (blockedHosts.includes(hostname) || blockedPrefixes.some(p => hostname.startsWith(p))) {
|
|
911
|
-
throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
|
|
912
|
-
}
|
|
913
|
-
if (urlObj.protocol !== 'https:') {
|
|
914
|
-
throw new Error('Only HTTPS URLs are supported for x402 payments.');
|
|
915
|
-
}
|
|
904
|
+
// Validate URL: canonicalizes IP literals (including IPv4-mapped IPv6) and
|
|
905
|
+
// resolves DNS names, rejecting any private/internal destination. Redirect
|
|
906
|
+
// hops are re-validated by safeFetch below.
|
|
907
|
+
await assertPublicUrl(url);
|
|
916
908
|
// Step 1: Make initial request
|
|
917
909
|
const reqOptions = {
|
|
918
910
|
method,
|
|
@@ -924,7 +916,7 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
|
|
|
924
916
|
if (!reqHeaders['Content-Type'])
|
|
925
917
|
reqHeaders['Content-Type'] = 'application/json';
|
|
926
918
|
}
|
|
927
|
-
const initialRes = await
|
|
919
|
+
const initialRes = await safeFetch(url, reqOptions);
|
|
928
920
|
// If not 402, return the response as-is (no payment needed)
|
|
929
921
|
if (initialRes.status !== 402) {
|
|
930
922
|
const text = await initialRes.text();
|
|
@@ -1058,7 +1050,7 @@ server.tool('pay_x402', 'Handle an x402 payment flow. Fetches a URL, and if the
|
|
|
1058
1050
|
if (reqBody && method !== 'GET') {
|
|
1059
1051
|
retryOptions.body = reqBody;
|
|
1060
1052
|
}
|
|
1061
|
-
const retryRes = await
|
|
1053
|
+
const retryRes = await safeFetch(url, retryOptions);
|
|
1062
1054
|
const retryText = await retryRes.text();
|
|
1063
1055
|
let retryParsed;
|
|
1064
1056
|
try {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guard for outbound x402 requests.
|
|
3
|
+
*
|
|
4
|
+
* The previous guard compared `new URL(url).hostname` against literal strings
|
|
5
|
+
* and dotted-decimal prefixes. That misses every alternate spelling of a
|
|
6
|
+
* private destination: IPv4-mapped IPv6 (`[::ffff:127.0.0.1]` parses to
|
|
7
|
+
* `[::ffff:7f00:1]`), the rest of 127.0.0.0/8, IPv6 unique-local and
|
|
8
|
+
* link-local, the whole 169.254.0.0/16 link-local range rather than only the
|
|
9
|
+
* metadata address, and 100.64.0.0/10.
|
|
10
|
+
*
|
|
11
|
+
* This module canonicalizes the host into actual IP addresses and tests them
|
|
12
|
+
* against numeric ranges, then resolves DNS names and applies the same test to
|
|
13
|
+
* every answer, so a public name pointing at a private address is refused too.
|
|
14
|
+
*
|
|
15
|
+
* Reported privately by ARC Security Research, 2026-07-31.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Reduce a host to the IP address it actually denotes.
|
|
19
|
+
* Strips IPv6 brackets and unwraps IPv4-mapped and IPv4-compatible IPv6.
|
|
20
|
+
*/
|
|
21
|
+
export declare function canonicalizeHost(hostRaw: string): {
|
|
22
|
+
ip: string | null;
|
|
23
|
+
family: 0 | 4 | 6;
|
|
24
|
+
};
|
|
25
|
+
/** True when the address is loopback, private, link-local, multicast or reserved. */
|
|
26
|
+
export declare function isPrivateAddress(ipRaw: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Throw unless `url` is an https URL pointing at a public destination.
|
|
29
|
+
* DNS names are resolved and every answer is checked, so a public name that
|
|
30
|
+
* maps to a private address is rejected.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertPublicUrl(url: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* fetch() that validates the target, and every redirect hop, against
|
|
35
|
+
* assertPublicUrl. Redirects are followed manually because the automatic
|
|
36
|
+
* follower would happily land on a private address after a public first hop.
|
|
37
|
+
*/
|
|
38
|
+
export declare function safeFetch(url: string, options?: RequestInit, maxHops?: number): Promise<Response>;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guard for outbound x402 requests.
|
|
3
|
+
*
|
|
4
|
+
* The previous guard compared `new URL(url).hostname` against literal strings
|
|
5
|
+
* and dotted-decimal prefixes. That misses every alternate spelling of a
|
|
6
|
+
* private destination: IPv4-mapped IPv6 (`[::ffff:127.0.0.1]` parses to
|
|
7
|
+
* `[::ffff:7f00:1]`), the rest of 127.0.0.0/8, IPv6 unique-local and
|
|
8
|
+
* link-local, the whole 169.254.0.0/16 link-local range rather than only the
|
|
9
|
+
* metadata address, and 100.64.0.0/10.
|
|
10
|
+
*
|
|
11
|
+
* This module canonicalizes the host into actual IP addresses and tests them
|
|
12
|
+
* against numeric ranges, then resolves DNS names and applies the same test to
|
|
13
|
+
* every answer, so a public name pointing at a private address is refused too.
|
|
14
|
+
*
|
|
15
|
+
* Reported privately by ARC Security Research, 2026-07-31.
|
|
16
|
+
*/
|
|
17
|
+
import { isIP } from 'node:net';
|
|
18
|
+
import { lookup } from 'node:dns/promises';
|
|
19
|
+
/** Hostnames that must never be reached regardless of what they resolve to. */
|
|
20
|
+
const BLOCKED_NAMES = new Set([
|
|
21
|
+
'localhost',
|
|
22
|
+
'metadata.google.internal',
|
|
23
|
+
'metadata.goog',
|
|
24
|
+
'instance-data',
|
|
25
|
+
]);
|
|
26
|
+
function ipv4ToInt(ip) {
|
|
27
|
+
const parts = ip.split('.');
|
|
28
|
+
if (parts.length !== 4)
|
|
29
|
+
return null;
|
|
30
|
+
let n = 0;
|
|
31
|
+
for (const p of parts) {
|
|
32
|
+
const v = Number(p);
|
|
33
|
+
if (!Number.isInteger(v) || v < 0 || v > 255)
|
|
34
|
+
return null;
|
|
35
|
+
n = (n << 8 >>> 0) + v;
|
|
36
|
+
}
|
|
37
|
+
return n >>> 0;
|
|
38
|
+
}
|
|
39
|
+
function inV4Range(ip, cidr) {
|
|
40
|
+
const [base, bitsRaw] = cidr.split('/');
|
|
41
|
+
const bits = Number(bitsRaw);
|
|
42
|
+
const a = ipv4ToInt(ip);
|
|
43
|
+
const b = ipv4ToInt(base);
|
|
44
|
+
if (a === null || b === null)
|
|
45
|
+
return false;
|
|
46
|
+
if (bits === 0)
|
|
47
|
+
return true;
|
|
48
|
+
const mask = (0xffffffff << (32 - bits)) >>> 0;
|
|
49
|
+
return (a & mask) === (b & mask);
|
|
50
|
+
}
|
|
51
|
+
/** Ranges that are never a legitimate x402 payment endpoint. */
|
|
52
|
+
const V4_BLOCKED = [
|
|
53
|
+
'0.0.0.0/8', // this network / unspecified
|
|
54
|
+
'10.0.0.0/8', // private
|
|
55
|
+
'100.64.0.0/10', // carrier-grade NAT
|
|
56
|
+
'127.0.0.0/8', // loopback, all of it
|
|
57
|
+
'169.254.0.0/16', // link-local, includes cloud metadata
|
|
58
|
+
'172.16.0.0/12', // private
|
|
59
|
+
'192.0.0.0/24', // IETF protocol assignments
|
|
60
|
+
'192.168.0.0/16', // private
|
|
61
|
+
'198.18.0.0/15', // benchmarking
|
|
62
|
+
'224.0.0.0/4', // multicast
|
|
63
|
+
'240.0.0.0/4', // reserved, includes 255.255.255.255
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Reduce a host to the IP address it actually denotes.
|
|
67
|
+
* Strips IPv6 brackets and unwraps IPv4-mapped and IPv4-compatible IPv6.
|
|
68
|
+
*/
|
|
69
|
+
export function canonicalizeHost(hostRaw) {
|
|
70
|
+
let host = hostRaw.trim().toLowerCase();
|
|
71
|
+
if (host.startsWith('[') && host.endsWith(']'))
|
|
72
|
+
host = host.slice(1, -1);
|
|
73
|
+
const zone = host.indexOf('%'); // fe80::1%eth0
|
|
74
|
+
if (zone !== -1)
|
|
75
|
+
host = host.slice(0, zone);
|
|
76
|
+
const fam = isIP(host);
|
|
77
|
+
if (fam === 0)
|
|
78
|
+
return { ip: null, family: 0 };
|
|
79
|
+
if (fam === 4)
|
|
80
|
+
return { ip: host, family: 4 };
|
|
81
|
+
// IPv6: unwrap ::ffff:a.b.c.d and its hex spelling ::ffff:7f00:1
|
|
82
|
+
const mapped = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
83
|
+
if (mapped)
|
|
84
|
+
return { ip: mapped[1], family: 4 };
|
|
85
|
+
const hexMapped = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
|
|
86
|
+
if (hexMapped) {
|
|
87
|
+
const hi = parseInt(hexMapped[1], 16);
|
|
88
|
+
const lo = parseInt(hexMapped[2], 16);
|
|
89
|
+
return { ip: [(hi >> 8) & 255, hi & 255, (lo >> 8) & 255, lo & 255].join('.'), family: 4 };
|
|
90
|
+
}
|
|
91
|
+
return { ip: host, family: 6 };
|
|
92
|
+
}
|
|
93
|
+
/** True when the address is loopback, private, link-local, multicast or reserved. */
|
|
94
|
+
export function isPrivateAddress(ipRaw) {
|
|
95
|
+
const { ip, family } = canonicalizeHost(ipRaw);
|
|
96
|
+
if (!ip)
|
|
97
|
+
return false;
|
|
98
|
+
if (family === 4)
|
|
99
|
+
return V4_BLOCKED.some(c => inV4Range(ip, c));
|
|
100
|
+
const v6 = ip;
|
|
101
|
+
if (v6 === '::' || v6 === '::1')
|
|
102
|
+
return true; // unspecified, loopback
|
|
103
|
+
if (/^f[cd][0-9a-f]{2}:/.test(v6))
|
|
104
|
+
return true; // fc00::/7 unique-local
|
|
105
|
+
if (/^fe[89ab][0-9a-f]:/.test(v6))
|
|
106
|
+
return true; // fe80::/10 link-local
|
|
107
|
+
if (/^ff[0-9a-f]{2}:/.test(v6))
|
|
108
|
+
return true; // ff00::/8 multicast
|
|
109
|
+
if (/^(64:ff9b|2002):/.test(v6))
|
|
110
|
+
return true; // NAT64 / 6to4 translation
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Throw unless `url` is an https URL pointing at a public destination.
|
|
115
|
+
* DNS names are resolved and every answer is checked, so a public name that
|
|
116
|
+
* maps to a private address is rejected.
|
|
117
|
+
*/
|
|
118
|
+
export async function assertPublicUrl(url) {
|
|
119
|
+
let u;
|
|
120
|
+
try {
|
|
121
|
+
u = new URL(url);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new Error('Invalid URL.');
|
|
125
|
+
}
|
|
126
|
+
if (u.protocol !== 'https:') {
|
|
127
|
+
throw new Error('Only HTTPS URLs are supported for x402 payments.');
|
|
128
|
+
}
|
|
129
|
+
const hostRaw = u.hostname.toLowerCase();
|
|
130
|
+
const bare = hostRaw.startsWith('[') && hostRaw.endsWith(']') ? hostRaw.slice(1, -1) : hostRaw;
|
|
131
|
+
if (BLOCKED_NAMES.has(bare)) {
|
|
132
|
+
throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
|
|
133
|
+
}
|
|
134
|
+
const { ip, family } = canonicalizeHost(hostRaw);
|
|
135
|
+
if (family !== 0) {
|
|
136
|
+
if (isPrivateAddress(ip)) {
|
|
137
|
+
throw new Error('URL points to a private/internal address. Only public URLs are allowed.');
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// A name: every address it resolves to must be public.
|
|
142
|
+
let answers;
|
|
143
|
+
try {
|
|
144
|
+
answers = await lookup(bare, { all: true });
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
throw new Error(`Could not resolve host "${bare}".`);
|
|
148
|
+
}
|
|
149
|
+
if (!answers.length)
|
|
150
|
+
throw new Error(`Could not resolve host "${bare}".`);
|
|
151
|
+
for (const a of answers) {
|
|
152
|
+
if (isPrivateAddress(a.address)) {
|
|
153
|
+
throw new Error('URL resolves to a private/internal address. Only public URLs are allowed.');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* fetch() that validates the target, and every redirect hop, against
|
|
159
|
+
* assertPublicUrl. Redirects are followed manually because the automatic
|
|
160
|
+
* follower would happily land on a private address after a public first hop.
|
|
161
|
+
*/
|
|
162
|
+
export async function safeFetch(url, options = {}, maxHops = 3) {
|
|
163
|
+
let current = url;
|
|
164
|
+
for (let hop = 0; hop <= maxHops; hop++) {
|
|
165
|
+
await assertPublicUrl(current);
|
|
166
|
+
const res = await fetch(current, { ...options, redirect: 'manual' });
|
|
167
|
+
const isRedirect = res.status >= 300 && res.status < 400 && res.headers.has('location');
|
|
168
|
+
if (!isRedirect)
|
|
169
|
+
return res;
|
|
170
|
+
const next = new URL(res.headers.get('location'), current).toString();
|
|
171
|
+
// A redirected request must not replay the body or method blindly.
|
|
172
|
+
if (res.status === 303 || ((res.status === 301 || res.status === 302) && options.method && options.method !== 'GET')) {
|
|
173
|
+
options = { ...options, method: 'GET', body: undefined };
|
|
174
|
+
}
|
|
175
|
+
current = next;
|
|
176
|
+
}
|
|
177
|
+
throw new Error('Too many redirects while contacting the x402 endpoint.');
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agentwallet-mcp",
|
|
3
|
-
"mcpName": "io.github.hifriendbot/agentwallet",
|
|
4
|
-
"version": "1.10.
|
|
5
|
-
"description": "MCP server for AgentWallet: self-custody local signing on EVM and Solana, or hosted wallets. Sign, broadcast, pay x402 invoices, and accept x402 payments.",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "build/index.js",
|
|
8
|
-
"bin": {
|
|
9
|
-
"agentwallet-mcp": "build/index.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"build",
|
|
13
|
-
"README.md",
|
|
14
|
-
"LICENSE"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc",
|
|
18
|
-
"start": "node build/index.js",
|
|
19
|
-
"test": "node test/x402-payment.test.mjs && node test/local-mode.test.mjs",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
21
|
-
},
|
|
22
|
-
"keywords": [
|
|
23
|
-
"mcp",
|
|
24
|
-
"model-context-protocol",
|
|
25
|
-
"wallet",
|
|
26
|
-
"ai-agent",
|
|
27
|
-
"blockchain",
|
|
28
|
-
"evm",
|
|
29
|
-
"ethereum",
|
|
30
|
-
"base",
|
|
31
|
-
"arbitrum",
|
|
32
|
-
"optimism",
|
|
33
|
-
"polygon",
|
|
34
|
-
"avalanche",
|
|
35
|
-
"bsc",
|
|
36
|
-
"zora",
|
|
37
|
-
"pulsechain",
|
|
38
|
-
"solana",
|
|
39
|
-
"spl-token",
|
|
40
|
-
"erc20",
|
|
41
|
-
"token-transfer",
|
|
42
|
-
"transaction-signing",
|
|
43
|
-
"x402",
|
|
44
|
-
"http-402",
|
|
45
|
-
"agent-payments",
|
|
46
|
-
"agentwallet",
|
|
47
|
-
"self-custody",
|
|
48
|
-
"non-custodial",
|
|
49
|
-
"local-signing"
|
|
50
|
-
],
|
|
51
|
-
"author": "HiFriendbot (https://hifriendbot.com)",
|
|
52
|
-
"license": "MIT",
|
|
53
|
-
"homepage": "https://hifriendbot.com/wallet/",
|
|
54
|
-
"engines": {
|
|
55
|
-
"node": ">=18.0.0"
|
|
56
|
-
},
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"@modelcontextprotocol/sdk": "^1.11.4",
|
|
59
|
-
"@solana/web3.js": "^1.98.4",
|
|
60
|
-
"bs58": "^6.0.0",
|
|
61
|
-
"viem": "^2.55.10",
|
|
62
|
-
"zod": "^3.24.0"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"@types/node": "^20.0.0",
|
|
66
|
-
"typescript": "^5.3.0"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "agentwallet-mcp",
|
|
3
|
+
"mcpName": "io.github.hifriendbot/agentwallet",
|
|
4
|
+
"version": "1.10.1",
|
|
5
|
+
"description": "MCP server for AgentWallet: self-custody local signing on EVM and Solana, or hosted wallets. Sign, broadcast, pay x402 invoices, and accept x402 payments.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "build/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"agentwallet-mcp": "build/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"build",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "node build/index.js",
|
|
19
|
+
"test": "node test/x402-payment.test.mjs && node test/local-mode.test.mjs && node test/ssrf-guard.test.mjs",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"wallet",
|
|
26
|
+
"ai-agent",
|
|
27
|
+
"blockchain",
|
|
28
|
+
"evm",
|
|
29
|
+
"ethereum",
|
|
30
|
+
"base",
|
|
31
|
+
"arbitrum",
|
|
32
|
+
"optimism",
|
|
33
|
+
"polygon",
|
|
34
|
+
"avalanche",
|
|
35
|
+
"bsc",
|
|
36
|
+
"zora",
|
|
37
|
+
"pulsechain",
|
|
38
|
+
"solana",
|
|
39
|
+
"spl-token",
|
|
40
|
+
"erc20",
|
|
41
|
+
"token-transfer",
|
|
42
|
+
"transaction-signing",
|
|
43
|
+
"x402",
|
|
44
|
+
"http-402",
|
|
45
|
+
"agent-payments",
|
|
46
|
+
"agentwallet",
|
|
47
|
+
"self-custody",
|
|
48
|
+
"non-custodial",
|
|
49
|
+
"local-signing"
|
|
50
|
+
],
|
|
51
|
+
"author": "HiFriendbot (https://hifriendbot.com)",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"homepage": "https://hifriendbot.com/wallet/",
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.11.4",
|
|
59
|
+
"@solana/web3.js": "^1.98.4",
|
|
60
|
+
"bs58": "^6.0.0",
|
|
61
|
+
"viem": "^2.55.10",
|
|
62
|
+
"zod": "^3.24.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^20.0.0",
|
|
66
|
+
"typescript": "^5.3.0"
|
|
67
|
+
}
|
|
68
|
+
}
|