@puga-labs/x402-mantle-sdk 0.3.10 → 0.4.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 +488 -0
- package/dist/chunk-2IGT3ZXX.js +179 -0
- package/dist/chunk-3YIQEQDT.js +96 -0
- package/dist/chunk-4BUJR6G5.js +100 -0
- package/dist/chunk-5WQRPHQL.js +108 -0
- package/dist/chunk-6IR4GUG7.js +326 -0
- package/dist/chunk-HUKLI4UV.js +318 -0
- package/dist/chunk-HVQ2RGMT.js +126 -0
- package/dist/chunk-IWRQEN5H.js +97 -0
- package/dist/chunk-KFLH22GF.js +179 -0
- package/dist/chunk-N2XBZWB6.js +111 -0
- package/dist/chunk-NC5OU47J.js +99 -0
- package/dist/chunk-NWWXJFJ4.js +293 -0
- package/dist/chunk-RVPI6FMV.js +307 -0
- package/dist/chunk-UGW2GEWW.js +303 -0
- package/dist/chunk-UOY3C2LF.js +128 -0
- package/dist/chunk-YCCM6HBF.js +149 -0
- package/dist/client.cjs +13 -7
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/createMantleClient-CMwzWrtB.d.ts +103 -0
- package/dist/createMantleClient-DEqYPoMs.d.cts +103 -0
- package/dist/createMantleClient-DyOpGjHf.d.ts +103 -0
- package/dist/createMantleClient-MlVEklNy.d.cts +103 -0
- package/dist/express-BIIgsBDf.d.ts +73 -0
- package/dist/express-BrBmK46G.d.cts +73 -0
- package/dist/express-DVL-tJDp.d.ts +80 -0
- package/dist/express-DcopaNmZ.d.cts +80 -0
- package/dist/index.cjs +95 -34
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -4
- package/dist/nextjs-BD0e-jPY.d.cts +89 -0
- package/dist/nextjs-C062eZ_O.d.ts +89 -0
- package/dist/nextjs-CvArUfyl.d.ts +89 -0
- package/dist/nextjs-Xr2VtN1D.d.cts +89 -0
- package/dist/react.cjs +13 -7
- package/dist/react.d.cts +10 -4
- package/dist/react.d.ts +10 -4
- package/dist/react.js +2 -2
- package/dist/server-express.cjs +92 -27
- package/dist/server-express.d.cts +2 -2
- package/dist/server-express.d.ts +2 -2
- package/dist/server-express.js +2 -2
- package/dist/server-nextjs.cjs +78 -24
- package/dist/server-nextjs.d.cts +2 -2
- package/dist/server-nextjs.d.ts +2 -2
- package/dist/server-nextjs.js +2 -2
- package/dist/server-web.cjs +78 -24
- package/dist/server-web.d.cts +2 -2
- package/dist/server-web.d.ts +2 -2
- package/dist/server-web.js +2 -2
- package/dist/server.cjs +138 -47
- package/dist/server.d.cts +35 -7
- package/dist/server.d.ts +35 -7
- package/dist/server.js +6 -4
- package/dist/types-AMVZT3Hf.d.ts +125 -0
- package/dist/types-CED_oMWa.d.cts +143 -0
- package/dist/types-D1mhWKmb.d.ts +143 -0
- package/dist/types-pF3IaKvC.d.cts +125 -0
- package/dist/web-standards-BxG0Rdwk.d.ts +77 -0
- package/dist/web-standards-C8EBfBLy.d.ts +77 -0
- package/dist/web-standards-CXf21iDH.d.cts +77 -0
- package/dist/web-standards-D-2Neqyr.d.cts +77 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
# @puga-labs/x402-mantle-sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@puga-labs/x402-mantle-sdk)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
SDK for x402 payments on Mantle blockchain. Protect your APIs with USDC payments using a simple middleware.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @puga-labs/x402-mantle-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- React hooks (`useMantleX402`, `useEthersWallet`)
|
|
17
|
+
- Vanilla JS client (`createMantleClient`)
|
|
18
|
+
- Server middleware for Express, Next.js, Hono, Cloudflare Workers, Deno
|
|
19
|
+
- Full TypeScript support
|
|
20
|
+
- Automatic payment flow handling
|
|
21
|
+
- Analytics & telemetry integration
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Client-Side Usage
|
|
26
|
+
|
|
27
|
+
### React (Recommended)
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { useMantleX402 } from '@puga-labs/x402-mantle-sdk/react';
|
|
31
|
+
|
|
32
|
+
function PaymentButton() {
|
|
33
|
+
const { postWithPayment } = useMantleX402({
|
|
34
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const handlePay = async () => {
|
|
38
|
+
try {
|
|
39
|
+
const { response, txHash } = await postWithPayment('/api/generate', {
|
|
40
|
+
prompt: 'Hello world'
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log('Result:', response);
|
|
44
|
+
console.log('Transaction:', txHash);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Payment failed:', error);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return <button onClick={handlePay}>Generate ($0.01)</button>;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Wallet Hook
|
|
55
|
+
|
|
56
|
+
Separate hook for wallet management:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { useEthersWallet } from '@puga-labs/x402-mantle-sdk/react';
|
|
60
|
+
|
|
61
|
+
function WalletConnect() {
|
|
62
|
+
const {
|
|
63
|
+
address,
|
|
64
|
+
isConnected,
|
|
65
|
+
chainId,
|
|
66
|
+
connect,
|
|
67
|
+
disconnect,
|
|
68
|
+
error
|
|
69
|
+
} = useEthersWallet({ autoConnect: false });
|
|
70
|
+
|
|
71
|
+
if (isConnected) {
|
|
72
|
+
return (
|
|
73
|
+
<div>
|
|
74
|
+
<p>Connected: {address}</p>
|
|
75
|
+
<p>Chain ID: {chainId}</p>
|
|
76
|
+
<button onClick={disconnect}>Disconnect</button>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return <button onClick={connect}>Connect Wallet</button>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Vanilla JavaScript
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createMantleClient } from '@puga-labs/x402-mantle-sdk/client';
|
|
89
|
+
|
|
90
|
+
// Create client
|
|
91
|
+
const client = createMantleClient({
|
|
92
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz',
|
|
93
|
+
resourceUrl: 'https://your-api.com',
|
|
94
|
+
getProvider: () => window.ethereum,
|
|
95
|
+
getAccount: () => userAddress
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Make paid request
|
|
99
|
+
const { response, txHash } = await client.postWithPayment('/api/generate', {
|
|
100
|
+
prompt: 'Hello world'
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Low-Level Payment Client
|
|
105
|
+
|
|
106
|
+
For full control over the payment flow:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { createPaymentClient } from '@puga-labs/x402-mantle-sdk/client';
|
|
110
|
+
|
|
111
|
+
const paymentClient = createPaymentClient({
|
|
112
|
+
resourceUrl: 'https://your-api.com',
|
|
113
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz',
|
|
114
|
+
provider: window.ethereum,
|
|
115
|
+
userAddress: '0x...' // Optional
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = await paymentClient.callWithPayment('/api/endpoint', {
|
|
119
|
+
data: 'payload'
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Server-Side Usage
|
|
126
|
+
|
|
127
|
+
### Express.js
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import express from 'express';
|
|
131
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/express';
|
|
132
|
+
|
|
133
|
+
const app = express();
|
|
134
|
+
app.use(express.json());
|
|
135
|
+
|
|
136
|
+
// Create paywall middleware
|
|
137
|
+
const pay = mantlePaywall({
|
|
138
|
+
priceUsd: 0.01,
|
|
139
|
+
payTo: '0xYourWalletAddress',
|
|
140
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Protected route
|
|
144
|
+
app.post('/api/generate', pay, async (req, res) => {
|
|
145
|
+
const { prompt } = req.body;
|
|
146
|
+
// Your logic here - only runs after payment
|
|
147
|
+
res.json({ result: 'Generated content' });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Unprotected route
|
|
151
|
+
app.get('/api/health', (req, res) => {
|
|
152
|
+
res.json({ status: 'ok' });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
app.listen(3000);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Next.js (App Router)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// app/api/generate/route.ts
|
|
162
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/nextjs';
|
|
163
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
164
|
+
|
|
165
|
+
const pay = mantlePaywall({
|
|
166
|
+
priceUsd: 0.01,
|
|
167
|
+
payTo: '0xYourWalletAddress',
|
|
168
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const POST = pay(async (req: NextRequest) => {
|
|
172
|
+
const { prompt } = await req.json();
|
|
173
|
+
// Your logic here
|
|
174
|
+
return NextResponse.json({ result: 'Generated content' });
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Hono
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { Hono } from 'hono';
|
|
182
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/web';
|
|
183
|
+
|
|
184
|
+
const app = new Hono();
|
|
185
|
+
|
|
186
|
+
const pay = mantlePaywall({
|
|
187
|
+
priceUsd: 0.01,
|
|
188
|
+
payTo: '0xYourWalletAddress',
|
|
189
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
app.post('/api/generate', pay(async (c) => {
|
|
193
|
+
const { prompt } = await c.req.json();
|
|
194
|
+
return c.json({ result: 'Generated content' });
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
export default app;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Cloudflare Workers
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/web';
|
|
204
|
+
|
|
205
|
+
const pay = mantlePaywall({
|
|
206
|
+
priceUsd: 0.01,
|
|
207
|
+
payTo: '0xYourWalletAddress',
|
|
208
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export default {
|
|
212
|
+
async fetch(request: Request): Promise<Response> {
|
|
213
|
+
if (request.method === 'POST') {
|
|
214
|
+
return pay(async (req) => {
|
|
215
|
+
const body = await req.json();
|
|
216
|
+
return new Response(JSON.stringify({ result: 'success' }), {
|
|
217
|
+
headers: { 'Content-Type': 'application/json' }
|
|
218
|
+
});
|
|
219
|
+
})(request);
|
|
220
|
+
}
|
|
221
|
+
return new Response('Method not allowed', { status: 405 });
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Deno
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/web';
|
|
230
|
+
|
|
231
|
+
const pay = mantlePaywall({
|
|
232
|
+
priceUsd: 0.01,
|
|
233
|
+
payTo: '0xYourWalletAddress',
|
|
234
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz'
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
Deno.serve(pay(async (req) => {
|
|
238
|
+
const body = await req.json();
|
|
239
|
+
return new Response(JSON.stringify({ result: 'success' }), {
|
|
240
|
+
headers: { 'Content-Type': 'application/json' }
|
|
241
|
+
});
|
|
242
|
+
}));
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Configuration
|
|
248
|
+
|
|
249
|
+
### Client Options
|
|
250
|
+
|
|
251
|
+
| Option | Type | Default | Description |
|
|
252
|
+
|--------|------|---------|-------------|
|
|
253
|
+
| `facilitatorUrl` | `string` | From 402 response | Facilitator service URL (auto-detected from backend) |
|
|
254
|
+
| `resourceUrl` | `string` | `window.location.origin` | Your API base URL |
|
|
255
|
+
| `projectKey` | `string` | From 402 response | Project key (auto-detected from backend) |
|
|
256
|
+
| `getProvider` | `() => EIP1193Provider` | - | Function returning wallet provider |
|
|
257
|
+
| `getAccount` | `() => string` | - | Function returning user address |
|
|
258
|
+
|
|
259
|
+
**Note:** Both `facilitatorUrl` and `projectKey` are automatically passed from your backend via 402 responses, so clients typically don't need to configure them.
|
|
260
|
+
|
|
261
|
+
### Server Options (mantlePaywall)
|
|
262
|
+
|
|
263
|
+
| Option | Type | Required | Description |
|
|
264
|
+
|--------|------|----------|-------------|
|
|
265
|
+
| `priceUsd` | `number` | Yes | Price in USD (e.g., `0.01` for 1 cent) |
|
|
266
|
+
| `payTo` | `string` | Yes | Your wallet address to receive payments |
|
|
267
|
+
| `facilitatorUrl` | `string` | No | Facilitator URL (default: localhost:8080) |
|
|
268
|
+
| `projectKey` | `string` | No | Project key from dashboard (for hosted facilitator + analytics) |
|
|
269
|
+
| `facilitatorSecret` | `string` | For self-hosted | Shared secret with your facilitator (required for self-hosted) |
|
|
270
|
+
| `network` | `string` | No | Network ID (default: `mantle-mainnet`) |
|
|
271
|
+
| `onPaymentSettled` | `function` | No | Callback when payment is verified |
|
|
272
|
+
| `telemetry` | `object` | No | Analytics configuration (auto-uses projectKey if not set) |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Hosted vs Self-Hosted Facilitator
|
|
277
|
+
|
|
278
|
+
### Hosted Facilitator (Recommended)
|
|
279
|
+
|
|
280
|
+
Use our managed facilitator service:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const pay = mantlePaywall({
|
|
284
|
+
priceUsd: 0.01,
|
|
285
|
+
payTo: '0xYourWallet',
|
|
286
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz',
|
|
287
|
+
projectKey: 'pk_xxx' // Get from dashboard (used for billing + analytics)
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Get your project key from [Dashboard](https://x402mantlesdk.xyz/dashboard).
|
|
292
|
+
|
|
293
|
+
**Note:** `projectKey` is automatically passed to clients via 402 responses, so they don't need to configure it separately.
|
|
294
|
+
|
|
295
|
+
### Self-Hosted Facilitator
|
|
296
|
+
|
|
297
|
+
Run your own facilitator for full control and cost savings:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const pay = mantlePaywall({
|
|
301
|
+
priceUsd: 0.01,
|
|
302
|
+
payTo: '0xYourWallet',
|
|
303
|
+
facilitatorUrl: 'https://your-facilitator.com',
|
|
304
|
+
// REQUIRED: Must match FACILITATOR_SECRET in your facilitator's .env
|
|
305
|
+
facilitatorSecret: process.env.FACILITATOR_SECRET
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The `facilitatorSecret` is **required** for self-hosted facilitators to prevent unauthorized usage of your facilitator by third parties.
|
|
310
|
+
|
|
311
|
+
Create a facilitator with:
|
|
312
|
+
```bash
|
|
313
|
+
npx create-mantle-facilitator my-facilitator
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
After setup, copy `FACILITATOR_SECRET` from the generated `.env` file to your backend's environment variables.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Analytics & Telemetry
|
|
321
|
+
|
|
322
|
+
Track payments with built-in analytics:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
const pay = mantlePaywall({
|
|
326
|
+
priceUsd: 0.01,
|
|
327
|
+
payTo: '0x...',
|
|
328
|
+
facilitatorUrl: 'https://facilitator.x402mantlesdk.xyz',
|
|
329
|
+
projectKey: 'pk_xxx', // Get from dashboard - used for both billing AND analytics
|
|
330
|
+
|
|
331
|
+
// Optional: Advanced telemetry config
|
|
332
|
+
telemetry: {
|
|
333
|
+
debug: true // Enable console logging (projectKey auto-derived from above)
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
// Callback on each successful payment
|
|
337
|
+
onPaymentSettled: (entry) => {
|
|
338
|
+
console.log('Payment received!');
|
|
339
|
+
console.log('Amount:', entry.valueAtomic);
|
|
340
|
+
console.log('From:', entry.from);
|
|
341
|
+
console.log('To:', entry.to);
|
|
342
|
+
console.log('TxHash:', entry.txHash);
|
|
343
|
+
console.log('Route:', entry.route);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### PaymentLogEntry Fields
|
|
349
|
+
|
|
350
|
+
| Field | Type | Description |
|
|
351
|
+
|-------|------|-------------|
|
|
352
|
+
| `id` | `string` | Unique payment identifier (nonce) |
|
|
353
|
+
| `from` | `string` | Payer's wallet address |
|
|
354
|
+
| `to` | `string` | Recipient's wallet address |
|
|
355
|
+
| `valueAtomic` | `string` | Amount in atomic units |
|
|
356
|
+
| `network` | `string` | Network ID |
|
|
357
|
+
| `asset` | `string` | Token contract address |
|
|
358
|
+
| `route` | `string` | API route (e.g., `POST /api/generate`) |
|
|
359
|
+
| `txHash` | `string` | Blockchain transaction hash |
|
|
360
|
+
| `timestamp` | `number` | Unix timestamp (ms) |
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## TypeScript Types
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
import type {
|
|
368
|
+
// Shared types
|
|
369
|
+
PaymentRequirements,
|
|
370
|
+
Authorization,
|
|
371
|
+
PaymentHeaderObject,
|
|
372
|
+
NetworkId,
|
|
373
|
+
|
|
374
|
+
// Client types
|
|
375
|
+
MantleClient,
|
|
376
|
+
MantleClientConfig,
|
|
377
|
+
PaymentClient,
|
|
378
|
+
PaymentClientConfig,
|
|
379
|
+
CallWithPaymentResult,
|
|
380
|
+
|
|
381
|
+
// Server types
|
|
382
|
+
PaymentLogEntry,
|
|
383
|
+
TelemetryConfig,
|
|
384
|
+
MinimalPaywallOptions,
|
|
385
|
+
|
|
386
|
+
// React types
|
|
387
|
+
UseMantleX402Options,
|
|
388
|
+
UseEthersWalletOptions,
|
|
389
|
+
UseEthersWalletReturn
|
|
390
|
+
} from '@puga-labs/x402-mantle-sdk';
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Import Paths
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// Main exports (types + constants)
|
|
399
|
+
import { MANTLE_DEFAULTS } from '@puga-labs/x402-mantle-sdk';
|
|
400
|
+
|
|
401
|
+
// Client
|
|
402
|
+
import { createMantleClient, createPaymentClient } from '@puga-labs/x402-mantle-sdk/client';
|
|
403
|
+
|
|
404
|
+
// React hooks
|
|
405
|
+
import { useMantleX402, useEthersWallet } from '@puga-labs/x402-mantle-sdk/react';
|
|
406
|
+
|
|
407
|
+
// Server - Express
|
|
408
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/express';
|
|
409
|
+
|
|
410
|
+
// Server - Next.js
|
|
411
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/nextjs';
|
|
412
|
+
|
|
413
|
+
// Server - Web Standards (Hono, CF Workers, Deno)
|
|
414
|
+
import { mantlePaywall } from '@puga-labs/x402-mantle-sdk/server/web';
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Network Configuration
|
|
420
|
+
|
|
421
|
+
| Network | Chain ID | USDC Address | Decimals |
|
|
422
|
+
|---------|----------|--------------|----------|
|
|
423
|
+
| Mantle Mainnet | 5000 | `0x09Bc4E0D864854c6aFB6eB9A9cdF58aC190D0dF9` | 6 |
|
|
424
|
+
|
|
425
|
+
### Price Conversion
|
|
426
|
+
|
|
427
|
+
- `priceUsd: 0.01` = 1 cent = 10,000 atomic units
|
|
428
|
+
- Formula: `atomicUnits = priceUsd * 10^6`
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Error Handling
|
|
433
|
+
|
|
434
|
+
### Client-Side Errors
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
try {
|
|
438
|
+
const { response, txHash } = await postWithPayment('/api/generate', data);
|
|
439
|
+
} catch (error) {
|
|
440
|
+
if (error.code === 4001) {
|
|
441
|
+
// User rejected the transaction
|
|
442
|
+
console.log('User cancelled');
|
|
443
|
+
} else if (error.code === -32603) {
|
|
444
|
+
// Internal error (insufficient funds, etc.)
|
|
445
|
+
console.log('Transaction failed:', error.message);
|
|
446
|
+
} else {
|
|
447
|
+
// Network or facilitator error
|
|
448
|
+
console.log('Error:', error.message);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Server-Side Error Responses
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// 402 Payment Required
|
|
457
|
+
{
|
|
458
|
+
"error": "Payment Required",
|
|
459
|
+
"paymentRequirements": {
|
|
460
|
+
"scheme": "exact",
|
|
461
|
+
"network": "mantle-mainnet",
|
|
462
|
+
"asset": "0x09Bc4E0D...",
|
|
463
|
+
"maxAmountRequired": "10000",
|
|
464
|
+
"payTo": "0x...",
|
|
465
|
+
"price": "$0.01",
|
|
466
|
+
"currency": "USD"
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// 400 Invalid Payment
|
|
471
|
+
{
|
|
472
|
+
"error": "Invalid payment",
|
|
473
|
+
"invalidReason": "Payment amount too low"
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Links
|
|
480
|
+
|
|
481
|
+
- [Full Documentation](https://x402mantlesdk.xyz/docs)
|
|
482
|
+
- [Dashboard](https://x402mantlesdk.xyz/dashboard)
|
|
483
|
+
- [GitHub](https://github.com/puga-labs/x402-mantle-sdk)
|
|
484
|
+
- [Facilitator Kit](../create-mantle-facilitator)
|
|
485
|
+
|
|
486
|
+
## License
|
|
487
|
+
|
|
488
|
+
MIT
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMantleClient
|
|
3
|
+
} from "./chunk-RVPI6FMV.js";
|
|
4
|
+
|
|
5
|
+
// src/client/react/useEthersWallet.ts
|
|
6
|
+
import { useState, useEffect, useCallback } from "react";
|
|
7
|
+
import { ethers } from "ethers";
|
|
8
|
+
function useEthersWallet(options) {
|
|
9
|
+
const [address, setAddress] = useState(void 0);
|
|
10
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
11
|
+
const [provider, setProvider] = useState(
|
|
12
|
+
void 0
|
|
13
|
+
);
|
|
14
|
+
const [chainId, setChainId] = useState(void 0);
|
|
15
|
+
const [error, setError] = useState(void 0);
|
|
16
|
+
const setProviderAndChain = useCallback(async () => {
|
|
17
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
18
|
+
const browserProvider = new ethers.BrowserProvider(
|
|
19
|
+
window.ethereum
|
|
20
|
+
);
|
|
21
|
+
setProvider(browserProvider);
|
|
22
|
+
const network = await browserProvider.getNetwork();
|
|
23
|
+
setChainId(Number(network.chainId));
|
|
24
|
+
}, []);
|
|
25
|
+
const hydrateFromPermissions = useCallback(async () => {
|
|
26
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
27
|
+
try {
|
|
28
|
+
const accounts = await window.ethereum.request({
|
|
29
|
+
method: "eth_accounts"
|
|
30
|
+
});
|
|
31
|
+
if (accounts && accounts.length > 0) {
|
|
32
|
+
const userAddress = accounts[0];
|
|
33
|
+
setAddress(userAddress);
|
|
34
|
+
setIsConnected(true);
|
|
35
|
+
await setProviderAndChain();
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.warn("[useEthersWallet] Failed to hydrate from permissions:", err);
|
|
39
|
+
}
|
|
40
|
+
}, [setProviderAndChain]);
|
|
41
|
+
const connect = useCallback(async () => {
|
|
42
|
+
try {
|
|
43
|
+
setError(void 0);
|
|
44
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"No Ethereum wallet detected. Please install MetaMask or another wallet."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
const browserProvider = new ethers.BrowserProvider(
|
|
50
|
+
window.ethereum
|
|
51
|
+
);
|
|
52
|
+
setProvider(browserProvider);
|
|
53
|
+
const accounts = await window.ethereum.request({
|
|
54
|
+
method: "eth_requestAccounts"
|
|
55
|
+
});
|
|
56
|
+
if (!accounts || accounts.length === 0) {
|
|
57
|
+
throw new Error("No accounts returned from wallet");
|
|
58
|
+
}
|
|
59
|
+
const userAddress = accounts[0];
|
|
60
|
+
setAddress(userAddress);
|
|
61
|
+
setIsConnected(true);
|
|
62
|
+
const network = await browserProvider.getNetwork();
|
|
63
|
+
setChainId(Number(network.chainId));
|
|
64
|
+
} catch (err) {
|
|
65
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
66
|
+
setError(errorObj);
|
|
67
|
+
setIsConnected(false);
|
|
68
|
+
setAddress(void 0);
|
|
69
|
+
setChainId(void 0);
|
|
70
|
+
throw errorObj;
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
const disconnect = useCallback(() => {
|
|
74
|
+
setAddress(void 0);
|
|
75
|
+
setIsConnected(false);
|
|
76
|
+
setChainId(void 0);
|
|
77
|
+
setError(void 0);
|
|
78
|
+
}, []);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
81
|
+
const ethereum = window.ethereum;
|
|
82
|
+
const handleAccountsChanged = (accounts) => {
|
|
83
|
+
const accountsArray = accounts;
|
|
84
|
+
if (!accountsArray || accountsArray.length === 0) {
|
|
85
|
+
disconnect();
|
|
86
|
+
} else {
|
|
87
|
+
setAddress(accountsArray[0]);
|
|
88
|
+
setIsConnected(true);
|
|
89
|
+
void setProviderAndChain();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (ethereum.on) {
|
|
93
|
+
ethereum.on("accountsChanged", handleAccountsChanged);
|
|
94
|
+
}
|
|
95
|
+
return () => {
|
|
96
|
+
if (ethereum.removeListener) {
|
|
97
|
+
ethereum.removeListener("accountsChanged", handleAccountsChanged);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}, [disconnect, setProviderAndChain]);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
103
|
+
const ethereum = window.ethereum;
|
|
104
|
+
const handleConnect = () => {
|
|
105
|
+
void hydrateFromPermissions();
|
|
106
|
+
};
|
|
107
|
+
if (ethereum.on) {
|
|
108
|
+
ethereum.on("connect", handleConnect);
|
|
109
|
+
}
|
|
110
|
+
return () => {
|
|
111
|
+
if (ethereum.removeListener) {
|
|
112
|
+
ethereum.removeListener("connect", handleConnect);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}, [hydrateFromPermissions]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
118
|
+
const ethereum = window.ethereum;
|
|
119
|
+
const handleChainChanged = (chainIdHex) => {
|
|
120
|
+
const newChainId = parseInt(chainIdHex, 16);
|
|
121
|
+
setChainId(newChainId);
|
|
122
|
+
};
|
|
123
|
+
if (ethereum.on) {
|
|
124
|
+
ethereum.on("chainChanged", handleChainChanged);
|
|
125
|
+
}
|
|
126
|
+
return () => {
|
|
127
|
+
if (ethereum.removeListener) {
|
|
128
|
+
ethereum.removeListener("chainChanged", handleChainChanged);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
void hydrateFromPermissions();
|
|
134
|
+
}, [hydrateFromPermissions]);
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (options?.autoConnect) {
|
|
137
|
+
connect().catch((err) => {
|
|
138
|
+
console.warn("[useEthersWallet] Auto-connect failed:", err);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}, [options?.autoConnect, connect]);
|
|
142
|
+
return {
|
|
143
|
+
address,
|
|
144
|
+
isConnected,
|
|
145
|
+
provider,
|
|
146
|
+
chainId,
|
|
147
|
+
connect,
|
|
148
|
+
disconnect,
|
|
149
|
+
error
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/client/react/useMantleX402.ts
|
|
154
|
+
function useMantleX402(opts) {
|
|
155
|
+
const { address, isConnected } = useEthersWallet({
|
|
156
|
+
autoConnect: opts?.autoConnect ?? false
|
|
157
|
+
});
|
|
158
|
+
const client = createMantleClient({
|
|
159
|
+
facilitatorUrl: opts?.facilitatorUrl,
|
|
160
|
+
resourceUrl: opts?.resourceUrl,
|
|
161
|
+
projectKey: opts?.projectKey,
|
|
162
|
+
getAccount: () => {
|
|
163
|
+
if (!isConnected || !address) return void 0;
|
|
164
|
+
return address;
|
|
165
|
+
},
|
|
166
|
+
getProvider: () => {
|
|
167
|
+
if (typeof window !== "undefined" && window.ethereum) {
|
|
168
|
+
return window.ethereum;
|
|
169
|
+
}
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
return client;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
useEthersWallet,
|
|
178
|
+
useMantleX402
|
|
179
|
+
};
|