Skip to content

Commit fa61ccd

Browse files
committed
feat(metamask-agent-wallet): add x402 buyer payment support
Agents that fetch APIs and resources increasingly hit HTTP 402 (x402) paywalls and must pay per request, but the CLI has no native x402 support, so an agent improvises the protocol and the EIP-3009 signing, which is error-prone and skips payment safety checks. Guide the agent to detect a 402, select an offered exact-scheme option from an asset allowlist, sign an EIP-3009 transferWithAuthorization with mm wallet sign-typed-data, retry with the payment header, and verify settlement. Handle protocol v1 (X-PAYMENT) and v2 (PAYMENT-SIGNATURE). Confirm before signing; no autonomous auto-pay.
1 parent 51f4f07 commit fa61ccd

4 files changed

Lines changed: 568 additions & 2 deletions

File tree

skills/metamask-agent-wallet/SKILL.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
name: metamask-agent-wallet
3-
description: Use when the user asks anything about blockchain wallets, transactions, signing, token transfers, supported chains, wallet balances, perpetual futures trading, prediction markets, token swaps, cross-chain bridges, market data, token discovery, decoding EVM calldata, Aave V3 lending and borrowing, or authentication via the MetaMask Agentic CLI. Single entry point for all mm CLI operations.
3+
description: Use when the user asks anything about blockchain wallets, transactions, signing, token transfers, supported chains, wallet balances, perpetual futures trading, prediction markets, token swaps, cross-chain bridges, market data, token discovery, decoding EVM calldata, Aave V3 lending and borrowing, or authentication via the MetaMask Agentic CLI; also when an HTTP request returns 402 Payment Required (x402) or the agent needs to pay for a paywalled API, endpoint, file, or resource over HTTP. Single entry point for all mm CLI operations.
44
license: MIT
55
metadata:
66
author: metamask
7-
version: "4.0.0"
7+
version: "4.1.0"
88
cliVersion: "3.0.0"
99
---
1010

@@ -104,6 +104,7 @@ Match the user's intent to a command and reference file, then read the reference
104104
| Execute a token swap or bridge | `mm swap execute` | [swap.md](references/swap.md) |
105105
| Check swap or bridge status | `mm swap status` | [swap.md](references/swap.md) |
106106
| Bridge tokens to another chain | `mm swap execute` | [swap.md](references/swap.md) |
107+
| Pay an HTTP `402` / x402 paywalled request | `python3 scripts/x402_pay.py` | [x402.md](references/x402.md) |
107108

108109
## Workflows
109110

@@ -133,6 +134,7 @@ CLI behavior lives in `references/`. Repeatable patterns live in `workflows/`. L
133134
| Toggle Aave V3 collateral | [aave-collateral.md](workflows/aave-collateral.md) |
134135
| Check Aave V3 positions and health factor | [aave-positions.md](workflows/aave-positions.md) |
135136
| Discover Aave V3 tokens, rates, and liquidity | [aave-markets.md](workflows/aave-markets.md) |
137+
| Pay an HTTP `402` (x402) paywalled request | [x402-pay.md](workflows/x402-pay.md) |
136138

137139
## Global Flags
138140

@@ -218,6 +220,10 @@ Before constructing any command, validate all user-provided values:
218220
| `--to-address` | Must match `^0x[0-9a-fA-F]{40}$`. Only valid for cross-chain swaps (`--to-chain` differs from `--from-chain`); rejected for same-chain swaps |
219221
| `--refuel` | Boolean flag (no value). Only meaningful for cross-chain swaps (`--to-chain` differs from `--from-chain`); no effect on same-chain swaps |
220222
| `--password` | Must be a non-empty string. Never log, display, or store the value. |
223+
| x402 `asset` | Must be a valid contract address on a network returned by `mm chains list`. The currency choice is the server's offer confirmed by the user; the script keeps no currency allowlist. |
224+
| x402 `payTo` / authorization `to` | Must match `^0x[0-9a-fA-F]{40}$` and equal the recipient in the `402` |
225+
| x402 `value` | Atomic-unit integer that exactly equals the offered amount (the `exact` scheme is not a maximum) |
226+
| x402 resource URL | Must be `https://`. Reject a `402` reached via an unexpected cross-host redirect |
221227

222228
Do not pass unvalidated user input into any command.
223229

@@ -230,6 +236,7 @@ Do not pass unvalidated user input into any command.
230236
| Message signing | Always show exact message and chain before signing |
231237
| Typed-data signing | Always show domain, primary type, chain, verifying contract, and message summary before signing |
232238
| Swaps / bridges | Always confirm from/to tokens, amount, source/destination chain, slippage, quoted output, recipient address (if `--to-address` is set), and the destination gas top-up (if `--refuel` is set) before executing |
239+
| x402 payments | Always confirm asset, decimals-correct amount, network, `payTo`, and resource URL before signing the authorization. One payment attempt per resource, never auto-retry a payment. Autonomous auto-pay is not supported. |
233240
| Perps trading | Always confirm symbol, side, size, leverage, venue, order type, and limit price if present before executing |
234241
| Perps deposit/withdraw | Always confirm amount, asset, venue, network, and destination where applicable before executing |
235242
| Predict trading | Always confirm token ID, side, size, price, order type, market, and outcome before executing |
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# x402 Payments (buyer / payer)
2+
3+
Use this when an HTTP request returns **`402 Payment Required`** following the
4+
[x402 protocol](https://docs.x402.org), or when the user asks to pay for / fetch a paywalled API,
5+
endpoint, file, or resource over HTTP.
6+
7+
`scripts/x402_pay.py` performs the payment: it fetches the URL, reads the server's payment
8+
requirements, signs an EIP-3009 authorization with `mm wallet sign-typed-data`, retries with the
9+
payment header, and reports the settlement. Signing is delegated to `mm`, so the private key stays
10+
in the wallet. The script is pure Python standard library (run with `python3`).
11+
12+
## How x402 works (exact scheme, EVM)
13+
14+
1. The client requests a resource. The server replies `402` with one or more **payment options**
15+
(scheme, network, amount, asset, `payTo`, validity window).
16+
2. The client signs an EIP-3009 `TransferWithAuthorization` for one option. This is a gasless,
17+
off-chain authorization (no on-chain tx from the payer) permitting a pull of exactly the amount
18+
to `payTo`.
19+
3. The client retries with the payment header. The server's facilitator settles on-chain and
20+
returns the resource plus a settlement header (tx hash).
21+
22+
The script handles both protocol versions: v1 (requirements in the `402` body, retry header
23+
`X-PAYMENT`) and v2 (requirements in the `PAYMENT-REQUIRED` header, retry header
24+
`PAYMENT-SIGNATURE`).
25+
26+
## Usage
27+
28+
Inspect first (read-only: no signing, no spending), show the result to the user, then pay.
29+
30+
```bash
31+
python3 scripts/x402_pay.py inspect <url>
32+
python3 scripts/x402_pay.py pay <url> --confirm [--asset <contract>] [--network <network>]
33+
```
34+
35+
`inspect` prints the payment requirement(s) as JSON, including the human-readable amount, asset
36+
symbol, network, `payTo`, and resource. `pay` runs the payment for a single eligible option and
37+
prints the settlement (`transaction`) and the resource body. It requires `--confirm`. When the
38+
`402` offers more than one eligible option, choose one with `--asset` or `--network`.
39+
40+
### Example
41+
42+
```bash
43+
python3 scripts/x402_pay.py inspect https://api.example.com/premium
44+
# review the amount / payTo / resource with the user, then:
45+
python3 scripts/x402_pay.py pay https://api.example.com/premium --confirm
46+
```
47+
48+
## What the script checks
49+
50+
It pays an option whose `scheme` is `exact` on a network `mm` supports (resolved from
51+
`mm chains list`). Before signing it verifies the amount is a positive atomic-unit integer, the
52+
asset and `payTo` are valid addresses, the EIP-712 domain (`name`/`version`) is present, and the
53+
URL is `https://` (plain `http` is allowed only on loopback for local testing). It does not follow
54+
redirects, so a cross-host redirect cannot divert payment. After paying it makes a single retry
55+
and reports settlement; it never retries a payment.
56+
57+
These are protocol-correctness checks. Which currencies and amounts are allowed is the wallet's
58+
decision: the asset comes from the server's offer, the user confirms it, and `mm` signs (and is
59+
where spend policy applies). The script does not keep its own currency allowlist.
60+
61+
## Assets and decimals
62+
63+
The server chooses which assets it accepts; the client can only pay one the server offered. The
64+
script reads the asset's symbol and decimals from `mm token assets` for display. When `mm` cannot
65+
resolve them (for example on testnets the Token API does not index), it shows the raw atomic
66+
amount; the signed value is the server's atomic amount either way.
67+
68+
## Wallet modes
69+
70+
- **Server-wallet:** signing runs synchronously via `--wait` (guard mode permits EIP-712 signing).
71+
The x402 window is short, so if a signature needs manual approval and is slow, the authorization
72+
can expire; rerun `pay` to sign again with fresh values.
73+
- **BYOK:** signing returns immediately. Set `MM_PASSWORD` for an encrypted mnemonic so signing is
74+
non-interactive.
75+
76+
## Confirmation
77+
78+
Run `inspect` and show the user the asset, amount, network, `payTo`, and resource URL. Run
79+
`pay ... --confirm` only after the user approves. A signature authorizes a real token debit.
80+
81+
## Idempotency
82+
83+
The script is stateless. The EIP-3009 nonce prevents the same signed authorization from settling
84+
twice, and each run makes one payment, but rerunning `pay` for the same resource makes a new
85+
payment. Guard repeated calls at the caller. A local idempotency ledger (a caller-supplied key
86+
mapped to prior settlements) is a possible future improvement.

0 commit comments

Comments
 (0)