yeetful 0.1.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/LICENSE +21 -0
- package/README.md +307 -0
- package/dist/client.cjs +138 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +40 -0
- package/dist/client.d.ts +40 -0
- package/dist/client.js +134 -0
- package/dist/client.js.map +1 -0
- package/dist/express.cjs +224 -0
- package/dist/express.cjs.map +1 -0
- package/dist/express.d.cts +32 -0
- package/dist/express.d.ts +32 -0
- package/dist/express.js +221 -0
- package/dist/express.js.map +1 -0
- package/dist/index.cjs +301 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +290 -0
- package/dist/index.js.map +1 -0
- package/dist/next.cjs +195 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +23 -0
- package/dist/next.d.ts +23 -0
- package/dist/next.js +192 -0
- package/dist/next.js.map +1 -0
- package/dist/server.cjs +176 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +60 -0
- package/dist/server.d.ts +60 -0
- package/dist/server.js +172 -0
- package/dist/server.js.map +1 -0
- package/dist/types-DzGpKiV3.d.cts +83 -0
- package/dist/types-DzGpKiV3.d.ts +83 -0
- package/package.json +92 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yeetful
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# yeetful
|
|
2
|
+
|
|
3
|
+
**Drop-in [x402](https://www.x402.org) payments for your APIs and clients.** Gate any HTTP route behind a stablecoin micropayment in a few lines — no accounts, no API keys, no webhooks. Built for [Yeetful](https://yeetful.com), MIT-licensed, works anywhere TypeScript does.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install yeetful viem
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// Server — gate a route for 1¢ USDC
|
|
11
|
+
import { withPayment } from 'yeetful/next'
|
|
12
|
+
|
|
13
|
+
export const GET = withPayment(
|
|
14
|
+
{ price: '0.01', recipient: '0xYourAddress', network: 'base' },
|
|
15
|
+
async () => Response.json({ secret: 'gm' })
|
|
16
|
+
)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// Client — auto-pay when a server returns 402
|
|
21
|
+
import { createPaymentClient } from 'yeetful/client'
|
|
22
|
+
import { createWalletClient, http } from 'viem'
|
|
23
|
+
import { base } from 'viem/chains'
|
|
24
|
+
import { privateKeyToAccount } from 'viem/accounts'
|
|
25
|
+
|
|
26
|
+
const wallet = createWalletClient({
|
|
27
|
+
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
|
|
28
|
+
chain: base,
|
|
29
|
+
transport: http(),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const pay = createPaymentClient({ wallet })
|
|
33
|
+
const res = await pay('https://api.example.com/premium')
|
|
34
|
+
console.log(await res.json()) // → { secret: 'gm' }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Why x402?
|
|
40
|
+
|
|
41
|
+
x402 is a reborn HTTP `402 Payment Required` — a protocol where servers quote a price, clients sign a stablecoin authorization, and a facilitator settles on-chain. No accounts, no Stripe dashboards, no webhook retries. Works on EVM chains today (USDC on Base, Optimism, Arbitrum, Polygon, Ethereum).
|
|
42
|
+
|
|
43
|
+
**You get:**
|
|
44
|
+
- Per-request pricing for any API — LLM calls, data feeds, premium endpoints, MCP tools.
|
|
45
|
+
- One-sentence paywalls for agents: an LLM with a wallet can now pay for what it uses.
|
|
46
|
+
- Instant settlement on L2 — no chargebacks, no holds, no 30-day payout delay.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install yeetful viem
|
|
54
|
+
# or
|
|
55
|
+
pnpm add yeetful viem
|
|
56
|
+
# or
|
|
57
|
+
yarn add yeetful viem
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`viem` is a peer dependency so the SDK stays light and stays in sync with whatever viem version your app already uses.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
### Server: gate a route
|
|
67
|
+
|
|
68
|
+
#### Next.js (App Router)
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// app/api/premium/route.ts
|
|
72
|
+
import { withPayment } from 'yeetful/next'
|
|
73
|
+
|
|
74
|
+
export const GET = withPayment(
|
|
75
|
+
{
|
|
76
|
+
price: '0.01', // USD
|
|
77
|
+
recipient: '0xYourWalletAddress', // gets paid
|
|
78
|
+
network: 'base', // or ['base', 'optimism']
|
|
79
|
+
description: 'Premium GM endpoint',
|
|
80
|
+
},
|
|
81
|
+
async (req) => {
|
|
82
|
+
return Response.json({ message: 'gm, thanks for the cent' })
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Express
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import express from 'express'
|
|
91
|
+
import { paymentRequired } from 'yeetful/express'
|
|
92
|
+
|
|
93
|
+
const app = express()
|
|
94
|
+
|
|
95
|
+
app.get(
|
|
96
|
+
'/premium',
|
|
97
|
+
paymentRequired({
|
|
98
|
+
price: '0.01',
|
|
99
|
+
recipient: '0xYourWalletAddress',
|
|
100
|
+
network: 'base',
|
|
101
|
+
}),
|
|
102
|
+
(req, res) => {
|
|
103
|
+
res.json({ message: 'gm', payer: req.x402?.payer })
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
app.listen(3000)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Anywhere else (Hono, Bun, Cloudflare Workers, raw Node)
|
|
111
|
+
|
|
112
|
+
Use the runtime-agnostic `gate()` helper. Give it a standard `Request`, get back either a 402 `Response` or a `settle()` handle.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { gate } from 'yeetful/server'
|
|
116
|
+
|
|
117
|
+
export default {
|
|
118
|
+
async fetch(request: Request) {
|
|
119
|
+
const result = await gate(request, {
|
|
120
|
+
price: '0.01',
|
|
121
|
+
recipient: '0xYourWalletAddress',
|
|
122
|
+
network: 'base',
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (result.type === 'paymentRequired') return result.response
|
|
126
|
+
|
|
127
|
+
// …do the paid work…
|
|
128
|
+
const body = Response.json({ message: 'gm' })
|
|
129
|
+
|
|
130
|
+
const { header } = await result.settle()
|
|
131
|
+
body.headers.set('X-PAYMENT-RESPONSE', header)
|
|
132
|
+
return body
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Client: auto-pay
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { createPaymentClient } from 'yeetful/client'
|
|
141
|
+
|
|
142
|
+
const pay = createPaymentClient({
|
|
143
|
+
wallet, // any viem WalletClient
|
|
144
|
+
maxAmountAtomic: 1_000_000n, // cap: 1 USDC per call
|
|
145
|
+
allowedNetworks: ['base'], // only pay on Base
|
|
146
|
+
onPaymentRequired: async (req) => {
|
|
147
|
+
console.log(`Pay ${req.maxAmountRequired} to ${req.payTo}?`)
|
|
148
|
+
return true // return false to cancel
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// Use exactly like fetch.
|
|
153
|
+
const res = await pay('https://api.example.com/premium')
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
### `RouteGateOptions` — server
|
|
161
|
+
|
|
162
|
+
| Option | Type | Default | Notes |
|
|
163
|
+
| --- | --- | --- | --- |
|
|
164
|
+
| `price` | `string \| number` | **required** | USD amount, e.g. `'0.01'`. Converted to USDC atomic units. |
|
|
165
|
+
| `recipient` | `Address` | **required** | Address that receives the payment. |
|
|
166
|
+
| `network` | `X402Network \| X402Network[]` | `'base'` | Networks you'll accept. Multi-chain = multi-item discovery. |
|
|
167
|
+
| `asset` | `Address` | USDC for network | Override to use a different ERC-20. |
|
|
168
|
+
| `description` | `string` | — | Shown to the paying client. |
|
|
169
|
+
| `maxTimeoutSeconds` | `number` | `600` | Validity window of the signed authorization. |
|
|
170
|
+
| `facilitator` | `FacilitatorConfig \| false` | hosted facilitator | Pass `false` to skip on-chain settlement (testing only). |
|
|
171
|
+
|
|
172
|
+
Supported networks: `base`, `base-sepolia`, `ethereum`, `optimism`, `arbitrum`, `polygon`.
|
|
173
|
+
|
|
174
|
+
### `ClientOptions` — client
|
|
175
|
+
|
|
176
|
+
| Option | Type | Notes |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| `wallet` | `WalletClient` | Any viem wallet capable of signing EIP-712 typed data. |
|
|
179
|
+
| `maxAmountAtomic` | `bigint` | Reject requirements above this cap — safety belt. |
|
|
180
|
+
| `allowedNetworks` | `X402Network[]` | Only pay on these networks. |
|
|
181
|
+
| `onPaymentRequired` | `(req) => boolean \| Promise<boolean>` | Approval hook; return `false` to cancel. |
|
|
182
|
+
| `fetch` | `typeof fetch` | Override the underlying fetch (e.g. for timeouts). |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## How it works
|
|
187
|
+
|
|
188
|
+
1. **Client requests** a paid resource normally.
|
|
189
|
+
2. **Server** responds with `402 Payment Required` and a JSON body listing acceptable requirements (network, asset, amount, recipient).
|
|
190
|
+
3. **Client** picks the cheapest requirement, signs an [EIP-3009 `TransferWithAuthorization`](https://eips.ethereum.org/EIPS/eip-3009) with the user's wallet, and retries the request with an `X-PAYMENT` header (base64 JSON).
|
|
191
|
+
4. **Server** hands the signed payload to a facilitator which `verify`s the signature and `settle`s the transfer on-chain.
|
|
192
|
+
5. **Server** runs the handler and returns the response with an `X-PAYMENT-RESPONSE` header containing the transaction hash.
|
|
193
|
+
|
|
194
|
+
The signing is gasless for the payer — the facilitator broadcasts the transfer and picks up gas.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Facilitators
|
|
199
|
+
|
|
200
|
+
By default the SDK uses the hosted facilitator at `https://facilitator.yeetful.com`. Override it anywhere you configure the server:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
withPayment(
|
|
204
|
+
{
|
|
205
|
+
price: '0.01',
|
|
206
|
+
recipient: '0xYourAddress',
|
|
207
|
+
facilitator: {
|
|
208
|
+
url: 'https://your-facilitator.example.com',
|
|
209
|
+
authHeader: 'Bearer your-token',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
handler,
|
|
213
|
+
)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Pass `facilitator: false` to skip verification and settlement entirely — only useful for local testing.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Advanced
|
|
221
|
+
|
|
222
|
+
### Accept multiple networks
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
withPayment(
|
|
226
|
+
{
|
|
227
|
+
price: '0.01',
|
|
228
|
+
recipient: '0xYourAddress',
|
|
229
|
+
network: ['base', 'optimism', 'arbitrum'],
|
|
230
|
+
},
|
|
231
|
+
handler,
|
|
232
|
+
)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Clients automatically pick the cheapest network they're configured to use.
|
|
236
|
+
|
|
237
|
+
### Sign a payment manually
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { signPayment } from 'yeetful/client'
|
|
241
|
+
|
|
242
|
+
const payment = await signPayment(wallet, {
|
|
243
|
+
scheme: 'exact',
|
|
244
|
+
network: 'base',
|
|
245
|
+
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC
|
|
246
|
+
maxAmountRequired: '10000', // 0.01 USDC
|
|
247
|
+
payTo: '0xRecipient',
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Use with AI agents / MCP tools
|
|
252
|
+
|
|
253
|
+
x402 is a natural fit for agent tooling — drop `withPayment` in front of any MCP tool endpoint and agents with wallets can pay per-call. This SDK is what powers paid tools on [Yeetful](https://yeetful.com).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## API reference
|
|
258
|
+
|
|
259
|
+
### `yeetful/server`
|
|
260
|
+
|
|
261
|
+
- `gate(request, options)` — runtime-agnostic. Returns `{ type: 'paymentRequired', response }` or `{ type: 'ok', payer, settle }`.
|
|
262
|
+
- `Facilitator` — thin wrapper around verify/settle HTTP endpoints.
|
|
263
|
+
- `DEFAULT_FACILITATOR_URL` — the hosted facilitator URL.
|
|
264
|
+
|
|
265
|
+
### `yeetful/next`
|
|
266
|
+
|
|
267
|
+
- `withPayment(options, handler)` — wraps a Next.js route handler.
|
|
268
|
+
|
|
269
|
+
### `yeetful/express`
|
|
270
|
+
|
|
271
|
+
- `paymentRequired(options)` — returns an Express `RequestHandler`. Sets `req.x402.payer` after successful verification.
|
|
272
|
+
|
|
273
|
+
### `yeetful/client`
|
|
274
|
+
|
|
275
|
+
- `createPaymentClient(options)` — returns a `fetch`-compatible function that handles 402s automatically.
|
|
276
|
+
- `signPayment(wallet, requirement)` — sign a payment payload by hand.
|
|
277
|
+
- `PaymentError` — thrown when the client declines to pay.
|
|
278
|
+
|
|
279
|
+
### Helpers
|
|
280
|
+
|
|
281
|
+
- `usdcAddress(network)` — canonical USDC contract for a supported network.
|
|
282
|
+
- `usdToAtomic(amount, decimals?)` — safe USD → atomic-units conversion.
|
|
283
|
+
- `encodePayment` / `decodePayment` — base64 JSON codec for headers.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Development
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npm install
|
|
291
|
+
npm run build # bundles ESM + CJS + d.ts via tsup
|
|
292
|
+
npm run typecheck
|
|
293
|
+
npm test
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
To publish:
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
npm run build
|
|
300
|
+
npm publish
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT © Yeetful
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils.ts
|
|
4
|
+
var utf8Encoder = new TextEncoder();
|
|
5
|
+
new TextDecoder();
|
|
6
|
+
function encodePayment(value) {
|
|
7
|
+
const bytes = utf8Encoder.encode(JSON.stringify(value));
|
|
8
|
+
if (typeof Buffer !== "undefined") {
|
|
9
|
+
return Buffer.from(bytes).toString("base64");
|
|
10
|
+
}
|
|
11
|
+
let binary = "";
|
|
12
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
13
|
+
return btoa(binary);
|
|
14
|
+
}
|
|
15
|
+
function randomNonce() {
|
|
16
|
+
const bytes = new Uint8Array(32);
|
|
17
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
18
|
+
return "0x" + Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/client.ts
|
|
22
|
+
function createPaymentClient(options) {
|
|
23
|
+
const baseFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
24
|
+
return async function payFetch(input, init = {}) {
|
|
25
|
+
const first = await baseFetch(input, init);
|
|
26
|
+
if (first.status !== 402) return first;
|
|
27
|
+
const requirements = await parsePaymentRequired(first);
|
|
28
|
+
const requirement = selectRequirement(requirements.accepts, options);
|
|
29
|
+
if (!requirement) {
|
|
30
|
+
throw new PaymentError("No acceptable payment requirement matched client constraints", requirements);
|
|
31
|
+
}
|
|
32
|
+
if (options.onPaymentRequired) {
|
|
33
|
+
const approved = await options.onPaymentRequired(requirement);
|
|
34
|
+
if (!approved) throw new PaymentError("Payment rejected by user", requirements);
|
|
35
|
+
}
|
|
36
|
+
const payment = await signPayment(options.wallet, requirement);
|
|
37
|
+
const headers = new Headers(init.headers);
|
|
38
|
+
headers.set("X-PAYMENT", encodePayment(payment));
|
|
39
|
+
return baseFetch(input, { ...init, headers });
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
var PaymentError = class extends Error {
|
|
43
|
+
requirements;
|
|
44
|
+
constructor(message, requirements) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = "PaymentError";
|
|
47
|
+
this.requirements = requirements;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
async function parsePaymentRequired(res) {
|
|
51
|
+
try {
|
|
52
|
+
return await res.clone().json();
|
|
53
|
+
} catch {
|
|
54
|
+
throw new PaymentError("402 response did not contain a valid x402 discovery body");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function selectRequirement(accepts, opts) {
|
|
58
|
+
const filtered = accepts.filter((a) => a.scheme === "exact").filter((a) => !opts.allowedNetworks || opts.allowedNetworks.includes(a.network)).filter((a) => !opts.maxAmountAtomic || BigInt(a.maxAmountRequired) <= opts.maxAmountAtomic);
|
|
59
|
+
return filtered.sort(
|
|
60
|
+
(a, b) => BigInt(a.maxAmountRequired) < BigInt(b.maxAmountRequired) ? -1 : 1
|
|
61
|
+
)[0];
|
|
62
|
+
}
|
|
63
|
+
async function signPayment(wallet, requirement) {
|
|
64
|
+
const account = wallet.account;
|
|
65
|
+
if (!account) throw new PaymentError("Wallet has no account attached");
|
|
66
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
67
|
+
const validAfter = BigInt(now - 60);
|
|
68
|
+
const validBefore = BigInt(now + (requirement.maxTimeoutSeconds ?? 600));
|
|
69
|
+
const nonce = randomNonce();
|
|
70
|
+
const tokenName = requirement.extra?.name ?? "USD Coin";
|
|
71
|
+
const tokenVersion = requirement.extra?.version ?? "2";
|
|
72
|
+
const signature = await wallet.signTypedData({
|
|
73
|
+
account,
|
|
74
|
+
domain: {
|
|
75
|
+
name: tokenName,
|
|
76
|
+
version: tokenVersion,
|
|
77
|
+
chainId: chainIdForNetwork(requirement.network),
|
|
78
|
+
verifyingContract: requirement.asset
|
|
79
|
+
},
|
|
80
|
+
types: {
|
|
81
|
+
TransferWithAuthorization: [
|
|
82
|
+
{ name: "from", type: "address" },
|
|
83
|
+
{ name: "to", type: "address" },
|
|
84
|
+
{ name: "value", type: "uint256" },
|
|
85
|
+
{ name: "validAfter", type: "uint256" },
|
|
86
|
+
{ name: "validBefore", type: "uint256" },
|
|
87
|
+
{ name: "nonce", type: "bytes32" }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
primaryType: "TransferWithAuthorization",
|
|
91
|
+
message: {
|
|
92
|
+
from: account.address,
|
|
93
|
+
to: requirement.payTo,
|
|
94
|
+
value: BigInt(requirement.maxAmountRequired),
|
|
95
|
+
validAfter,
|
|
96
|
+
validBefore,
|
|
97
|
+
nonce
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
x402Version: 1,
|
|
102
|
+
scheme: "exact",
|
|
103
|
+
network: requirement.network,
|
|
104
|
+
payload: {
|
|
105
|
+
signature,
|
|
106
|
+
authorization: {
|
|
107
|
+
from: account.address,
|
|
108
|
+
to: requirement.payTo,
|
|
109
|
+
value: requirement.maxAmountRequired,
|
|
110
|
+
validAfter: validAfter.toString(),
|
|
111
|
+
validBefore: validBefore.toString(),
|
|
112
|
+
nonce
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function chainIdForNetwork(network) {
|
|
118
|
+
switch (network) {
|
|
119
|
+
case "base":
|
|
120
|
+
return 8453;
|
|
121
|
+
case "base-sepolia":
|
|
122
|
+
return 84532;
|
|
123
|
+
case "ethereum":
|
|
124
|
+
return 1;
|
|
125
|
+
case "optimism":
|
|
126
|
+
return 10;
|
|
127
|
+
case "arbitrum":
|
|
128
|
+
return 42161;
|
|
129
|
+
case "polygon":
|
|
130
|
+
return 137;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.PaymentError = PaymentError;
|
|
135
|
+
exports.createPaymentClient = createPaymentClient;
|
|
136
|
+
exports.signPayment = signPayment;
|
|
137
|
+
//# sourceMappingURL=client.cjs.map
|
|
138
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/client.ts"],"names":[],"mappings":"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","file":"client.cjs","sourcesContent":["import type { X402Network } from './types.js'\n\nconst USDC_BY_NETWORK: Record<X402Network, `0x${string}`> = {\n base: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n 'base-sepolia': '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n ethereum: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n optimism: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',\n arbitrum: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',\n polygon: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n}\n\nexport const USDC_DECIMALS = 6\n\n/** Returns the canonical USDC contract address for a supported network. */\nexport function usdcAddress(network: X402Network): `0x${string}` {\n return USDC_BY_NETWORK[network]\n}\n\n/**\n * Convert a human-friendly USD amount (e.g. \"0.01\") into atomic USDC units.\n * Fixed-point to avoid float drift — safer than Number math for payments.\n */\nexport function usdToAtomic(amount: string | number, decimals = USDC_DECIMALS): string {\n const str = typeof amount === 'number' ? amount.toString() : amount\n if (!/^\\d+(\\.\\d+)?$/.test(str)) {\n throw new Error(`Invalid amount: ${str}`)\n }\n const [whole, frac = ''] = str.split('.')\n const padded = frac.slice(0, decimals).padEnd(decimals, '0')\n return (BigInt(whole ?? '0') * 10n ** BigInt(decimals) + BigInt(padded || '0')).toString()\n}\n\nconst utf8Encoder = new TextEncoder()\nconst utf8Decoder = new TextDecoder()\n\n/** Base64 encode a JSON value — works in Node 18+ and browsers. */\nexport function encodePayment(value: unknown): string {\n const bytes = utf8Encoder.encode(JSON.stringify(value))\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64')\n }\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return btoa(binary)\n}\n\n/** Base64 decode a payment header value back into JSON. */\nexport function decodePayment<T = unknown>(b64: string): T {\n let bytes: Uint8Array\n if (typeof Buffer !== 'undefined') {\n bytes = new Uint8Array(Buffer.from(b64, 'base64'))\n } else {\n const binary = atob(b64)\n bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n }\n return JSON.parse(utf8Decoder.decode(bytes)) as T\n}\n\n/** Generate a random 32-byte nonce as a 0x-prefixed hex string. */\nexport function randomNonce(): `0x${string}` {\n const bytes = new Uint8Array(32)\n globalThis.crypto.getRandomValues(bytes)\n return ('0x' + Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')) as `0x${string}`\n}\n","import type { Address, Hex, WalletClient } from 'viem'\nimport type {\n PaymentPayload,\n PaymentRequiredResponse,\n PaymentRequirement,\n X402Network,\n} from './types.js'\nimport { encodePayment, randomNonce } from './utils.js'\n\nexport interface ClientOptions {\n /** A viem WalletClient able to sign EIP-712 typed data. */\n wallet: WalletClient\n /** Underlying fetch to wrap (defaults to global fetch). */\n fetch?: typeof fetch\n /**\n * Hook called before signing. Return `false` to reject the payment.\n * Useful for showing a confirmation UI to the user.\n */\n onPaymentRequired?: (requirement: PaymentRequirement) => boolean | Promise<boolean>\n /** Only allow payments up to this atomic-units cap. Safety belt. */\n maxAmountAtomic?: bigint\n /** Restrict to specific networks (defaults to all). */\n allowedNetworks?: X402Network[]\n}\n\n/**\n * Create a fetch wrapper that transparently handles x402 payments.\n *\n * On a 402 response, it picks the cheapest acceptable requirement,\n * signs an EIP-3009 authorization with the provided wallet, and retries\n * the request with an `X-PAYMENT` header.\n *\n * @example\n * ```ts\n * const pay = createPaymentClient({ wallet })\n * const res = await pay('https://api.example.com/premium')\n * ```\n */\nexport function createPaymentClient(options: ClientOptions) {\n const baseFetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n\n return async function payFetch(\n input: string | URL | Request,\n init: RequestInit = {},\n ): Promise<Response> {\n const first = await baseFetch(input, init)\n if (first.status !== 402) return first\n\n const requirements = await parsePaymentRequired(first)\n const requirement = selectRequirement(requirements.accepts, options)\n if (!requirement) {\n throw new PaymentError('No acceptable payment requirement matched client constraints', requirements)\n }\n\n if (options.onPaymentRequired) {\n const approved = await options.onPaymentRequired(requirement)\n if (!approved) throw new PaymentError('Payment rejected by user', requirements)\n }\n\n const payment = await signPayment(options.wallet, requirement)\n const headers = new Headers(init.headers)\n headers.set('X-PAYMENT', encodePayment(payment))\n\n return baseFetch(input, { ...init, headers })\n }\n}\n\nexport class PaymentError extends Error {\n readonly requirements?: PaymentRequiredResponse\n constructor(message: string, requirements?: PaymentRequiredResponse) {\n super(message)\n this.name = 'PaymentError'\n this.requirements = requirements\n }\n}\n\nasync function parsePaymentRequired(res: Response): Promise<PaymentRequiredResponse> {\n try {\n return (await res.clone().json()) as PaymentRequiredResponse\n } catch {\n throw new PaymentError('402 response did not contain a valid x402 discovery body')\n }\n}\n\nfunction selectRequirement(\n accepts: PaymentRequirement[],\n opts: ClientOptions,\n): PaymentRequirement | undefined {\n const filtered = accepts\n .filter((a) => a.scheme === 'exact')\n .filter((a) => !opts.allowedNetworks || opts.allowedNetworks.includes(a.network))\n .filter((a) => !opts.maxAmountAtomic || BigInt(a.maxAmountRequired) <= opts.maxAmountAtomic)\n\n return filtered.sort((a, b) =>\n BigInt(a.maxAmountRequired) < BigInt(b.maxAmountRequired) ? -1 : 1,\n )[0]\n}\n\n/** Sign an EIP-3009 `TransferWithAuthorization` for the given requirement. */\nexport async function signPayment(\n wallet: WalletClient,\n requirement: PaymentRequirement,\n): Promise<PaymentPayload> {\n const account = wallet.account\n if (!account) throw new PaymentError('Wallet has no account attached')\n\n const now = Math.floor(Date.now() / 1000)\n const validAfter = BigInt(now - 60)\n const validBefore = BigInt(now + (requirement.maxTimeoutSeconds ?? 600))\n const nonce = randomNonce()\n\n const tokenName =\n (requirement.extra?.name as string | undefined) ?? 'USD Coin'\n const tokenVersion =\n (requirement.extra?.version as string | undefined) ?? '2'\n\n const signature = (await wallet.signTypedData({\n account,\n domain: {\n name: tokenName,\n version: tokenVersion,\n chainId: chainIdForNetwork(requirement.network),\n verifyingContract: requirement.asset,\n },\n types: {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n },\n primaryType: 'TransferWithAuthorization',\n message: {\n from: account.address as Address,\n to: requirement.payTo,\n value: BigInt(requirement.maxAmountRequired),\n validAfter,\n validBefore,\n nonce,\n },\n })) as Hex\n\n return {\n x402Version: 1,\n scheme: 'exact',\n network: requirement.network,\n payload: {\n signature,\n authorization: {\n from: account.address as Address,\n to: requirement.payTo,\n value: requirement.maxAmountRequired,\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n },\n }\n}\n\nfunction chainIdForNetwork(network: X402Network): number {\n switch (network) {\n case 'base':\n return 8453\n case 'base-sepolia':\n return 84532\n case 'ethereum':\n return 1\n case 'optimism':\n return 10\n case 'arbitrum':\n return 42161\n case 'polygon':\n return 137\n }\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { WalletClient } from 'viem';
|
|
2
|
+
import { b as PaymentRequirement, X as X402Network, a as PaymentRequiredResponse, P as PaymentPayload } from './types-DzGpKiV3.cjs';
|
|
3
|
+
|
|
4
|
+
interface ClientOptions {
|
|
5
|
+
/** A viem WalletClient able to sign EIP-712 typed data. */
|
|
6
|
+
wallet: WalletClient;
|
|
7
|
+
/** Underlying fetch to wrap (defaults to global fetch). */
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
/**
|
|
10
|
+
* Hook called before signing. Return `false` to reject the payment.
|
|
11
|
+
* Useful for showing a confirmation UI to the user.
|
|
12
|
+
*/
|
|
13
|
+
onPaymentRequired?: (requirement: PaymentRequirement) => boolean | Promise<boolean>;
|
|
14
|
+
/** Only allow payments up to this atomic-units cap. Safety belt. */
|
|
15
|
+
maxAmountAtomic?: bigint;
|
|
16
|
+
/** Restrict to specific networks (defaults to all). */
|
|
17
|
+
allowedNetworks?: X402Network[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a fetch wrapper that transparently handles x402 payments.
|
|
21
|
+
*
|
|
22
|
+
* On a 402 response, it picks the cheapest acceptable requirement,
|
|
23
|
+
* signs an EIP-3009 authorization with the provided wallet, and retries
|
|
24
|
+
* the request with an `X-PAYMENT` header.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const pay = createPaymentClient({ wallet })
|
|
29
|
+
* const res = await pay('https://api.example.com/premium')
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function createPaymentClient(options: ClientOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
33
|
+
declare class PaymentError extends Error {
|
|
34
|
+
readonly requirements?: PaymentRequiredResponse;
|
|
35
|
+
constructor(message: string, requirements?: PaymentRequiredResponse);
|
|
36
|
+
}
|
|
37
|
+
/** Sign an EIP-3009 `TransferWithAuthorization` for the given requirement. */
|
|
38
|
+
declare function signPayment(wallet: WalletClient, requirement: PaymentRequirement): Promise<PaymentPayload>;
|
|
39
|
+
|
|
40
|
+
export { type ClientOptions, PaymentError, createPaymentClient, signPayment };
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { WalletClient } from 'viem';
|
|
2
|
+
import { b as PaymentRequirement, X as X402Network, a as PaymentRequiredResponse, P as PaymentPayload } from './types-DzGpKiV3.js';
|
|
3
|
+
|
|
4
|
+
interface ClientOptions {
|
|
5
|
+
/** A viem WalletClient able to sign EIP-712 typed data. */
|
|
6
|
+
wallet: WalletClient;
|
|
7
|
+
/** Underlying fetch to wrap (defaults to global fetch). */
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
/**
|
|
10
|
+
* Hook called before signing. Return `false` to reject the payment.
|
|
11
|
+
* Useful for showing a confirmation UI to the user.
|
|
12
|
+
*/
|
|
13
|
+
onPaymentRequired?: (requirement: PaymentRequirement) => boolean | Promise<boolean>;
|
|
14
|
+
/** Only allow payments up to this atomic-units cap. Safety belt. */
|
|
15
|
+
maxAmountAtomic?: bigint;
|
|
16
|
+
/** Restrict to specific networks (defaults to all). */
|
|
17
|
+
allowedNetworks?: X402Network[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a fetch wrapper that transparently handles x402 payments.
|
|
21
|
+
*
|
|
22
|
+
* On a 402 response, it picks the cheapest acceptable requirement,
|
|
23
|
+
* signs an EIP-3009 authorization with the provided wallet, and retries
|
|
24
|
+
* the request with an `X-PAYMENT` header.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const pay = createPaymentClient({ wallet })
|
|
29
|
+
* const res = await pay('https://api.example.com/premium')
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function createPaymentClient(options: ClientOptions): (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
33
|
+
declare class PaymentError extends Error {
|
|
34
|
+
readonly requirements?: PaymentRequiredResponse;
|
|
35
|
+
constructor(message: string, requirements?: PaymentRequiredResponse);
|
|
36
|
+
}
|
|
37
|
+
/** Sign an EIP-3009 `TransferWithAuthorization` for the given requirement. */
|
|
38
|
+
declare function signPayment(wallet: WalletClient, requirement: PaymentRequirement): Promise<PaymentPayload>;
|
|
39
|
+
|
|
40
|
+
export { type ClientOptions, PaymentError, createPaymentClient, signPayment };
|