@dexterai/x402 1.2.4 → 1.3.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 +49 -2
- package/dist/client/index.cjs +278 -3
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +154 -0
- package/dist/client/index.d.ts +154 -0
- package/dist/client/index.js +274 -2
- package/dist/client/index.js.map +1 -1
- package/dist/react/index.cjs +24 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +24 -1
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +100 -2
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +121 -1
- package/dist/server/index.d.ts +121 -1
- package/dist/server/index.js +98 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -52,6 +52,21 @@ This SDK handles the entire flow automatically—you just call `fetch()` and pay
|
|
|
52
52
|
npm install @dexterai/x402
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Client (Node.js) — NEW!
|
|
56
|
+
|
|
57
|
+
The simplest way to make x402 payments from scripts:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { wrapFetch } from '@dexterai/x402/client';
|
|
61
|
+
|
|
62
|
+
const x402Fetch = wrapFetch(fetch, {
|
|
63
|
+
walletPrivateKey: process.env.SOLANA_PRIVATE_KEY,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// That's it. 402 responses are handled automatically.
|
|
67
|
+
const response = await x402Fetch('https://api.example.com/protected');
|
|
68
|
+
```
|
|
69
|
+
|
|
55
70
|
### Client (Browser)
|
|
56
71
|
|
|
57
72
|
```typescript
|
|
@@ -167,6 +182,40 @@ fromAtomicUnits(1500000n, 6); // 1.5
|
|
|
167
182
|
|
|
168
183
|
## Server SDK
|
|
169
184
|
|
|
185
|
+
### Express Middleware — NEW!
|
|
186
|
+
|
|
187
|
+
One-liner payment protection for any Express endpoint:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import express from 'express';
|
|
191
|
+
import { x402Middleware } from '@dexterai/x402/server';
|
|
192
|
+
|
|
193
|
+
const app = express();
|
|
194
|
+
|
|
195
|
+
app.get('/api/protected',
|
|
196
|
+
x402Middleware({
|
|
197
|
+
payTo: 'YourSolanaAddress...',
|
|
198
|
+
amount: '0.01', // $0.01 USD
|
|
199
|
+
}),
|
|
200
|
+
(req, res) => {
|
|
201
|
+
// This only runs after successful payment
|
|
202
|
+
res.json({ data: 'protected content' });
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Options:
|
|
208
|
+
- `payTo` — Address to receive payments
|
|
209
|
+
- `amount` — Price in USD (e.g., `'0.01'` for 1 cent)
|
|
210
|
+
- `network` — CAIP-2 network (default: Solana mainnet)
|
|
211
|
+
- `description` — Human-readable description
|
|
212
|
+
- `facilitatorUrl` — Override facilitator (default: x402.dexter.cash)
|
|
213
|
+
- `verbose` — Enable debug logging
|
|
214
|
+
|
|
215
|
+
### Manual Server (Advanced)
|
|
216
|
+
|
|
217
|
+
For more control over the payment flow:
|
|
218
|
+
|
|
170
219
|
```typescript
|
|
171
220
|
import { createX402Server } from '@dexterai/x402/server';
|
|
172
221
|
|
|
@@ -197,8 +246,6 @@ app.post('/protected', async (req, res) => {
|
|
|
197
246
|
});
|
|
198
247
|
```
|
|
199
248
|
|
|
200
|
-
*Client SDK, React hook, and pricing utilities are production-verified at [dexter.cash/sdk](https://dexter.cash/sdk). `createX402Server` is a convenience wrapper not yet used in production.*
|
|
201
|
-
|
|
202
249
|
---
|
|
203
250
|
|
|
204
251
|
## Dynamic Pricing
|
package/dist/client/index.cjs
CHANGED
|
@@ -6,6 +6,9 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
6
6
|
var __esm = (fn, res) => function __init() {
|
|
7
7
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
8
|
};
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
};
|
|
9
12
|
var __export = (target, all) => {
|
|
10
13
|
for (var name in all)
|
|
11
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -412,6 +415,157 @@ var init_evm = __esm({
|
|
|
412
415
|
}
|
|
413
416
|
});
|
|
414
417
|
|
|
418
|
+
// node_modules/base-x/src/cjs/index.cjs
|
|
419
|
+
var require_cjs = __commonJS({
|
|
420
|
+
"node_modules/base-x/src/cjs/index.cjs"(exports2) {
|
|
421
|
+
"use strict";
|
|
422
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
423
|
+
function base(ALPHABET) {
|
|
424
|
+
if (ALPHABET.length >= 255) {
|
|
425
|
+
throw new TypeError("Alphabet too long");
|
|
426
|
+
}
|
|
427
|
+
const BASE_MAP = new Uint8Array(256);
|
|
428
|
+
for (let j = 0; j < BASE_MAP.length; j++) {
|
|
429
|
+
BASE_MAP[j] = 255;
|
|
430
|
+
}
|
|
431
|
+
for (let i = 0; i < ALPHABET.length; i++) {
|
|
432
|
+
const x = ALPHABET.charAt(i);
|
|
433
|
+
const xc = x.charCodeAt(0);
|
|
434
|
+
if (BASE_MAP[xc] !== 255) {
|
|
435
|
+
throw new TypeError(x + " is ambiguous");
|
|
436
|
+
}
|
|
437
|
+
BASE_MAP[xc] = i;
|
|
438
|
+
}
|
|
439
|
+
const BASE = ALPHABET.length;
|
|
440
|
+
const LEADER = ALPHABET.charAt(0);
|
|
441
|
+
const FACTOR = Math.log(BASE) / Math.log(256);
|
|
442
|
+
const iFACTOR = Math.log(256) / Math.log(BASE);
|
|
443
|
+
function encode(source) {
|
|
444
|
+
if (source instanceof Uint8Array) {
|
|
445
|
+
} else if (ArrayBuffer.isView(source)) {
|
|
446
|
+
source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
|
|
447
|
+
} else if (Array.isArray(source)) {
|
|
448
|
+
source = Uint8Array.from(source);
|
|
449
|
+
}
|
|
450
|
+
if (!(source instanceof Uint8Array)) {
|
|
451
|
+
throw new TypeError("Expected Uint8Array");
|
|
452
|
+
}
|
|
453
|
+
if (source.length === 0) {
|
|
454
|
+
return "";
|
|
455
|
+
}
|
|
456
|
+
let zeroes = 0;
|
|
457
|
+
let length = 0;
|
|
458
|
+
let pbegin = 0;
|
|
459
|
+
const pend = source.length;
|
|
460
|
+
while (pbegin !== pend && source[pbegin] === 0) {
|
|
461
|
+
pbegin++;
|
|
462
|
+
zeroes++;
|
|
463
|
+
}
|
|
464
|
+
const size = (pend - pbegin) * iFACTOR + 1 >>> 0;
|
|
465
|
+
const b58 = new Uint8Array(size);
|
|
466
|
+
while (pbegin !== pend) {
|
|
467
|
+
let carry = source[pbegin];
|
|
468
|
+
let i = 0;
|
|
469
|
+
for (let it1 = size - 1; (carry !== 0 || i < length) && it1 !== -1; it1--, i++) {
|
|
470
|
+
carry += 256 * b58[it1] >>> 0;
|
|
471
|
+
b58[it1] = carry % BASE >>> 0;
|
|
472
|
+
carry = carry / BASE >>> 0;
|
|
473
|
+
}
|
|
474
|
+
if (carry !== 0) {
|
|
475
|
+
throw new Error("Non-zero carry");
|
|
476
|
+
}
|
|
477
|
+
length = i;
|
|
478
|
+
pbegin++;
|
|
479
|
+
}
|
|
480
|
+
let it2 = size - length;
|
|
481
|
+
while (it2 !== size && b58[it2] === 0) {
|
|
482
|
+
it2++;
|
|
483
|
+
}
|
|
484
|
+
let str = LEADER.repeat(zeroes);
|
|
485
|
+
for (; it2 < size; ++it2) {
|
|
486
|
+
str += ALPHABET.charAt(b58[it2]);
|
|
487
|
+
}
|
|
488
|
+
return str;
|
|
489
|
+
}
|
|
490
|
+
function decodeUnsafe(source) {
|
|
491
|
+
if (typeof source !== "string") {
|
|
492
|
+
throw new TypeError("Expected String");
|
|
493
|
+
}
|
|
494
|
+
if (source.length === 0) {
|
|
495
|
+
return new Uint8Array();
|
|
496
|
+
}
|
|
497
|
+
let psz = 0;
|
|
498
|
+
let zeroes = 0;
|
|
499
|
+
let length = 0;
|
|
500
|
+
while (source[psz] === LEADER) {
|
|
501
|
+
zeroes++;
|
|
502
|
+
psz++;
|
|
503
|
+
}
|
|
504
|
+
const size = (source.length - psz) * FACTOR + 1 >>> 0;
|
|
505
|
+
const b256 = new Uint8Array(size);
|
|
506
|
+
while (psz < source.length) {
|
|
507
|
+
const charCode = source.charCodeAt(psz);
|
|
508
|
+
if (charCode > 255) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
let carry = BASE_MAP[charCode];
|
|
512
|
+
if (carry === 255) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
let i = 0;
|
|
516
|
+
for (let it3 = size - 1; (carry !== 0 || i < length) && it3 !== -1; it3--, i++) {
|
|
517
|
+
carry += BASE * b256[it3] >>> 0;
|
|
518
|
+
b256[it3] = carry % 256 >>> 0;
|
|
519
|
+
carry = carry / 256 >>> 0;
|
|
520
|
+
}
|
|
521
|
+
if (carry !== 0) {
|
|
522
|
+
throw new Error("Non-zero carry");
|
|
523
|
+
}
|
|
524
|
+
length = i;
|
|
525
|
+
psz++;
|
|
526
|
+
}
|
|
527
|
+
let it4 = size - length;
|
|
528
|
+
while (it4 !== size && b256[it4] === 0) {
|
|
529
|
+
it4++;
|
|
530
|
+
}
|
|
531
|
+
const vch = new Uint8Array(zeroes + (size - it4));
|
|
532
|
+
let j = zeroes;
|
|
533
|
+
while (it4 !== size) {
|
|
534
|
+
vch[j++] = b256[it4++];
|
|
535
|
+
}
|
|
536
|
+
return vch;
|
|
537
|
+
}
|
|
538
|
+
function decode(string) {
|
|
539
|
+
const buffer = decodeUnsafe(string);
|
|
540
|
+
if (buffer) {
|
|
541
|
+
return buffer;
|
|
542
|
+
}
|
|
543
|
+
throw new Error("Non-base" + BASE + " character");
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
encode,
|
|
547
|
+
decodeUnsafe,
|
|
548
|
+
decode
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
exports2.default = base;
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// node_modules/bs58/src/cjs/index.cjs
|
|
556
|
+
var require_cjs2 = __commonJS({
|
|
557
|
+
"node_modules/bs58/src/cjs/index.cjs"(exports2) {
|
|
558
|
+
"use strict";
|
|
559
|
+
var __importDefault = exports2 && exports2.__importDefault || function(mod) {
|
|
560
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
561
|
+
};
|
|
562
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
563
|
+
var base_x_1 = __importDefault(require_cjs());
|
|
564
|
+
var ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
565
|
+
exports2.default = (0, base_x_1.default)(ALPHABET);
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
415
569
|
// src/client/index.ts
|
|
416
570
|
var client_exports = {};
|
|
417
571
|
__export(client_exports, {
|
|
@@ -421,8 +575,11 @@ __export(client_exports, {
|
|
|
421
575
|
USDC_MINT: () => USDC_MINT,
|
|
422
576
|
X402Error: () => X402Error,
|
|
423
577
|
createEvmAdapter: () => createEvmAdapter,
|
|
578
|
+
createKeypairWallet: () => createKeypairWallet,
|
|
424
579
|
createSolanaAdapter: () => createSolanaAdapter,
|
|
425
|
-
createX402Client: () => createX402Client
|
|
580
|
+
createX402Client: () => createX402Client,
|
|
581
|
+
isKeypairWallet: () => isKeypairWallet,
|
|
582
|
+
wrapFetch: () => wrapFetch
|
|
426
583
|
});
|
|
427
584
|
module.exports = __toCommonJS(client_exports);
|
|
428
585
|
|
|
@@ -588,10 +745,33 @@ function createX402Client(config) {
|
|
|
588
745
|
} else {
|
|
589
746
|
payload = { transaction: signedTx.serialized };
|
|
590
747
|
}
|
|
748
|
+
const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
749
|
+
let resolvedResource = requirements.resource;
|
|
750
|
+
if (typeof requirements.resource === "string") {
|
|
751
|
+
try {
|
|
752
|
+
const resolvedUrl = new URL(requirements.resource, originalUrl).toString();
|
|
753
|
+
if (resolvedUrl !== requirements.resource) {
|
|
754
|
+
log("Resolved relative resource URL:", requirements.resource, "\u2192", resolvedUrl);
|
|
755
|
+
}
|
|
756
|
+
resolvedResource = resolvedUrl;
|
|
757
|
+
} catch {
|
|
758
|
+
resolvedResource = requirements.resource;
|
|
759
|
+
}
|
|
760
|
+
} else if (requirements.resource && typeof requirements.resource === "object" && "url" in requirements.resource) {
|
|
761
|
+
const resourceObj = requirements.resource;
|
|
762
|
+
try {
|
|
763
|
+
const resolvedUrl = new URL(resourceObj.url, originalUrl).toString();
|
|
764
|
+
if (resolvedUrl !== resourceObj.url) {
|
|
765
|
+
log("Resolved relative resource URL:", resourceObj.url, "\u2192", resolvedUrl);
|
|
766
|
+
resolvedResource = { ...resourceObj, url: resolvedUrl };
|
|
767
|
+
}
|
|
768
|
+
} catch {
|
|
769
|
+
}
|
|
770
|
+
}
|
|
591
771
|
const paymentSignature = {
|
|
592
772
|
x402Version: accept.x402Version ?? 2,
|
|
593
773
|
// Echo version from 402 response, default to 2
|
|
594
|
-
resource:
|
|
774
|
+
resource: resolvedResource,
|
|
595
775
|
accepted: accept,
|
|
596
776
|
payload
|
|
597
777
|
};
|
|
@@ -626,6 +806,98 @@ function createX402Client(config) {
|
|
|
626
806
|
fetch: x402Fetch
|
|
627
807
|
};
|
|
628
808
|
}
|
|
809
|
+
|
|
810
|
+
// src/client/keypair-wallet.ts
|
|
811
|
+
var import_web32 = require("@solana/web3.js");
|
|
812
|
+
function createKeypairWallet(privateKey) {
|
|
813
|
+
let keypair;
|
|
814
|
+
if (typeof privateKey === "string") {
|
|
815
|
+
const bs58 = require_cjs2();
|
|
816
|
+
const decode = bs58.decode || bs58.default?.decode;
|
|
817
|
+
if (!decode) {
|
|
818
|
+
throw new Error("bs58 module not found or incompatible version");
|
|
819
|
+
}
|
|
820
|
+
try {
|
|
821
|
+
const decoded = decode(privateKey);
|
|
822
|
+
keypair = import_web32.Keypair.fromSecretKey(decoded);
|
|
823
|
+
} catch (e) {
|
|
824
|
+
try {
|
|
825
|
+
const parsed = JSON.parse(privateKey);
|
|
826
|
+
if (Array.isArray(parsed)) {
|
|
827
|
+
keypair = import_web32.Keypair.fromSecretKey(Uint8Array.from(parsed));
|
|
828
|
+
} else {
|
|
829
|
+
throw new Error("Invalid private key format");
|
|
830
|
+
}
|
|
831
|
+
} catch {
|
|
832
|
+
throw new Error(
|
|
833
|
+
"Invalid private key. Expected base58 string or JSON array of bytes."
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
} else if (Array.isArray(privateKey)) {
|
|
838
|
+
keypair = import_web32.Keypair.fromSecretKey(Uint8Array.from(privateKey));
|
|
839
|
+
} else if (privateKey instanceof Uint8Array) {
|
|
840
|
+
keypair = import_web32.Keypair.fromSecretKey(privateKey);
|
|
841
|
+
} else {
|
|
842
|
+
throw new Error(
|
|
843
|
+
"Invalid private key type. Expected string, number[], or Uint8Array."
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
return {
|
|
847
|
+
publicKey: {
|
|
848
|
+
toBase58: () => keypair.publicKey.toBase58()
|
|
849
|
+
},
|
|
850
|
+
signTransaction: async (tx) => {
|
|
851
|
+
if (tx instanceof import_web32.VersionedTransaction) {
|
|
852
|
+
tx.sign([keypair]);
|
|
853
|
+
return tx;
|
|
854
|
+
} else if (tx instanceof import_web32.Transaction) {
|
|
855
|
+
tx.sign(keypair);
|
|
856
|
+
return tx;
|
|
857
|
+
}
|
|
858
|
+
throw new Error("Unknown transaction type");
|
|
859
|
+
},
|
|
860
|
+
keypair
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function isKeypairWallet(wallet) {
|
|
864
|
+
if (!wallet || typeof wallet !== "object") return false;
|
|
865
|
+
const w = wallet;
|
|
866
|
+
return "keypair" in w && w.keypair instanceof import_web32.Keypair && "publicKey" in w && "signTransaction" in w;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/client/wrap-fetch.ts
|
|
870
|
+
function wrapFetch(fetchImpl, options) {
|
|
871
|
+
const {
|
|
872
|
+
walletPrivateKey,
|
|
873
|
+
evmPrivateKey,
|
|
874
|
+
preferredNetwork,
|
|
875
|
+
// facilitatorUrl is reserved for future use when we add facilitator selection
|
|
876
|
+
rpcUrls,
|
|
877
|
+
maxAmountAtomic,
|
|
878
|
+
verbose
|
|
879
|
+
} = options;
|
|
880
|
+
if (!walletPrivateKey && !evmPrivateKey) {
|
|
881
|
+
throw new Error("At least one wallet private key is required (walletPrivateKey or evmPrivateKey)");
|
|
882
|
+
}
|
|
883
|
+
const wallets = {};
|
|
884
|
+
if (walletPrivateKey) {
|
|
885
|
+
wallets.solana = createKeypairWallet(walletPrivateKey);
|
|
886
|
+
}
|
|
887
|
+
if (evmPrivateKey) {
|
|
888
|
+
console.warn("[x402] EVM private key support in wrapFetch is not yet implemented. Use createX402Client with a viem wallet instead.");
|
|
889
|
+
}
|
|
890
|
+
const clientConfig = {
|
|
891
|
+
wallets,
|
|
892
|
+
preferredNetwork,
|
|
893
|
+
rpcUrls,
|
|
894
|
+
maxAmountAtomic,
|
|
895
|
+
fetch: fetchImpl,
|
|
896
|
+
verbose
|
|
897
|
+
};
|
|
898
|
+
const client = createX402Client(clientConfig);
|
|
899
|
+
return client.fetch.bind(client);
|
|
900
|
+
}
|
|
629
901
|
// Annotate the CommonJS export names for ESM import in node:
|
|
630
902
|
0 && (module.exports = {
|
|
631
903
|
BASE_MAINNET,
|
|
@@ -634,7 +906,10 @@ function createX402Client(config) {
|
|
|
634
906
|
USDC_MINT,
|
|
635
907
|
X402Error,
|
|
636
908
|
createEvmAdapter,
|
|
909
|
+
createKeypairWallet,
|
|
637
910
|
createSolanaAdapter,
|
|
638
|
-
createX402Client
|
|
911
|
+
createX402Client,
|
|
912
|
+
isKeypairWallet,
|
|
913
|
+
wrapFetch
|
|
639
914
|
});
|
|
640
915
|
//# sourceMappingURL=index.cjs.map
|