@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 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
@@ -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: requirements.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