@dexterai/x402 1.2.0 → 1.2.2

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
@@ -30,15 +30,17 @@ This SDK handles the entire flow automatically—you just call `fetch()` and pay
30
30
 
31
31
  ## Why This SDK?
32
32
 
33
- **Phantom wallet support on Solana mainnet.** The Dexter facilitator is the only x402 facilitator that handles Phantom's Lighthouse safety assertions. Other facilitators fail silently or reject Phantom transactions on mainnet. This SDK uses the Dexter facilitator by default.
33
+ **Monetize any API in minutes.** Add payments to your server in ~10 lines. Clients pay automatically—no checkout pages, no subscriptions, no invoices. Just HTTP.
34
34
 
35
- **Multi-chain.** Solana and Base with the same API. Add wallets for both chains and the SDK picks the right one based on what the server accepts.
35
+ **Dynamic pricing.** Charge based on usage: characters, tokens, records, pixels, API calls—whatever makes sense. Price scales with input, not fixed rates.
36
36
 
37
- **Built-in RPC.** Uses Dexter's RPC proxy by default—no need to configure Helius, QuickNode, or other providers. Just pass your wallet and go.
37
+ **Token-accurate LLM pricing.** Built-in [tiktoken](https://github.com/openai/tiktoken) support prices AI requests by actual token count. Works with OpenAI models out of the box, or bring your own rates for Anthropic, Gemini, Mistral, or local models.
38
38
 
39
- **Pre-flight balance check.** Shows "Insufficient USDC balance" *before* the wallet popup, not after a failed transaction.
39
+ **Full-stack.** Client SDK for browsers, server SDK for backends. React hooks, Express middleware patterns, facilitator client—everything you need.
40
40
 
41
- **React hook included.** `useX402Payment` with loading states, balances, and transaction tracking.
41
+ **Multi-chain.** Solana and Base (Ethereum L2) with the same API. Add wallets for both and the SDK picks the right one automatically.
42
+
43
+ **Works out of the box.** Built-in RPC proxy, pre-flight balance checks, automatic retry on 402. Uses the [Dexter facilitator](https://x402.dexter.cash) by default—the only x402 facilitator with full Phantom wallet support on Solana mainnet.
42
44
 
43
45
  ---
44
46
 
@@ -181,13 +183,19 @@ app.post('/protected', async (req, res) => {
181
183
  });
182
184
  ```
183
185
 
184
- > **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).
186
+ *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.*
185
187
 
186
188
  ---
187
189
 
188
190
  ## Dynamic Pricing
189
191
 
190
- For LLM/AI endpoints where cost scales with input size:
192
+ **Generic pricing for any use case** - charge by characters, bytes, API calls, or any unit you define. No external dependencies.
193
+
194
+ Works for:
195
+ - LLM/AI endpoints (by character count)
196
+ - Image processing (by pixel count or file size)
197
+ - Data APIs (by record count)
198
+ - Any service where cost scales with input
191
199
 
192
200
  ```typescript
193
201
  import { createX402Server, createDynamicPricing } from '@dexterai/x402/server';
@@ -235,7 +243,7 @@ The client SDK automatically forwards `X-Quote-Hash` on retry.
235
243
 
236
244
  ## Token Pricing (LLM-Accurate)
237
245
 
238
- For accurate LLM pricing using actual token counts (not character estimates):
246
+ **Accurate token-based pricing for LLMs.** Uses tiktoken for token counting. Supports OpenAI models out of the box, plus custom rates for Anthropic, Gemini, Mistral, or any model.
239
247
 
240
248
  ```typescript
241
249
  import { createX402Server, createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';
@@ -304,7 +312,37 @@ MODEL_PRICING['gpt-4o-mini'];
304
312
  // → { input: 0.15, output: 0.6, maxTokens: 4096, tier: 'fast' }
305
313
  ```
306
314
 
307
- **Supported tiers:** `fast`, `standard`, `reasoning`, `premium`
315
+ **Supported tiers:** `fast`, `standard`, `reasoning`, `premium`, `custom`
316
+
317
+ ### Custom Models (Anthropic, Gemini, etc.)
318
+
319
+ Not using OpenAI? Pass your own rates:
320
+
321
+ ```typescript
322
+ // Anthropic Claude
323
+ const pricing = createTokenPricing({
324
+ model: 'claude-3-sonnet',
325
+ inputRate: 3.0, // $3.00 per 1M input tokens
326
+ outputRate: 15.0, // $15.00 per 1M output tokens
327
+ maxTokens: 4096,
328
+ });
329
+
330
+ // Google Gemini
331
+ const pricing = createTokenPricing({
332
+ model: 'gemini-1.5-pro',
333
+ inputRate: 1.25,
334
+ outputRate: 5.0,
335
+ });
336
+
337
+ // Custom/local model with custom tokenizer
338
+ const pricing = createTokenPricing({
339
+ model: 'llama-3-70b',
340
+ inputRate: 0.50,
341
+ tokenizer: (text) => llamaTokenizer.encode(text).length,
342
+ });
343
+ ```
344
+
345
+ tiktoken's default encoding works well for most transformer models. Only use a custom tokenizer if your model has significantly different tokenization.
308
346
 
309
347
  ---
310
348
 
@@ -507,16 +507,31 @@ function generateQuoteHash(prompt, model, rate, tokens) {
507
507
  return (0, import_crypto.createHash)("sha256").update(prompt + configString).digest("hex").slice(0, 16);
508
508
  }
509
509
  function createTokenPricing(config = {}) {
510
- const model = config.model && MODEL_PRICING[config.model] ? config.model : DEFAULT_MODEL;
511
- const modelInfo = MODEL_PRICING[model];
510
+ const model = config.model ?? DEFAULT_MODEL;
511
+ const builtInPricing = MODEL_PRICING[model];
512
+ const modelInfo = {
513
+ input: config.inputRate ?? builtInPricing?.input ?? MODEL_PRICING[DEFAULT_MODEL].input,
514
+ output: config.outputRate ?? builtInPricing?.output ?? MODEL_PRICING[DEFAULT_MODEL].output,
515
+ maxTokens: config.maxTokens ?? builtInPricing?.maxTokens ?? 4096,
516
+ tier: config.tier ?? builtInPricing?.tier ?? "custom"
517
+ };
518
+ const customTokenizer = config.tokenizer;
512
519
  const fullConfig = {
513
520
  model,
521
+ inputRate: modelInfo.input,
522
+ outputRate: modelInfo.output,
523
+ maxTokens: modelInfo.maxTokens,
524
+ tier: modelInfo.tier,
525
+ tokenizer: customTokenizer ?? ((text) => countTokens(text, model)),
514
526
  minUsd: config.minUsd ?? 1e-3,
515
527
  maxUsd: config.maxUsd ?? 50,
516
528
  decimals: config.decimals ?? 6
517
529
  };
518
530
  const { minUsd, maxUsd, decimals } = fullConfig;
519
531
  function countTokensInternal(input) {
532
+ if (customTokenizer) {
533
+ return customTokenizer(input);
534
+ }
520
535
  return countTokens(input, model);
521
536
  }
522
537
  function calculate(input, systemPrompt) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts","../../src/types.ts","../../src/utils.ts","../../src/server/facilitator-client.ts","../../src/server/x402-server.ts","../../src/server/dynamic-pricing.ts","../../src/server/token-pricing.ts"],"sourcesContent":["/**\n * @dexterai/x402 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Works with any x402 v2 facilitator.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * // Create server for Solana payments\n * const solanaServer = createX402Server({\n * payTo: 'YourSolanaAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Create server for Base payments\n * const baseServer = createX402Server({\n * payTo: '0xYourEvmAddress...',\n * network: 'eip155:8453',\n * asset: { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6 },\n * });\n *\n * // In your Express handler:\n * app.post('/protected', async (req, res) => {\n * const paymentSig = req.headers['payment-signature'];\n *\n * if (!paymentSig) {\n * const requirements = await solanaServer.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: req.originalUrl,\n * });\n * res.setHeader('PAYMENT-REQUIRED', solanaServer.encodeRequirements(requirements));\n * return res.status(402).json({});\n * }\n *\n * const result = await solanaServer.settlePayment(paymentSig);\n * if (!result.success) {\n * return res.status(402).json({ error: result.errorReason });\n * }\n *\n * res.json({ data: 'protected content', transaction: result.transaction });\n * });\n * ```\n */\n\nexport { createX402Server } from './x402-server';\nexport type {\n X402ServerConfig,\n X402Server,\n BuildRequirementsOptions,\n AssetConfig,\n} from './x402-server';\n\nexport { FacilitatorClient, type SupportedKind, type SupportedResponse } from './facilitator-client';\n\n// Dynamic pricing (character-based)\nexport { createDynamicPricing, formatPricing } from './dynamic-pricing';\nexport type { DynamicPricingConfig, DynamicPricing, PriceQuote } from './dynamic-pricing';\n\n// Token pricing (LLM-accurate with tiktoken)\nexport { \n createTokenPricing, \n countTokens, \n getAvailableModels, \n isValidModel, \n formatTokenPricing,\n MODEL_PRICING,\n} from './token-pricing';\nexport type { \n TokenPricingConfig, \n TokenPricing, \n TokenPriceQuote, \n ModelPricing,\n} from './token-pricing';\n\n// Re-export types for convenience\nexport type { VerifyResponse, SettleResponse, PaymentRequired, PaymentAccept } from '../types';\nexport { DEXTER_FACILITATOR_URL, SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK, USDC_MINT, USDC_BASE } from '../types';\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 for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\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 (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\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 | 'missing_amount'\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 // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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://api.dexter.cash/api/base/rpc'; // Dexter proxy\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://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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","/**\n * Facilitator Client\n *\n * Communicates with the x402 facilitator for:\n * - /supported - Get supported payment schemes and fee payer addresses\n * - /verify - Verify a payment signature before processing\n * - /settle - Submit the payment for execution\n *\n * Works with any x402 v2 facilitator (Dexter or others).\n */\n\nimport type { PaymentAccept, PaymentSignature, VerifyResponse, SettleResponse } from '../types';\nimport { DEXTER_FACILITATOR_URL } from '../types';\nimport { decodeBase64Json } from '../utils';\n\n/**\n * Supported payment kind from facilitator /supported endpoint\n */\nexport interface SupportedKind {\n x402Version: number;\n scheme: string;\n network: string;\n extra?: {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from facilitator /supported endpoint\n */\nexport interface SupportedResponse {\n kinds: SupportedKind[];\n}\n\n/**\n * Client for communicating with an x402 v2 facilitator\n */\nexport class FacilitatorClient {\n private facilitatorUrl: string;\n private cachedSupported: SupportedResponse | null = null;\n private cacheTime: number = 0;\n private readonly CACHE_TTL_MS = 60_000; // 1 minute cache\n\n constructor(facilitatorUrl: string = DEXTER_FACILITATOR_URL) {\n this.facilitatorUrl = facilitatorUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Get supported payment kinds from the facilitator\n * Results are cached for 1 minute to reduce network calls\n */\n async getSupported(): Promise<SupportedResponse> {\n const now = Date.now();\n if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {\n return this.cachedSupported;\n }\n\n const response = await fetch(`${this.facilitatorUrl}/supported`);\n if (!response.ok) {\n throw new Error(`Facilitator /supported returned ${response.status}`);\n }\n\n this.cachedSupported = (await response.json()) as SupportedResponse;\n this.cacheTime = now;\n return this.cachedSupported;\n }\n\n /**\n * Get the fee payer address for a specific network\n *\n * @param network - CAIP-2 network identifier\n * @returns Fee payer address\n */\n async getFeePayer(network: string): Promise<string> {\n const supported = await this.getSupported();\n\n // Find matching network support (exact match or v2)\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n if (!kind?.extra?.feePayer) {\n throw new Error(\n `Facilitator does not support network \"${network}\" with scheme \"exact\", or feePayer not provided`\n );\n }\n\n return kind.extra.feePayer;\n }\n\n /**\n * Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)\n *\n * @param network - CAIP-2 network identifier\n * @returns Extra data from /supported\n */\n async getNetworkExtra(network: string): Promise<SupportedKind['extra']> {\n const supported = await this.getSupported();\n\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n return kind?.extra;\n }\n\n /**\n * Verify a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Verification response\n */\n async verifyPayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<VerifyResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const verifyPayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(verifyPayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /verify returned ${response.status}:`, errorText);\n return {\n isValid: false,\n invalidReason: `facilitator_error_${response.status}`,\n };\n }\n\n return (await response.json()) as VerifyResponse;\n } catch (error) {\n console.error('Payment verification failed:', error);\n return {\n isValid: false,\n invalidReason: error instanceof Error ? error.message : 'unexpected_verify_error',\n };\n }\n }\n\n /**\n * Settle a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Settlement response with transaction signature on success\n */\n async settlePayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<SettleResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const settlePayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/settle`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(settlePayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /settle returned ${response.status}:`, errorText);\n return {\n success: false,\n network: requirements.network,\n errorReason: `facilitator_error_${response.status}`,\n };\n }\n\n const result = (await response.json()) as SettleResponse;\n return {\n ...result,\n network: requirements.network,\n };\n } catch (error) {\n console.error('Payment settlement failed:', error);\n return {\n success: false,\n network: requirements.network,\n errorReason: error instanceof Error ? error.message : 'unexpected_settle_error',\n };\n }\n }\n}\n","/**\n * x402 v2 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Chain-agnostic - works with Solana, Base, and any x402-compatible network.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * const server = createX402Server({\n * payTo: 'YourAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Handle 402 responses\n * if (!paymentSignature) {\n * const requirements = await server.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: '/api/protected',\n * });\n * res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));\n * res.status(402).json({});\n * return;\n * }\n *\n * // Verify and settle\n * const verify = await server.verifyPayment(paymentSignature);\n * if (!verify.isValid) throw new Error(verify.invalidReason);\n *\n * const settle = await server.settlePayment(paymentSignature);\n * if (!settle.success) throw new Error(settle.errorReason);\n *\n * // Payment successful!\n * res.json({ transaction: settle.transaction });\n * ```\n */\n\nimport type {\n PaymentRequired,\n PaymentAccept,\n ResourceInfo,\n AcceptsExtra,\n VerifyResponse,\n SettleResponse,\n} from '../types';\nimport {\n SOLANA_MAINNET_NETWORK,\n USDC_MINT,\n DEXTER_FACILITATOR_URL,\n} from '../types';\nimport { FacilitatorClient, type SupportedKind } from './facilitator-client';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Asset configuration\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n}\n\n/**\n * Server configuration\n */\nexport interface X402ServerConfig {\n /** Address to receive payments */\n payTo: string;\n /** Facilitator URL (defaults to Dexter) */\n facilitatorUrl?: string;\n /** CAIP-2 network identifier */\n network?: string;\n /** Asset configuration (defaults to USDC) */\n asset?: AssetConfig;\n /** Default payment timeout in seconds */\n defaultTimeoutSeconds?: number;\n}\n\n/**\n * Options for building payment requirements\n */\nexport interface BuildRequirementsOptions {\n /** Amount in atomic units (e.g., '50000' for 0.05 USDC) */\n amountAtomic: string;\n /** Full URL of the resource */\n resourceUrl: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the response */\n mimeType?: string;\n /** Override timeout for this request */\n timeoutSeconds?: number;\n}\n\n/**\n * x402 Server interface\n */\nexport interface X402Server {\n /** Build payment requirements (fetches feePayer from facilitator) */\n buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;\n\n /** Encode requirements for PAYMENT-REQUIRED header */\n encodeRequirements(requirements: PaymentRequired): string;\n\n /** Create complete 402 response object */\n create402Response(requirements: PaymentRequired): {\n status: 402;\n headers: { 'PAYMENT-REQUIRED': string };\n body: Record<string, unknown>;\n };\n\n /** Verify payment with facilitator */\n verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse>;\n\n /** Settle payment via facilitator */\n settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse>;\n\n /** Get PaymentAccept for verify/settle */\n getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;\n\n /** Get network this server is configured for */\n readonly network: string;\n\n /** Get facilitator client for advanced usage */\n readonly facilitator: FacilitatorClient;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an x402 server for accepting payments\n */\nexport function createX402Server(config: X402ServerConfig): X402Server {\n const {\n payTo,\n facilitatorUrl = DEXTER_FACILITATOR_URL,\n network = SOLANA_MAINNET_NETWORK,\n asset = { address: USDC_MINT, decimals: 6 },\n defaultTimeoutSeconds = 60,\n } = config;\n\n const facilitator = new FacilitatorClient(facilitatorUrl);\n\n // Cache for network extra data\n let cachedExtra: SupportedKind['extra'] | null = null;\n\n /**\n * Get extra data from facilitator (cached)\n */\n async function getNetworkExtra(): Promise<AcceptsExtra> {\n if (!cachedExtra) {\n cachedExtra = await facilitator.getNetworkExtra(network);\n }\n\n if (!cachedExtra?.feePayer) {\n throw new Error(`Facilitator does not provide feePayer for network \"${network}\"`);\n }\n\n return {\n feePayer: cachedExtra.feePayer,\n decimals: cachedExtra.decimals ?? asset.decimals,\n // Include any additional EIP-712 data for EVM chains\n name: cachedExtra.name,\n version: cachedExtra.version,\n };\n }\n\n /**\n * Build a PaymentAccept structure\n */\n async function getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept> {\n const {\n amountAtomic,\n timeoutSeconds = defaultTimeoutSeconds,\n } = options;\n\n const extra = await getNetworkExtra();\n\n return {\n scheme: 'exact',\n network,\n maxAmountRequired: amountAtomic,\n asset: asset.address,\n payTo,\n maxTimeoutSeconds: timeoutSeconds,\n extra,\n };\n }\n\n /**\n * Build payment requirements for a 402 response\n */\n async function buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired> {\n const {\n resourceUrl,\n description,\n mimeType = 'application/json',\n } = options;\n\n const resource: ResourceInfo = {\n url: resourceUrl,\n description,\n mimeType,\n };\n\n const accept = await getPaymentAccept(options);\n\n return {\n x402Version: 2,\n resource,\n accepts: [accept],\n error: 'Payment required',\n };\n }\n\n /**\n * Encode requirements for PAYMENT-REQUIRED header\n */\n function encodeRequirements(requirements: PaymentRequired): string {\n return btoa(JSON.stringify(requirements));\n }\n\n /**\n * Create complete 402 response object\n */\n function create402Response(requirements: PaymentRequired) {\n return {\n status: 402 as const,\n headers: {\n 'PAYMENT-REQUIRED': encodeRequirements(requirements),\n },\n body: {},\n };\n }\n\n /**\n * Verify payment with facilitator\n */\n async function verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse> {\n // If requirements not provided, build default\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.verifyPayment(paymentSignatureHeader, req);\n }\n\n /**\n * Settle payment via facilitator\n */\n async function settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse> {\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.settlePayment(paymentSignatureHeader, req);\n }\n\n return {\n buildRequirements,\n encodeRequirements,\n create402Response,\n verifyPayment,\n settlePayment,\n getPaymentAccept,\n network,\n facilitator,\n };\n}\n","/**\n * Dynamic Pricing for x402\n *\n * Calculate prices based on input length (characters, tokens, etc.)\n * Perfect for LLM/AI endpoints where cost scales with input size.\n *\n * @example\n * ```typescript\n * import { createDynamicPricing } from '@dexterai/x402/server';\n *\n * const pricing = createDynamicPricing({\n * unitSize: 1000, // chars per billing unit\n * ratePerUnit: 0.01, // $0.01 per unit\n * minUsd: 0.01, // floor\n * maxUsd: 10.00, // ceiling (optional)\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '23000', usdAmount: 0.023, quoteHash: 'abc...', units: 2.3 }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for dynamic pricing\n */\nexport interface DynamicPricingConfig {\n /**\n * Characters per billing unit.\n * Example: 1000 means every 1000 chars = 1 unit\n */\n unitSize: number;\n\n /**\n * USD per unit.\n * Example: 0.01 means $0.01 per unit\n */\n ratePerUnit: number;\n\n /**\n * Minimum USD amount (floor).\n * Recommended: 0.01 (practical minimum for settlement)\n * @default 0.01\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * Optional - prevents unexpectedly large bills.\n */\n maxUsd?: number;\n\n /**\n * Rounding mode for unit calculation.\n * - 'ceil': Always round up (fair to seller)\n * - 'floor': Always round down (fair to buyer)\n * - 'round': Standard rounding\n * @default 'ceil'\n */\n roundingMode?: 'ceil' | 'floor' | 'round';\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Price quote returned by calculate()\n */\nexport interface PriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount (for display) */\n usdAmount: number;\n\n /**\n * Quote hash for validation.\n * Includes input + config, so config changes invalidate quotes.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n\n /** Number of billing units */\n units: number;\n\n /** Input length in characters */\n inputLength: number;\n}\n\n/**\n * Dynamic pricing calculator\n */\nexport interface DynamicPricing {\n /** Calculate price from input */\n calculate(input: string): PriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Get pricing config (for display) */\n readonly config: Required<DynamicPricingConfig>;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Simple hash function (browser-compatible, no crypto needed)\n * Uses FNV-1a for speed and simplicity\n */\nfunction simpleHash(str: string): string {\n let hash = 2166136261; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 16777619); // FNV prime\n }\n // Convert to hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Create a dynamic pricing calculator\n */\nexport function createDynamicPricing(config: DynamicPricingConfig): DynamicPricing {\n const fullConfig: Required<DynamicPricingConfig> = {\n unitSize: config.unitSize,\n ratePerUnit: config.ratePerUnit,\n minUsd: config.minUsd ?? 0.01,\n maxUsd: config.maxUsd ?? Infinity,\n roundingMode: config.roundingMode ?? 'ceil',\n decimals: config.decimals ?? 6,\n };\n\n const { unitSize, ratePerUnit, minUsd, maxUsd, roundingMode, decimals } = fullConfig;\n\n // Validate config\n if (unitSize <= 0) throw new Error('unitSize must be positive');\n if (ratePerUnit <= 0) throw new Error('ratePerUnit must be positive');\n if (minUsd < 0) throw new Error('minUsd cannot be negative');\n if (maxUsd < minUsd) throw new Error('maxUsd must be >= minUsd');\n\n /**\n * Build hash input string (input + config)\n * Config is included so pricing changes invalidate old quotes\n */\n function buildHashInput(input: string): string {\n const configStr = JSON.stringify({\n unitSize,\n ratePerUnit,\n minUsd,\n maxUsd: maxUsd === Infinity ? 'none' : maxUsd,\n roundingMode,\n });\n return `${input}|${configStr}`;\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string): PriceQuote {\n const inputLength = input.length;\n\n // Calculate units based on rounding mode\n const rawUnits = inputLength / unitSize;\n let units: number;\n switch (roundingMode) {\n case 'ceil':\n units = Math.ceil(rawUnits);\n break;\n case 'floor':\n units = Math.floor(rawUnits);\n break;\n case 'round':\n units = Math.round(rawUnits);\n break;\n }\n\n // Ensure at least 1 unit if there's any input\n if (inputLength > 0 && units === 0) {\n units = 1;\n }\n\n // Calculate USD amount\n let usdAmount = units * ratePerUnit;\n\n // Apply min/max\n usdAmount = Math.max(minUsd, usdAmount);\n usdAmount = Math.min(maxUsd, usdAmount);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash (includes config)\n const quoteHash = simpleHash(buildHashInput(input));\n\n return {\n amountAtomic,\n usdAmount,\n quoteHash,\n units,\n inputLength,\n };\n }\n\n /**\n * Validate quote hash\n * Returns true if the hash matches (input + config unchanged)\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const expectedHash = simpleHash(buildHashInput(input));\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n config: fullConfig,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Format pricing for display\n * Example: \"from $0.01 per 1,000 chars\"\n */\nexport function formatPricing(config: DynamicPricingConfig): string {\n const rate = config.ratePerUnit.toFixed(2);\n const units = config.unitSize.toLocaleString();\n return `from $${rate} per ${units} chars`;\n}\n\n\n\n","/**\n * Token-Based Pricing for x402\n *\n * Accurate LLM pricing using tiktoken for token counting.\n * Uses real OpenAI model rates for precise cost calculation.\n *\n * @example\n * ```typescript\n * import { createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';\n *\n * const pricing = createTokenPricing({\n * model: 'gpt-4o-mini',\n * // Optional overrides:\n * // minUsd: 0.001,\n * // maxUsd: 50.0,\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '1500', usdAmount: 0.0015, inputTokens: 100, quoteHash: 'abc...' }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\nimport { createHash } from 'crypto';\nimport { encoding_for_model, get_encoding, type TiktokenModel } from 'tiktoken';\n\n// ============================================================================\n// Model Pricing Table\n// ============================================================================\n\n/**\n * Pricing info for a model\n */\nexport interface ModelPricing {\n /** USD per 1M input tokens */\n input: number;\n /** USD per 1M output tokens */\n output: number;\n /** USD per 1M cached input tokens (optional) */\n cached?: number;\n /** Default max output tokens for this model */\n maxTokens: number;\n /** Pricing tier */\n tier: 'fast' | 'standard' | 'reasoning' | 'premium';\n}\n\n/**\n * OpenAI Model Pricing - USD per million tokens\n * Updated: December 2024\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // === FAST TIER (cheapest, fastest) ===\n 'gpt-4o-mini': { \n input: 0.15, output: 0.6, cached: 0.075, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-mini': { \n input: 0.4, output: 1.6, cached: 0.1, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-nano': { \n input: 0.1, output: 0.4, cached: 0.025, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-nano': { \n input: 0.05, output: 0.4, cached: 0.005, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-mini': { \n input: 0.25, output: 2.0, cached: 0.025, \n maxTokens: 8192, tier: 'fast' \n },\n\n // === STANDARD TIER (balanced) ===\n 'gpt-4o': { \n input: 2.5, output: 10.0, cached: 1.25, \n maxTokens: 4096, tier: 'standard' \n },\n 'gpt-4.1': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 8192, tier: 'standard' \n },\n 'gpt-5': { \n input: 1.25, output: 10.0, cached: 0.125, \n maxTokens: 8192, tier: 'standard' \n },\n\n // === REASONING TIER (o-series) ===\n 'o1-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o4-mini': { \n input: 1.1, output: 4.4, cached: 0.275, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n 'o1': { \n input: 15.0, output: 60.0, cached: 7.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n\n // === PREMIUM TIER (expensive, specialized) ===\n 'o3-pro': { \n input: 20.0, output: 80.0, \n maxTokens: 32768, tier: 'premium' \n },\n 'o1-pro': { \n input: 150.0, output: 600.0, \n maxTokens: 32768, tier: 'premium' \n },\n};\n\n// Default model for fallback\nconst DEFAULT_MODEL = 'gpt-4o-mini';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for token-based pricing\n */\nexport interface TokenPricingConfig {\n /**\n * Model to use for pricing.\n * Must be a key in MODEL_PRICING or will fallback to gpt-4o-mini.\n */\n model?: string;\n\n /**\n * Minimum USD amount (floor).\n * @default 0.001\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * @default 50.0\n */\n maxUsd?: number;\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Token price quote\n */\nexport interface TokenPriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount */\n usdAmount: number;\n\n /** Number of input tokens */\n inputTokens: number;\n\n /** Model used for pricing */\n model: string;\n\n /** Pricing tier */\n tier: string;\n\n /** Input rate per million tokens */\n inputRatePerMillion: number;\n\n /** Output rate per million tokens */\n outputRatePerMillion: number;\n\n /** Max output tokens for this model */\n maxOutputTokens: number;\n\n /**\n * Quote hash for validation.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n}\n\n/**\n * Token pricing calculator\n */\nexport interface TokenPricing {\n /** Calculate price from input text */\n calculate(input: string, systemPrompt?: string): TokenPriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Count tokens in a string */\n countTokens(input: string): number;\n\n /** Get pricing config */\n readonly config: Required<TokenPricingConfig>;\n\n /** Get model info */\n readonly modelInfo: ModelPricing;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Get tiktoken encoding for a model.\n * Falls back to cl100k_base for unknown models.\n */\nfunction getEncodingForModel(model: string) {\n try {\n return encoding_for_model(model as TiktokenModel);\n } catch {\n // Fall back to cl100k_base (GPT-4/4o family encoding)\n return get_encoding('cl100k_base');\n }\n}\n\n/**\n * Count tokens in a string using tiktoken.\n */\nexport function countTokens(text: string, model: string = DEFAULT_MODEL): number {\n const encoding = getEncodingForModel(model);\n try {\n const tokens = encoding.encode(text);\n return tokens.length;\n } finally {\n encoding.free();\n }\n}\n\n/**\n * Generate a hash of the prompt + pricing config for validation.\n */\nfunction generateQuoteHash(prompt: string, model: string, rate: number, tokens: number): string {\n const configString = JSON.stringify({ model, rate, tokens });\n return createHash('sha256').update(prompt + configString).digest('hex').slice(0, 16);\n}\n\n/**\n * Create a token-based pricing calculator\n */\nexport function createTokenPricing(config: TokenPricingConfig = {}): TokenPricing {\n const model = config.model && MODEL_PRICING[config.model] ? config.model : DEFAULT_MODEL;\n const modelInfo = MODEL_PRICING[model];\n\n const fullConfig: Required<TokenPricingConfig> = {\n model,\n minUsd: config.minUsd ?? 0.001,\n maxUsd: config.maxUsd ?? 50.0,\n decimals: config.decimals ?? 6,\n };\n\n const { minUsd, maxUsd, decimals } = fullConfig;\n\n /**\n * Count tokens in text\n */\n function countTokensInternal(input: string): number {\n return countTokens(input, model);\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string, systemPrompt?: string): TokenPriceQuote {\n // Count input tokens (prompt + system prompt if provided)\n const fullInput = systemPrompt ? `${systemPrompt}\\n\\n${input}` : input;\n const inputTokens = countTokensInternal(fullInput);\n\n // Calculate USD cost based on input tokens only\n // Price = (inputTokens / 1,000,000) × inputRate\n let usdAmount = (inputTokens / 1_000_000) * modelInfo.input;\n\n // Apply min/max caps\n usdAmount = Math.max(usdAmount, minUsd);\n usdAmount = Math.min(usdAmount, maxUsd);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash for validation\n const quoteHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n\n return {\n amountAtomic,\n usdAmount,\n inputTokens,\n model,\n tier: modelInfo.tier,\n inputRatePerMillion: modelInfo.input,\n outputRatePerMillion: modelInfo.output,\n maxOutputTokens: modelInfo.maxTokens,\n quoteHash,\n };\n }\n\n /**\n * Validate quote hash\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const inputTokens = countTokensInternal(input);\n const expectedHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n countTokens: countTokensInternal,\n config: fullConfig,\n modelInfo,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Get list of available models with their pricing.\n */\nexport function getAvailableModels(): Array<{\n model: string;\n inputRate: number;\n outputRate: number;\n maxTokens: number;\n tier: string;\n}> {\n return Object.entries(MODEL_PRICING)\n .map(([model, pricing]) => ({\n model,\n inputRate: pricing.input,\n outputRate: pricing.output,\n maxTokens: pricing.maxTokens,\n tier: pricing.tier,\n }))\n .sort((a, b) => {\n // Sort by tier, then by input rate\n const tierOrder = { fast: 0, standard: 1, reasoning: 2, premium: 3 };\n const tierDiff = tierOrder[a.tier as keyof typeof tierOrder] - tierOrder[b.tier as keyof typeof tierOrder];\n if (tierDiff !== 0) return tierDiff;\n return a.inputRate - b.inputRate;\n });\n}\n\n/**\n * Check if a model exists in our pricing.\n */\nexport function isValidModel(model: string): boolean {\n return model in MODEL_PRICING;\n}\n\n/**\n * Format token pricing for display\n * Example: \"$0.15 per 1M tokens (gpt-4o-mini)\"\n */\nexport function formatTokenPricing(model: string = DEFAULT_MODEL): string {\n const pricing = MODEL_PRICING[model] ?? MODEL_PRICING[DEFAULT_MODEL];\n const actualModel = MODEL_PRICING[model] ? model : DEFAULT_MODEL;\n return `$${pricing.input.toFixed(2)} per 1M tokens (${actualModel})`;\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/server/index.ts","../../src/types.ts","../../src/utils.ts","../../src/server/facilitator-client.ts","../../src/server/x402-server.ts","../../src/server/dynamic-pricing.ts","../../src/server/token-pricing.ts"],"sourcesContent":["/**\n * @dexterai/x402 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Works with any x402 v2 facilitator.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * // Create server for Solana payments\n * const solanaServer = createX402Server({\n * payTo: 'YourSolanaAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Create server for Base payments\n * const baseServer = createX402Server({\n * payTo: '0xYourEvmAddress...',\n * network: 'eip155:8453',\n * asset: { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6 },\n * });\n *\n * // In your Express handler:\n * app.post('/protected', async (req, res) => {\n * const paymentSig = req.headers['payment-signature'];\n *\n * if (!paymentSig) {\n * const requirements = await solanaServer.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: req.originalUrl,\n * });\n * res.setHeader('PAYMENT-REQUIRED', solanaServer.encodeRequirements(requirements));\n * return res.status(402).json({});\n * }\n *\n * const result = await solanaServer.settlePayment(paymentSig);\n * if (!result.success) {\n * return res.status(402).json({ error: result.errorReason });\n * }\n *\n * res.json({ data: 'protected content', transaction: result.transaction });\n * });\n * ```\n */\n\nexport { createX402Server } from './x402-server';\nexport type {\n X402ServerConfig,\n X402Server,\n BuildRequirementsOptions,\n AssetConfig,\n} from './x402-server';\n\nexport { FacilitatorClient, type SupportedKind, type SupportedResponse } from './facilitator-client';\n\n// Dynamic pricing (character-based)\nexport { createDynamicPricing, formatPricing } from './dynamic-pricing';\nexport type { DynamicPricingConfig, DynamicPricing, PriceQuote } from './dynamic-pricing';\n\n// Token pricing (LLM-accurate with tiktoken)\nexport { \n createTokenPricing, \n countTokens, \n getAvailableModels, \n isValidModel, \n formatTokenPricing,\n MODEL_PRICING,\n} from './token-pricing';\nexport type { \n TokenPricingConfig, \n TokenPricing, \n TokenPriceQuote, \n ModelPricing,\n} from './token-pricing';\n\n// Re-export types for convenience\nexport type { VerifyResponse, SettleResponse, PaymentRequired, PaymentAccept } from '../types';\nexport { DEXTER_FACILITATOR_URL, SOLANA_MAINNET_NETWORK, BASE_MAINNET_NETWORK, USDC_MINT, USDC_BASE } from '../types';\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 for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\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 (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\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 | 'missing_amount'\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 // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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://api.dexter.cash/api/base/rpc'; // Dexter proxy\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://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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","/**\n * Facilitator Client\n *\n * Communicates with the x402 facilitator for:\n * - /supported - Get supported payment schemes and fee payer addresses\n * - /verify - Verify a payment signature before processing\n * - /settle - Submit the payment for execution\n *\n * Works with any x402 v2 facilitator (Dexter or others).\n */\n\nimport type { PaymentAccept, PaymentSignature, VerifyResponse, SettleResponse } from '../types';\nimport { DEXTER_FACILITATOR_URL } from '../types';\nimport { decodeBase64Json } from '../utils';\n\n/**\n * Supported payment kind from facilitator /supported endpoint\n */\nexport interface SupportedKind {\n x402Version: number;\n scheme: string;\n network: string;\n extra?: {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from facilitator /supported endpoint\n */\nexport interface SupportedResponse {\n kinds: SupportedKind[];\n}\n\n/**\n * Client for communicating with an x402 v2 facilitator\n */\nexport class FacilitatorClient {\n private facilitatorUrl: string;\n private cachedSupported: SupportedResponse | null = null;\n private cacheTime: number = 0;\n private readonly CACHE_TTL_MS = 60_000; // 1 minute cache\n\n constructor(facilitatorUrl: string = DEXTER_FACILITATOR_URL) {\n this.facilitatorUrl = facilitatorUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Get supported payment kinds from the facilitator\n * Results are cached for 1 minute to reduce network calls\n */\n async getSupported(): Promise<SupportedResponse> {\n const now = Date.now();\n if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {\n return this.cachedSupported;\n }\n\n const response = await fetch(`${this.facilitatorUrl}/supported`);\n if (!response.ok) {\n throw new Error(`Facilitator /supported returned ${response.status}`);\n }\n\n this.cachedSupported = (await response.json()) as SupportedResponse;\n this.cacheTime = now;\n return this.cachedSupported;\n }\n\n /**\n * Get the fee payer address for a specific network\n *\n * @param network - CAIP-2 network identifier\n * @returns Fee payer address\n */\n async getFeePayer(network: string): Promise<string> {\n const supported = await this.getSupported();\n\n // Find matching network support (exact match or v2)\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n if (!kind?.extra?.feePayer) {\n throw new Error(\n `Facilitator does not support network \"${network}\" with scheme \"exact\", or feePayer not provided`\n );\n }\n\n return kind.extra.feePayer;\n }\n\n /**\n * Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)\n *\n * @param network - CAIP-2 network identifier\n * @returns Extra data from /supported\n */\n async getNetworkExtra(network: string): Promise<SupportedKind['extra']> {\n const supported = await this.getSupported();\n\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n return kind?.extra;\n }\n\n /**\n * Verify a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Verification response\n */\n async verifyPayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<VerifyResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const verifyPayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(verifyPayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /verify returned ${response.status}:`, errorText);\n return {\n isValid: false,\n invalidReason: `facilitator_error_${response.status}`,\n };\n }\n\n return (await response.json()) as VerifyResponse;\n } catch (error) {\n console.error('Payment verification failed:', error);\n return {\n isValid: false,\n invalidReason: error instanceof Error ? error.message : 'unexpected_verify_error',\n };\n }\n }\n\n /**\n * Settle a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Settlement response with transaction signature on success\n */\n async settlePayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<SettleResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const settlePayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/settle`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(settlePayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /settle returned ${response.status}:`, errorText);\n return {\n success: false,\n network: requirements.network,\n errorReason: `facilitator_error_${response.status}`,\n };\n }\n\n const result = (await response.json()) as SettleResponse;\n return {\n ...result,\n network: requirements.network,\n };\n } catch (error) {\n console.error('Payment settlement failed:', error);\n return {\n success: false,\n network: requirements.network,\n errorReason: error instanceof Error ? error.message : 'unexpected_settle_error',\n };\n }\n }\n}\n","/**\n * x402 v2 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Chain-agnostic - works with Solana, Base, and any x402-compatible network.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * const server = createX402Server({\n * payTo: 'YourAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Handle 402 responses\n * if (!paymentSignature) {\n * const requirements = await server.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: '/api/protected',\n * });\n * res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));\n * res.status(402).json({});\n * return;\n * }\n *\n * // Verify and settle\n * const verify = await server.verifyPayment(paymentSignature);\n * if (!verify.isValid) throw new Error(verify.invalidReason);\n *\n * const settle = await server.settlePayment(paymentSignature);\n * if (!settle.success) throw new Error(settle.errorReason);\n *\n * // Payment successful!\n * res.json({ transaction: settle.transaction });\n * ```\n */\n\nimport type {\n PaymentRequired,\n PaymentAccept,\n ResourceInfo,\n AcceptsExtra,\n VerifyResponse,\n SettleResponse,\n} from '../types';\nimport {\n SOLANA_MAINNET_NETWORK,\n USDC_MINT,\n DEXTER_FACILITATOR_URL,\n} from '../types';\nimport { FacilitatorClient, type SupportedKind } from './facilitator-client';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Asset configuration\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n}\n\n/**\n * Server configuration\n */\nexport interface X402ServerConfig {\n /** Address to receive payments */\n payTo: string;\n /** Facilitator URL (defaults to Dexter) */\n facilitatorUrl?: string;\n /** CAIP-2 network identifier */\n network?: string;\n /** Asset configuration (defaults to USDC) */\n asset?: AssetConfig;\n /** Default payment timeout in seconds */\n defaultTimeoutSeconds?: number;\n}\n\n/**\n * Options for building payment requirements\n */\nexport interface BuildRequirementsOptions {\n /** Amount in atomic units (e.g., '50000' for 0.05 USDC) */\n amountAtomic: string;\n /** Full URL of the resource */\n resourceUrl: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the response */\n mimeType?: string;\n /** Override timeout for this request */\n timeoutSeconds?: number;\n}\n\n/**\n * x402 Server interface\n */\nexport interface X402Server {\n /** Build payment requirements (fetches feePayer from facilitator) */\n buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;\n\n /** Encode requirements for PAYMENT-REQUIRED header */\n encodeRequirements(requirements: PaymentRequired): string;\n\n /** Create complete 402 response object */\n create402Response(requirements: PaymentRequired): {\n status: 402;\n headers: { 'PAYMENT-REQUIRED': string };\n body: Record<string, unknown>;\n };\n\n /** Verify payment with facilitator */\n verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse>;\n\n /** Settle payment via facilitator */\n settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse>;\n\n /** Get PaymentAccept for verify/settle */\n getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;\n\n /** Get network this server is configured for */\n readonly network: string;\n\n /** Get facilitator client for advanced usage */\n readonly facilitator: FacilitatorClient;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an x402 server for accepting payments\n */\nexport function createX402Server(config: X402ServerConfig): X402Server {\n const {\n payTo,\n facilitatorUrl = DEXTER_FACILITATOR_URL,\n network = SOLANA_MAINNET_NETWORK,\n asset = { address: USDC_MINT, decimals: 6 },\n defaultTimeoutSeconds = 60,\n } = config;\n\n const facilitator = new FacilitatorClient(facilitatorUrl);\n\n // Cache for network extra data\n let cachedExtra: SupportedKind['extra'] | null = null;\n\n /**\n * Get extra data from facilitator (cached)\n */\n async function getNetworkExtra(): Promise<AcceptsExtra> {\n if (!cachedExtra) {\n cachedExtra = await facilitator.getNetworkExtra(network);\n }\n\n if (!cachedExtra?.feePayer) {\n throw new Error(`Facilitator does not provide feePayer for network \"${network}\"`);\n }\n\n return {\n feePayer: cachedExtra.feePayer,\n decimals: cachedExtra.decimals ?? asset.decimals,\n // Include any additional EIP-712 data for EVM chains\n name: cachedExtra.name,\n version: cachedExtra.version,\n };\n }\n\n /**\n * Build a PaymentAccept structure\n */\n async function getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept> {\n const {\n amountAtomic,\n timeoutSeconds = defaultTimeoutSeconds,\n } = options;\n\n const extra = await getNetworkExtra();\n\n return {\n scheme: 'exact',\n network,\n maxAmountRequired: amountAtomic,\n asset: asset.address,\n payTo,\n maxTimeoutSeconds: timeoutSeconds,\n extra,\n };\n }\n\n /**\n * Build payment requirements for a 402 response\n */\n async function buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired> {\n const {\n resourceUrl,\n description,\n mimeType = 'application/json',\n } = options;\n\n const resource: ResourceInfo = {\n url: resourceUrl,\n description,\n mimeType,\n };\n\n const accept = await getPaymentAccept(options);\n\n return {\n x402Version: 2,\n resource,\n accepts: [accept],\n error: 'Payment required',\n };\n }\n\n /**\n * Encode requirements for PAYMENT-REQUIRED header\n */\n function encodeRequirements(requirements: PaymentRequired): string {\n return btoa(JSON.stringify(requirements));\n }\n\n /**\n * Create complete 402 response object\n */\n function create402Response(requirements: PaymentRequired) {\n return {\n status: 402 as const,\n headers: {\n 'PAYMENT-REQUIRED': encodeRequirements(requirements),\n },\n body: {},\n };\n }\n\n /**\n * Verify payment with facilitator\n */\n async function verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse> {\n // If requirements not provided, build default\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.verifyPayment(paymentSignatureHeader, req);\n }\n\n /**\n * Settle payment via facilitator\n */\n async function settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse> {\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.settlePayment(paymentSignatureHeader, req);\n }\n\n return {\n buildRequirements,\n encodeRequirements,\n create402Response,\n verifyPayment,\n settlePayment,\n getPaymentAccept,\n network,\n facilitator,\n };\n}\n","/**\n * Dynamic Pricing for x402\n *\n * Calculate prices based on input length (characters, tokens, etc.)\n * Perfect for LLM/AI endpoints where cost scales with input size.\n *\n * @example\n * ```typescript\n * import { createDynamicPricing } from '@dexterai/x402/server';\n *\n * const pricing = createDynamicPricing({\n * unitSize: 1000, // chars per billing unit\n * ratePerUnit: 0.01, // $0.01 per unit\n * minUsd: 0.01, // floor\n * maxUsd: 10.00, // ceiling (optional)\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '23000', usdAmount: 0.023, quoteHash: 'abc...', units: 2.3 }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for dynamic pricing\n */\nexport interface DynamicPricingConfig {\n /**\n * Characters per billing unit.\n * Example: 1000 means every 1000 chars = 1 unit\n */\n unitSize: number;\n\n /**\n * USD per unit.\n * Example: 0.01 means $0.01 per unit\n */\n ratePerUnit: number;\n\n /**\n * Minimum USD amount (floor).\n * Recommended: 0.01 (practical minimum for settlement)\n * @default 0.01\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * Optional - prevents unexpectedly large bills.\n */\n maxUsd?: number;\n\n /**\n * Rounding mode for unit calculation.\n * - 'ceil': Always round up (fair to seller)\n * - 'floor': Always round down (fair to buyer)\n * - 'round': Standard rounding\n * @default 'ceil'\n */\n roundingMode?: 'ceil' | 'floor' | 'round';\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Price quote returned by calculate()\n */\nexport interface PriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount (for display) */\n usdAmount: number;\n\n /**\n * Quote hash for validation.\n * Includes input + config, so config changes invalidate quotes.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n\n /** Number of billing units */\n units: number;\n\n /** Input length in characters */\n inputLength: number;\n}\n\n/**\n * Dynamic pricing calculator\n */\nexport interface DynamicPricing {\n /** Calculate price from input */\n calculate(input: string): PriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Get pricing config (for display) */\n readonly config: Required<DynamicPricingConfig>;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Simple hash function (browser-compatible, no crypto needed)\n * Uses FNV-1a for speed and simplicity\n */\nfunction simpleHash(str: string): string {\n let hash = 2166136261; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 16777619); // FNV prime\n }\n // Convert to hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Create a dynamic pricing calculator\n */\nexport function createDynamicPricing(config: DynamicPricingConfig): DynamicPricing {\n const fullConfig: Required<DynamicPricingConfig> = {\n unitSize: config.unitSize,\n ratePerUnit: config.ratePerUnit,\n minUsd: config.minUsd ?? 0.01,\n maxUsd: config.maxUsd ?? Infinity,\n roundingMode: config.roundingMode ?? 'ceil',\n decimals: config.decimals ?? 6,\n };\n\n const { unitSize, ratePerUnit, minUsd, maxUsd, roundingMode, decimals } = fullConfig;\n\n // Validate config\n if (unitSize <= 0) throw new Error('unitSize must be positive');\n if (ratePerUnit <= 0) throw new Error('ratePerUnit must be positive');\n if (minUsd < 0) throw new Error('minUsd cannot be negative');\n if (maxUsd < minUsd) throw new Error('maxUsd must be >= minUsd');\n\n /**\n * Build hash input string (input + config)\n * Config is included so pricing changes invalidate old quotes\n */\n function buildHashInput(input: string): string {\n const configStr = JSON.stringify({\n unitSize,\n ratePerUnit,\n minUsd,\n maxUsd: maxUsd === Infinity ? 'none' : maxUsd,\n roundingMode,\n });\n return `${input}|${configStr}`;\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string): PriceQuote {\n const inputLength = input.length;\n\n // Calculate units based on rounding mode\n const rawUnits = inputLength / unitSize;\n let units: number;\n switch (roundingMode) {\n case 'ceil':\n units = Math.ceil(rawUnits);\n break;\n case 'floor':\n units = Math.floor(rawUnits);\n break;\n case 'round':\n units = Math.round(rawUnits);\n break;\n }\n\n // Ensure at least 1 unit if there's any input\n if (inputLength > 0 && units === 0) {\n units = 1;\n }\n\n // Calculate USD amount\n let usdAmount = units * ratePerUnit;\n\n // Apply min/max\n usdAmount = Math.max(minUsd, usdAmount);\n usdAmount = Math.min(maxUsd, usdAmount);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash (includes config)\n const quoteHash = simpleHash(buildHashInput(input));\n\n return {\n amountAtomic,\n usdAmount,\n quoteHash,\n units,\n inputLength,\n };\n }\n\n /**\n * Validate quote hash\n * Returns true if the hash matches (input + config unchanged)\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const expectedHash = simpleHash(buildHashInput(input));\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n config: fullConfig,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Format pricing for display\n * Example: \"from $0.01 per 1,000 chars\"\n */\nexport function formatPricing(config: DynamicPricingConfig): string {\n const rate = config.ratePerUnit.toFixed(2);\n const units = config.unitSize.toLocaleString();\n return `from $${rate} per ${units} chars`;\n}\n\n\n\n","/**\n * Token-Based Pricing for x402\n *\n * Accurate LLM pricing using tiktoken for token counting.\n * Uses real OpenAI model rates for precise cost calculation.\n *\n * @example\n * ```typescript\n * import { createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';\n *\n * const pricing = createTokenPricing({\n * model: 'gpt-4o-mini',\n * // Optional overrides:\n * // minUsd: 0.001,\n * // maxUsd: 50.0,\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '1500', usdAmount: 0.0015, inputTokens: 100, quoteHash: 'abc...' }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\nimport { createHash } from 'crypto';\nimport { encoding_for_model, get_encoding, type TiktokenModel } from 'tiktoken';\n\n// ============================================================================\n// Model Pricing Table\n// ============================================================================\n\n/**\n * Pricing info for a model\n */\nexport interface ModelPricing {\n /** USD per 1M input tokens */\n input: number;\n /** USD per 1M output tokens */\n output: number;\n /** USD per 1M cached input tokens (optional) */\n cached?: number;\n /** Default max output tokens for this model */\n maxTokens: number;\n /** Pricing tier */\n tier: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';\n}\n\n/**\n * OpenAI Model Pricing - USD per million tokens\n * Updated: December 2024\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // === FAST TIER (cheapest, fastest) ===\n 'gpt-4o-mini': { \n input: 0.15, output: 0.6, cached: 0.075, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-mini': { \n input: 0.4, output: 1.6, cached: 0.1, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-nano': { \n input: 0.1, output: 0.4, cached: 0.025, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-nano': { \n input: 0.05, output: 0.4, cached: 0.005, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-mini': { \n input: 0.25, output: 2.0, cached: 0.025, \n maxTokens: 8192, tier: 'fast' \n },\n\n // === STANDARD TIER (balanced) ===\n 'gpt-4o': { \n input: 2.5, output: 10.0, cached: 1.25, \n maxTokens: 4096, tier: 'standard' \n },\n 'gpt-4.1': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 8192, tier: 'standard' \n },\n 'gpt-5': { \n input: 1.25, output: 10.0, cached: 0.125, \n maxTokens: 8192, tier: 'standard' \n },\n\n // === REASONING TIER (o-series) ===\n 'o1-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o4-mini': { \n input: 1.1, output: 4.4, cached: 0.275, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n 'o1': { \n input: 15.0, output: 60.0, cached: 7.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n\n // === PREMIUM TIER (expensive, specialized) ===\n 'o3-pro': { \n input: 20.0, output: 80.0, \n maxTokens: 32768, tier: 'premium' \n },\n 'o1-pro': { \n input: 150.0, output: 600.0, \n maxTokens: 32768, tier: 'premium' \n },\n};\n\n// Default model for fallback\nconst DEFAULT_MODEL = 'gpt-4o-mini';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for token-based pricing\n */\nexport interface TokenPricingConfig {\n /**\n * Model name. Used for display and to look up built-in pricing.\n * If not in MODEL_PRICING and no custom rates provided, falls back to gpt-4o-mini rates.\n */\n model?: string;\n\n /**\n * Custom input rate (USD per 1M tokens).\n * Overrides built-in MODEL_PRICING for this model.\n * Use this for Anthropic, Gemini, Mistral, or any custom model.\n */\n inputRate?: number;\n\n /**\n * Custom output rate (USD per 1M tokens).\n * Optional - used for display/info purposes.\n */\n outputRate?: number;\n\n /**\n * Custom max output tokens.\n * @default 4096\n */\n maxTokens?: number;\n\n /**\n * Custom tier label.\n * @default 'custom'\n */\n tier?: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';\n\n /**\n * Custom tokenizer function.\n * If not provided, uses tiktoken (cl100k_base encoding).\n * Use this for models with different tokenization (e.g., Llama, Mistral).\n * \n * @example\n * tokenizer: (text) => llamaTokenizer.encode(text).length\n */\n tokenizer?: (text: string) => number;\n\n /**\n * Minimum USD amount (floor).\n * @default 0.001\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * @default 50.0\n */\n maxUsd?: number;\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Token price quote\n */\nexport interface TokenPriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount */\n usdAmount: number;\n\n /** Number of input tokens */\n inputTokens: number;\n\n /** Model used for pricing */\n model: string;\n\n /** Pricing tier */\n tier: string;\n\n /** Input rate per million tokens */\n inputRatePerMillion: number;\n\n /** Output rate per million tokens */\n outputRatePerMillion: number;\n\n /** Max output tokens for this model */\n maxOutputTokens: number;\n\n /**\n * Quote hash for validation.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n}\n\n/**\n * Token pricing calculator\n */\nexport interface TokenPricing {\n /** Calculate price from input text */\n calculate(input: string, systemPrompt?: string): TokenPriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Count tokens in a string */\n countTokens(input: string): number;\n\n /** Get pricing config */\n readonly config: Required<TokenPricingConfig>;\n\n /** Get model info */\n readonly modelInfo: ModelPricing;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Get tiktoken encoding for a model.\n * Falls back to cl100k_base for unknown models.\n */\nfunction getEncodingForModel(model: string) {\n try {\n return encoding_for_model(model as TiktokenModel);\n } catch {\n // Fall back to cl100k_base (GPT-4/4o family encoding)\n return get_encoding('cl100k_base');\n }\n}\n\n/**\n * Count tokens in a string using tiktoken.\n */\nexport function countTokens(text: string, model: string = DEFAULT_MODEL): number {\n const encoding = getEncodingForModel(model);\n try {\n const tokens = encoding.encode(text);\n return tokens.length;\n } finally {\n encoding.free();\n }\n}\n\n/**\n * Generate a hash of the prompt + pricing config for validation.\n */\nfunction generateQuoteHash(prompt: string, model: string, rate: number, tokens: number): string {\n const configString = JSON.stringify({ model, rate, tokens });\n return createHash('sha256').update(prompt + configString).digest('hex').slice(0, 16);\n}\n\n/**\n * Create a token-based pricing calculator\n */\nexport function createTokenPricing(config: TokenPricingConfig = {}): TokenPricing {\n // Determine model name\n const model = config.model ?? DEFAULT_MODEL;\n \n // Get built-in pricing if available, otherwise use custom or default\n const builtInPricing = MODEL_PRICING[model];\n \n // Build effective model info (custom rates override built-in)\n const modelInfo: ModelPricing = {\n input: config.inputRate ?? builtInPricing?.input ?? MODEL_PRICING[DEFAULT_MODEL].input,\n output: config.outputRate ?? builtInPricing?.output ?? MODEL_PRICING[DEFAULT_MODEL].output,\n maxTokens: config.maxTokens ?? builtInPricing?.maxTokens ?? 4096,\n tier: config.tier ?? builtInPricing?.tier ?? 'custom',\n };\n\n // Custom tokenizer or default tiktoken\n const customTokenizer = config.tokenizer;\n\n const fullConfig: Required<TokenPricingConfig> = {\n model,\n inputRate: modelInfo.input,\n outputRate: modelInfo.output,\n maxTokens: modelInfo.maxTokens,\n tier: modelInfo.tier,\n tokenizer: customTokenizer ?? ((text: string) => countTokens(text, model)),\n minUsd: config.minUsd ?? 0.001,\n maxUsd: config.maxUsd ?? 50.0,\n decimals: config.decimals ?? 6,\n };\n\n const { minUsd, maxUsd, decimals } = fullConfig;\n\n /**\n * Count tokens in text (uses custom tokenizer if provided)\n */\n function countTokensInternal(input: string): number {\n if (customTokenizer) {\n return customTokenizer(input);\n }\n return countTokens(input, model);\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string, systemPrompt?: string): TokenPriceQuote {\n // Count input tokens (prompt + system prompt if provided)\n const fullInput = systemPrompt ? `${systemPrompt}\\n\\n${input}` : input;\n const inputTokens = countTokensInternal(fullInput);\n\n // Calculate USD cost based on input tokens only\n // Price = (inputTokens / 1,000,000) × inputRate\n let usdAmount = (inputTokens / 1_000_000) * modelInfo.input;\n\n // Apply min/max caps\n usdAmount = Math.max(usdAmount, minUsd);\n usdAmount = Math.min(usdAmount, maxUsd);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash for validation\n const quoteHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n\n return {\n amountAtomic,\n usdAmount,\n inputTokens,\n model,\n tier: modelInfo.tier,\n inputRatePerMillion: modelInfo.input,\n outputRatePerMillion: modelInfo.output,\n maxOutputTokens: modelInfo.maxTokens,\n quoteHash,\n };\n }\n\n /**\n * Validate quote hash\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const inputTokens = countTokensInternal(input);\n const expectedHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n countTokens: countTokensInternal,\n config: fullConfig,\n modelInfo,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Get list of available models with their pricing.\n */\nexport function getAvailableModels(): Array<{\n model: string;\n inputRate: number;\n outputRate: number;\n maxTokens: number;\n tier: string;\n}> {\n return Object.entries(MODEL_PRICING)\n .map(([model, pricing]) => ({\n model,\n inputRate: pricing.input,\n outputRate: pricing.output,\n maxTokens: pricing.maxTokens,\n tier: pricing.tier,\n }))\n .sort((a, b) => {\n // Sort by tier, then by input rate\n const tierOrder = { fast: 0, standard: 1, reasoning: 2, premium: 3 };\n const tierDiff = tierOrder[a.tier as keyof typeof tierOrder] - tierOrder[b.tier as keyof typeof tierOrder];\n if (tierDiff !== 0) return tierDiff;\n return a.inputRate - b.inputRate;\n });\n}\n\n/**\n * Check if a model exists in our pricing.\n */\nexport function isValidModel(model: string): boolean {\n return model in MODEL_PRICING;\n}\n\n/**\n * Format token pricing for display\n * Example: \"$0.15 per 1M tokens (gpt-4o-mini)\"\n */\nexport function formatTokenPricing(model: string = DEFAULT_MODEL): string {\n const pricing = MODEL_PRICING[model] ?? MODEL_PRICING[DEFAULT_MODEL];\n const actualModel = MODEL_PRICING[model] ? model : DEFAULT_MODEL;\n return `$${pricing.input.toFixed(2)} per 1M tokens (${actualModel})`;\n}\n\n"],"mappings":"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","names":[]}
@@ -330,7 +330,7 @@ interface ModelPricing {
330
330
  /** Default max output tokens for this model */
331
331
  maxTokens: number;
332
332
  /** Pricing tier */
333
- tier: 'fast' | 'standard' | 'reasoning' | 'premium';
333
+ tier: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';
334
334
  }
335
335
  /**
336
336
  * OpenAI Model Pricing - USD per million tokens
@@ -342,10 +342,40 @@ declare const MODEL_PRICING: Record<string, ModelPricing>;
342
342
  */
343
343
  interface TokenPricingConfig {
344
344
  /**
345
- * Model to use for pricing.
346
- * Must be a key in MODEL_PRICING or will fallback to gpt-4o-mini.
345
+ * Model name. Used for display and to look up built-in pricing.
346
+ * If not in MODEL_PRICING and no custom rates provided, falls back to gpt-4o-mini rates.
347
347
  */
348
348
  model?: string;
349
+ /**
350
+ * Custom input rate (USD per 1M tokens).
351
+ * Overrides built-in MODEL_PRICING for this model.
352
+ * Use this for Anthropic, Gemini, Mistral, or any custom model.
353
+ */
354
+ inputRate?: number;
355
+ /**
356
+ * Custom output rate (USD per 1M tokens).
357
+ * Optional - used for display/info purposes.
358
+ */
359
+ outputRate?: number;
360
+ /**
361
+ * Custom max output tokens.
362
+ * @default 4096
363
+ */
364
+ maxTokens?: number;
365
+ /**
366
+ * Custom tier label.
367
+ * @default 'custom'
368
+ */
369
+ tier?: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';
370
+ /**
371
+ * Custom tokenizer function.
372
+ * If not provided, uses tiktoken (cl100k_base encoding).
373
+ * Use this for models with different tokenization (e.g., Llama, Mistral).
374
+ *
375
+ * @example
376
+ * tokenizer: (text) => llamaTokenizer.encode(text).length
377
+ */
378
+ tokenizer?: (text: string) => number;
349
379
  /**
350
380
  * Minimum USD amount (floor).
351
381
  * @default 0.001
@@ -330,7 +330,7 @@ interface ModelPricing {
330
330
  /** Default max output tokens for this model */
331
331
  maxTokens: number;
332
332
  /** Pricing tier */
333
- tier: 'fast' | 'standard' | 'reasoning' | 'premium';
333
+ tier: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';
334
334
  }
335
335
  /**
336
336
  * OpenAI Model Pricing - USD per million tokens
@@ -342,10 +342,40 @@ declare const MODEL_PRICING: Record<string, ModelPricing>;
342
342
  */
343
343
  interface TokenPricingConfig {
344
344
  /**
345
- * Model to use for pricing.
346
- * Must be a key in MODEL_PRICING or will fallback to gpt-4o-mini.
345
+ * Model name. Used for display and to look up built-in pricing.
346
+ * If not in MODEL_PRICING and no custom rates provided, falls back to gpt-4o-mini rates.
347
347
  */
348
348
  model?: string;
349
+ /**
350
+ * Custom input rate (USD per 1M tokens).
351
+ * Overrides built-in MODEL_PRICING for this model.
352
+ * Use this for Anthropic, Gemini, Mistral, or any custom model.
353
+ */
354
+ inputRate?: number;
355
+ /**
356
+ * Custom output rate (USD per 1M tokens).
357
+ * Optional - used for display/info purposes.
358
+ */
359
+ outputRate?: number;
360
+ /**
361
+ * Custom max output tokens.
362
+ * @default 4096
363
+ */
364
+ maxTokens?: number;
365
+ /**
366
+ * Custom tier label.
367
+ * @default 'custom'
368
+ */
369
+ tier?: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';
370
+ /**
371
+ * Custom tokenizer function.
372
+ * If not provided, uses tiktoken (cl100k_base encoding).
373
+ * Use this for models with different tokenization (e.g., Llama, Mistral).
374
+ *
375
+ * @example
376
+ * tokenizer: (text) => llamaTokenizer.encode(text).length
377
+ */
378
+ tokenizer?: (text: string) => number;
349
379
  /**
350
380
  * Minimum USD amount (floor).
351
381
  * @default 0.001
@@ -467,16 +467,31 @@ function generateQuoteHash(prompt, model, rate, tokens) {
467
467
  return createHash("sha256").update(prompt + configString).digest("hex").slice(0, 16);
468
468
  }
469
469
  function createTokenPricing(config = {}) {
470
- const model = config.model && MODEL_PRICING[config.model] ? config.model : DEFAULT_MODEL;
471
- const modelInfo = MODEL_PRICING[model];
470
+ const model = config.model ?? DEFAULT_MODEL;
471
+ const builtInPricing = MODEL_PRICING[model];
472
+ const modelInfo = {
473
+ input: config.inputRate ?? builtInPricing?.input ?? MODEL_PRICING[DEFAULT_MODEL].input,
474
+ output: config.outputRate ?? builtInPricing?.output ?? MODEL_PRICING[DEFAULT_MODEL].output,
475
+ maxTokens: config.maxTokens ?? builtInPricing?.maxTokens ?? 4096,
476
+ tier: config.tier ?? builtInPricing?.tier ?? "custom"
477
+ };
478
+ const customTokenizer = config.tokenizer;
472
479
  const fullConfig = {
473
480
  model,
481
+ inputRate: modelInfo.input,
482
+ outputRate: modelInfo.output,
483
+ maxTokens: modelInfo.maxTokens,
484
+ tier: modelInfo.tier,
485
+ tokenizer: customTokenizer ?? ((text) => countTokens(text, model)),
474
486
  minUsd: config.minUsd ?? 1e-3,
475
487
  maxUsd: config.maxUsd ?? 50,
476
488
  decimals: config.decimals ?? 6
477
489
  };
478
490
  const { minUsd, maxUsd, decimals } = fullConfig;
479
491
  function countTokensInternal(input) {
492
+ if (customTokenizer) {
493
+ return customTokenizer(input);
494
+ }
480
495
  return countTokens(input, model);
481
496
  }
482
497
  function calculate(input, systemPrompt) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts","../../src/utils.ts","../../src/server/facilitator-client.ts","../../src/server/x402-server.ts","../../src/server/dynamic-pricing.ts","../../src/server/token-pricing.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 for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\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 (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\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 | 'missing_amount'\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 // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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://api.dexter.cash/api/base/rpc'; // Dexter proxy\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://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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","/**\n * Facilitator Client\n *\n * Communicates with the x402 facilitator for:\n * - /supported - Get supported payment schemes and fee payer addresses\n * - /verify - Verify a payment signature before processing\n * - /settle - Submit the payment for execution\n *\n * Works with any x402 v2 facilitator (Dexter or others).\n */\n\nimport type { PaymentAccept, PaymentSignature, VerifyResponse, SettleResponse } from '../types';\nimport { DEXTER_FACILITATOR_URL } from '../types';\nimport { decodeBase64Json } from '../utils';\n\n/**\n * Supported payment kind from facilitator /supported endpoint\n */\nexport interface SupportedKind {\n x402Version: number;\n scheme: string;\n network: string;\n extra?: {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from facilitator /supported endpoint\n */\nexport interface SupportedResponse {\n kinds: SupportedKind[];\n}\n\n/**\n * Client for communicating with an x402 v2 facilitator\n */\nexport class FacilitatorClient {\n private facilitatorUrl: string;\n private cachedSupported: SupportedResponse | null = null;\n private cacheTime: number = 0;\n private readonly CACHE_TTL_MS = 60_000; // 1 minute cache\n\n constructor(facilitatorUrl: string = DEXTER_FACILITATOR_URL) {\n this.facilitatorUrl = facilitatorUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Get supported payment kinds from the facilitator\n * Results are cached for 1 minute to reduce network calls\n */\n async getSupported(): Promise<SupportedResponse> {\n const now = Date.now();\n if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {\n return this.cachedSupported;\n }\n\n const response = await fetch(`${this.facilitatorUrl}/supported`);\n if (!response.ok) {\n throw new Error(`Facilitator /supported returned ${response.status}`);\n }\n\n this.cachedSupported = (await response.json()) as SupportedResponse;\n this.cacheTime = now;\n return this.cachedSupported;\n }\n\n /**\n * Get the fee payer address for a specific network\n *\n * @param network - CAIP-2 network identifier\n * @returns Fee payer address\n */\n async getFeePayer(network: string): Promise<string> {\n const supported = await this.getSupported();\n\n // Find matching network support (exact match or v2)\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n if (!kind?.extra?.feePayer) {\n throw new Error(\n `Facilitator does not support network \"${network}\" with scheme \"exact\", or feePayer not provided`\n );\n }\n\n return kind.extra.feePayer;\n }\n\n /**\n * Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)\n *\n * @param network - CAIP-2 network identifier\n * @returns Extra data from /supported\n */\n async getNetworkExtra(network: string): Promise<SupportedKind['extra']> {\n const supported = await this.getSupported();\n\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n return kind?.extra;\n }\n\n /**\n * Verify a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Verification response\n */\n async verifyPayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<VerifyResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const verifyPayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(verifyPayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /verify returned ${response.status}:`, errorText);\n return {\n isValid: false,\n invalidReason: `facilitator_error_${response.status}`,\n };\n }\n\n return (await response.json()) as VerifyResponse;\n } catch (error) {\n console.error('Payment verification failed:', error);\n return {\n isValid: false,\n invalidReason: error instanceof Error ? error.message : 'unexpected_verify_error',\n };\n }\n }\n\n /**\n * Settle a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Settlement response with transaction signature on success\n */\n async settlePayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<SettleResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const settlePayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/settle`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(settlePayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /settle returned ${response.status}:`, errorText);\n return {\n success: false,\n network: requirements.network,\n errorReason: `facilitator_error_${response.status}`,\n };\n }\n\n const result = (await response.json()) as SettleResponse;\n return {\n ...result,\n network: requirements.network,\n };\n } catch (error) {\n console.error('Payment settlement failed:', error);\n return {\n success: false,\n network: requirements.network,\n errorReason: error instanceof Error ? error.message : 'unexpected_settle_error',\n };\n }\n }\n}\n","/**\n * x402 v2 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Chain-agnostic - works with Solana, Base, and any x402-compatible network.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * const server = createX402Server({\n * payTo: 'YourAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Handle 402 responses\n * if (!paymentSignature) {\n * const requirements = await server.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: '/api/protected',\n * });\n * res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));\n * res.status(402).json({});\n * return;\n * }\n *\n * // Verify and settle\n * const verify = await server.verifyPayment(paymentSignature);\n * if (!verify.isValid) throw new Error(verify.invalidReason);\n *\n * const settle = await server.settlePayment(paymentSignature);\n * if (!settle.success) throw new Error(settle.errorReason);\n *\n * // Payment successful!\n * res.json({ transaction: settle.transaction });\n * ```\n */\n\nimport type {\n PaymentRequired,\n PaymentAccept,\n ResourceInfo,\n AcceptsExtra,\n VerifyResponse,\n SettleResponse,\n} from '../types';\nimport {\n SOLANA_MAINNET_NETWORK,\n USDC_MINT,\n DEXTER_FACILITATOR_URL,\n} from '../types';\nimport { FacilitatorClient, type SupportedKind } from './facilitator-client';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Asset configuration\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n}\n\n/**\n * Server configuration\n */\nexport interface X402ServerConfig {\n /** Address to receive payments */\n payTo: string;\n /** Facilitator URL (defaults to Dexter) */\n facilitatorUrl?: string;\n /** CAIP-2 network identifier */\n network?: string;\n /** Asset configuration (defaults to USDC) */\n asset?: AssetConfig;\n /** Default payment timeout in seconds */\n defaultTimeoutSeconds?: number;\n}\n\n/**\n * Options for building payment requirements\n */\nexport interface BuildRequirementsOptions {\n /** Amount in atomic units (e.g., '50000' for 0.05 USDC) */\n amountAtomic: string;\n /** Full URL of the resource */\n resourceUrl: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the response */\n mimeType?: string;\n /** Override timeout for this request */\n timeoutSeconds?: number;\n}\n\n/**\n * x402 Server interface\n */\nexport interface X402Server {\n /** Build payment requirements (fetches feePayer from facilitator) */\n buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;\n\n /** Encode requirements for PAYMENT-REQUIRED header */\n encodeRequirements(requirements: PaymentRequired): string;\n\n /** Create complete 402 response object */\n create402Response(requirements: PaymentRequired): {\n status: 402;\n headers: { 'PAYMENT-REQUIRED': string };\n body: Record<string, unknown>;\n };\n\n /** Verify payment with facilitator */\n verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse>;\n\n /** Settle payment via facilitator */\n settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse>;\n\n /** Get PaymentAccept for verify/settle */\n getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;\n\n /** Get network this server is configured for */\n readonly network: string;\n\n /** Get facilitator client for advanced usage */\n readonly facilitator: FacilitatorClient;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an x402 server for accepting payments\n */\nexport function createX402Server(config: X402ServerConfig): X402Server {\n const {\n payTo,\n facilitatorUrl = DEXTER_FACILITATOR_URL,\n network = SOLANA_MAINNET_NETWORK,\n asset = { address: USDC_MINT, decimals: 6 },\n defaultTimeoutSeconds = 60,\n } = config;\n\n const facilitator = new FacilitatorClient(facilitatorUrl);\n\n // Cache for network extra data\n let cachedExtra: SupportedKind['extra'] | null = null;\n\n /**\n * Get extra data from facilitator (cached)\n */\n async function getNetworkExtra(): Promise<AcceptsExtra> {\n if (!cachedExtra) {\n cachedExtra = await facilitator.getNetworkExtra(network);\n }\n\n if (!cachedExtra?.feePayer) {\n throw new Error(`Facilitator does not provide feePayer for network \"${network}\"`);\n }\n\n return {\n feePayer: cachedExtra.feePayer,\n decimals: cachedExtra.decimals ?? asset.decimals,\n // Include any additional EIP-712 data for EVM chains\n name: cachedExtra.name,\n version: cachedExtra.version,\n };\n }\n\n /**\n * Build a PaymentAccept structure\n */\n async function getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept> {\n const {\n amountAtomic,\n timeoutSeconds = defaultTimeoutSeconds,\n } = options;\n\n const extra = await getNetworkExtra();\n\n return {\n scheme: 'exact',\n network,\n maxAmountRequired: amountAtomic,\n asset: asset.address,\n payTo,\n maxTimeoutSeconds: timeoutSeconds,\n extra,\n };\n }\n\n /**\n * Build payment requirements for a 402 response\n */\n async function buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired> {\n const {\n resourceUrl,\n description,\n mimeType = 'application/json',\n } = options;\n\n const resource: ResourceInfo = {\n url: resourceUrl,\n description,\n mimeType,\n };\n\n const accept = await getPaymentAccept(options);\n\n return {\n x402Version: 2,\n resource,\n accepts: [accept],\n error: 'Payment required',\n };\n }\n\n /**\n * Encode requirements for PAYMENT-REQUIRED header\n */\n function encodeRequirements(requirements: PaymentRequired): string {\n return btoa(JSON.stringify(requirements));\n }\n\n /**\n * Create complete 402 response object\n */\n function create402Response(requirements: PaymentRequired) {\n return {\n status: 402 as const,\n headers: {\n 'PAYMENT-REQUIRED': encodeRequirements(requirements),\n },\n body: {},\n };\n }\n\n /**\n * Verify payment with facilitator\n */\n async function verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse> {\n // If requirements not provided, build default\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.verifyPayment(paymentSignatureHeader, req);\n }\n\n /**\n * Settle payment via facilitator\n */\n async function settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse> {\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.settlePayment(paymentSignatureHeader, req);\n }\n\n return {\n buildRequirements,\n encodeRequirements,\n create402Response,\n verifyPayment,\n settlePayment,\n getPaymentAccept,\n network,\n facilitator,\n };\n}\n","/**\n * Dynamic Pricing for x402\n *\n * Calculate prices based on input length (characters, tokens, etc.)\n * Perfect for LLM/AI endpoints where cost scales with input size.\n *\n * @example\n * ```typescript\n * import { createDynamicPricing } from '@dexterai/x402/server';\n *\n * const pricing = createDynamicPricing({\n * unitSize: 1000, // chars per billing unit\n * ratePerUnit: 0.01, // $0.01 per unit\n * minUsd: 0.01, // floor\n * maxUsd: 10.00, // ceiling (optional)\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '23000', usdAmount: 0.023, quoteHash: 'abc...', units: 2.3 }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for dynamic pricing\n */\nexport interface DynamicPricingConfig {\n /**\n * Characters per billing unit.\n * Example: 1000 means every 1000 chars = 1 unit\n */\n unitSize: number;\n\n /**\n * USD per unit.\n * Example: 0.01 means $0.01 per unit\n */\n ratePerUnit: number;\n\n /**\n * Minimum USD amount (floor).\n * Recommended: 0.01 (practical minimum for settlement)\n * @default 0.01\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * Optional - prevents unexpectedly large bills.\n */\n maxUsd?: number;\n\n /**\n * Rounding mode for unit calculation.\n * - 'ceil': Always round up (fair to seller)\n * - 'floor': Always round down (fair to buyer)\n * - 'round': Standard rounding\n * @default 'ceil'\n */\n roundingMode?: 'ceil' | 'floor' | 'round';\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Price quote returned by calculate()\n */\nexport interface PriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount (for display) */\n usdAmount: number;\n\n /**\n * Quote hash for validation.\n * Includes input + config, so config changes invalidate quotes.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n\n /** Number of billing units */\n units: number;\n\n /** Input length in characters */\n inputLength: number;\n}\n\n/**\n * Dynamic pricing calculator\n */\nexport interface DynamicPricing {\n /** Calculate price from input */\n calculate(input: string): PriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Get pricing config (for display) */\n readonly config: Required<DynamicPricingConfig>;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Simple hash function (browser-compatible, no crypto needed)\n * Uses FNV-1a for speed and simplicity\n */\nfunction simpleHash(str: string): string {\n let hash = 2166136261; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 16777619); // FNV prime\n }\n // Convert to hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Create a dynamic pricing calculator\n */\nexport function createDynamicPricing(config: DynamicPricingConfig): DynamicPricing {\n const fullConfig: Required<DynamicPricingConfig> = {\n unitSize: config.unitSize,\n ratePerUnit: config.ratePerUnit,\n minUsd: config.minUsd ?? 0.01,\n maxUsd: config.maxUsd ?? Infinity,\n roundingMode: config.roundingMode ?? 'ceil',\n decimals: config.decimals ?? 6,\n };\n\n const { unitSize, ratePerUnit, minUsd, maxUsd, roundingMode, decimals } = fullConfig;\n\n // Validate config\n if (unitSize <= 0) throw new Error('unitSize must be positive');\n if (ratePerUnit <= 0) throw new Error('ratePerUnit must be positive');\n if (minUsd < 0) throw new Error('minUsd cannot be negative');\n if (maxUsd < minUsd) throw new Error('maxUsd must be >= minUsd');\n\n /**\n * Build hash input string (input + config)\n * Config is included so pricing changes invalidate old quotes\n */\n function buildHashInput(input: string): string {\n const configStr = JSON.stringify({\n unitSize,\n ratePerUnit,\n minUsd,\n maxUsd: maxUsd === Infinity ? 'none' : maxUsd,\n roundingMode,\n });\n return `${input}|${configStr}`;\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string): PriceQuote {\n const inputLength = input.length;\n\n // Calculate units based on rounding mode\n const rawUnits = inputLength / unitSize;\n let units: number;\n switch (roundingMode) {\n case 'ceil':\n units = Math.ceil(rawUnits);\n break;\n case 'floor':\n units = Math.floor(rawUnits);\n break;\n case 'round':\n units = Math.round(rawUnits);\n break;\n }\n\n // Ensure at least 1 unit if there's any input\n if (inputLength > 0 && units === 0) {\n units = 1;\n }\n\n // Calculate USD amount\n let usdAmount = units * ratePerUnit;\n\n // Apply min/max\n usdAmount = Math.max(minUsd, usdAmount);\n usdAmount = Math.min(maxUsd, usdAmount);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash (includes config)\n const quoteHash = simpleHash(buildHashInput(input));\n\n return {\n amountAtomic,\n usdAmount,\n quoteHash,\n units,\n inputLength,\n };\n }\n\n /**\n * Validate quote hash\n * Returns true if the hash matches (input + config unchanged)\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const expectedHash = simpleHash(buildHashInput(input));\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n config: fullConfig,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Format pricing for display\n * Example: \"from $0.01 per 1,000 chars\"\n */\nexport function formatPricing(config: DynamicPricingConfig): string {\n const rate = config.ratePerUnit.toFixed(2);\n const units = config.unitSize.toLocaleString();\n return `from $${rate} per ${units} chars`;\n}\n\n\n\n","/**\n * Token-Based Pricing for x402\n *\n * Accurate LLM pricing using tiktoken for token counting.\n * Uses real OpenAI model rates for precise cost calculation.\n *\n * @example\n * ```typescript\n * import { createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';\n *\n * const pricing = createTokenPricing({\n * model: 'gpt-4o-mini',\n * // Optional overrides:\n * // minUsd: 0.001,\n * // maxUsd: 50.0,\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '1500', usdAmount: 0.0015, inputTokens: 100, quoteHash: 'abc...' }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\nimport { createHash } from 'crypto';\nimport { encoding_for_model, get_encoding, type TiktokenModel } from 'tiktoken';\n\n// ============================================================================\n// Model Pricing Table\n// ============================================================================\n\n/**\n * Pricing info for a model\n */\nexport interface ModelPricing {\n /** USD per 1M input tokens */\n input: number;\n /** USD per 1M output tokens */\n output: number;\n /** USD per 1M cached input tokens (optional) */\n cached?: number;\n /** Default max output tokens for this model */\n maxTokens: number;\n /** Pricing tier */\n tier: 'fast' | 'standard' | 'reasoning' | 'premium';\n}\n\n/**\n * OpenAI Model Pricing - USD per million tokens\n * Updated: December 2024\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // === FAST TIER (cheapest, fastest) ===\n 'gpt-4o-mini': { \n input: 0.15, output: 0.6, cached: 0.075, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-mini': { \n input: 0.4, output: 1.6, cached: 0.1, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-nano': { \n input: 0.1, output: 0.4, cached: 0.025, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-nano': { \n input: 0.05, output: 0.4, cached: 0.005, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-mini': { \n input: 0.25, output: 2.0, cached: 0.025, \n maxTokens: 8192, tier: 'fast' \n },\n\n // === STANDARD TIER (balanced) ===\n 'gpt-4o': { \n input: 2.5, output: 10.0, cached: 1.25, \n maxTokens: 4096, tier: 'standard' \n },\n 'gpt-4.1': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 8192, tier: 'standard' \n },\n 'gpt-5': { \n input: 1.25, output: 10.0, cached: 0.125, \n maxTokens: 8192, tier: 'standard' \n },\n\n // === REASONING TIER (o-series) ===\n 'o1-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o4-mini': { \n input: 1.1, output: 4.4, cached: 0.275, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n 'o1': { \n input: 15.0, output: 60.0, cached: 7.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n\n // === PREMIUM TIER (expensive, specialized) ===\n 'o3-pro': { \n input: 20.0, output: 80.0, \n maxTokens: 32768, tier: 'premium' \n },\n 'o1-pro': { \n input: 150.0, output: 600.0, \n maxTokens: 32768, tier: 'premium' \n },\n};\n\n// Default model for fallback\nconst DEFAULT_MODEL = 'gpt-4o-mini';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for token-based pricing\n */\nexport interface TokenPricingConfig {\n /**\n * Model to use for pricing.\n * Must be a key in MODEL_PRICING or will fallback to gpt-4o-mini.\n */\n model?: string;\n\n /**\n * Minimum USD amount (floor).\n * @default 0.001\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * @default 50.0\n */\n maxUsd?: number;\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Token price quote\n */\nexport interface TokenPriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount */\n usdAmount: number;\n\n /** Number of input tokens */\n inputTokens: number;\n\n /** Model used for pricing */\n model: string;\n\n /** Pricing tier */\n tier: string;\n\n /** Input rate per million tokens */\n inputRatePerMillion: number;\n\n /** Output rate per million tokens */\n outputRatePerMillion: number;\n\n /** Max output tokens for this model */\n maxOutputTokens: number;\n\n /**\n * Quote hash for validation.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n}\n\n/**\n * Token pricing calculator\n */\nexport interface TokenPricing {\n /** Calculate price from input text */\n calculate(input: string, systemPrompt?: string): TokenPriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Count tokens in a string */\n countTokens(input: string): number;\n\n /** Get pricing config */\n readonly config: Required<TokenPricingConfig>;\n\n /** Get model info */\n readonly modelInfo: ModelPricing;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Get tiktoken encoding for a model.\n * Falls back to cl100k_base for unknown models.\n */\nfunction getEncodingForModel(model: string) {\n try {\n return encoding_for_model(model as TiktokenModel);\n } catch {\n // Fall back to cl100k_base (GPT-4/4o family encoding)\n return get_encoding('cl100k_base');\n }\n}\n\n/**\n * Count tokens in a string using tiktoken.\n */\nexport function countTokens(text: string, model: string = DEFAULT_MODEL): number {\n const encoding = getEncodingForModel(model);\n try {\n const tokens = encoding.encode(text);\n return tokens.length;\n } finally {\n encoding.free();\n }\n}\n\n/**\n * Generate a hash of the prompt + pricing config for validation.\n */\nfunction generateQuoteHash(prompt: string, model: string, rate: number, tokens: number): string {\n const configString = JSON.stringify({ model, rate, tokens });\n return createHash('sha256').update(prompt + configString).digest('hex').slice(0, 16);\n}\n\n/**\n * Create a token-based pricing calculator\n */\nexport function createTokenPricing(config: TokenPricingConfig = {}): TokenPricing {\n const model = config.model && MODEL_PRICING[config.model] ? config.model : DEFAULT_MODEL;\n const modelInfo = MODEL_PRICING[model];\n\n const fullConfig: Required<TokenPricingConfig> = {\n model,\n minUsd: config.minUsd ?? 0.001,\n maxUsd: config.maxUsd ?? 50.0,\n decimals: config.decimals ?? 6,\n };\n\n const { minUsd, maxUsd, decimals } = fullConfig;\n\n /**\n * Count tokens in text\n */\n function countTokensInternal(input: string): number {\n return countTokens(input, model);\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string, systemPrompt?: string): TokenPriceQuote {\n // Count input tokens (prompt + system prompt if provided)\n const fullInput = systemPrompt ? `${systemPrompt}\\n\\n${input}` : input;\n const inputTokens = countTokensInternal(fullInput);\n\n // Calculate USD cost based on input tokens only\n // Price = (inputTokens / 1,000,000) × inputRate\n let usdAmount = (inputTokens / 1_000_000) * modelInfo.input;\n\n // Apply min/max caps\n usdAmount = Math.max(usdAmount, minUsd);\n usdAmount = Math.min(usdAmount, maxUsd);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash for validation\n const quoteHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n\n return {\n amountAtomic,\n usdAmount,\n inputTokens,\n model,\n tier: modelInfo.tier,\n inputRatePerMillion: modelInfo.input,\n outputRatePerMillion: modelInfo.output,\n maxOutputTokens: modelInfo.maxTokens,\n quoteHash,\n };\n }\n\n /**\n * Validate quote hash\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const inputTokens = countTokensInternal(input);\n const expectedHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n countTokens: countTokensInternal,\n config: fullConfig,\n modelInfo,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Get list of available models with their pricing.\n */\nexport function getAvailableModels(): Array<{\n model: string;\n inputRate: number;\n outputRate: number;\n maxTokens: number;\n tier: string;\n}> {\n return Object.entries(MODEL_PRICING)\n .map(([model, pricing]) => ({\n model,\n inputRate: pricing.input,\n outputRate: pricing.output,\n maxTokens: pricing.maxTokens,\n tier: pricing.tier,\n }))\n .sort((a, b) => {\n // Sort by tier, then by input rate\n const tierOrder = { fast: 0, standard: 1, reasoning: 2, premium: 3 };\n const tierDiff = tierOrder[a.tier as keyof typeof tierOrder] - tierOrder[b.tier as keyof typeof tierOrder];\n if (tierDiff !== 0) return tierDiff;\n return a.inputRate - b.inputRate;\n });\n}\n\n/**\n * Check if a model exists in our pricing.\n */\nexport function isValidModel(model: string): boolean {\n return model in MODEL_PRICING;\n}\n\n/**\n * Format token pricing for display\n * Example: \"$0.15 per 1M tokens (gpt-4o-mini)\"\n */\nexport function formatTokenPricing(model: string = DEFAULT_MODEL): string {\n const pricing = MODEL_PRICING[model] ?? MODEL_PRICING[DEFAULT_MODEL];\n const actualModel = MODEL_PRICING[model] ? model : DEFAULT_MODEL;\n return `$${pricing.input.toFixed(2)} per 1M tokens (${actualModel})`;\n}\n\n"],"mappings":";AAYO,IAAM,yBAAyB;AAG/B,IAAM,uBAAuB;AAa7B,IAAM,YAAY;AAGlB,IAAM,YAAY;AAOlB,IAAM,yBAAyB;;;AC0K/B,SAAS,iBAAoB,SAAoB;AACtD,SAAO,KAAK,MAAM,KAAK,OAAO,CAAC;AACjC;;;ACzKO,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA,kBAA4C;AAAA,EAC5C,YAAoB;AAAA,EACX,eAAe;AAAA;AAAA,EAEhC,YAAY,iBAAyB,wBAAwB;AAC3D,SAAK,iBAAiB,eAAe,QAAQ,OAAO,EAAE;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAA2C;AAC/C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,KAAK,mBAAmB,MAAM,KAAK,YAAY,KAAK,cAAc;AACpE,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,cAAc,YAAY;AAC/D,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,mCAAmC,SAAS,MAAM,EAAE;AAAA,IACtE;AAEA,SAAK,kBAAmB,MAAM,SAAS,KAAK;AAC5C,SAAK,YAAY;AACjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAY,SAAkC;AAClD,UAAM,YAAY,MAAM,KAAK,aAAa;AAG1C,UAAM,OAAO,UAAU,MAAM;AAAA,MAC3B,CAAC,MACC,EAAE,gBAAgB,KAClB,EAAE,WAAW,WACb,EAAE,YAAY;AAAA,IAClB;AAEA,QAAI,CAAC,MAAM,OAAO,UAAU;AAC1B,YAAM,IAAI;AAAA,QACR,yCAAyC,OAAO;AAAA,MAClD;AAAA,IACF;AAEA,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBAAgB,SAAkD;AACtE,UAAM,YAAY,MAAM,KAAK,aAAa;AAE1C,UAAM,OAAO,UAAU,MAAM;AAAA,MAC3B,CAAC,MACC,EAAE,gBAAgB,KAClB,EAAE,WAAW,WACb,EAAE,YAAY;AAAA,IAClB;AAEA,WAAO,MAAM;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cACJ,wBACA,cACyB;AACzB,QAAI;AACF,YAAM,iBAAiB,iBAAmC,sBAAsB;AAEhF,YAAM,gBAAgB;AAAA,QACpB;AAAA,QACA,qBAAqB;AAAA,MACvB;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,cAAc,WAAW;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,aAAa;AAAA,MACpC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,gBAAQ,MAAM,gCAAgC,SAAS,MAAM,KAAK,SAAS;AAC3E,eAAO;AAAA,UACL,SAAS;AAAA,UACT,eAAe,qBAAqB,SAAS,MAAM;AAAA,QACrD;AAAA,MACF;AAEA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,SAAS,OAAO;AACd,cAAQ,MAAM,gCAAgC,KAAK;AACnD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cACJ,wBACA,cACyB;AACzB,QAAI;AACF,YAAM,iBAAiB,iBAAmC,sBAAsB;AAEhF,YAAM,gBAAgB;AAAA,QACpB;AAAA,QACA,qBAAqB;AAAA,MACvB;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,cAAc,WAAW;AAAA,QAC5D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,aAAa;AAAA,MACpC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,gBAAQ,MAAM,gCAAgC,SAAS,MAAM,KAAK,SAAS;AAC3E,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,aAAa;AAAA,UACtB,aAAa,qBAAqB,SAAS,MAAM;AAAA,QACnD;AAAA,MACF;AAEA,YAAM,SAAU,MAAM,SAAS,KAAK;AACpC,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS,aAAa;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,KAAK;AACjD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,aAAa;AAAA,QACtB,aAAa,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;;;ACpEO,SAAS,iBAAiB,QAAsC;AACrE,QAAM;AAAA,IACJ;AAAA,IACA,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,QAAQ,EAAE,SAAS,WAAW,UAAU,EAAE;AAAA,IAC1C,wBAAwB;AAAA,EAC1B,IAAI;AAEJ,QAAM,cAAc,IAAI,kBAAkB,cAAc;AAGxD,MAAI,cAA6C;AAKjD,iBAAe,kBAAyC;AACtD,QAAI,CAAC,aAAa;AAChB,oBAAc,MAAM,YAAY,gBAAgB,OAAO;AAAA,IACzD;AAEA,QAAI,CAAC,aAAa,UAAU;AAC1B,YAAM,IAAI,MAAM,sDAAsD,OAAO,GAAG;AAAA,IAClF;AAEA,WAAO;AAAA,MACL,UAAU,YAAY;AAAA,MACtB,UAAU,YAAY,YAAY,MAAM;AAAA;AAAA,MAExC,MAAM,YAAY;AAAA,MAClB,SAAS,YAAY;AAAA,IACvB;AAAA,EACF;AAKA,iBAAe,iBAAiB,SAA2D;AACzF,UAAM;AAAA,MACJ;AAAA,MACA,iBAAiB;AAAA,IACnB,IAAI;AAEJ,UAAM,QAAQ,MAAM,gBAAgB;AAEpC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR;AAAA,MACA,mBAAmB;AAAA,MACnB,OAAO,MAAM;AAAA,MACb;AAAA,MACA,mBAAmB;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAKA,iBAAe,kBAAkB,SAA6D;AAC5F,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,WAAW;AAAA,IACb,IAAI;AAEJ,UAAM,WAAyB;AAAA,MAC7B,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,iBAAiB,OAAO;AAE7C,WAAO;AAAA,MACL,aAAa;AAAA,MACb;AAAA,MACA,SAAS,CAAC,MAAM;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,EACF;AAKA,WAAS,mBAAmB,cAAuC;AACjE,WAAO,KAAK,KAAK,UAAU,YAAY,CAAC;AAAA,EAC1C;AAKA,WAAS,kBAAkB,cAA+B;AACxD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,oBAAoB,mBAAmB,YAAY;AAAA,MACrD;AAAA,MACA,MAAM,CAAC;AAAA,IACT;AAAA,EACF;AAKA,iBAAe,cACb,wBACA,cACyB;AAEzB,UAAM,MAAM,gBAAgB,MAAM,iBAAiB;AAAA,MACjD,cAAc;AAAA,MACd,aAAa;AAAA,IACf,CAAC;AAED,WAAO,YAAY,cAAc,wBAAwB,GAAG;AAAA,EAC9D;AAKA,iBAAe,cACb,wBACA,cACyB;AACzB,UAAM,MAAM,gBAAgB,MAAM,iBAAiB;AAAA,MACjD,cAAc;AAAA,MACd,aAAa;AAAA,IACf,CAAC;AAED,WAAO,YAAY,cAAc,wBAAwB,GAAG;AAAA,EAC9D;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxKA,SAAS,WAAW,KAAqB;AACvC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAQ,IAAI,WAAW,CAAC;AACxB,WAAO,KAAK,KAAK,MAAM,QAAQ;AAAA,EACjC;AAEA,UAAQ,SAAS,GAAG,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAClD;AAKO,SAAS,qBAAqB,QAA8C;AACjF,QAAM,aAA6C;AAAA,IACjD,UAAU,OAAO;AAAA,IACjB,aAAa,OAAO;AAAA,IACpB,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO,UAAU;AAAA,IACzB,cAAc,OAAO,gBAAgB;AAAA,IACrC,UAAU,OAAO,YAAY;AAAA,EAC/B;AAEA,QAAM,EAAE,UAAU,aAAa,QAAQ,QAAQ,cAAc,SAAS,IAAI;AAG1E,MAAI,YAAY,EAAG,OAAM,IAAI,MAAM,2BAA2B;AAC9D,MAAI,eAAe,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACpE,MAAI,SAAS,EAAG,OAAM,IAAI,MAAM,2BAA2B;AAC3D,MAAI,SAAS,OAAQ,OAAM,IAAI,MAAM,0BAA0B;AAM/D,WAAS,eAAe,OAAuB;AAC7C,UAAM,YAAY,KAAK,UAAU;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,WAAW,WAAW,SAAS;AAAA,MACvC;AAAA,IACF,CAAC;AACD,WAAO,GAAG,KAAK,IAAI,SAAS;AAAA,EAC9B;AAKA,WAAS,UAAU,OAA2B;AAC5C,UAAM,cAAc,MAAM;AAG1B,UAAM,WAAW,cAAc;AAC/B,QAAI;AACJ,YAAQ,cAAc;AAAA,MACpB,KAAK;AACH,gBAAQ,KAAK,KAAK,QAAQ;AAC1B;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,MAAM,QAAQ;AAC3B;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,MAAM,QAAQ;AAC3B;AAAA,IACJ;AAGA,QAAI,cAAc,KAAK,UAAU,GAAG;AAClC,cAAQ;AAAA,IACV;AAGA,QAAI,YAAY,QAAQ;AAGxB,gBAAY,KAAK,IAAI,QAAQ,SAAS;AACtC,gBAAY,KAAK,IAAI,QAAQ,SAAS;AAGtC,UAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,UAAM,eAAe,KAAK,MAAM,YAAY,UAAU,EAAE,SAAS;AAGjE,UAAM,YAAY,WAAW,eAAe,KAAK,CAAC;AAElD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAMA,WAAS,cAAc,OAAe,WAA4B;AAChE,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,eAAe,WAAW,eAAe,KAAK,CAAC;AACrD,WAAO,iBAAiB;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV;AACF;AAUO,SAAS,cAAc,QAAsC;AAClE,QAAM,OAAO,OAAO,YAAY,QAAQ,CAAC;AACzC,QAAM,QAAQ,OAAO,SAAS,eAAe;AAC7C,SAAO,SAAS,IAAI,QAAQ,KAAK;AACnC;;;AC3NA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB,oBAAwC;AA0B9D,IAAM,gBAA8C;AAAA;AAAA,EAEzD,eAAe;AAAA,IACb,OAAO;AAAA,IAAM,QAAQ;AAAA,IAAK,QAAQ;AAAA,IAClC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,gBAAgB;AAAA,IACd,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,gBAAgB;AAAA,IACd,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IAAM,QAAQ;AAAA,IAAK,QAAQ;AAAA,IAClC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,cAAc;AAAA,IACZ,OAAO;AAAA,IAAM,QAAQ;AAAA,IAAK,QAAQ;AAAA,IAClC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAM,QAAQ;AAAA,IAClC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IAAM,QAAQ;AAAA,IAAM,QAAQ;AAAA,IACnC,WAAW;AAAA,IAAM,MAAM;AAAA,EACzB;AAAA;AAAA,EAGA,WAAW;AAAA,IACT,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IAAK,QAAQ;AAAA,IAAK,QAAQ;AAAA,IACjC,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA,EACA,MAAM;AAAA,IACJ,OAAO;AAAA,IAAM,QAAQ;AAAA,IAAM,QAAQ;AAAA,IACnC,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA;AAAA,EAGA,UAAU;AAAA,IACR,OAAO;AAAA,IAAM,QAAQ;AAAA,IACrB,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IAAO,QAAQ;AAAA,IACtB,WAAW;AAAA,IAAO,MAAM;AAAA,EAC1B;AACF;AAGA,IAAM,gBAAgB;AAkGtB,SAAS,oBAAoB,OAAe;AAC1C,MAAI;AACF,WAAO,mBAAmB,KAAsB;AAAA,EAClD,QAAQ;AAEN,WAAO,aAAa,aAAa;AAAA,EACnC;AACF;AAKO,SAAS,YAAY,MAAc,QAAgB,eAAuB;AAC/E,QAAM,WAAW,oBAAoB,KAAK;AAC1C,MAAI;AACF,UAAM,SAAS,SAAS,OAAO,IAAI;AACnC,WAAO,OAAO;AAAA,EAChB,UAAE;AACA,aAAS,KAAK;AAAA,EAChB;AACF;AAKA,SAAS,kBAAkB,QAAgB,OAAe,MAAc,QAAwB;AAC9F,QAAM,eAAe,KAAK,UAAU,EAAE,OAAO,MAAM,OAAO,CAAC;AAC3D,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,YAAY,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACrF;AAKO,SAAS,mBAAmB,SAA6B,CAAC,GAAiB;AAChF,QAAM,QAAQ,OAAO,SAAS,cAAc,OAAO,KAAK,IAAI,OAAO,QAAQ;AAC3E,QAAM,YAAY,cAAc,KAAK;AAErC,QAAM,aAA2C;AAAA,IAC/C;AAAA,IACA,QAAQ,OAAO,UAAU;AAAA,IACzB,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,EAC/B;AAEA,QAAM,EAAE,QAAQ,QAAQ,SAAS,IAAI;AAKrC,WAAS,oBAAoB,OAAuB;AAClD,WAAO,YAAY,OAAO,KAAK;AAAA,EACjC;AAKA,WAAS,UAAU,OAAe,cAAwC;AAExE,UAAM,YAAY,eAAe,GAAG,YAAY;AAAA;AAAA,EAAO,KAAK,KAAK;AACjE,UAAM,cAAc,oBAAoB,SAAS;AAIjD,QAAI,YAAa,cAAc,MAAa,UAAU;AAGtD,gBAAY,KAAK,IAAI,WAAW,MAAM;AACtC,gBAAY,KAAK,IAAI,WAAW,MAAM;AAGtC,UAAM,aAAa,KAAK,IAAI,IAAI,QAAQ;AACxC,UAAM,eAAe,KAAK,MAAM,YAAY,UAAU,EAAE,SAAS;AAGjE,UAAM,YAAY,kBAAkB,OAAO,OAAO,UAAU,OAAO,WAAW;AAE9E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,UAAU;AAAA,MAChB,qBAAqB,UAAU;AAAA,MAC/B,sBAAsB,UAAU;AAAA,MAChC,iBAAiB,UAAU;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAKA,WAAS,cAAc,OAAe,WAA4B;AAChE,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,cAAc,oBAAoB,KAAK;AAC7C,UAAM,eAAe,kBAAkB,OAAO,OAAO,UAAU,OAAO,WAAW;AACjF,WAAO,iBAAiB;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,QAAQ;AAAA,IACR;AAAA,EACF;AACF;AASO,SAAS,qBAMb;AACD,SAAO,OAAO,QAAQ,aAAa,EAChC,IAAI,CAAC,CAAC,OAAO,OAAO,OAAO;AAAA,IAC1B;AAAA,IACA,WAAW,QAAQ;AAAA,IACnB,YAAY,QAAQ;AAAA,IACpB,WAAW,QAAQ;AAAA,IACnB,MAAM,QAAQ;AAAA,EAChB,EAAE,EACD,KAAK,CAAC,GAAG,MAAM;AAEd,UAAM,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,EAAE;AACnE,UAAM,WAAW,UAAU,EAAE,IAA8B,IAAI,UAAU,EAAE,IAA8B;AACzG,QAAI,aAAa,EAAG,QAAO;AAC3B,WAAO,EAAE,YAAY,EAAE;AAAA,EACzB,CAAC;AACL;AAKO,SAAS,aAAa,OAAwB;AACnD,SAAO,SAAS;AAClB;AAMO,SAAS,mBAAmB,QAAgB,eAAuB;AACxE,QAAM,UAAU,cAAc,KAAK,KAAK,cAAc,aAAa;AACnE,QAAM,cAAc,cAAc,KAAK,IAAI,QAAQ;AACnD,SAAO,IAAI,QAAQ,MAAM,QAAQ,CAAC,CAAC,mBAAmB,WAAW;AACnE;","names":[]}
1
+ {"version":3,"sources":["../../src/types.ts","../../src/utils.ts","../../src/server/facilitator-client.ts","../../src/server/x402-server.ts","../../src/server/dynamic-pricing.ts","../../src/server/token-pricing.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 for Solana) */\n feePayer?: string;\n /** Token decimals (optional - defaults to 6 for USDC) */\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 (x402 spec field - REQUIRED) */\n maxAmountRequired: string;\n /** Alias for maxAmountRequired (for convenience) */\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 | 'missing_amount'\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 // Mainnet uses Dexter's RPC proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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://api.dexter.cash/api/base/rpc'; // Dexter proxy\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://api.dexter.cash/api/base/rpc';\n }\n }\n // Legacy names\n if (network === 'base') return 'https://api.dexter.cash/api/base/rpc';\n if (network === 'ethereum') return 'https://eth.llamarpc.com';\n if (network === 'arbitrum') return 'https://arb1.arbitrum.io/rpc';\n return 'https://api.dexter.cash/api/base/rpc';\n }\n\n // Unknown - return Dexter's Solana proxy\n return 'https://api.dexter.cash/api/solana/rpc';\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","/**\n * Facilitator Client\n *\n * Communicates with the x402 facilitator for:\n * - /supported - Get supported payment schemes and fee payer addresses\n * - /verify - Verify a payment signature before processing\n * - /settle - Submit the payment for execution\n *\n * Works with any x402 v2 facilitator (Dexter or others).\n */\n\nimport type { PaymentAccept, PaymentSignature, VerifyResponse, SettleResponse } from '../types';\nimport { DEXTER_FACILITATOR_URL } from '../types';\nimport { decodeBase64Json } from '../utils';\n\n/**\n * Supported payment kind from facilitator /supported endpoint\n */\nexport interface SupportedKind {\n x402Version: number;\n scheme: string;\n network: string;\n extra?: {\n feePayer?: string;\n decimals?: number;\n name?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Response from facilitator /supported endpoint\n */\nexport interface SupportedResponse {\n kinds: SupportedKind[];\n}\n\n/**\n * Client for communicating with an x402 v2 facilitator\n */\nexport class FacilitatorClient {\n private facilitatorUrl: string;\n private cachedSupported: SupportedResponse | null = null;\n private cacheTime: number = 0;\n private readonly CACHE_TTL_MS = 60_000; // 1 minute cache\n\n constructor(facilitatorUrl: string = DEXTER_FACILITATOR_URL) {\n this.facilitatorUrl = facilitatorUrl.replace(/\\/$/, ''); // Remove trailing slash\n }\n\n /**\n * Get supported payment kinds from the facilitator\n * Results are cached for 1 minute to reduce network calls\n */\n async getSupported(): Promise<SupportedResponse> {\n const now = Date.now();\n if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {\n return this.cachedSupported;\n }\n\n const response = await fetch(`${this.facilitatorUrl}/supported`);\n if (!response.ok) {\n throw new Error(`Facilitator /supported returned ${response.status}`);\n }\n\n this.cachedSupported = (await response.json()) as SupportedResponse;\n this.cacheTime = now;\n return this.cachedSupported;\n }\n\n /**\n * Get the fee payer address for a specific network\n *\n * @param network - CAIP-2 network identifier\n * @returns Fee payer address\n */\n async getFeePayer(network: string): Promise<string> {\n const supported = await this.getSupported();\n\n // Find matching network support (exact match or v2)\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n if (!kind?.extra?.feePayer) {\n throw new Error(\n `Facilitator does not support network \"${network}\" with scheme \"exact\", or feePayer not provided`\n );\n }\n\n return kind.extra.feePayer;\n }\n\n /**\n * Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)\n *\n * @param network - CAIP-2 network identifier\n * @returns Extra data from /supported\n */\n async getNetworkExtra(network: string): Promise<SupportedKind['extra']> {\n const supported = await this.getSupported();\n\n const kind = supported.kinds.find(\n (k) =>\n k.x402Version === 2 &&\n k.scheme === 'exact' &&\n k.network === network\n );\n\n return kind?.extra;\n }\n\n /**\n * Verify a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Verification response\n */\n async verifyPayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<VerifyResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const verifyPayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(verifyPayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /verify returned ${response.status}:`, errorText);\n return {\n isValid: false,\n invalidReason: `facilitator_error_${response.status}`,\n };\n }\n\n return (await response.json()) as VerifyResponse;\n } catch (error) {\n console.error('Payment verification failed:', error);\n return {\n isValid: false,\n invalidReason: error instanceof Error ? error.message : 'unexpected_verify_error',\n };\n }\n }\n\n /**\n * Settle a payment with the facilitator\n *\n * @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value\n * @param requirements - The payment requirements that were sent to the client\n * @returns Settlement response with transaction signature on success\n */\n async settlePayment(\n paymentSignatureHeader: string,\n requirements: PaymentAccept\n ): Promise<SettleResponse> {\n try {\n const paymentPayload = decodeBase64Json<PaymentSignature>(paymentSignatureHeader);\n\n const settlePayload = {\n paymentPayload,\n paymentRequirements: requirements,\n };\n\n const response = await fetch(`${this.facilitatorUrl}/settle`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(settlePayload),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(`Facilitator /settle returned ${response.status}:`, errorText);\n return {\n success: false,\n network: requirements.network,\n errorReason: `facilitator_error_${response.status}`,\n };\n }\n\n const result = (await response.json()) as SettleResponse;\n return {\n ...result,\n network: requirements.network,\n };\n } catch (error) {\n console.error('Payment settlement failed:', error);\n return {\n success: false,\n network: requirements.network,\n errorReason: error instanceof Error ? error.message : 'unexpected_settle_error',\n };\n }\n }\n}\n","/**\n * x402 v2 Server\n *\n * Server-side helpers for accepting x402 payments.\n * Chain-agnostic - works with Solana, Base, and any x402-compatible network.\n *\n * @example\n * ```typescript\n * import { createX402Server } from '@dexterai/x402/server';\n *\n * const server = createX402Server({\n * payTo: 'YourAddress...',\n * network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',\n * });\n *\n * // Handle 402 responses\n * if (!paymentSignature) {\n * const requirements = await server.buildRequirements({\n * amountAtomic: '50000',\n * resourceUrl: '/api/protected',\n * });\n * res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));\n * res.status(402).json({});\n * return;\n * }\n *\n * // Verify and settle\n * const verify = await server.verifyPayment(paymentSignature);\n * if (!verify.isValid) throw new Error(verify.invalidReason);\n *\n * const settle = await server.settlePayment(paymentSignature);\n * if (!settle.success) throw new Error(settle.errorReason);\n *\n * // Payment successful!\n * res.json({ transaction: settle.transaction });\n * ```\n */\n\nimport type {\n PaymentRequired,\n PaymentAccept,\n ResourceInfo,\n AcceptsExtra,\n VerifyResponse,\n SettleResponse,\n} from '../types';\nimport {\n SOLANA_MAINNET_NETWORK,\n USDC_MINT,\n DEXTER_FACILITATOR_URL,\n} from '../types';\nimport { FacilitatorClient, type SupportedKind } from './facilitator-client';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Asset configuration\n */\nexport interface AssetConfig {\n /** Token address (mint on Solana, contract on EVM) */\n address: string;\n /** Token decimals */\n decimals: number;\n}\n\n/**\n * Server configuration\n */\nexport interface X402ServerConfig {\n /** Address to receive payments */\n payTo: string;\n /** Facilitator URL (defaults to Dexter) */\n facilitatorUrl?: string;\n /** CAIP-2 network identifier */\n network?: string;\n /** Asset configuration (defaults to USDC) */\n asset?: AssetConfig;\n /** Default payment timeout in seconds */\n defaultTimeoutSeconds?: number;\n}\n\n/**\n * Options for building payment requirements\n */\nexport interface BuildRequirementsOptions {\n /** Amount in atomic units (e.g., '50000' for 0.05 USDC) */\n amountAtomic: string;\n /** Full URL of the resource */\n resourceUrl: string;\n /** Human-readable description */\n description?: string;\n /** MIME type of the response */\n mimeType?: string;\n /** Override timeout for this request */\n timeoutSeconds?: number;\n}\n\n/**\n * x402 Server interface\n */\nexport interface X402Server {\n /** Build payment requirements (fetches feePayer from facilitator) */\n buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;\n\n /** Encode requirements for PAYMENT-REQUIRED header */\n encodeRequirements(requirements: PaymentRequired): string;\n\n /** Create complete 402 response object */\n create402Response(requirements: PaymentRequired): {\n status: 402;\n headers: { 'PAYMENT-REQUIRED': string };\n body: Record<string, unknown>;\n };\n\n /** Verify payment with facilitator */\n verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse>;\n\n /** Settle payment via facilitator */\n settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse>;\n\n /** Get PaymentAccept for verify/settle */\n getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;\n\n /** Get network this server is configured for */\n readonly network: string;\n\n /** Get facilitator client for advanced usage */\n readonly facilitator: FacilitatorClient;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Create an x402 server for accepting payments\n */\nexport function createX402Server(config: X402ServerConfig): X402Server {\n const {\n payTo,\n facilitatorUrl = DEXTER_FACILITATOR_URL,\n network = SOLANA_MAINNET_NETWORK,\n asset = { address: USDC_MINT, decimals: 6 },\n defaultTimeoutSeconds = 60,\n } = config;\n\n const facilitator = new FacilitatorClient(facilitatorUrl);\n\n // Cache for network extra data\n let cachedExtra: SupportedKind['extra'] | null = null;\n\n /**\n * Get extra data from facilitator (cached)\n */\n async function getNetworkExtra(): Promise<AcceptsExtra> {\n if (!cachedExtra) {\n cachedExtra = await facilitator.getNetworkExtra(network);\n }\n\n if (!cachedExtra?.feePayer) {\n throw new Error(`Facilitator does not provide feePayer for network \"${network}\"`);\n }\n\n return {\n feePayer: cachedExtra.feePayer,\n decimals: cachedExtra.decimals ?? asset.decimals,\n // Include any additional EIP-712 data for EVM chains\n name: cachedExtra.name,\n version: cachedExtra.version,\n };\n }\n\n /**\n * Build a PaymentAccept structure\n */\n async function getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept> {\n const {\n amountAtomic,\n timeoutSeconds = defaultTimeoutSeconds,\n } = options;\n\n const extra = await getNetworkExtra();\n\n return {\n scheme: 'exact',\n network,\n maxAmountRequired: amountAtomic,\n asset: asset.address,\n payTo,\n maxTimeoutSeconds: timeoutSeconds,\n extra,\n };\n }\n\n /**\n * Build payment requirements for a 402 response\n */\n async function buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired> {\n const {\n resourceUrl,\n description,\n mimeType = 'application/json',\n } = options;\n\n const resource: ResourceInfo = {\n url: resourceUrl,\n description,\n mimeType,\n };\n\n const accept = await getPaymentAccept(options);\n\n return {\n x402Version: 2,\n resource,\n accepts: [accept],\n error: 'Payment required',\n };\n }\n\n /**\n * Encode requirements for PAYMENT-REQUIRED header\n */\n function encodeRequirements(requirements: PaymentRequired): string {\n return btoa(JSON.stringify(requirements));\n }\n\n /**\n * Create complete 402 response object\n */\n function create402Response(requirements: PaymentRequired) {\n return {\n status: 402 as const,\n headers: {\n 'PAYMENT-REQUIRED': encodeRequirements(requirements),\n },\n body: {},\n };\n }\n\n /**\n * Verify payment with facilitator\n */\n async function verifyPayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<VerifyResponse> {\n // If requirements not provided, build default\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.verifyPayment(paymentSignatureHeader, req);\n }\n\n /**\n * Settle payment via facilitator\n */\n async function settlePayment(\n paymentSignatureHeader: string,\n requirements?: PaymentAccept\n ): Promise<SettleResponse> {\n const req = requirements || await getPaymentAccept({\n amountAtomic: '0',\n resourceUrl: '',\n });\n\n return facilitator.settlePayment(paymentSignatureHeader, req);\n }\n\n return {\n buildRequirements,\n encodeRequirements,\n create402Response,\n verifyPayment,\n settlePayment,\n getPaymentAccept,\n network,\n facilitator,\n };\n}\n","/**\n * Dynamic Pricing for x402\n *\n * Calculate prices based on input length (characters, tokens, etc.)\n * Perfect for LLM/AI endpoints where cost scales with input size.\n *\n * @example\n * ```typescript\n * import { createDynamicPricing } from '@dexterai/x402/server';\n *\n * const pricing = createDynamicPricing({\n * unitSize: 1000, // chars per billing unit\n * ratePerUnit: 0.01, // $0.01 per unit\n * minUsd: 0.01, // floor\n * maxUsd: 10.00, // ceiling (optional)\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '23000', usdAmount: 0.023, quoteHash: 'abc...', units: 2.3 }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for dynamic pricing\n */\nexport interface DynamicPricingConfig {\n /**\n * Characters per billing unit.\n * Example: 1000 means every 1000 chars = 1 unit\n */\n unitSize: number;\n\n /**\n * USD per unit.\n * Example: 0.01 means $0.01 per unit\n */\n ratePerUnit: number;\n\n /**\n * Minimum USD amount (floor).\n * Recommended: 0.01 (practical minimum for settlement)\n * @default 0.01\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * Optional - prevents unexpectedly large bills.\n */\n maxUsd?: number;\n\n /**\n * Rounding mode for unit calculation.\n * - 'ceil': Always round up (fair to seller)\n * - 'floor': Always round down (fair to buyer)\n * - 'round': Standard rounding\n * @default 'ceil'\n */\n roundingMode?: 'ceil' | 'floor' | 'round';\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Price quote returned by calculate()\n */\nexport interface PriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount (for display) */\n usdAmount: number;\n\n /**\n * Quote hash for validation.\n * Includes input + config, so config changes invalidate quotes.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n\n /** Number of billing units */\n units: number;\n\n /** Input length in characters */\n inputLength: number;\n}\n\n/**\n * Dynamic pricing calculator\n */\nexport interface DynamicPricing {\n /** Calculate price from input */\n calculate(input: string): PriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Get pricing config (for display) */\n readonly config: Required<DynamicPricingConfig>;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Simple hash function (browser-compatible, no crypto needed)\n * Uses FNV-1a for speed and simplicity\n */\nfunction simpleHash(str: string): string {\n let hash = 2166136261; // FNV offset basis\n for (let i = 0; i < str.length; i++) {\n hash ^= str.charCodeAt(i);\n hash = Math.imul(hash, 16777619); // FNV prime\n }\n // Convert to hex string\n return (hash >>> 0).toString(16).padStart(8, '0');\n}\n\n/**\n * Create a dynamic pricing calculator\n */\nexport function createDynamicPricing(config: DynamicPricingConfig): DynamicPricing {\n const fullConfig: Required<DynamicPricingConfig> = {\n unitSize: config.unitSize,\n ratePerUnit: config.ratePerUnit,\n minUsd: config.minUsd ?? 0.01,\n maxUsd: config.maxUsd ?? Infinity,\n roundingMode: config.roundingMode ?? 'ceil',\n decimals: config.decimals ?? 6,\n };\n\n const { unitSize, ratePerUnit, minUsd, maxUsd, roundingMode, decimals } = fullConfig;\n\n // Validate config\n if (unitSize <= 0) throw new Error('unitSize must be positive');\n if (ratePerUnit <= 0) throw new Error('ratePerUnit must be positive');\n if (minUsd < 0) throw new Error('minUsd cannot be negative');\n if (maxUsd < minUsd) throw new Error('maxUsd must be >= minUsd');\n\n /**\n * Build hash input string (input + config)\n * Config is included so pricing changes invalidate old quotes\n */\n function buildHashInput(input: string): string {\n const configStr = JSON.stringify({\n unitSize,\n ratePerUnit,\n minUsd,\n maxUsd: maxUsd === Infinity ? 'none' : maxUsd,\n roundingMode,\n });\n return `${input}|${configStr}`;\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string): PriceQuote {\n const inputLength = input.length;\n\n // Calculate units based on rounding mode\n const rawUnits = inputLength / unitSize;\n let units: number;\n switch (roundingMode) {\n case 'ceil':\n units = Math.ceil(rawUnits);\n break;\n case 'floor':\n units = Math.floor(rawUnits);\n break;\n case 'round':\n units = Math.round(rawUnits);\n break;\n }\n\n // Ensure at least 1 unit if there's any input\n if (inputLength > 0 && units === 0) {\n units = 1;\n }\n\n // Calculate USD amount\n let usdAmount = units * ratePerUnit;\n\n // Apply min/max\n usdAmount = Math.max(minUsd, usdAmount);\n usdAmount = Math.min(maxUsd, usdAmount);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash (includes config)\n const quoteHash = simpleHash(buildHashInput(input));\n\n return {\n amountAtomic,\n usdAmount,\n quoteHash,\n units,\n inputLength,\n };\n }\n\n /**\n * Validate quote hash\n * Returns true if the hash matches (input + config unchanged)\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const expectedHash = simpleHash(buildHashInput(input));\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n config: fullConfig,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Format pricing for display\n * Example: \"from $0.01 per 1,000 chars\"\n */\nexport function formatPricing(config: DynamicPricingConfig): string {\n const rate = config.ratePerUnit.toFixed(2);\n const units = config.unitSize.toLocaleString();\n return `from $${rate} per ${units} chars`;\n}\n\n\n\n","/**\n * Token-Based Pricing for x402\n *\n * Accurate LLM pricing using tiktoken for token counting.\n * Uses real OpenAI model rates for precise cost calculation.\n *\n * @example\n * ```typescript\n * import { createTokenPricing, MODEL_PRICING } from '@dexterai/x402/server';\n *\n * const pricing = createTokenPricing({\n * model: 'gpt-4o-mini',\n * // Optional overrides:\n * // minUsd: 0.001,\n * // maxUsd: 50.0,\n * });\n *\n * // Calculate price from input\n * const quote = pricing.calculate(userPrompt);\n * // → { amountAtomic: '1500', usdAmount: 0.0015, inputTokens: 100, quoteHash: 'abc...' }\n *\n * // Validate on retry (prevents prompt manipulation)\n * const isValid = pricing.validateQuote(userPrompt, req.headers['x-quote-hash']);\n * ```\n */\n\nimport { createHash } from 'crypto';\nimport { encoding_for_model, get_encoding, type TiktokenModel } from 'tiktoken';\n\n// ============================================================================\n// Model Pricing Table\n// ============================================================================\n\n/**\n * Pricing info for a model\n */\nexport interface ModelPricing {\n /** USD per 1M input tokens */\n input: number;\n /** USD per 1M output tokens */\n output: number;\n /** USD per 1M cached input tokens (optional) */\n cached?: number;\n /** Default max output tokens for this model */\n maxTokens: number;\n /** Pricing tier */\n tier: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';\n}\n\n/**\n * OpenAI Model Pricing - USD per million tokens\n * Updated: December 2024\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // === FAST TIER (cheapest, fastest) ===\n 'gpt-4o-mini': { \n input: 0.15, output: 0.6, cached: 0.075, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-mini': { \n input: 0.4, output: 1.6, cached: 0.1, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-4.1-nano': { \n input: 0.1, output: 0.4, cached: 0.025, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-nano': { \n input: 0.05, output: 0.4, cached: 0.005, \n maxTokens: 4096, tier: 'fast' \n },\n 'gpt-5-mini': { \n input: 0.25, output: 2.0, cached: 0.025, \n maxTokens: 8192, tier: 'fast' \n },\n\n // === STANDARD TIER (balanced) ===\n 'gpt-4o': { \n input: 2.5, output: 10.0, cached: 1.25, \n maxTokens: 4096, tier: 'standard' \n },\n 'gpt-4.1': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 8192, tier: 'standard' \n },\n 'gpt-5': { \n input: 1.25, output: 10.0, cached: 0.125, \n maxTokens: 8192, tier: 'standard' \n },\n\n // === REASONING TIER (o-series) ===\n 'o1-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3-mini': { \n input: 1.1, output: 4.4, cached: 0.55, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o4-mini': { \n input: 1.1, output: 4.4, cached: 0.275, \n maxTokens: 16384, tier: 'reasoning' \n },\n 'o3': { \n input: 2.0, output: 8.0, cached: 0.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n 'o1': { \n input: 15.0, output: 60.0, cached: 7.5, \n maxTokens: 32768, tier: 'reasoning' \n },\n\n // === PREMIUM TIER (expensive, specialized) ===\n 'o3-pro': { \n input: 20.0, output: 80.0, \n maxTokens: 32768, tier: 'premium' \n },\n 'o1-pro': { \n input: 150.0, output: 600.0, \n maxTokens: 32768, tier: 'premium' \n },\n};\n\n// Default model for fallback\nconst DEFAULT_MODEL = 'gpt-4o-mini';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Configuration for token-based pricing\n */\nexport interface TokenPricingConfig {\n /**\n * Model name. Used for display and to look up built-in pricing.\n * If not in MODEL_PRICING and no custom rates provided, falls back to gpt-4o-mini rates.\n */\n model?: string;\n\n /**\n * Custom input rate (USD per 1M tokens).\n * Overrides built-in MODEL_PRICING for this model.\n * Use this for Anthropic, Gemini, Mistral, or any custom model.\n */\n inputRate?: number;\n\n /**\n * Custom output rate (USD per 1M tokens).\n * Optional - used for display/info purposes.\n */\n outputRate?: number;\n\n /**\n * Custom max output tokens.\n * @default 4096\n */\n maxTokens?: number;\n\n /**\n * Custom tier label.\n * @default 'custom'\n */\n tier?: 'fast' | 'standard' | 'reasoning' | 'premium' | 'custom';\n\n /**\n * Custom tokenizer function.\n * If not provided, uses tiktoken (cl100k_base encoding).\n * Use this for models with different tokenization (e.g., Llama, Mistral).\n * \n * @example\n * tokenizer: (text) => llamaTokenizer.encode(text).length\n */\n tokenizer?: (text: string) => number;\n\n /**\n * Minimum USD amount (floor).\n * @default 0.001\n */\n minUsd?: number;\n\n /**\n * Maximum USD amount (ceiling).\n * @default 50.0\n */\n maxUsd?: number;\n\n /**\n * Token decimals for atomic conversion.\n * @default 6 (USDC)\n */\n decimals?: number;\n}\n\n/**\n * Token price quote\n */\nexport interface TokenPriceQuote {\n /** Amount in atomic units (for buildRequirements) */\n amountAtomic: string;\n\n /** Human-readable USD amount */\n usdAmount: number;\n\n /** Number of input tokens */\n inputTokens: number;\n\n /** Model used for pricing */\n model: string;\n\n /** Pricing tier */\n tier: string;\n\n /** Input rate per million tokens */\n inputRatePerMillion: number;\n\n /** Output rate per million tokens */\n outputRatePerMillion: number;\n\n /** Max output tokens for this model */\n maxOutputTokens: number;\n\n /**\n * Quote hash for validation.\n * Client should send this back as X-Quote-Hash header.\n */\n quoteHash: string;\n}\n\n/**\n * Token pricing calculator\n */\nexport interface TokenPricing {\n /** Calculate price from input text */\n calculate(input: string, systemPrompt?: string): TokenPriceQuote;\n\n /** Validate quote hash (returns true if valid) */\n validateQuote(input: string, quoteHash: string): boolean;\n\n /** Count tokens in a string */\n countTokens(input: string): number;\n\n /** Get pricing config */\n readonly config: Required<TokenPricingConfig>;\n\n /** Get model info */\n readonly modelInfo: ModelPricing;\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\n/**\n * Get tiktoken encoding for a model.\n * Falls back to cl100k_base for unknown models.\n */\nfunction getEncodingForModel(model: string) {\n try {\n return encoding_for_model(model as TiktokenModel);\n } catch {\n // Fall back to cl100k_base (GPT-4/4o family encoding)\n return get_encoding('cl100k_base');\n }\n}\n\n/**\n * Count tokens in a string using tiktoken.\n */\nexport function countTokens(text: string, model: string = DEFAULT_MODEL): number {\n const encoding = getEncodingForModel(model);\n try {\n const tokens = encoding.encode(text);\n return tokens.length;\n } finally {\n encoding.free();\n }\n}\n\n/**\n * Generate a hash of the prompt + pricing config for validation.\n */\nfunction generateQuoteHash(prompt: string, model: string, rate: number, tokens: number): string {\n const configString = JSON.stringify({ model, rate, tokens });\n return createHash('sha256').update(prompt + configString).digest('hex').slice(0, 16);\n}\n\n/**\n * Create a token-based pricing calculator\n */\nexport function createTokenPricing(config: TokenPricingConfig = {}): TokenPricing {\n // Determine model name\n const model = config.model ?? DEFAULT_MODEL;\n \n // Get built-in pricing if available, otherwise use custom or default\n const builtInPricing = MODEL_PRICING[model];\n \n // Build effective model info (custom rates override built-in)\n const modelInfo: ModelPricing = {\n input: config.inputRate ?? builtInPricing?.input ?? MODEL_PRICING[DEFAULT_MODEL].input,\n output: config.outputRate ?? builtInPricing?.output ?? MODEL_PRICING[DEFAULT_MODEL].output,\n maxTokens: config.maxTokens ?? builtInPricing?.maxTokens ?? 4096,\n tier: config.tier ?? builtInPricing?.tier ?? 'custom',\n };\n\n // Custom tokenizer or default tiktoken\n const customTokenizer = config.tokenizer;\n\n const fullConfig: Required<TokenPricingConfig> = {\n model,\n inputRate: modelInfo.input,\n outputRate: modelInfo.output,\n maxTokens: modelInfo.maxTokens,\n tier: modelInfo.tier,\n tokenizer: customTokenizer ?? ((text: string) => countTokens(text, model)),\n minUsd: config.minUsd ?? 0.001,\n maxUsd: config.maxUsd ?? 50.0,\n decimals: config.decimals ?? 6,\n };\n\n const { minUsd, maxUsd, decimals } = fullConfig;\n\n /**\n * Count tokens in text (uses custom tokenizer if provided)\n */\n function countTokensInternal(input: string): number {\n if (customTokenizer) {\n return customTokenizer(input);\n }\n return countTokens(input, model);\n }\n\n /**\n * Calculate price from input\n */\n function calculate(input: string, systemPrompt?: string): TokenPriceQuote {\n // Count input tokens (prompt + system prompt if provided)\n const fullInput = systemPrompt ? `${systemPrompt}\\n\\n${input}` : input;\n const inputTokens = countTokensInternal(fullInput);\n\n // Calculate USD cost based on input tokens only\n // Price = (inputTokens / 1,000,000) × inputRate\n let usdAmount = (inputTokens / 1_000_000) * modelInfo.input;\n\n // Apply min/max caps\n usdAmount = Math.max(usdAmount, minUsd);\n usdAmount = Math.min(usdAmount, maxUsd);\n\n // Convert to atomic units\n const multiplier = Math.pow(10, decimals);\n const amountAtomic = Math.floor(usdAmount * multiplier).toString();\n\n // Generate quote hash for validation\n const quoteHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n\n return {\n amountAtomic,\n usdAmount,\n inputTokens,\n model,\n tier: modelInfo.tier,\n inputRatePerMillion: modelInfo.input,\n outputRatePerMillion: modelInfo.output,\n maxOutputTokens: modelInfo.maxTokens,\n quoteHash,\n };\n }\n\n /**\n * Validate quote hash\n */\n function validateQuote(input: string, quoteHash: string): boolean {\n if (!quoteHash) return false;\n const inputTokens = countTokensInternal(input);\n const expectedHash = generateQuoteHash(input, model, modelInfo.input, inputTokens);\n return expectedHash === quoteHash;\n }\n\n return {\n calculate,\n validateQuote,\n countTokens: countTokensInternal,\n config: fullConfig,\n modelInfo,\n };\n}\n\n// ============================================================================\n// Utilities\n// ============================================================================\n\n/**\n * Get list of available models with their pricing.\n */\nexport function getAvailableModels(): Array<{\n model: string;\n inputRate: number;\n outputRate: number;\n maxTokens: number;\n tier: string;\n}> {\n return Object.entries(MODEL_PRICING)\n .map(([model, pricing]) => ({\n model,\n inputRate: pricing.input,\n outputRate: pricing.output,\n maxTokens: pricing.maxTokens,\n tier: pricing.tier,\n }))\n .sort((a, b) => {\n // Sort by tier, then by input rate\n const tierOrder = { fast: 0, standard: 1, reasoning: 2, premium: 3 };\n const tierDiff = tierOrder[a.tier as keyof typeof tierOrder] - tierOrder[b.tier as keyof typeof tierOrder];\n if (tierDiff !== 0) return tierDiff;\n return a.inputRate - b.inputRate;\n });\n}\n\n/**\n * Check if a model exists in our pricing.\n */\nexport function isValidModel(model: string): boolean {\n return model in MODEL_PRICING;\n}\n\n/**\n * Format token pricing for display\n * Example: \"$0.15 per 1M tokens (gpt-4o-mini)\"\n */\nexport function formatTokenPricing(model: string = DEFAULT_MODEL): string {\n const pricing = MODEL_PRICING[model] ?? MODEL_PRICING[DEFAULT_MODEL];\n const actualModel = MODEL_PRICING[model] ? model : DEFAULT_MODEL;\n return `$${pricing.input.toFixed(2)} per 1M tokens (${actualModel})`;\n}\n\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/x402",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Chain-agnostic x402 v2 SDK for Solana, Base, and EVM payments",
5
5
  "author": "Dexter",
6
6
  "license": "MIT",