@relai-fi/x402 0.5.30 → 0.5.32
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 +142 -0
- package/dist/client.cjs +3 -3
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +67 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +67 -4
- package/dist/index.js.map +1 -1
- package/dist/plugins.cjs +150 -0
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +2 -0
- package/dist/plugins.d.ts +2 -0
- package/dist/plugins.js +125 -0
- package/dist/plugins.js.map +1 -0
- package/dist/react/index.cjs +3 -3
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/server-B89hUwBK.d.ts +229 -0
- package/dist/server-DqvJNI2_.d.cts +229 -0
- package/dist/server.cjs +64 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -138
- package/dist/server.d.ts +2 -138
- package/dist/server.js +64 -1
- package/dist/server.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -174,6 +174,46 @@ const result = await response.json();
|
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
177
|
+
### Crossmint Smart Wallet (server-side / agent)
|
|
178
|
+
|
|
179
|
+
Use `createCrossmintX402Fetch` from `@relai-fi/x402/crossmint` — no `signTransaction` needed, Crossmint handles signing and broadcasting.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { createCrossmintX402Fetch } from '@relai-fi/x402/crossmint';
|
|
183
|
+
import { Connection } from '@solana/web3.js';
|
|
184
|
+
|
|
185
|
+
const fetch402 = createCrossmintX402Fetch({
|
|
186
|
+
apiKey: process.env.CROSSMINT_API_KEY!, // sk_production_... or sk_staging_...
|
|
187
|
+
wallet: process.env.CROSSMINT_WALLET!, // Crossmint smart wallet address
|
|
188
|
+
connection: new Connection(process.env.SOLANA_RPC_URL!),
|
|
189
|
+
onPayment: (txHash) => console.log('On-chain tx:', txHash),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// RelAI sponsors SOL gas — wallet only needs USDC
|
|
193
|
+
const response = await fetch402('https://api.example.com/protected');
|
|
194
|
+
const data = await response.json();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
For agents that require explicit transaction approval before Crossmint broadcasts, use the **delegated mode** — an external Ed25519 signer must be registered on the Crossmint smart wallet:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { createCrossmintDelegatedX402Fetch } from '@relai-fi/x402/crossmint';
|
|
201
|
+
import { Connection } from '@solana/web3.js';
|
|
202
|
+
|
|
203
|
+
const fetch402 = createCrossmintDelegatedX402Fetch({
|
|
204
|
+
apiKey: process.env.CROSSMINT_API_KEY!,
|
|
205
|
+
wallet: process.env.CROSSMINT_WALLET!,
|
|
206
|
+
signerSecretKey: Buffer.from(process.env.SIGNER_SECRET_KEY!, 'base64'), // 64-byte Ed25519
|
|
207
|
+
connection: new Connection(process.env.SOLANA_RPC_URL!),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const response = await fetch402('https://api.example.com/protected');
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
> Both modes use Crossmint's API to sign and broadcast — no private key handling in your code.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
177
217
|
### WebSocket relay transport (optional)
|
|
178
218
|
|
|
179
219
|
If your protected API is behind a relay URL like `https://api.relai.fi/relay/:apiId/...`
|
|
@@ -300,6 +340,9 @@ import {
|
|
|
300
340
|
fromAtomicUnits,
|
|
301
341
|
} from '@relai-fi/x402/utils';
|
|
302
342
|
|
|
343
|
+
// Plugins — extend protect() with free tier, custom logic
|
|
344
|
+
import { freeTier } from '@relai-fi/x402/plugins';
|
|
345
|
+
|
|
303
346
|
// Management API — create/manage APIs, pricing, analytics, agent bootstrap
|
|
304
347
|
import {
|
|
305
348
|
createManagementClient,
|
|
@@ -468,6 +511,105 @@ app.get('/api/solana-data', relai.protect({
|
|
|
468
511
|
|
|
469
512
|
---
|
|
470
513
|
|
|
514
|
+
## Plugins
|
|
515
|
+
|
|
516
|
+
Extend `Relai.protect()` with plugins that run before payment checks. Plugins can skip payment (e.g. free tier), add headers, or attach metadata to requests.
|
|
517
|
+
|
|
518
|
+
### Free Tier Plugin
|
|
519
|
+
|
|
520
|
+
Allow buyers to make free API calls before requiring x402 payment. Usage is tracked per buyer (by JWT `sub`, wallet address, or IP) with optional global caps and periodic resets.
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
import Relai from '@relai-fi/x402/server';
|
|
524
|
+
import { freeTier } from '@relai-fi/x402/plugins';
|
|
525
|
+
|
|
526
|
+
const relai = new Relai({
|
|
527
|
+
network: 'base',
|
|
528
|
+
plugins: [
|
|
529
|
+
freeTier({
|
|
530
|
+
serviceKey: process.env.RELAI_SERVICE_KEY!,
|
|
531
|
+
perBuyerLimit: 10, // 10 free calls per buyer
|
|
532
|
+
resetPeriod: 'daily', // reset daily (or 'monthly', 'never')
|
|
533
|
+
globalCap: 1000, // optional: max 1000 free calls total
|
|
534
|
+
paths: ['*'], // optional: apply to all endpoints (default)
|
|
535
|
+
}),
|
|
536
|
+
],
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
app.get('/api/data', relai.protect({
|
|
540
|
+
payTo: '0xYourWallet',
|
|
541
|
+
price: 0.01,
|
|
542
|
+
}), (req, res) => {
|
|
543
|
+
if (req.x402Free) {
|
|
544
|
+
// Free tier call — no payment was made
|
|
545
|
+
console.log('Free call from:', req.x402Plugin);
|
|
546
|
+
}
|
|
547
|
+
res.json({ data: 'content' });
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**How it works:**
|
|
552
|
+
1. On server start, the plugin syncs its config to the RelAI backend via your service key.
|
|
553
|
+
2. On each request, `beforePaymentCheck` asks the RelAI API if the buyer has free calls remaining.
|
|
554
|
+
3. If free → `next()` is called without payment, `req.x402Free = true`, and usage is recorded.
|
|
555
|
+
4. If exhausted → normal x402 payment flow continues.
|
|
556
|
+
|
|
557
|
+
**Free Tier config:**
|
|
558
|
+
|
|
559
|
+
| Option | Type | Default | Description |
|
|
560
|
+
|--------|------|---------|-------------|
|
|
561
|
+
| `serviceKey` | `string` | **required** | Your RelAI service key (`sk_live_...`) |
|
|
562
|
+
| `perBuyerLimit` | `number` | **required** | Free calls each buyer gets per period |
|
|
563
|
+
| `resetPeriod` | `'never' \| 'daily' \| 'monthly'` | `'never'` | When counters reset |
|
|
564
|
+
| `globalCap` | `number` | — | Max total free calls across all buyers |
|
|
565
|
+
| `paths` | `string[]` | `['*']` | Which endpoints the free tier applies to |
|
|
566
|
+
| `baseUrl` | `string` | `https://api.relai.fi` | RelAI API URL (override for testing) |
|
|
567
|
+
|
|
568
|
+
**Request properties set on free-tier bypass:**
|
|
569
|
+
|
|
570
|
+
| Property | Type | Description |
|
|
571
|
+
|----------|------|-------------|
|
|
572
|
+
| `req.x402Free` | `boolean` | `true` when request was served for free |
|
|
573
|
+
| `req.x402Paid` | `boolean` | `false` on free tier, `true` on paid |
|
|
574
|
+
| `req.x402Plugin` | `string` | Plugin name that granted the bypass (`'freeTier'`) |
|
|
575
|
+
| `req.pluginMeta` | `object` | `{ freeTier: true, remaining: number }` |
|
|
576
|
+
|
|
577
|
+
**Dashboard:** Manage plugin config and view usage in the RelAI dashboard under **SDK Plugins**.
|
|
578
|
+
|
|
579
|
+
### Custom Plugins
|
|
580
|
+
|
|
581
|
+
```typescript
|
|
582
|
+
import type { RelaiPlugin } from '@relai-fi/x402';
|
|
583
|
+
|
|
584
|
+
const myPlugin: RelaiPlugin = {
|
|
585
|
+
name: 'myPlugin',
|
|
586
|
+
beforePaymentCheck: async (ctx) => {
|
|
587
|
+
if (ctx.req.headers['x-vip'] === 'true') {
|
|
588
|
+
return { skip: true, reason: 'VIP bypass' };
|
|
589
|
+
}
|
|
590
|
+
return {};
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const relai = new Relai({
|
|
595
|
+
network: 'base',
|
|
596
|
+
plugins: [myPlugin],
|
|
597
|
+
});
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Plugin interface:**
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
interface RelaiPlugin {
|
|
604
|
+
name: string;
|
|
605
|
+
beforePaymentCheck?: (ctx: PluginContext) => Promise<PluginResult>;
|
|
606
|
+
afterSettled?: (ctx: PluginContext) => Promise<void>;
|
|
607
|
+
onInit?: (config: RelaiServerConfig) => Promise<void>;
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
471
613
|
## Management API
|
|
472
614
|
|
|
473
615
|
Programmatically create and manage monetised APIs, update pricing, and read analytics. Designed for agents and CI/CD — no browser needed.
|
package/dist/client.cjs
CHANGED
|
@@ -742,7 +742,7 @@ function createX402Client(config) {
|
|
|
742
742
|
}
|
|
743
743
|
function getBridgeExtension(requirements) {
|
|
744
744
|
const ext = requirements?.extensions?.bridge;
|
|
745
|
-
if (!ext?.info?.
|
|
745
|
+
if (!ext?.info?.settleEndpoint || !Array.isArray(ext.info.supportedSourceChains)) return null;
|
|
746
746
|
return ext.info;
|
|
747
747
|
}
|
|
748
748
|
function selectBridgeSource(bridge) {
|
|
@@ -783,7 +783,7 @@ function createX402Client(config) {
|
|
|
783
783
|
payTo: bridge.payTo,
|
|
784
784
|
amount: targetAccept.amount || targetAccept.maxAmountRequired,
|
|
785
785
|
extra: {
|
|
786
|
-
...bridge.
|
|
786
|
+
...bridge.feePayerSvm ? { feePayer: bridge.feePayerSvm } : {},
|
|
787
787
|
decimals: 6
|
|
788
788
|
}
|
|
789
789
|
};
|
|
@@ -799,7 +799,7 @@ function createX402Client(config) {
|
|
|
799
799
|
};
|
|
800
800
|
sourcePaymentHeader = usePermit ? await buildEvmPermitPayment(sourceAccept, requirements, url) : await buildEvmPayment(sourceAccept, requirements, url);
|
|
801
801
|
}
|
|
802
|
-
const bridgeRes = await fetch(bridge.
|
|
802
|
+
const bridgeRes = await fetch(bridge.settleEndpoint, {
|
|
803
803
|
method: "POST",
|
|
804
804
|
headers: { "Content-Type": "application/json" },
|
|
805
805
|
body: JSON.stringify({
|