@dexterai/x402 1.0.2 → 1.0.4

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
@@ -2,132 +2,91 @@
2
2
  <img src="./assets/dexter-wordmark.svg" alt="Dexter" width="360">
3
3
  </p>
4
4
 
5
- # @dexterai/x402
5
+ <h1 align="center">@dexterai/x402</h1>
6
6
 
7
7
  <p align="center">
8
- <a href="https://nodejs.org/en/download"><img src="https://img.shields.io/badge/node-%3E=18-green.svg" alt="Node >= 18"></a>
9
- <a href="https://www.npmjs.com/package/@dexterai/x402"><img src="https://img.shields.io/npm/v/@dexterai/x402.svg" alt="npm version"></a>
10
- <a href="https://x402.dexter.cash"><img src="https://img.shields.io/badge/facilitator-x402.dexter.cash-orange.svg" alt="x402 Facilitator"></a>
8
+ <strong>The x402 SDK that actually works.</strong>
11
9
  </p>
12
10
 
13
- Chain-agnostic SDK for x402 v2 payments. Works with **Solana**, **Base**, and any x402-compatible network.
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@dexterai/x402"><img src="https://img.shields.io/npm/v/@dexterai/x402.svg" alt="npm"></a>
13
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E=18-brightgreen.svg" alt="Node"></a>
14
+ <a href="https://dexter.cash/sdk"><img src="https://img.shields.io/badge/🎮_Live_Demo-dexter.cash%2Fsdk-blueviolet" alt="Live Demo"></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ <a href="https://dexter.cash/sdk"><strong>👉 Try it with real payments →</strong></a>
19
+ </p>
20
+
21
+ ---
22
+
23
+ ## ✨ Why This SDK?
24
+
25
+ - **🔗 Multi-chain** — Solana and Base, same API
26
+ - **⚡ x402 v2** — Full protocol support, verified working
27
+ - **⚛️ React Hook** — `useX402Payment` with loading states, balances, and transaction tracking
28
+ - **💰 Smart Balance Check** — Clear "insufficient funds" error *before* the wallet popup
29
+ - **👻 Phantom Compatible** — Handles Lighthouse safety assertions automatically
30
+ - **📦 Zero Config** — Wrap `fetch()`, payments just work
14
31
 
15
32
  ---
16
33
 
17
- ## Highlights
34
+ ## 🎮 See It Working
18
35
 
19
- - **Chain-agnostic** pay on Solana, Base, or any supported chain
20
- - **Zero-config client** – wrap `fetch`, auto-handles 402 responses
21
- - **Server helpers** – generate requirements, verify & settle payments
22
- - **React hook** – multi-wallet support with balance tracking
23
- - **Dual ESM/CJS** full TypeScript definitions
36
+ **Don't take our word for it.** Make a real payment yourself:
37
+
38
+ **[→ dexter.cash/sdk](https://dexter.cash/sdk)**
39
+
40
+ The demo uses this exact SDK. Solana and Base. Real USDC. Real transactions.
24
41
 
25
42
  ---
26
43
 
27
- ## Quick Start
44
+ ## 🚀 Quick Start
28
45
 
29
46
  ### Install
30
47
 
31
48
  ```bash
32
- npm install @dexterai/x402 @solana/web3.js @solana/spl-token
49
+ npm install @dexterai/x402
33
50
  ```
34
51
 
35
- ### Client (Browser/Node)
52
+ ### Client (Browser)
36
53
 
37
54
  ```typescript
38
55
  import { createX402Client } from '@dexterai/x402/client';
39
56
 
40
- // Single wallet (Solana)
41
- const client = createX402Client({
42
- wallet: solanaWallet,
43
- });
44
-
45
- // Multi-chain: provide wallets for each chain
46
57
  const client = createX402Client({
47
58
  wallets: {
48
59
  solana: solanaWallet,
49
- evm: evmWallet, // from wagmi, viem, etc.
60
+ evm: evmWallet,
61
+ },
62
+ rpcUrls: {
63
+ 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': 'https://your-solana-rpc.com',
64
+ 'eip155:8453': 'https://your-base-rpc.com',
50
65
  },
51
- preferredNetwork: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
52
66
  });
53
67
 
54
- // Automatically handles 402 responses
68
+ // That's it. 402 responses are handled automatically.
55
69
  const response = await client.fetch('https://api.example.com/protected');
56
70
  ```
57
71
 
58
- ### Server (Express/Next.js)
59
-
60
- ```typescript
61
- import { createX402Server } from '@dexterai/x402/server';
62
-
63
- // Create server for Solana payments
64
- const server = createX402Server({
65
- payTo: 'YourSolanaAddress...',
66
- network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
67
- });
68
-
69
- // Or for Base payments
70
- const baseServer = createX402Server({
71
- payTo: '0xYourEvmAddress...',
72
- network: 'eip155:8453',
73
- asset: { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6 },
74
- });
75
-
76
- // In your route handler
77
- app.post('/protected', async (req, res) => {
78
- const paymentSig = req.headers['payment-signature'];
79
-
80
- if (!paymentSig) {
81
- const requirements = await server.buildRequirements({
82
- amountAtomic: '50000', // 0.05 USDC
83
- resourceUrl: req.originalUrl,
84
- });
85
- res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
86
- return res.status(402).json({});
87
- }
88
-
89
- const result = await server.settlePayment(paymentSig);
90
- if (!result.success) {
91
- return res.status(402).json({ error: result.errorReason });
92
- }
93
-
94
- res.json({ data: 'protected content', transaction: result.transaction });
95
- });
96
- ```
97
-
98
72
  ### React
99
73
 
100
74
  ```tsx
101
75
  import { useX402Payment } from '@dexterai/x402/react';
102
- import { useWallet } from '@solana/wallet-adapter-react';
103
- import { useAccount } from 'wagmi';
104
76
 
105
77
  function PayButton() {
106
- const solanaWallet = useWallet();
107
- const evmWallet = useAccount();
108
-
109
- const {
110
- fetch,
111
- isLoading,
112
- balances,
113
- connectedChains,
114
- transactionUrl,
115
- } = useX402Payment({
116
- wallets: {
117
- solana: solanaWallet,
118
- evm: evmWallet,
119
- },
78
+ const { fetch, isLoading, balances, transactionUrl } = useX402Payment({
79
+ wallets: { solana: solanaWallet, evm: evmWallet },
80
+ rpcUrls: { /* your RPC endpoints */ },
120
81
  });
121
82
 
122
83
  return (
123
84
  <div>
124
- {balances.map(b => (
125
- <p key={b.network}>{b.chainName}: ${b.balance.toFixed(2)}</p>
126
- ))}
85
+ <p>Balance: ${balances[0]?.balance.toFixed(2)}</p>
127
86
  <button onClick={() => fetch(url)} disabled={isLoading}>
128
- {isLoading ? 'Paying...' : 'Pay $0.05'}
87
+ {isLoading ? 'Paying...' : 'Pay'}
129
88
  </button>
130
- {transactionUrl && <a href={transactionUrl}>View Transaction</a>}
89
+ {transactionUrl && <a href={transactionUrl}>View Transaction ↗</a>}
131
90
  </div>
132
91
  );
133
92
  }
@@ -135,120 +94,141 @@ function PayButton() {
135
94
 
136
95
  ---
137
96
 
138
- ## Supported Networks
97
+ ## 🌐 Supported Networks
139
98
 
140
- | Network | CAIP-2 ID | Asset |
141
- |---------|-----------|-------|
142
- | Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | USDC |
143
- | Base Mainnet | `eip155:8453` | USDC |
144
- | Arbitrum One | `eip155:42161` | USDC |
145
- | Ethereum | `eip155:1` | USDC |
99
+ | Network | Identifier | Status |
100
+ |---------|------------|--------|
101
+ | Solana Mainnet | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Verified |
102
+ | Base Mainnet | `eip155:8453` | Verified |
146
103
 
147
- ---
104
+ All networks use USDC.
148
105
 
149
- ## API
106
+ ---
150
107
 
151
- ### Client
108
+ ## 📦 Package Exports
152
109
 
153
110
  ```typescript
111
+ // Client - browser & Node.js
154
112
  import { createX402Client } from '@dexterai/x402/client';
155
113
 
156
- const client = createX402Client({
157
- wallets: { solana, evm }, // Multi-chain wallets
158
- wallet: solanaWallet, // Legacy: single wallet
159
- preferredNetwork: '...', // Prefer this network
160
- rpcUrls: { 'eip155:8453': 'https://...' }, // Custom RPCs
161
- maxAmountAtomic: '100000', // Payment cap
162
- verbose: true, // Debug logging
163
- });
114
+ // React hook
115
+ import { useX402Payment } from '@dexterai/x402/react';
116
+
117
+ // Server helpers (see note below)
118
+ import { createX402Server } from '@dexterai/x402/server';
119
+
120
+ // Chain adapters (advanced)
121
+ import { createSolanaAdapter, createEvmAdapter } from '@dexterai/x402/adapters';
164
122
 
165
- const response = await client.fetch(url, init);
123
+ // Utilities
124
+ import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';
166
125
  ```
167
126
 
168
- ### Server
127
+ ---
128
+
129
+ ## 🛠️ Utilities
130
+
131
+ ```typescript
132
+ import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';
133
+
134
+ // Convert dollars to atomic units (for API calls)
135
+ toAtomicUnits(0.05, 6); // '50000'
136
+ toAtomicUnits(1.50, 6); // '1500000'
137
+
138
+ // Convert atomic units back to dollars (for display)
139
+ fromAtomicUnits('50000', 6); // 0.05
140
+ fromAtomicUnits(1500000n, 6); // 1.5
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 🖥️ Server SDK
169
146
 
170
147
  ```typescript
171
148
  import { createX402Server } from '@dexterai/x402/server';
172
149
 
173
150
  const server = createX402Server({
174
- payTo: 'address',
151
+ payTo: 'YourAddress...',
175
152
  network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
176
- asset: { address: 'mint', decimals: 6 },
177
153
  facilitatorUrl: 'https://x402.dexter.cash',
178
- defaultTimeoutSeconds: 60,
179
154
  });
180
155
 
181
- await server.buildRequirements({ amountAtomic, resourceUrl });
182
- server.encodeRequirements(requirements);
183
- await server.verifyPayment(header);
184
- await server.settlePayment(header);
185
- ```
156
+ // In your route handler
157
+ app.post('/protected', async (req, res) => {
158
+ const paymentSig = req.headers['payment-signature'];
186
159
 
187
- ### React Hook
160
+ if (!paymentSig) {
161
+ const requirements = await server.buildRequirements({
162
+ amountAtomic: '50000', // $0.05 USDC
163
+ resourceUrl: req.originalUrl,
164
+ });
165
+ res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
166
+ return res.status(402).json({});
167
+ }
188
168
 
189
- ```typescript
190
- import { useX402Payment } from '@dexterai/x402/react';
169
+ const result = await server.settlePayment(paymentSig);
170
+ if (!result.success) {
171
+ return res.status(402).json({ error: result.errorReason });
172
+ }
191
173
 
192
- const {
193
- fetch, // Payment-aware fetch
194
- isLoading, // Payment in progress
195
- status, // 'idle' | 'pending' | 'success' | 'error'
196
- error, // Error if failed
197
- transactionId, // Tx signature on success
198
- transactionUrl, // Explorer link
199
- balances, // Token balances per chain
200
- connectedChains, // { solana: bool, evm: bool }
201
- reset, // Clear state
202
- refreshBalances, // Manual balance refresh
203
- } = useX402Payment({ wallets, preferredNetwork, verbose });
174
+ res.json({ data: 'Your protected content' });
175
+ });
204
176
  ```
205
177
 
206
- ### Adapters (Advanced)
178
+ > ⚠️ **Note:** The server SDK has not been battle-tested in production yet. The client SDK and React hook have been verified with real payments at [dexter.cash/sdk](https://dexter.cash/sdk).
207
179
 
208
- ```typescript
209
- import {
210
- createSolanaAdapter,
211
- createEvmAdapter,
212
- SOLANA_MAINNET,
213
- BASE_MAINNET,
214
- } from '@dexterai/x402/adapters';
215
-
216
- const adapters = [
217
- createSolanaAdapter({ verbose: true }),
218
- createEvmAdapter({ rpcUrls: { 'eip155:8453': '...' } }),
219
- ];
220
-
221
- // Find adapter for a network
222
- const adapter = adapters.find(a => a.canHandle('eip155:8453'));
223
-
224
- // Build transaction manually
225
- const signedTx = await adapter.buildTransaction(accept, wallet);
226
-
227
- // Check balance
228
- const balance = await adapter.getBalance(accept, wallet);
229
- ```
180
+ ---
181
+
182
+ ## 📋 API Reference
183
+
184
+ ### `createX402Client(options)`
185
+
186
+ | Option | Type | Description |
187
+ |--------|------|-------------|
188
+ | `wallet` | `SolanaWallet` | Single Solana wallet (legacy) |
189
+ | `wallets` | `{ solana?, evm? }` | Multi-chain wallets |
190
+ | `preferredNetwork` | `string` | Prefer this network when multiple options available |
191
+ | `rpcUrls` | `Record<string, string>` | RPC endpoints per network (CAIP-2 format) |
192
+ | `maxAmountAtomic` | `string` | Maximum payment cap |
193
+ | `verbose` | `boolean` | Enable debug logging |
194
+
195
+ ### `useX402Payment(options)`
196
+
197
+ Returns:
198
+
199
+ | Property | Type | Description |
200
+ |----------|------|-------------|
201
+ | `fetch` | `function` | Payment-aware fetch |
202
+ | `isLoading` | `boolean` | Payment in progress |
203
+ | `status` | `string` | `'idle'` \| `'pending'` \| `'success'` \| `'error'` |
204
+ | `error` | `X402Error?` | Error details if failed |
205
+ | `transactionId` | `string?` | Transaction signature |
206
+ | `transactionUrl` | `string?` | Block explorer link |
207
+ | `balances` | `Balance[]` | Token balances per chain |
208
+ | `refreshBalances` | `function` | Manual refresh |
209
+ | `reset` | `function` | Clear state |
230
210
 
231
211
  ---
232
212
 
233
- ## Development
213
+ ## 🔧 Development
234
214
 
235
215
  ```bash
236
216
  npm run build # Build ESM + CJS
237
217
  npm run dev # Watch mode
238
- npm run typecheck # TypeScript checks
218
+ npm run typecheck # TypeScript
239
219
  npm test # Run tests
240
220
  ```
241
221
 
242
222
  ---
243
223
 
244
- ## Resources
224
+ ## 📄 License
245
225
 
246
- - [Dexter Facilitator](https://x402.dexter.cash)
247
- - [x402 Protocol Spec](https://docs.cdp.coinbase.com/x402)
248
- - [Seller Onboarding](https://dexter.cash/onboard)
226
+ MIT — see [LICENSE](./LICENSE)
249
227
 
250
228
  ---
251
229
 
252
- ## License
253
-
254
- MIT see [LICENSE](./LICENSE)
230
+ <p align="center">
231
+ <a href="https://x402.dexter.cash">Dexter Facilitator</a> ·
232
+ <a href="https://dexter.cash/sdk">Live Demo</a> ·
233
+ <a href="https://dexter.cash/onboard">Become a Seller</a>
234
+ </p>
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/utils/index.ts
21
+ var utils_exports = {};
22
+ __export(utils_exports, {
23
+ fromAtomicUnits: () => fromAtomicUnits,
24
+ getChainFamily: () => getChainFamily,
25
+ getChainName: () => getChainName,
26
+ getExplorerUrl: () => getExplorerUrl,
27
+ toAtomicUnits: () => toAtomicUnits
28
+ });
29
+ module.exports = __toCommonJS(utils_exports);
30
+
31
+ // src/types.ts
32
+ var SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
33
+ var BASE_MAINNET_NETWORK = "eip155:8453";
34
+
35
+ // src/utils.ts
36
+ function toAtomicUnits(amount, decimals) {
37
+ const multiplier = Math.pow(10, decimals);
38
+ return Math.floor(amount * multiplier).toString();
39
+ }
40
+ function fromAtomicUnits(atomicUnits, decimals) {
41
+ const divisor = Math.pow(10, decimals);
42
+ return Number(atomicUnits) / divisor;
43
+ }
44
+ function getChainFamily(network) {
45
+ if (network.startsWith("solana:") || network === "solana") {
46
+ return "solana";
47
+ }
48
+ if (network.startsWith("eip155:") || ["base", "ethereum", "arbitrum"].includes(network)) {
49
+ return "evm";
50
+ }
51
+ return "unknown";
52
+ }
53
+ function getChainName(network) {
54
+ const mapping = {
55
+ [SOLANA_MAINNET_NETWORK]: "Solana",
56
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "Solana Devnet",
57
+ "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "Solana Testnet",
58
+ "solana": "Solana",
59
+ [BASE_MAINNET_NETWORK]: "Base",
60
+ "eip155:84532": "Base Sepolia",
61
+ "eip155:1": "Ethereum",
62
+ "eip155:42161": "Arbitrum One",
63
+ "base": "Base",
64
+ "ethereum": "Ethereum",
65
+ "arbitrum": "Arbitrum"
66
+ };
67
+ return mapping[network] || network;
68
+ }
69
+ function getExplorerUrl(txSignature, network) {
70
+ const family = getChainFamily(network);
71
+ if (family === "solana") {
72
+ const isDevnet = network.includes("devnet") || network === "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
73
+ if (isDevnet) {
74
+ return `https://solscan.io/tx/${txSignature}?cluster=devnet`;
75
+ }
76
+ return `https://www.orbmarkets.io/tx/${txSignature}`;
77
+ }
78
+ if (family === "evm") {
79
+ let chainId = "8453";
80
+ if (network.startsWith("eip155:")) {
81
+ chainId = network.split(":")[1];
82
+ } else if (network === "ethereum") {
83
+ chainId = "1";
84
+ } else if (network === "arbitrum") {
85
+ chainId = "42161";
86
+ }
87
+ switch (chainId) {
88
+ case "8453":
89
+ return `https://basescan.org/tx/${txSignature}`;
90
+ case "84532":
91
+ return `https://sepolia.basescan.org/tx/${txSignature}`;
92
+ case "1":
93
+ return `https://etherscan.io/tx/${txSignature}`;
94
+ case "42161":
95
+ return `https://arbiscan.io/tx/${txSignature}`;
96
+ default:
97
+ return `https://basescan.org/tx/${txSignature}`;
98
+ }
99
+ }
100
+ return `https://solscan.io/tx/${txSignature}`;
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ fromAtomicUnits,
105
+ getChainFamily,
106
+ getChainName,
107
+ getExplorerUrl,
108
+ toAtomicUnits
109
+ });
110
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/index.ts","../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * @dexterai/x402 Utils\n *\n * Helper functions for x402 payments.\n *\n * @example\n * ```typescript\n * import { toAtomicUnits, fromAtomicUnits } from '@dexterai/x402/utils';\n *\n * const atomic = toAtomicUnits(0.05, 6); // '50000'\n * const human = fromAtomicUnits('50000', 6); // 0.05\n * ```\n */\n\nexport {\n toAtomicUnits,\n fromAtomicUnits,\n getChainFamily,\n getChainName,\n getExplorerUrl,\n type ChainFamily,\n} from '../utils';\n","/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required) */\n feePayer: string;\n /** Token decimals (required) */\n decimals: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier */\n network: string;\n /** Payment amount in atomic units (as string to avoid precision loss) */\n amount: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n return 'https://api.mainnet-beta.solana.com';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://mainnet.base.org';\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://mainnet.base.org';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://mainnet.base.org';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://mainnet.base.org';\n }\n\n // Unknown - return a generic\n return 'https://api.mainnet-beta.solana.com';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Encode an object as base64 JSON\n */\nexport function encodeBase64Json(obj: unknown): string {\n return btoa(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(atob(encoded)) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAkDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Utility Functions
3
+ *
4
+ * Chain-agnostic helpers for x402 payments.
5
+ */
6
+ /**
7
+ * Convert human-readable amount to atomic units
8
+ *
9
+ * @param amount - Human-readable amount (e.g., 0.05 for $0.05)
10
+ * @param decimals - Token decimals (e.g., 6 for USDC)
11
+ * @returns Amount in atomic units as string
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * toAtomicUnits(0.05, 6) // '50000'
16
+ * toAtomicUnits(1.50, 6) // '1500000'
17
+ * ```
18
+ */
19
+ declare function toAtomicUnits(amount: number, decimals: number): string;
20
+ /**
21
+ * Convert atomic units to human-readable amount
22
+ *
23
+ * @param atomicUnits - Amount in smallest units
24
+ * @param decimals - Token decimals
25
+ * @returns Human-readable amount
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * fromAtomicUnits('50000', 6) // 0.05
30
+ * fromAtomicUnits(1500000n, 6) // 1.5
31
+ * ```
32
+ */
33
+ declare function fromAtomicUnits(atomicUnits: string | bigint | number, decimals: number): number;
34
+ /**
35
+ * Network type
36
+ */
37
+ type ChainFamily = 'solana' | 'evm' | 'unknown';
38
+ /**
39
+ * Get the chain family from a CAIP-2 network identifier
40
+ *
41
+ * @param network - CAIP-2 network identifier
42
+ * @returns Chain family
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'
47
+ * getChainFamily('eip155:8453') // 'evm'
48
+ * ```
49
+ */
50
+ declare function getChainFamily(network: string): ChainFamily;
51
+ /**
52
+ * Get human-readable chain name
53
+ *
54
+ * @param network - CAIP-2 network identifier
55
+ * @returns Human-readable name
56
+ */
57
+ declare function getChainName(network: string): string;
58
+ /**
59
+ * Get explorer URL for a transaction
60
+ *
61
+ * @param txSignature - Transaction signature/hash
62
+ * @param network - CAIP-2 network identifier
63
+ * @returns Explorer URL
64
+ */
65
+ declare function getExplorerUrl(txSignature: string, network: string): string;
66
+
67
+ export { type ChainFamily, fromAtomicUnits, getChainFamily, getChainName, getExplorerUrl, toAtomicUnits };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Utility Functions
3
+ *
4
+ * Chain-agnostic helpers for x402 payments.
5
+ */
6
+ /**
7
+ * Convert human-readable amount to atomic units
8
+ *
9
+ * @param amount - Human-readable amount (e.g., 0.05 for $0.05)
10
+ * @param decimals - Token decimals (e.g., 6 for USDC)
11
+ * @returns Amount in atomic units as string
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * toAtomicUnits(0.05, 6) // '50000'
16
+ * toAtomicUnits(1.50, 6) // '1500000'
17
+ * ```
18
+ */
19
+ declare function toAtomicUnits(amount: number, decimals: number): string;
20
+ /**
21
+ * Convert atomic units to human-readable amount
22
+ *
23
+ * @param atomicUnits - Amount in smallest units
24
+ * @param decimals - Token decimals
25
+ * @returns Human-readable amount
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * fromAtomicUnits('50000', 6) // 0.05
30
+ * fromAtomicUnits(1500000n, 6) // 1.5
31
+ * ```
32
+ */
33
+ declare function fromAtomicUnits(atomicUnits: string | bigint | number, decimals: number): number;
34
+ /**
35
+ * Network type
36
+ */
37
+ type ChainFamily = 'solana' | 'evm' | 'unknown';
38
+ /**
39
+ * Get the chain family from a CAIP-2 network identifier
40
+ *
41
+ * @param network - CAIP-2 network identifier
42
+ * @returns Chain family
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'
47
+ * getChainFamily('eip155:8453') // 'evm'
48
+ * ```
49
+ */
50
+ declare function getChainFamily(network: string): ChainFamily;
51
+ /**
52
+ * Get human-readable chain name
53
+ *
54
+ * @param network - CAIP-2 network identifier
55
+ * @returns Human-readable name
56
+ */
57
+ declare function getChainName(network: string): string;
58
+ /**
59
+ * Get explorer URL for a transaction
60
+ *
61
+ * @param txSignature - Transaction signature/hash
62
+ * @param network - CAIP-2 network identifier
63
+ * @returns Explorer URL
64
+ */
65
+ declare function getExplorerUrl(txSignature: string, network: string): string;
66
+
67
+ export { type ChainFamily, fromAtomicUnits, getChainFamily, getChainName, getExplorerUrl, toAtomicUnits };
@@ -0,0 +1,79 @@
1
+ // src/types.ts
2
+ var SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
3
+ var BASE_MAINNET_NETWORK = "eip155:8453";
4
+
5
+ // src/utils.ts
6
+ function toAtomicUnits(amount, decimals) {
7
+ const multiplier = Math.pow(10, decimals);
8
+ return Math.floor(amount * multiplier).toString();
9
+ }
10
+ function fromAtomicUnits(atomicUnits, decimals) {
11
+ const divisor = Math.pow(10, decimals);
12
+ return Number(atomicUnits) / divisor;
13
+ }
14
+ function getChainFamily(network) {
15
+ if (network.startsWith("solana:") || network === "solana") {
16
+ return "solana";
17
+ }
18
+ if (network.startsWith("eip155:") || ["base", "ethereum", "arbitrum"].includes(network)) {
19
+ return "evm";
20
+ }
21
+ return "unknown";
22
+ }
23
+ function getChainName(network) {
24
+ const mapping = {
25
+ [SOLANA_MAINNET_NETWORK]: "Solana",
26
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": "Solana Devnet",
27
+ "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "Solana Testnet",
28
+ "solana": "Solana",
29
+ [BASE_MAINNET_NETWORK]: "Base",
30
+ "eip155:84532": "Base Sepolia",
31
+ "eip155:1": "Ethereum",
32
+ "eip155:42161": "Arbitrum One",
33
+ "base": "Base",
34
+ "ethereum": "Ethereum",
35
+ "arbitrum": "Arbitrum"
36
+ };
37
+ return mapping[network] || network;
38
+ }
39
+ function getExplorerUrl(txSignature, network) {
40
+ const family = getChainFamily(network);
41
+ if (family === "solana") {
42
+ const isDevnet = network.includes("devnet") || network === "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
43
+ if (isDevnet) {
44
+ return `https://solscan.io/tx/${txSignature}?cluster=devnet`;
45
+ }
46
+ return `https://www.orbmarkets.io/tx/${txSignature}`;
47
+ }
48
+ if (family === "evm") {
49
+ let chainId = "8453";
50
+ if (network.startsWith("eip155:")) {
51
+ chainId = network.split(":")[1];
52
+ } else if (network === "ethereum") {
53
+ chainId = "1";
54
+ } else if (network === "arbitrum") {
55
+ chainId = "42161";
56
+ }
57
+ switch (chainId) {
58
+ case "8453":
59
+ return `https://basescan.org/tx/${txSignature}`;
60
+ case "84532":
61
+ return `https://sepolia.basescan.org/tx/${txSignature}`;
62
+ case "1":
63
+ return `https://etherscan.io/tx/${txSignature}`;
64
+ case "42161":
65
+ return `https://arbiscan.io/tx/${txSignature}`;
66
+ default:
67
+ return `https://basescan.org/tx/${txSignature}`;
68
+ }
69
+ }
70
+ return `https://solscan.io/tx/${txSignature}`;
71
+ }
72
+ export {
73
+ fromAtomicUnits,
74
+ getChainFamily,
75
+ getChainName,
76
+ getExplorerUrl,
77
+ toAtomicUnits
78
+ };
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/utils.ts"],"sourcesContent":["/**\n * x402 v2 SDK — Shared Types\n *\n * Chain-agnostic types for x402 v2 payments.\n * Works with Solana, Base, and any future x402-compatible networks.\n */\n\n// ============================================================================\n// Network Constants\n// ============================================================================\n\n/** CAIP-2 network identifier for Solana mainnet */\nexport const SOLANA_MAINNET_NETWORK = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';\n\n/** CAIP-2 network identifier for Base mainnet */\nexport const BASE_MAINNET_NETWORK = 'eip155:8453';\n\n/** Alias for Solana mainnet */\nexport const SOLANA_MAINNET = SOLANA_MAINNET_NETWORK;\n\n/** Alias for Base mainnet */\nexport const BASE_MAINNET = BASE_MAINNET_NETWORK;\n\n// ============================================================================\n// Asset Constants\n// ============================================================================\n\n/** USDC mint on Solana mainnet */\nexport const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';\n\n/** USDC address on Base mainnet */\nexport const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';\n\n// ============================================================================\n// Facilitator Constants\n// ============================================================================\n\n/** Dexter's public x402 v2 facilitator URL */\nexport const DEXTER_FACILITATOR_URL = 'https://x402.dexter.cash';\n\n// ============================================================================\n// Payment Types\n// ============================================================================\n\n/**\n * Asset configuration for payments\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n /** Optional: Human-readable symbol */\n symbol?: string;\n}\n\n/**\n * Resource info included in payment requirements\n */\nexport interface ResourceInfo {\n /** Resource URL */\n url: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the resource */\n mimeType?: string;\n}\n\n/**\n * Extra fields in payment requirements\n * Chain-specific fields may vary\n */\nexport interface AcceptsExtra {\n /** Facilitator address that pays tx fees (required) */\n feePayer: string;\n /** Token decimals (required) */\n decimals: number;\n /** EIP-712: Token name (EVM only) */\n name?: string;\n /** EIP-712: Token version (EVM only) */\n version?: string;\n /** Additional chain-specific fields */\n [key: string]: unknown;\n}\n\n/**\n * A single payment option in the accepts array\n */\nexport interface PaymentAccept {\n /** Payment scheme (always 'exact' for x402 v2) */\n scheme: 'exact';\n /** CAIP-2 network identifier */\n network: string;\n /** Payment amount in atomic units (as string to avoid precision loss) */\n amount: string;\n /** Token address */\n asset: string;\n /** Seller's address to receive payment */\n payTo: string;\n /** Maximum seconds until payment expires */\n maxTimeoutSeconds: number;\n /** Chain-specific extra data */\n extra: AcceptsExtra;\n}\n\n/**\n * Full PaymentRequired structure (sent in PAYMENT-REQUIRED header)\n */\nexport interface PaymentRequired {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** Available payment options */\n accepts: PaymentAccept[];\n /** Optional error message */\n error?: string;\n}\n\n/**\n * PaymentSignature structure (sent in PAYMENT-SIGNATURE header)\n */\nexport interface PaymentSignature {\n /** x402 version (always 2) */\n x402Version: 2;\n /** Resource being accessed */\n resource: ResourceInfo;\n /** The payment option that was accepted */\n accepted: PaymentAccept;\n /** The signed payment */\n payload: {\n /** Signed transaction (base64 for Solana, JSON for EVM) */\n transaction: string;\n };\n}\n\n// ============================================================================\n// Facilitator Response Types\n// ============================================================================\n\n/**\n * Response from /verify endpoint\n */\nexport interface VerifyResponse {\n /** Whether the payment is valid */\n isValid: boolean;\n /** Reason for invalidity (if invalid) */\n invalidReason?: string;\n /** Payer address */\n payer?: string;\n}\n\n/**\n * Response from /settle endpoint\n */\nexport interface SettleResponse {\n /** Whether settlement succeeded */\n success: boolean;\n /** Transaction signature/hash */\n transaction?: string;\n /** Network the payment was made on */\n network: string;\n /** Error reason (if failed) */\n errorReason?: string;\n /** Error code (if failed) */\n errorCode?: string;\n /** Payer address */\n payer?: string;\n}\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\n/**\n * SDK error codes\n */\nexport type X402ErrorCode =\n // Client errors\n | 'missing_payment_required_header'\n | 'invalid_payment_required'\n | 'unsupported_network'\n | 'no_matching_payment_option'\n | 'no_solana_accept' // Legacy, kept for compatibility\n | 'missing_fee_payer'\n | 'missing_decimals'\n | 'amount_exceeds_max'\n | 'insufficient_balance'\n | 'wallet_missing_sign_transaction'\n | 'wallet_not_connected'\n | 'transaction_build_failed'\n | 'payment_rejected'\n // Server errors\n | 'invalid_payment_signature'\n | 'facilitator_verify_failed'\n | 'facilitator_settle_failed'\n | 'facilitator_request_failed'\n | 'no_matching_requirement';\n\n/**\n * Custom error class for x402 operations\n */\nexport class X402Error extends Error {\n /** Error code for programmatic handling */\n code: X402ErrorCode;\n /** Additional error details */\n details?: unknown;\n\n constructor(code: X402ErrorCode, message: string, details?: unknown) {\n super(message);\n this.name = 'X402Error';\n this.code = code;\n this.details = details;\n // Maintain proper prototype chain\n Object.setPrototypeOf(this, X402Error.prototype);\n }\n}\n","/**\n * Utility Functions\n *\n * Chain-agnostic helpers for x402 payments.\n */\n\nimport { SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK } from './types';\n\n// ============================================================================\n// Amount Conversion\n// ============================================================================\n\n/**\n * Convert human-readable amount to atomic units\n *\n * @param amount - Human-readable amount (e.g., 0.05 for $0.05)\n * @param decimals - Token decimals (e.g., 6 for USDC)\n * @returns Amount in atomic units as string\n *\n * @example\n * ```typescript\n * toAtomicUnits(0.05, 6) // '50000'\n * toAtomicUnits(1.50, 6) // '1500000'\n * ```\n */\nexport function toAtomicUnits(amount: number, decimals: number): string {\n const multiplier = Math.pow(10, decimals);\n return Math.floor(amount * multiplier).toString();\n}\n\n/**\n * Convert atomic units to human-readable amount\n *\n * @param atomicUnits - Amount in smallest units\n * @param decimals - Token decimals\n * @returns Human-readable amount\n *\n * @example\n * ```typescript\n * fromAtomicUnits('50000', 6) // 0.05\n * fromAtomicUnits(1500000n, 6) // 1.5\n * ```\n */\nexport function fromAtomicUnits(\n atomicUnits: string | bigint | number,\n decimals: number\n): number {\n const divisor = Math.pow(10, decimals);\n return Number(atomicUnits) / divisor;\n}\n\n// ============================================================================\n// Network Helpers\n// ============================================================================\n\n/**\n * Network type\n */\nexport type ChainFamily = 'solana' | 'evm' | 'unknown';\n\n/**\n * Get the chain family from a CAIP-2 network identifier\n *\n * @param network - CAIP-2 network identifier\n * @returns Chain family\n *\n * @example\n * ```typescript\n * getChainFamily('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp') // 'solana'\n * getChainFamily('eip155:8453') // 'evm'\n * ```\n */\nexport function getChainFamily(network: string): ChainFamily {\n if (network.startsWith('solana:') || network === 'solana') {\n return 'solana';\n }\n if (network.startsWith('eip155:') || ['base', 'ethereum', 'arbitrum'].includes(network)) {\n return 'evm';\n }\n return 'unknown';\n}\n\n/**\n * Get default RPC URL for a network\n *\n * @param network - CAIP-2 network identifier\n * @returns Default RPC URL\n */\nexport function getDefaultRpcUrl(network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n if (network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1') {\n return 'https://api.devnet.solana.com';\n }\n if (network.includes('testnet') || network === 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z') {\n return 'https://api.testnet.solana.com';\n }\n return 'https://api.mainnet-beta.solana.com';\n }\n\n if (family === 'evm') {\n // Extract chain ID from CAIP-2\n if (network.startsWith('eip155:')) {\n const chainId = network.split(':')[1];\n switch (chainId) {\n case '8453': return 'https://mainnet.base.org';\n case '84532': return 'https://sepolia.base.org';\n case '1': return 'https://eth.llamarpc.com';\n case '42161': return 'https://arb1.arbitrum.io/rpc';\n default: return 'https://mainnet.base.org';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://mainnet.base.org';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://mainnet.base.org';\n }\n\n // Unknown - return a generic\n return 'https://api.mainnet-beta.solana.com';\n}\n\n/**\n * Get human-readable chain name\n *\n * @param network - CAIP-2 network identifier\n * @returns Human-readable name\n */\nexport function getChainName(network: string): string {\n const mapping: Record<string, string> = {\n [SOLANA_MAINNET_NETWORK]: 'Solana',\n 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1': 'Solana Devnet',\n 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': 'Solana Testnet',\n 'solana': 'Solana',\n [BASE_MAINNET_NETWORK]: 'Base',\n 'eip155:84532': 'Base Sepolia',\n 'eip155:1': 'Ethereum',\n 'eip155:42161': 'Arbitrum One',\n 'base': 'Base',\n 'ethereum': 'Ethereum',\n 'arbitrum': 'Arbitrum',\n };\n return mapping[network] || network;\n}\n\n// ============================================================================\n// Transaction URL Helpers\n// ============================================================================\n\n/**\n * Get explorer URL for a transaction\n *\n * @param txSignature - Transaction signature/hash\n * @param network - CAIP-2 network identifier\n * @returns Explorer URL\n */\nexport function getExplorerUrl(txSignature: string, network: string): string {\n const family = getChainFamily(network);\n\n if (family === 'solana') {\n const isDevnet = network.includes('devnet') || network === 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1';\n if (isDevnet) {\n return `https://solscan.io/tx/${txSignature}?cluster=devnet`;\n }\n // Prefer Orb Markets for mainnet\n return `https://www.orbmarkets.io/tx/${txSignature}`;\n }\n\n if (family === 'evm') {\n // Extract chain ID\n let chainId = '8453'; // Default to Base\n if (network.startsWith('eip155:')) {\n chainId = network.split(':')[1];\n } else if (network === 'ethereum') {\n chainId = '1';\n } else if (network === 'arbitrum') {\n chainId = '42161';\n }\n\n switch (chainId) {\n case '8453': return `https://basescan.org/tx/${txSignature}`;\n case '84532': return `https://sepolia.basescan.org/tx/${txSignature}`;\n case '1': return `https://etherscan.io/tx/${txSignature}`;\n case '42161': return `https://arbiscan.io/tx/${txSignature}`;\n default: return `https://basescan.org/tx/${txSignature}`;\n }\n }\n\n return `https://solscan.io/tx/${txSignature}`;\n}\n\n// ============================================================================\n// Encoding Helpers\n// ============================================================================\n\n/**\n * Encode an object as base64 JSON\n */\nexport function encodeBase64Json(obj: unknown): string {\n return btoa(JSON.stringify(obj));\n}\n\n/**\n * Decode base64 JSON to object\n */\nexport function decodeBase64Json<T>(encoded: string): T {\n return JSON.parse(atob(encoded)) as T;\n}\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;;;ACU7B,SAAS,cAAc,QAAgB,UAA0B;AACtE,QAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,SAAO,KAAK,MAAM,SAAS,UAAU,EAAE,SAAS;AAClD;AAeO,SAAS,gBACd,aACA,UACQ;AACR,QAAM,UAAU,KAAK,IAAI,IAAI,QAAQ;AACrC,SAAO,OAAO,WAAW,IAAI;AAC/B;AAuBO,SAAS,eAAe,SAA8B;AAC3D,MAAI,QAAQ,WAAW,SAAS,KAAK,YAAY,UAAU;AACzD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,WAAW,SAAS,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,SAAS,OAAO,GAAG;AACvF,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAkDO,SAAS,aAAa,SAAyB;AACpD,QAAM,UAAkC;AAAA,IACtC,CAAC,sBAAsB,GAAG;AAAA,IAC1B,2CAA2C;AAAA,IAC3C,2CAA2C;AAAA,IAC3C,UAAU;AAAA,IACV,CAAC,oBAAoB,GAAG;AAAA,IACxB,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,SAAO,QAAQ,OAAO,KAAK;AAC7B;AAaO,SAAS,eAAe,aAAqB,SAAyB;AAC3E,QAAM,SAAS,eAAe,OAAO;AAErC,MAAI,WAAW,UAAU;AACvB,UAAM,WAAW,QAAQ,SAAS,QAAQ,KAAK,YAAY;AAC3D,QAAI,UAAU;AACZ,aAAO,yBAAyB,WAAW;AAAA,IAC7C;AAEA,WAAO,gCAAgC,WAAW;AAAA,EACpD;AAEA,MAAI,WAAW,OAAO;AAEpB,QAAI,UAAU;AACd,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,gBAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,IAChC,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ,WAAW,YAAY,YAAY;AACjC,gBAAU;AAAA,IACZ;AAEA,YAAQ,SAAS;AAAA,MACf,KAAK;AAAQ,eAAO,2BAA2B,WAAW;AAAA,MAC1D,KAAK;AAAS,eAAO,mCAAmC,WAAW;AAAA,MACnE,KAAK;AAAK,eAAO,2BAA2B,WAAW;AAAA,MACvD,KAAK;AAAS,eAAO,0BAA0B,WAAW;AAAA,MAC1D;AAAS,eAAO,2BAA2B,WAAW;AAAA,IACxD;AAAA,EACF;AAEA,SAAO,yBAAyB,WAAW;AAC7C;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/x402",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Chain-agnostic x402 v2 SDK for Solana, Base, and EVM payments",
5
5
  "author": "Dexter",
6
6
  "license": "MIT",
@@ -25,6 +25,11 @@
25
25
  "types": "./dist/adapters/index.d.ts",
26
26
  "import": "./dist/adapters/index.js",
27
27
  "require": "./dist/adapters/index.cjs"
28
+ },
29
+ "./utils": {
30
+ "types": "./dist/utils/index.d.ts",
31
+ "import": "./dist/utils/index.js",
32
+ "require": "./dist/utils/index.cjs"
28
33
  }
29
34
  },
30
35
  "files": [
@@ -37,7 +42,7 @@
37
42
  "build": "tsup",
38
43
  "dev": "tsup --watch",
39
44
  "typecheck": "tsc --noEmit",
40
- "test": "vitest run",
45
+ "test": "echo 'Real payment tests require funded wallets. Run: npx tsx test/real-payment-solana.ts'",
41
46
  "test:watch": "vitest",
42
47
  "prepublishOnly": "npm run build",
43
48
  "release": "npm version patch && npm publish --access public",
@@ -90,7 +95,7 @@
90
95
  ],
91
96
  "repository": {
92
97
  "type": "git",
93
- "url": "https://github.com/Dexter-DAO/x402-sdk"
98
+ "url": "https://github.com/Dexter-DAO/dexter-x402-sdk"
94
99
  },
95
100
  "homepage": "https://dexter.cash/sdk",
96
101
  "bugs": {