thebuyside-x402-agent 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -15
- package/dist/chains/solana-usdc.d.ts +13 -0
- package/dist/chains/solana-usdc.d.ts.map +1 -1
- package/dist/chains/solana-usdc.js +87 -19
- package/dist/chains/solana-usdc.js.map +1 -1
- package/dist/mpp/auth-header.d.ts +47 -0
- package/dist/mpp/auth-header.d.ts.map +1 -0
- package/dist/mpp/auth-header.js +135 -0
- package/dist/mpp/auth-header.js.map +1 -0
- package/dist/mpp/client.d.ts +64 -0
- package/dist/mpp/client.d.ts.map +1 -0
- package/dist/mpp/client.js +208 -0
- package/dist/mpp/client.js.map +1 -0
- package/dist/mpp/jcs.d.ts +25 -0
- package/dist/mpp/jcs.d.ts.map +1 -0
- package/dist/mpp/jcs.js +74 -0
- package/dist/mpp/jcs.js.map +1 -0
- package/dist/mpp/types.d.ts +96 -0
- package/dist/mpp/types.d.ts.map +1 -0
- package/dist/mpp/types.js +20 -0
- package/dist/mpp/types.js.map +1 -0
- package/dist/registry/seed.candidates.json +17 -0
- package/dist/registry/types.d.ts +8 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/discover.d.ts +4 -2
- package/dist/tools/discover.d.ts.map +1 -1
- package/dist/tools/discover.js +14 -10
- package/dist/tools/discover.js.map +1 -1
- package/dist/tools/fetch.d.ts +12 -8
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +124 -35
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/wallet_status.d.ts +3 -2
- package/dist/tools/wallet_status.d.ts.map +1 -1
- package/dist/tools/wallet_status.js +4 -3
- package/dist/tools/wallet_status.js.map +1 -1
- package/dist/x402/client.d.ts +7 -0
- package/dist/x402/client.d.ts.map +1 -1
- package/dist/x402/client.js +4 -2
- package/dist/x402/client.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
# thebuyside-x402-agent
|
|
2
2
|
|
|
3
|
-
The MCP gateway that lets any AI agent discover and pay
|
|
3
|
+
The MCP gateway that lets any AI agent discover and pay metered APIs on Base or Solana — without the user wiring payments themselves.
|
|
4
4
|
|
|
5
|
-
`thebuyside-x402-agent` is the canonical buyer-side reference implementation for
|
|
5
|
+
`thebuyside-x402-agent` is the canonical buyer-side reference implementation for two open agent-payment protocols:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
7
|
+
- **[x402](https://x402.org)** — the HTTP 402 payment standard stewarded by the Linux Foundation. EVM (Base) and SVM (Solana) via the `exact` scheme.
|
|
8
|
+
- **MPP** — [Machine Payments Protocol](https://paymentauth.org/draft-solana-charge-00.html), the new RFC-7235-style protocol behind [pay.sh](https://pay.sh) (Solana Foundation × Google Cloud, launched May 2026). Solana mainnet USDC.
|
|
9
|
+
|
|
10
|
+
Drop it into Claude Code, Claude Desktop, Cursor, or any MCP client, and your agent gains three tools:
|
|
11
|
+
|
|
12
|
+
- **`pay.discover`** — search the curated registry plus three federated indexes (CDP Bazaar, agentic.market, x402watch) for paid APIs
|
|
13
|
+
- **`pay.fetch`** — call one (the gateway pays the 402 challenge automatically, on whichever chain you have a key for, speaking whichever protocol the seller uses)
|
|
14
|
+
- **`pay.wallet_status`** — show the gateway's wallet(s), today's spend, and caps
|
|
10
15
|
|
|
11
16
|
The agent never sees the 402, never sees a wallet, never holds a private key.
|
|
12
17
|
|
|
13
18
|
## Status
|
|
14
19
|
|
|
15
|
-
**v0.
|
|
20
|
+
**v0.5.0 — MPP support landed 2026-05-10.** Previously validated end-to-end on both supported chains via x402:
|
|
16
21
|
|
|
17
22
|
> Base mainnet · `$0.005 USDC` · tx [`0xd0917b35…`](https://basescan.org/tx/0xd0917b35d8b778cf8d0249cc1b107a48ff7125b9fcaf7b4b257d823f73cc6aac)
|
|
18
23
|
>
|
|
19
24
|
> Solana mainnet · `$0.005 USDC` · tx [`4DYWUMEx…`](https://solscan.io/tx/4DYWUMExSrMNxYLjUuH9G8feN4fmYXm4ToCx7gGaAEjJRf2QNrE8LsvoFSGhXwQJrchhgrnGpUFwjxrci9PRLF71)
|
|
20
25
|
|
|
21
|
-
-
|
|
22
|
-
- x402 v1 + v2
|
|
26
|
+
- 154 unit tests + MCP smoke test, all green
|
|
27
|
+
- **Dual-protocol**: speaks x402 v1 + v2 *and* MPP (`solana`/`charge` intent). `pay.fetch` peeks at the 402 and dispatches transparently — agents never know which protocol the seller uses.
|
|
23
28
|
- Multi-chain: configure either Base (EVM/EIP-3009) or Solana (SVM/SPL-TransferChecked) — or both. Sellers offering multiple chains are routed to whichever you have a signer for. On Solana, the seller's facilitator covers SOL gas — buyer wallet only needs USDC.
|
|
24
|
-
- Federated discovery: `
|
|
25
|
-
- Spend caps, host allowlist, receipts log, self-transfer guard
|
|
29
|
+
- Federated discovery: `pay.discover` queries the curated `seed.json` + CDP Bazaar + agentic.market + x402watch in parallel and dedupes by canonical endpoint URL
|
|
30
|
+
- Spend caps, host allowlist, receipts log, self-transfer guard — protocol-agnostic
|
|
26
31
|
- Confirm-before-pay via MCP elicitation, with graceful fallback for clients lacking task-creation support (Claude Code)
|
|
27
32
|
- Apache 2.0, DCO not CLA
|
|
28
33
|
|
|
@@ -60,7 +65,7 @@ Use a fresh wallet, not your main one. (You can also pass these via your MCP cli
|
|
|
60
65
|
claude mcp add x402-pay -- npx -y thebuyside-x402-agent
|
|
61
66
|
```
|
|
62
67
|
|
|
63
|
-
Open a session, type `/mcp` to verify, then ask: *"Use
|
|
68
|
+
Open a session, type `/mcp` to verify, then ask: *"Use pay.wallet_status to show my wallet."*
|
|
64
69
|
|
|
65
70
|
**Claude Desktop:**
|
|
66
71
|
|
|
@@ -72,13 +77,13 @@ See [docs/install-claude-desktop.md](docs/install-claude-desktop.md).
|
|
|
72
77
|
|
|
73
78
|
Once connected, ask the model:
|
|
74
79
|
|
|
75
|
-
> *"Use
|
|
80
|
+
> *"Use pay.discover to find APIs about news."*
|
|
76
81
|
|
|
77
|
-
> *"Use
|
|
82
|
+
> *"Use pay.fetch to get https://news-ep.com/api/v1/stories?market=houston&limit=5"*
|
|
78
83
|
|
|
79
84
|
The first returns the registry plus federated matches from CDP Bazaar, agentic.market, and x402watch (each result tagged with its `source`). The second pays `$0.005 USDC` and returns Houston news. news-ep advertises both Base and Solana — the gateway picks whichever chain you have a key for.
|
|
80
85
|
|
|
81
|
-
After a successful call, ask `
|
|
86
|
+
After a successful call, ask `pay.wallet_status` and you'll see today's spend reflected (and which chains have signers configured).
|
|
82
87
|
|
|
83
88
|
## Configuration
|
|
84
89
|
|
|
@@ -104,7 +109,7 @@ Spend controls have safe defaults. Override via env if needed.
|
|
|
104
109
|
| `X402_CONFIRM_STRICT` | *(off)* | `1` refuses payment when the client lacks elicitation OR advertises elicitation but lacks tasks/create (e.g. Claude Code as of 2026-05). Default: log a one-time warning and proceed |
|
|
105
110
|
| `X402_RECEIPTS_PATH` | `.local/receipts.jsonl` | Where the receipts log is written |
|
|
106
111
|
|
|
107
|
-
**Federated discovery** (`
|
|
112
|
+
**Federated discovery** (`pay.discover` queries these in parallel and merges with the local `seed.json`):
|
|
108
113
|
|
|
109
114
|
| Var | Default | What it does |
|
|
110
115
|
| --- | --- | --- |
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* and an x402 round trip typically fits well inside that.
|
|
29
29
|
*/
|
|
30
30
|
import { VersionedTransaction } from '@solana/web3.js';
|
|
31
|
+
import type { ChargeRequest } from '../mpp/types.js';
|
|
31
32
|
import type { PaymentRequirements } from '../x402/types.js';
|
|
32
33
|
import type { SolanaChainAdapter } from './adapter.js';
|
|
33
34
|
export type SolanaUsdcAdapterOptions = {
|
|
@@ -46,6 +47,18 @@ export declare class SolanaUsdcAdapter implements SolanaChainAdapter {
|
|
|
46
47
|
buildPayment(reqs: PaymentRequirements, payerPubkey: string): Promise<{
|
|
47
48
|
tx: VersionedTransaction;
|
|
48
49
|
}>;
|
|
50
|
+
/**
|
|
51
|
+
* MPP-flavored payment build. Mirrors `buildPayment` but reads the inputs
|
|
52
|
+
* from an MPP `ChargeRequest` (paymentauth.org/draft-solana-charge-00) and
|
|
53
|
+
* uses the seller-supplied `recentBlockhash` instead of fetching one.
|
|
54
|
+
*
|
|
55
|
+
* Scope: USDC mainnet, pull mode (server is feePayer), standard SPL Token
|
|
56
|
+
* program. Push mode (`feePayer: false`) is not supported here — the buyer
|
|
57
|
+
* would need its own SOL balance and gas-pricing logic.
|
|
58
|
+
*/
|
|
59
|
+
buildPaymentMpp(charge: ChargeRequest, payerPubkey: string): Promise<{
|
|
60
|
+
tx: VersionedTransaction;
|
|
61
|
+
}>;
|
|
49
62
|
private fetchBlockhash;
|
|
50
63
|
}
|
|
51
64
|
//# sourceMappingURL=solana-usdc.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solana-usdc.d.ts","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAML,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAavD,MAAM,MAAM,wBAAwB,GAAG;IACrC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C,CAAC;AAEF,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwB;gBAE9C,IAAI,GAAE,wBAA6B;IAK/C,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO3B,YAAY,CAChB,IAAI,EAAE,mBAAmB,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,EAAE,EAAE,oBAAoB,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"solana-usdc.d.ts","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAML,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAavD,MAAM,MAAM,wBAAwB,GAAG;IACrC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C,CAAC;AAEF,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwB;gBAE9C,IAAI,GAAE,wBAA6B;IAK/C,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO3B,YAAY,CAChB,IAAI,EAAE,mBAAmB,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,EAAE,EAAE,oBAAoB,CAAA;KAAE,CAAC;IA0DxC;;;;;;;;OAQG;IACG,eAAe,CACnB,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,EAAE,EAAE,oBAAoB,CAAA;KAAE,CAAC;YA0D1B,cAAc;CAM7B"}
|
|
@@ -74,32 +74,77 @@ export class SolanaUsdcAdapter {
|
|
|
74
74
|
}
|
|
75
75
|
const sourceAta = getAssociatedTokenAddressSync(mint, payer);
|
|
76
76
|
const destAta = getAssociatedTokenAddressSync(mint, seller);
|
|
77
|
-
//
|
|
77
|
+
// x402 SVM scheme: feePayer MUST NOT appear in any instruction's accounts.
|
|
78
|
+
// MPP relaxes this rule — see `buildPaymentMpp` below.
|
|
78
79
|
if (feePayer.equals(sourceAta) || feePayer.equals(destAta) || feePayer.equals(seller) || feePayer.equals(mint)) {
|
|
79
80
|
throw new Error('feePayer collides with an instruction account (source ATA, dest ATA, ' +
|
|
80
81
|
'seller, or mint) — refusing to build tx. This violates the SVM scheme ' +
|
|
81
82
|
'safety rule.');
|
|
82
83
|
}
|
|
83
84
|
const memoText = readMemo(reqs.extra) ?? randomHexNonce(16);
|
|
84
|
-
const amount = BigInt(reqs.maxAmountRequired);
|
|
85
|
-
const instructions = [
|
|
86
|
-
ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }),
|
|
87
|
-
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
|
|
88
|
-
createTransferCheckedInstruction(sourceAta, mint, destAta, payer, amount, USDC_DECIMALS, [], TOKEN_PROGRAM_ID),
|
|
89
|
-
new TransactionInstruction({
|
|
90
|
-
programId: MEMO_PROGRAM_ID,
|
|
91
|
-
keys: [],
|
|
92
|
-
data: Buffer.from(memoText, 'utf8'),
|
|
93
|
-
}),
|
|
94
|
-
];
|
|
95
85
|
const recentBlockhash = await this.fetchBlockhash();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
86
|
+
return {
|
|
87
|
+
tx: buildTransferTx({
|
|
88
|
+
payer,
|
|
89
|
+
seller,
|
|
90
|
+
feePayer,
|
|
91
|
+
mint,
|
|
92
|
+
amount: BigInt(reqs.maxAmountRequired),
|
|
93
|
+
memo: memoText,
|
|
94
|
+
recentBlockhash,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* MPP-flavored payment build. Mirrors `buildPayment` but reads the inputs
|
|
100
|
+
* from an MPP `ChargeRequest` (paymentauth.org/draft-solana-charge-00) and
|
|
101
|
+
* uses the seller-supplied `recentBlockhash` instead of fetching one.
|
|
102
|
+
*
|
|
103
|
+
* Scope: USDC mainnet, pull mode (server is feePayer), standard SPL Token
|
|
104
|
+
* program. Push mode (`feePayer: false`) is not supported here — the buyer
|
|
105
|
+
* would need its own SOL balance and gas-pricing logic.
|
|
106
|
+
*/
|
|
107
|
+
async buildPaymentMpp(charge, payerPubkey) {
|
|
108
|
+
if (charge.currency !== USDC_MAINNET_MINT) {
|
|
109
|
+
throw new Error(`solana-usdc adapter only handles the USDC mint (${USDC_MAINNET_MINT}); ` +
|
|
110
|
+
`MPP challenge asked for currency=${charge.currency}.`);
|
|
111
|
+
}
|
|
112
|
+
if (charge.methodDetails.tokenProgram !== TOKEN_PROGRAM_ID.toBase58()) {
|
|
113
|
+
throw new Error(`MPP challenge requires non-standard token program ${charge.methodDetails.tokenProgram}; ` +
|
|
114
|
+
`only the standard SPL Token program is supported in v0.5.0.`);
|
|
115
|
+
}
|
|
116
|
+
if (charge.methodDetails.decimals !== USDC_DECIMALS) {
|
|
117
|
+
throw new Error(`MPP challenge declares decimals=${charge.methodDetails.decimals}; ` +
|
|
118
|
+
`USDC requires decimals=${USDC_DECIMALS}.`);
|
|
119
|
+
}
|
|
120
|
+
if (!charge.methodDetails.feePayer) {
|
|
121
|
+
throw new Error('MPP push mode (`methodDetails.feePayer: false`) is not supported in ' +
|
|
122
|
+
'v0.5.0 — the buyer cannot be the fee payer in this gateway.');
|
|
123
|
+
}
|
|
124
|
+
if (!charge.methodDetails.feePayerKey) {
|
|
125
|
+
throw new Error('MPP pull-mode challenge missing `methodDetails.feePayerKey` — required ' +
|
|
126
|
+
'when `feePayer: true` so the buyer knows whose pubkey to record as ' +
|
|
127
|
+
'the transaction fee payer.');
|
|
128
|
+
}
|
|
129
|
+
const payer = new PublicKey(payerPubkey);
|
|
130
|
+
const seller = new PublicKey(charge.recipient);
|
|
131
|
+
const feePayer = new PublicKey(charge.methodDetails.feePayerKey);
|
|
132
|
+
const mint = new PublicKey(charge.currency);
|
|
133
|
+
if (feePayer.equals(payer)) {
|
|
134
|
+
throw new Error('seller-declared feePayer equals the buyer pubkey — refusing to sign. ' +
|
|
135
|
+
'Buyer must not appear as feePayer in pull mode.');
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
tx: buildTransferTx({
|
|
139
|
+
payer,
|
|
140
|
+
seller,
|
|
141
|
+
feePayer,
|
|
142
|
+
mint,
|
|
143
|
+
amount: BigInt(charge.amount),
|
|
144
|
+
memo: null,
|
|
145
|
+
recentBlockhash: charge.methodDetails.recentBlockhash,
|
|
146
|
+
}),
|
|
147
|
+
};
|
|
103
148
|
}
|
|
104
149
|
async fetchBlockhash() {
|
|
105
150
|
if (this.blockhashFetcher)
|
|
@@ -109,6 +154,29 @@ export class SolanaUsdcAdapter {
|
|
|
109
154
|
return blockhash;
|
|
110
155
|
}
|
|
111
156
|
}
|
|
157
|
+
/** Build the 4-instruction USDC transferChecked tx shared by x402 and MPP. */
|
|
158
|
+
function buildTransferTx(args) {
|
|
159
|
+
const sourceAta = getAssociatedTokenAddressSync(args.mint, args.payer);
|
|
160
|
+
const destAta = getAssociatedTokenAddressSync(args.mint, args.seller);
|
|
161
|
+
const instructions = [
|
|
162
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }),
|
|
163
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
|
|
164
|
+
createTransferCheckedInstruction(sourceAta, args.mint, destAta, args.payer, args.amount, USDC_DECIMALS, [], TOKEN_PROGRAM_ID),
|
|
165
|
+
];
|
|
166
|
+
if (args.memo !== null) {
|
|
167
|
+
instructions.push(new TransactionInstruction({
|
|
168
|
+
programId: MEMO_PROGRAM_ID,
|
|
169
|
+
keys: [],
|
|
170
|
+
data: Buffer.from(args.memo, 'utf8'),
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
const messageV0 = new TransactionMessage({
|
|
174
|
+
payerKey: args.feePayer,
|
|
175
|
+
recentBlockhash: args.recentBlockhash,
|
|
176
|
+
instructions,
|
|
177
|
+
}).compileToV0Message();
|
|
178
|
+
return new VersionedTransaction(messageV0);
|
|
179
|
+
}
|
|
112
180
|
function readFeePayer(extra) {
|
|
113
181
|
if (extra && typeof extra === 'object' && 'feePayer' in extra) {
|
|
114
182
|
const f = extra.feePayer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solana-usdc.js","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"solana-usdc.js","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 7235 HTTP Authentication header parser/builder, scoped to the
|
|
3
|
+
* `Payment` auth-scheme used by MPP (paymentauth.org/draft-solana-charge-00).
|
|
4
|
+
*
|
|
5
|
+
* Servers emit:
|
|
6
|
+
* WWW-Authenticate: Payment id="...", realm="...", method="solana",
|
|
7
|
+
* intent="charge", request="<b64url-JCS>", expires="<ISO-8601>",
|
|
8
|
+
* description="<optional human label>"
|
|
9
|
+
*
|
|
10
|
+
* Buyers reply with:
|
|
11
|
+
* Authorization: Payment <b64url-JCS credential>
|
|
12
|
+
*
|
|
13
|
+
* The `Payment` scheme does not use auth-params on the request side — the
|
|
14
|
+
* value after the scheme name is a single base64url token (the credential).
|
|
15
|
+
*
|
|
16
|
+
* Multi-challenge headers (RFC 7235 §4.1 allows several comma-separated
|
|
17
|
+
* challenges) are not handled here — we accept the first `Payment` challenge
|
|
18
|
+
* and throw if a non-Payment scheme appears before it. In practice MPP
|
|
19
|
+
* sellers emit a single-challenge header.
|
|
20
|
+
*/
|
|
21
|
+
export type ParsedPaymentChallenge = {
|
|
22
|
+
scheme: 'Payment';
|
|
23
|
+
id: string;
|
|
24
|
+
realm: string;
|
|
25
|
+
/** Always `"solana"` in this spec, but parsed verbatim. */
|
|
26
|
+
method: string;
|
|
27
|
+
/** Always `"charge"` in this spec, but parsed verbatim. */
|
|
28
|
+
intent: string;
|
|
29
|
+
/** base64url(JCS(ChargeRequest)) — still encoded; decode separately. */
|
|
30
|
+
request: string;
|
|
31
|
+
/** ISO-8601 datetime string. Caller compares against current time. */
|
|
32
|
+
expires: string;
|
|
33
|
+
/** Optional human-readable label some sellers include. */
|
|
34
|
+
description?: string;
|
|
35
|
+
/** Any auth-params we don't recognize, lowercased. Surfaced for forward-compat. */
|
|
36
|
+
extras: Record<string, string>;
|
|
37
|
+
};
|
|
38
|
+
export declare function parsePaymentChallenge(header: string): ParsedPaymentChallenge;
|
|
39
|
+
/** Build the value of the `Authorization` header for an MPP credential. */
|
|
40
|
+
export declare function buildAuthorizationHeader(credentialBase64Url: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Test whether a `WWW-Authenticate` header value advertises an MPP challenge.
|
|
43
|
+
* Used by the unified fetch handler to decide between x402 and MPP routing.
|
|
44
|
+
* Cheap shape check only — full parsing happens in `parsePaymentChallenge`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isMppChallengeHeader(header: string | null | undefined): boolean;
|
|
47
|
+
//# sourceMappingURL=auth-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-header.d.ts","sourceRoot":"","sources":["../../src/mpp/auth-header.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,SAAS,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC,CAAC;AAMF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,CAmG5E;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,mBAAmB,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAG/E"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 7235 HTTP Authentication header parser/builder, scoped to the
|
|
3
|
+
* `Payment` auth-scheme used by MPP (paymentauth.org/draft-solana-charge-00).
|
|
4
|
+
*
|
|
5
|
+
* Servers emit:
|
|
6
|
+
* WWW-Authenticate: Payment id="...", realm="...", method="solana",
|
|
7
|
+
* intent="charge", request="<b64url-JCS>", expires="<ISO-8601>",
|
|
8
|
+
* description="<optional human label>"
|
|
9
|
+
*
|
|
10
|
+
* Buyers reply with:
|
|
11
|
+
* Authorization: Payment <b64url-JCS credential>
|
|
12
|
+
*
|
|
13
|
+
* The `Payment` scheme does not use auth-params on the request side — the
|
|
14
|
+
* value after the scheme name is a single base64url token (the credential).
|
|
15
|
+
*
|
|
16
|
+
* Multi-challenge headers (RFC 7235 §4.1 allows several comma-separated
|
|
17
|
+
* challenges) are not handled here — we accept the first `Payment` challenge
|
|
18
|
+
* and throw if a non-Payment scheme appears before it. In practice MPP
|
|
19
|
+
* sellers emit a single-challenge header.
|
|
20
|
+
*/
|
|
21
|
+
const TOKEN_CHAR_RE = /[A-Za-z0-9!#$%&'*+\-.^_`|~]/;
|
|
22
|
+
/** Token + `/` (for slash-bearing base64url values used as bare tokens by some sellers). */
|
|
23
|
+
const TOKEN_VALUE_CHAR_RE = /[A-Za-z0-9!#$%&'*+\-.^_`|~/]/;
|
|
24
|
+
export function parsePaymentChallenge(header) {
|
|
25
|
+
let i = 0;
|
|
26
|
+
const len = header.length;
|
|
27
|
+
const skipWs = () => {
|
|
28
|
+
while (i < len && (header[i] === ' ' || header[i] === '\t'))
|
|
29
|
+
i++;
|
|
30
|
+
};
|
|
31
|
+
skipWs();
|
|
32
|
+
const schemeStart = i;
|
|
33
|
+
while (i < len && header[i] !== ' ' && header[i] !== '\t')
|
|
34
|
+
i++;
|
|
35
|
+
const scheme = header.slice(schemeStart, i);
|
|
36
|
+
if (scheme.toLowerCase() !== 'payment') {
|
|
37
|
+
throw new Error(`unexpected WWW-Authenticate scheme: "${scheme}" (expected "Payment")`);
|
|
38
|
+
}
|
|
39
|
+
skipWs();
|
|
40
|
+
const params = new Map();
|
|
41
|
+
while (i < len) {
|
|
42
|
+
const nameStart = i;
|
|
43
|
+
while (i < len && TOKEN_CHAR_RE.test(header[i]))
|
|
44
|
+
i++;
|
|
45
|
+
if (i === nameStart) {
|
|
46
|
+
throw new Error(`expected auth-param name at index ${i} of WWW-Authenticate header`);
|
|
47
|
+
}
|
|
48
|
+
const name = header.slice(nameStart, i).toLowerCase();
|
|
49
|
+
skipWs();
|
|
50
|
+
if (header[i] !== '=') {
|
|
51
|
+
throw new Error(`expected "=" after auth-param "${name}" at index ${i}`);
|
|
52
|
+
}
|
|
53
|
+
i++;
|
|
54
|
+
skipWs();
|
|
55
|
+
let value;
|
|
56
|
+
if (header[i] === '"') {
|
|
57
|
+
i++;
|
|
58
|
+
let v = '';
|
|
59
|
+
while (i < len && header[i] !== '"') {
|
|
60
|
+
if (header[i] === '\\') {
|
|
61
|
+
i++;
|
|
62
|
+
if (i >= len)
|
|
63
|
+
throw new Error('unterminated escape inside quoted-string');
|
|
64
|
+
v += header[i];
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
v += header[i];
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (header[i] !== '"') {
|
|
73
|
+
throw new Error(`unterminated quoted-string for auth-param "${name}"`);
|
|
74
|
+
}
|
|
75
|
+
i++;
|
|
76
|
+
value = v;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const valStart = i;
|
|
80
|
+
while (i < len && TOKEN_VALUE_CHAR_RE.test(header[i]))
|
|
81
|
+
i++;
|
|
82
|
+
if (i === valStart) {
|
|
83
|
+
throw new Error(`expected value for auth-param "${name}" at index ${i}`);
|
|
84
|
+
}
|
|
85
|
+
value = header.slice(valStart, i);
|
|
86
|
+
}
|
|
87
|
+
params.set(name, value);
|
|
88
|
+
skipWs();
|
|
89
|
+
if (i < len) {
|
|
90
|
+
if (header[i] !== ',') {
|
|
91
|
+
throw new Error(`expected "," between auth-params at index ${i}, found "${header[i]}"`);
|
|
92
|
+
}
|
|
93
|
+
i++;
|
|
94
|
+
skipWs();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const required = ['id', 'realm', 'method', 'intent', 'request', 'expires'];
|
|
98
|
+
for (const k of required) {
|
|
99
|
+
if (!params.has(k)) {
|
|
100
|
+
throw new Error(`MPP challenge missing required auth-param "${k}"`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const known = new Set([...required, 'description']);
|
|
104
|
+
const extras = {};
|
|
105
|
+
for (const [k, v] of params) {
|
|
106
|
+
if (!known.has(k))
|
|
107
|
+
extras[k] = v;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
scheme: 'Payment',
|
|
111
|
+
id: params.get('id'),
|
|
112
|
+
realm: params.get('realm'),
|
|
113
|
+
method: params.get('method'),
|
|
114
|
+
intent: params.get('intent'),
|
|
115
|
+
request: params.get('request'),
|
|
116
|
+
expires: params.get('expires'),
|
|
117
|
+
description: params.get('description'),
|
|
118
|
+
extras,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Build the value of the `Authorization` header for an MPP credential. */
|
|
122
|
+
export function buildAuthorizationHeader(credentialBase64Url) {
|
|
123
|
+
return `Payment ${credentialBase64Url}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Test whether a `WWW-Authenticate` header value advertises an MPP challenge.
|
|
127
|
+
* Used by the unified fetch handler to decide between x402 and MPP routing.
|
|
128
|
+
* Cheap shape check only — full parsing happens in `parsePaymentChallenge`.
|
|
129
|
+
*/
|
|
130
|
+
export function isMppChallengeHeader(header) {
|
|
131
|
+
if (!header)
|
|
132
|
+
return false;
|
|
133
|
+
return /^\s*payment\b/i.test(header);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=auth-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-header.js","sourceRoot":"","sources":["../../src/mpp/auth-header.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MPP (Machine Payments Protocol) client — drives the full payment loop
|
|
3
|
+
* for the `solana`/`charge` intent per paymentauth.org/draft-solana-charge-00:
|
|
4
|
+
*
|
|
5
|
+
* 1. Make the initial request.
|
|
6
|
+
* 2. If 402 with `WWW-Authenticate: Payment ...`, parse the challenge and
|
|
7
|
+
* decode the base64url JCS `request` payload.
|
|
8
|
+
* 3. Validate scope (USDC mint, standard SPL Token, pull mode, network).
|
|
9
|
+
* 4. Have `SolanaUsdcAdapter.buildPaymentMpp` build the partially-signed tx.
|
|
10
|
+
* 5. Sign with the buyer's Solana key.
|
|
11
|
+
* 6. Wrap in a JCS-canonical credential JSON, base64url-encode, send as
|
|
12
|
+
* `Authorization: Payment <b64url>`.
|
|
13
|
+
* 7. On 200, decode the `Payment-Receipt` header.
|
|
14
|
+
*
|
|
15
|
+
* No spend-policy enforcement here — that lives in `src/policy/` and is wired
|
|
16
|
+
* by the calling fetch tool via the `beforePay` / `onPaid` hooks. Mirrors the
|
|
17
|
+
* shape of `src/x402/client.ts` so the two protocols feel symmetric.
|
|
18
|
+
*/
|
|
19
|
+
import type { SolanaUsdcAdapter } from '../chains/solana-usdc.js';
|
|
20
|
+
import type { SolanaSigner } from '../signer/signer.js';
|
|
21
|
+
import type { ChargeRequest } from './types.js';
|
|
22
|
+
export type MppPayAndFetchOptions = {
|
|
23
|
+
url: string;
|
|
24
|
+
method?: 'GET' | 'POST';
|
|
25
|
+
body?: unknown;
|
|
26
|
+
signer: SolanaSigner;
|
|
27
|
+
adapter: SolanaUsdcAdapter;
|
|
28
|
+
/** Test-only: override the global `fetch`. */
|
|
29
|
+
fetchFn?: typeof fetch;
|
|
30
|
+
/**
|
|
31
|
+
* Optimization: the caller already fetched the initial response and
|
|
32
|
+
* confirmed it's a 402 with an MPP challenge. Skips the duplicate GET.
|
|
33
|
+
*/
|
|
34
|
+
prefetchedResponse?: Response;
|
|
35
|
+
/**
|
|
36
|
+
* If true, accept non-mainnet networks (`devnet`, `testnet`, `localnet`).
|
|
37
|
+
* Off by default — production callers should only pay real money on
|
|
38
|
+
* mainnet. Used by integration tests and the pay.sh debugger probe.
|
|
39
|
+
*/
|
|
40
|
+
allowNonMainnet?: boolean;
|
|
41
|
+
/** Pre-pay hook. Throw to abort before any signature is produced. */
|
|
42
|
+
beforePay?: (charge: ChargeRequest) => Promise<void> | void;
|
|
43
|
+
/** Post-pay hook. Called only on 200; receives the settle tx signature. */
|
|
44
|
+
onPaid?: (info: {
|
|
45
|
+
charge: ChargeRequest;
|
|
46
|
+
tx: string | undefined;
|
|
47
|
+
}) => Promise<void> | void;
|
|
48
|
+
};
|
|
49
|
+
export type MppPayAndFetchResult = {
|
|
50
|
+
status: number;
|
|
51
|
+
body: unknown;
|
|
52
|
+
paid: boolean;
|
|
53
|
+
/** The decoded MPP charge we paid against, if any. */
|
|
54
|
+
paidCharge?: ChargeRequest;
|
|
55
|
+
/** Solana tx signature from `Payment-Receipt`, if the server emitted one. */
|
|
56
|
+
settledTx?: string;
|
|
57
|
+
/** Populated when the retry returns non-200. */
|
|
58
|
+
failure?: {
|
|
59
|
+
status: number;
|
|
60
|
+
body: unknown;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export declare function payAndFetchMpp(opts: MppPayAndFetchOptions): Promise<MppPayAndFetchResult>;
|
|
64
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/mpp/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAOxD,OAAO,KAAK,EACV,aAAa,EAGd,MAAM,YAAY,CAAC;AAMpB,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,QAAQ,CAAC;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5D,2EAA2E;IAC3E,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5F,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,sDAAsD;IACtD,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7C,CAAC;AAEF,wBAAsB,cAAc,CAClC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CAgL/B"}
|