moltspay 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/client/web/index.ts","../../../src/client/core/types.ts","../../../src/client/core/chain-map.ts","../../../src/client/core/base64.ts","../../../src/client/core/errors.ts","../../../src/client/core/eip3009.ts","../../../src/client/core/eip2612.ts","../../../src/client/core/bnb-intent.ts","../../../src/facilitators/solana.ts","../../../src/chains/solana.ts","../../../src/client/core/x402.ts","../../../src/chains/index.ts","../../../src/client/web/storage.ts","../../../src/client/web/signers/eip1193.ts","../../../src/client/web/signers/solana-adapter.ts","../../../src/client/web/signers/compose.ts"],"sourcesContent":["/**\n * MoltsPay Web Client\n *\n * Browser-native companion to `MoltsPayClient` (Node). The two share one\n * protocol core (`src/client/core/`) — every x402 detail, typed-data builder,\n * and chain mapping is identical. The Web Client differs only in that:\n *\n * - It never reads or writes a wallet file. All signing is delegated to an\n * injected `PaymentSigner` (an EIP-1193 / wallet-adapter shim the app owns).\n * - It always posts to `/execute` and consumes `X-Payment-Required`. It\n * never negotiates MPP over `WWW-Authenticate` — Tempo is reached via the\n * EIP-2612 permit branch instead. See docs/WEB-CLIENT-DESIGN.md §Tempo.\n * - Spending limits are opt-in and backed by `localStorage` when enabled.\n */\n\nimport { ethers } from 'ethers';\nimport { Connection, PublicKey } from '@solana/web3.js';\nimport {\n PAYMENT_REQUIRED_HEADER,\n PAYMENT_HEADER,\n parsePaymentRequiredHeader,\n serverAcceptedChains,\n selectChain,\n findRequirementForChain,\n buildEIP3009TypedData,\n buildEIP2612PermitTypedData,\n buildBnbIntentTypedData,\n chainNameToNetwork,\n encodePaymentHeader,\n buildPaymentPayload,\n uint8ArrayToBase64,\n NotInitializedError,\n NeedsApprovalError,\n UnsupportedChainError,\n ServerError,\n InsufficientBalanceError,\n type X402PaymentRequirements,\n type ChainName as CoreChainName,\n} from '../core/index.js';\nimport { createSolanaPaymentTransaction } from '../../facilitators/solana.js';\nimport { CHAINS, type EvmChainName } from '../../chains/index.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../../chains/solana.js';\nimport type { PaymentSigner } from '../signer.js';\nimport type { ServicesResponse } from '../types.js';\nimport { SpendingLedger, type SpendingLimitsConfig } from './storage.js';\n\nexport { eip1193Signer } from './signers/eip1193.js';\nexport type { Eip1193Provider, Eip1193ChainMetadata, Eip1193SignerOptions } from './signers/eip1193.js';\nexport { solanaSigner } from './signers/solana-adapter.js';\nexport type { SolanaSignerAdapter } from './signers/solana-adapter.js';\nexport { composeSigners } from './signers/compose.js';\nexport { SpendingLedger } from './storage.js';\nexport type { SpendingLimitsConfig } from './storage.js';\nexport type { PaymentSigner } from '../signer.js';\nexport type { CoreChainName as ChainName };\nexport {\n NeedsApprovalError,\n UnsupportedChainError,\n PaymentRejectedError,\n InsufficientBalanceError,\n SpendingLimitExceededError,\n ServerError,\n MoltsPayError,\n} from '../core/index.js';\n\nexport interface MoltsPayWebClientOptions {\n /** PaymentSigner to authorize every payment. Typically `eip1193Signer(window.ethereum)` or `composeSigners(...)`. */\n signer: PaymentSigner;\n\n /** Default chain for `pay()` when the server accepts multiple and the caller omits `options.chain`. */\n defaultChain?: CoreChainName;\n\n /** Enable `localStorage`-backed spending limits. Off by default. */\n spendingLimits?: SpendingLimitsConfig;\n\n /** Optional fetch override — useful for MSW tests or proxying. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n\n /**\n * Per-chain Solana RPC URL override. Required on mainnet in practice —\n * the public `api.mainnet-beta.solana.com` endpoint returns 403 to browser\n * requests. Point at Helius / QuickNode / Alchemy etc. Falls back to\n * `SOLANA_CHAINS[chain].rpc` when omitted.\n */\n solanaRpc?: {\n solana?: string;\n solana_devnet?: string;\n };\n}\n\nexport interface WebPayOptions {\n /** Chain to pay on. Required when the server accepts more than one. */\n chain?: CoreChainName;\n /** Send user params at top level (`{ service, ...params }`) instead of wrapped (`{ service, params }`). */\n rawData?: boolean;\n}\n\ntype ResolvedFetch = typeof fetch;\n\nexport class MoltsPayWebClient {\n private readonly signer: PaymentSigner;\n private readonly defaultChain?: CoreChainName;\n private readonly ledger: SpendingLedger | null;\n private readonly fetchImpl: ResolvedFetch;\n private readonly solanaRpc?: { solana?: string; solana_devnet?: string };\n\n constructor(options: MoltsPayWebClientOptions) {\n if (!options.signer) {\n throw new NotInitializedError('MoltsPayWebClient: signer is required');\n }\n this.signer = options.signer;\n this.defaultChain = options.defaultChain;\n this.ledger = options.spendingLimits ? new SpendingLedger(options.spendingLimits) : null;\n this.fetchImpl = (options.fetch ?? globalThis.fetch).bind(globalThis);\n this.solanaRpc = options.solanaRpc;\n }\n\n private getSolanaConnection(chain: SolanaChainName): Connection {\n const rpc = this.solanaRpc?.[chain] ?? SOLANA_CHAINS[chain].rpc;\n return new Connection(rpc, 'confirmed');\n }\n\n /** Fetch a provider's service manifest. Mirrors `MoltsPayClient.getServices`. */\n async getServices(serverUrl: string): Promise<ServicesResponse> {\n const normalizedUrl = serverUrl.replace(/\\/(services|api\\/services|registry\\/services)\\/?$/, '');\n const endpoints = ['/services', '/api/services', '/registry/services'];\n for (const endpoint of endpoints) {\n try {\n const res = await this.fetchImpl(`${normalizedUrl}${endpoint}`);\n if (!res.ok) continue;\n const contentType = res.headers.get('content-type') ?? '';\n if (!contentType.includes('application/json')) continue;\n return (await res.json()) as ServicesResponse;\n } catch {\n continue;\n }\n }\n throw new ServerError(0, `Failed to get services: no valid endpoint found at ${normalizedUrl}`);\n }\n\n /**\n * Pay for a service via x402. Returns the service `result` field (or the\n * whole JSON body if the server doesn't wrap). Throws `NeedsApprovalError`\n * for BNB when allowance is insufficient — call {@link approveBnb} and retry.\n */\n async pay(\n serverUrl: string,\n service: string,\n params: Record<string, unknown>,\n options: WebPayOptions = {}\n ): Promise<Record<string, unknown>> {\n const executeUrl = await this.resolveExecuteUrl(serverUrl, service);\n const requestBody = this.buildRequestBody(service, params, options);\n\n const initialRes = await this.fetchImpl(executeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n });\n\n // Happy path: server did not charge — return result directly.\n if (initialRes.status !== 402) {\n const data = (await initialRes.json()) as { result?: Record<string, unknown>; error?: string };\n if (initialRes.ok && data.result) {\n return data.result;\n }\n if (initialRes.ok) {\n return data as Record<string, unknown>;\n }\n throw new ServerError(initialRes.status, data.error ?? 'Unexpected response');\n }\n\n // Web Client consumes only X-Payment-Required. `WWW-Authenticate` (MPP) is\n // intentionally ignored — the server's `/execute` route does not emit it,\n // and Tempo reaches us through the x402 + permit path instead.\n const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);\n if (!paymentRequiredHeader) {\n throw new ServerError(402, 'Missing X-Payment-Required header in 402 response');\n }\n\n const requirements = parsePaymentRequiredHeader(paymentRequiredHeader);\n const chain = this.chooseChain(requirements, options.chain);\n const req = findRequirementForChain(requirements, chain);\n if (!req) {\n throw new UnsupportedChainError(chain, `No payment requirement for chain '${chain}' in server response`);\n }\n\n if (chain === 'solana' || chain === 'solana_devnet') {\n return this.paySolana(executeUrl, service, params, req, chain, options);\n }\n if (chain === 'tempo_moderato') {\n return this.payTempoPermit(executeUrl, service, params, req, options);\n }\n if (chain === 'bnb' || chain === 'bnb_testnet') {\n return this.payBnb(executeUrl, service, params, req, chain, options);\n }\n return this.payEIP3009(executeUrl, service, params, req, chain, options);\n }\n\n /** Read-only balance check on the specified chain (or `defaultChain` if configured). */\n async getBalance(chain?: CoreChainName): Promise<{ usdc: number; usdt?: number; native: number }> {\n const target = chain ?? this.defaultChain;\n if (!target) {\n throw new UnsupportedChainError('unspecified', 'No chain provided and no defaultChain configured');\n }\n if (target === 'solana' || target === 'solana_devnet') {\n const conn = this.getSolanaConnection(target);\n const owner = await this.signer.getSolanaAddress?.();\n if (!owner) {\n throw new NotInitializedError('No Solana address available from signer');\n }\n const native = await conn.getBalance(new PublicKey(owner));\n return { usdc: 0, native: native / 1e9 };\n }\n\n const evmChain = CHAINS[target as EvmChainName];\n const provider = new ethers.JsonRpcProvider(evmChain.rpc);\n const owner = await this.signer.getEvmAddress();\n const tokenAbi = ['function balanceOf(address) view returns (uint256)'];\n\n const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([\n provider.getBalance(owner),\n new ethers.Contract(evmChain.tokens.USDC.address, tokenAbi, provider).balanceOf(owner),\n new ethers.Contract(evmChain.tokens.USDT.address, tokenAbi, provider).balanceOf(owner),\n ]);\n\n return {\n usdc: parseFloat(ethers.formatUnits(usdcBalance, evmChain.tokens.USDC.decimals)),\n usdt: parseFloat(ethers.formatUnits(usdtBalance, evmChain.tokens.USDT.decimals)),\n native: parseFloat(ethers.formatEther(nativeBalance)),\n };\n }\n\n /**\n * Approve a spender for the BNB chain's USDC / USDT (one-time, costs BNB gas).\n * The spender address is what the server advertised as `bnbSpender` in the\n * 402 response. Amount defaults to `MaxUint256` so only one approval is ever\n * needed per (chain, token, spender) tuple.\n */\n async approveBnb(args: {\n chain: 'bnb' | 'bnb_testnet';\n spender: string;\n token: 'USDC' | 'USDT';\n amount?: string;\n }): Promise<string> {\n if (!this.signer.sendEvmTransaction) {\n throw new NotInitializedError('Signer does not support sendEvmTransaction');\n }\n const chain = CHAINS[args.chain];\n const tokenAddr = chain.tokens[args.token].address;\n const amount = args.amount ?? ethers.MaxUint256.toString();\n\n const iface = new ethers.Interface([\n 'function approve(address spender, uint256 amount) returns (bool)',\n ]);\n const data = iface.encodeFunctionData('approve', [args.spender, amount]);\n\n return this.signer.sendEvmTransaction({\n chainId: chain.chainId,\n to: tokenAddr,\n data,\n });\n }\n\n // ===== internals =====\n\n private async resolveExecuteUrl(serverUrl: string, service: string): Promise<string> {\n try {\n const services = await this.getServices(serverUrl);\n const svc = services.services?.find((s) => s.id === service);\n if (svc?.endpoint) {\n return `${serverUrl}${svc.endpoint}`;\n }\n } catch {\n // Service discovery failure is non-fatal — we fall back to /execute.\n }\n return `${serverUrl}/execute`;\n }\n\n private buildRequestBody(\n service: string,\n params: Record<string, unknown>,\n options: WebPayOptions\n ): Record<string, unknown> {\n const body: Record<string, unknown> = options.rawData\n ? { service, ...params }\n : { service, params };\n if (options.chain) body.chain = options.chain;\n return body;\n }\n\n private chooseChain(\n requirements: X402PaymentRequirements[],\n userChain?: CoreChainName\n ): CoreChainName {\n const preferred = userChain ?? this.defaultChain;\n if (preferred) {\n return selectChain(requirements, preferred);\n }\n // Mirror 1.5.x behavior: only default to `base` when it's the sole option.\n const accepted = serverAcceptedChains(requirements);\n if (accepted.length === 1) {\n return accepted[0];\n }\n return selectChain(requirements); // throws with a helpful list of accepted chains\n }\n\n /**\n * Submit the x402 payload and return the service result. Shared by all\n * scheme branches — the only thing they vary is how they build `payload`.\n */\n private async submitPayment(\n executeUrl: string,\n service: string,\n params: Record<string, unknown>,\n options: WebPayOptions,\n paymentHeader: string,\n chainForBody: CoreChainName,\n charge: number\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = options.rawData\n ? { service, ...params, chain: chainForBody }\n : { service, params, chain: chainForBody };\n\n const paidRes = await this.fetchImpl(executeUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [PAYMENT_HEADER]: paymentHeader,\n },\n body: JSON.stringify(body),\n });\n\n const result = (await paidRes.json()) as { result?: Record<string, unknown>; error?: string };\n if (!paidRes.ok) {\n throw new ServerError(paidRes.status, result.error ?? 'Payment failed');\n }\n if (this.ledger && charge > 0) {\n this.ledger.record(charge);\n }\n return (result.result ?? (result as Record<string, unknown>));\n }\n\n // ----- EIP-3009 (Base / Polygon / Base Sepolia) -----\n\n private async payEIP3009(\n executeUrl: string,\n service: string,\n params: Record<string, unknown>,\n req: X402PaymentRequirements,\n chainName: CoreChainName,\n options: WebPayOptions\n ): Promise<Record<string, unknown>> {\n const evmChain = CHAINS[chainName as EvmChainName];\n const amountRaw = req.amount ?? req.maxAmountRequired;\n if (!amountRaw) {\n throw new ServerError(402, 'Missing amount in payment requirements');\n }\n const amountDisplay = Number(amountRaw) / 1e6;\n this.ledger?.check(amountDisplay);\n\n const payTo = req.payTo ?? req.resource;\n if (!payTo) {\n throw new ServerError(402, 'Missing payTo in payment requirements');\n }\n\n const owner = await this.signer.getEvmAddress();\n const nonce = ethers.hexlify(ethers.randomBytes(32));\n\n // Use server's domain info when provided; otherwise fall back to our local\n // token config. This is the same precedence the Node client uses.\n const extra = (req.extra ?? {}) as { name?: string; version?: string };\n const tokenConfig = evmChain.tokens.USDC; // Web Client defaults to USDC for v1.6.0.\n const tokenName = extra.name ?? tokenConfig.eip712Name ?? 'USD Coin';\n const tokenVersion = extra.version ?? '2';\n\n const envelope = buildEIP3009TypedData({\n from: owner,\n to: payTo,\n value: amountRaw,\n nonce,\n chainId: evmChain.chainId,\n tokenAddress: req.asset ?? tokenConfig.address,\n tokenName,\n tokenVersion,\n });\n\n const signature = await this.signer.signTypedData(envelope);\n\n const payload = buildPaymentPayload({\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n payload: { authorization: envelope.message, signature },\n accepted: {\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n asset: req.asset ?? tokenConfig.address,\n amount: amountRaw,\n payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds ?? 300,\n extra: { name: tokenName, version: tokenVersion },\n },\n });\n\n const header = encodePaymentHeader(payload);\n return this.submitPayment(executeUrl, service, params, options, header, chainName, amountDisplay);\n }\n\n // ----- EIP-2612 permit (Tempo Moderato) -----\n\n private async payTempoPermit(\n executeUrl: string,\n service: string,\n params: Record<string, unknown>,\n req: X402PaymentRequirements,\n options: WebPayOptions\n ): Promise<Record<string, unknown>> {\n const amountRaw = req.amount ?? req.maxAmountRequired;\n if (!amountRaw) {\n throw new ServerError(402, 'Missing amount in Tempo requirements');\n }\n const amountDisplay = Number(amountRaw) / 1e6;\n this.ledger?.check(amountDisplay);\n\n const extra = (req.extra ?? {}) as { tempoSpender?: string; name?: string; version?: string };\n const spender = extra.tempoSpender;\n if (!spender) {\n throw new ServerError(\n 402,\n 'Tempo requirement missing extra.tempoSpender — server has no settler configured (TEMPO_SETTLER_KEY)'\n );\n }\n if (!req.asset) {\n throw new ServerError(402, 'Tempo requirement missing asset (token address)');\n }\n if (!extra.name || !extra.version) {\n throw new ServerError(402, 'Tempo requirement missing extra.name / extra.version for EIP-712 domain');\n }\n\n const owner = await this.signer.getEvmAddress();\n\n // Fetch nonces(owner) from Tempo RPC. Read-only — no signer needed here.\n const tempoRpc = CHAINS.tempo_moderato.rpc;\n const provider = new ethers.JsonRpcProvider(tempoRpc);\n const token = new ethers.Contract(\n req.asset,\n ['function nonces(address owner) view returns (uint256)'],\n provider\n );\n const nonce = (await token.nonces(owner)) as bigint;\n const deadline = Math.floor(Date.now() / 1000) + 3600;\n\n const envelope = buildEIP2612PermitTypedData({\n owner,\n spender,\n value: amountRaw,\n nonce: nonce.toString(),\n deadline: deadline.toString(),\n chainId: CHAINS.tempo_moderato.chainId,\n tokenAddress: req.asset,\n tokenName: extra.name,\n tokenVersion: extra.version,\n });\n\n const rawSig = await this.signer.signTypedData(envelope);\n const split = ethers.Signature.from(rawSig);\n\n const payload = buildPaymentPayload({\n scheme: 'permit',\n network: chainNameToNetwork('tempo_moderato'),\n payload: {\n permit: {\n owner,\n spender,\n value: amountRaw,\n nonce: nonce.toString(),\n deadline: deadline.toString(),\n v: split.v,\n r: split.r,\n s: split.s,\n },\n },\n accepted: {\n scheme: 'permit',\n network: chainNameToNetwork('tempo_moderato'),\n asset: req.asset,\n amount: amountRaw,\n payTo: req.payTo ?? '',\n maxTimeoutSeconds: req.maxTimeoutSeconds ?? 300,\n extra: { name: extra.name, version: extra.version, tempoSpender: spender },\n },\n });\n\n const header = encodePaymentHeader(payload);\n return this.submitPayment(executeUrl, service, params, options, header, 'tempo_moderato', amountDisplay);\n }\n\n // ----- BNB intent (BNB / BNB Testnet) -----\n\n private async payBnb(\n executeUrl: string,\n service: string,\n params: Record<string, unknown>,\n req: X402PaymentRequirements,\n chainName: 'bnb' | 'bnb_testnet',\n options: WebPayOptions\n ): Promise<Record<string, unknown>> {\n const evmChain = CHAINS[chainName];\n const extra = (req.extra ?? {}) as { bnbSpender?: string };\n const spender = extra.bnbSpender;\n if (!spender) {\n throw new ServerError(402, 'BNB requirement missing extra.bnbSpender');\n }\n if (!req.amount || !req.payTo) {\n throw new ServerError(402, 'BNB requirement missing amount or payTo');\n }\n\n const owner = await this.signer.getEvmAddress();\n const tokenConfig = evmChain.tokens.USDC; // Web Client v1.6.0 defaults to USDC on BNB.\n const tokenAddress = req.asset ?? tokenConfig.address;\n\n // The server advertises `amount` as a 6-decimal USD price (same encoding\n // as all other chains). BNB tokens use 18 decimals, so we re-scale for the\n // on-chain `amount` field. This matches Node CLI behavior at\n // src/client/node/index.ts §handleBNBPayment.\n const amountDisplay = Number(req.amount) / 1e6;\n this.ledger?.check(amountDisplay);\n const amountWei = BigInt(Math.floor(amountDisplay * 10 ** tokenConfig.decimals));\n const amountWeiStr = amountWei.toString();\n\n // Allowance check — throw NeedsApprovalError with enough detail for the\n // UI to offer an \"Approve BNB\" button.\n const provider = new ethers.JsonRpcProvider(evmChain.rpc);\n const erc20 = new ethers.Contract(\n tokenAddress,\n ['function allowance(address owner, address spender) view returns (uint256)'],\n provider\n );\n const allowance = (await erc20.allowance(owner, spender)) as bigint;\n if (allowance < amountWei) {\n throw new NeedsApprovalError({\n chain: chainName,\n spender,\n token: 'USDC',\n currentAllowance: allowance.toString(),\n required: amountWei.toString(),\n });\n }\n\n // Check user has some native BNB for the eventual approve() gas — only\n // relevant for first-time users, but surfaces a clearer error than a\n // wallet-side \"insufficient funds\". Skip on testnet since faucets are easy.\n if (chainName === 'bnb') {\n const nativeBalance = await provider.getBalance(owner);\n if (nativeBalance < ethers.parseEther('0.0001') && allowance < amountWei) {\n throw new InsufficientBalanceError(\n `Insufficient BNB for approve gas (have ${ethers.formatEther(nativeBalance)}, need ~0.001 BNB)`\n );\n }\n }\n\n const envelope = buildBnbIntentTypedData({\n from: owner,\n to: req.payTo,\n amount: amountWeiStr,\n tokenAddress,\n service,\n nonce: Date.now(),\n deadline: Date.now() + 3600000,\n chainId: evmChain.chainId,\n });\n\n const signature = await this.signer.signTypedData(envelope);\n\n const payload = buildPaymentPayload({\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n payload: {\n intent: { ...envelope.message, signature },\n chainId: evmChain.chainId,\n },\n accepted: {\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n asset: tokenAddress,\n amount: amountWeiStr,\n payTo: req.payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds ?? 300,\n },\n });\n\n const header = encodePaymentHeader(payload);\n return this.submitPayment(executeUrl, service, params, options, header, chainName, amountDisplay);\n }\n\n // ----- Solana (solana / solana_devnet) -----\n\n private async paySolana(\n executeUrl: string,\n service: string,\n params: Record<string, unknown>,\n req: X402PaymentRequirements,\n chainName: SolanaChainName,\n options: WebPayOptions\n ): Promise<Record<string, unknown>> {\n if (!this.signer.signSolanaTransaction) {\n throw new NotInitializedError('Signer does not support Solana — use solanaSigner(adapter) or composeSigners');\n }\n const ownerBase58 = await this.signer.getSolanaAddress?.();\n if (!ownerBase58) {\n throw new NotInitializedError('Solana wallet not connected');\n }\n if (!req.amount || !req.payTo) {\n throw new ServerError(402, 'Solana requirement missing amount or payTo');\n }\n\n const amountDisplay = Number(req.amount) / 1e6;\n this.ledger?.check(amountDisplay);\n\n const ownerPubkey = new PublicKey(ownerBase58);\n const recipientPubkey = new PublicKey(req.payTo);\n const extra = (req.extra ?? {}) as { solanaFeePayer?: string };\n const feePayerPubkey = extra.solanaFeePayer ? new PublicKey(extra.solanaFeePayer) : undefined;\n\n const unsignedTx = await createSolanaPaymentTransaction(\n ownerPubkey,\n recipientPubkey,\n BigInt(req.amount),\n chainName,\n feePayerPubkey,\n this.getSolanaConnection(chainName)\n );\n\n // Serialize without requiring all signatures so `partialSign: true` works\n // when the server is also signing as fee payer.\n const unsignedBytes = unsignedTx.serialize({\n requireAllSignatures: false,\n verifySignatures: false,\n });\n const transactionBase64 = uint8ArrayToBase64(unsignedBytes);\n\n const signedTx = await this.signer.signSolanaTransaction({\n transactionBase64,\n partialSign: !!feePayerPubkey,\n });\n\n const payload = buildPaymentPayload({\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n payload: {\n signedTransaction: signedTx,\n sender: ownerBase58,\n chain: chainName,\n },\n accepted: {\n scheme: 'exact',\n network: chainNameToNetwork(chainName),\n asset: req.asset ?? SOLANA_CHAINS[chainName].tokens.USDC.mint,\n amount: req.amount,\n payTo: req.payTo,\n maxTimeoutSeconds: req.maxTimeoutSeconds ?? 300,\n },\n });\n\n const header = encodePaymentHeader(payload);\n return this.submitPayment(executeUrl, service, params, options, header, chainName, amountDisplay);\n }\n}\n","/**\n * Core protocol types — shared between Node and Web clients.\n *\n * These describe x402 / MPP wire formats and EIP-712 typed-data shapes.\n * They MUST NOT import from any Node-only modules.\n */\n\n// ===== x402 protocol constants =====\n\nexport const X402_VERSION = 2;\nexport const PAYMENT_REQUIRED_HEADER = 'x-payment-required';\nexport const PAYMENT_HEADER = 'x-payment';\n\n// ===== x402 payment requirements (server → client) =====\n\nexport interface X402PaymentRequirements {\n scheme: string;\n network: string;\n\n // v2 fields\n amount?: string;\n asset?: string;\n payTo?: string;\n maxTimeoutSeconds?: number;\n extra?: Record<string, unknown>;\n\n // v1 legacy fields\n maxAmountRequired?: string;\n resource?: string;\n description?: string;\n}\n\n// ===== EIP-3009 TransferWithAuthorization (Base / Polygon / Base Sepolia) =====\n\nexport interface EIP3009Authorization {\n from: string;\n to: string;\n value: string;\n validAfter: string;\n validBefore: string;\n nonce: string;\n}\n\n// ===== EIP-2612 Permit (Tempo — pathUSD / AlphaUSD / BetaUSD / ThetaUSD) =====\n\nexport interface EIP2612PermitMessage {\n owner: string;\n spender: string;\n value: string;\n nonce: string;\n deadline: string;\n}\n\n/** Signed permit payload carried inside an x402 `scheme: \"permit\"` payment. */\nexport interface EIP2612PermitPayload {\n owner: string;\n spender: string;\n value: string;\n nonce: string;\n deadline: string;\n v: number;\n r: string;\n s: string;\n}\n\n// ===== BNB payment intent (Phase 3c+ pre-approval flow) =====\n\nexport interface BnbPaymentIntent {\n from: string;\n to: string;\n amount: string;\n token: string;\n service: string;\n nonce: number;\n deadline: number;\n}\n\n// ===== EIP-712 typed-data envelope (runtime-agnostic signing input) =====\n\nexport interface TypedDataDomain {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n salt?: string;\n}\n\nexport interface TypedDataField {\n name: string;\n type: string;\n}\n\nexport interface TypedDataEnvelope<TMessage = Record<string, unknown>> {\n domain: TypedDataDomain;\n types: Record<string, readonly TypedDataField[]>;\n primaryType: string;\n message: TMessage;\n}\n","/**\n * Chain identifier mapping — pure, runtime-agnostic.\n *\n * Translates between x402 `network` field values (CAIP-2 style, e.g.\n * `eip155:8453`, `solana:mainnet`) and MoltsPay's internal chain names\n * (e.g. `base`, `solana`, `tempo_moderato`).\n */\n\nexport type ChainName =\n | 'base'\n | 'polygon'\n | 'base_sepolia'\n | 'tempo_moderato'\n | 'bnb'\n | 'bnb_testnet'\n | 'solana'\n | 'solana_devnet';\n\nconst NETWORK_TO_CHAIN: Record<string, ChainName> = {\n 'eip155:8453': 'base',\n 'eip155:137': 'polygon',\n 'eip155:84532': 'base_sepolia',\n 'eip155:42431': 'tempo_moderato',\n 'eip155:56': 'bnb',\n 'eip155:97': 'bnb_testnet',\n 'solana:mainnet': 'solana',\n 'solana:devnet': 'solana_devnet',\n};\n\nconst CHAIN_TO_NETWORK: Record<ChainName, string> = Object.fromEntries(\n Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])\n) as Record<ChainName, string>;\n\n/** Convert an x402 `network` identifier to a MoltsPay chain name, or null if unknown. */\nexport function networkToChainName(network: string): ChainName | null {\n return NETWORK_TO_CHAIN[network] ?? null;\n}\n\n/** Convert a MoltsPay chain name to its x402 `network` identifier. */\nexport function chainNameToNetwork(chain: ChainName): string {\n return CHAIN_TO_NETWORK[chain];\n}\n\n/** Check if an x402 `network` identifier is Solana (SVM). */\nexport function isSolanaNetwork(network: string): boolean {\n return network.startsWith('solana:');\n}\n\n/** List every supported chain name. */\nexport function listSupportedChains(): ChainName[] {\n return Object.keys(CHAIN_TO_NETWORK) as ChainName[];\n}\n","/**\n * Universal base64 — works in both browser and Node without polyfills.\n *\n * Node has `Buffer`, browsers have `btoa`/`atob`. We detect at runtime to\n * avoid pulling a Buffer polyfill into the web bundle.\n */\n\ntype BufferLike = {\n from(input: string | Uint8Array, encoding?: string): { toString(enc: string): string };\n};\n\nconst BufferCtor: BufferLike | undefined =\n (globalThis as { Buffer?: BufferLike }).Buffer;\n\n/** Encode a UTF-8 string as base64 (standard, with padding). */\nexport function encodeBase64(input: string): string {\n if (BufferCtor) {\n return BufferCtor.from(input, 'utf-8').toString('base64');\n }\n // Browser path: btoa requires binary (Latin-1) string; UTF-8 → bytes → binary string.\n const bytes = new TextEncoder().encode(input);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\n/** Decode a standard base64 string into a UTF-8 string. */\nexport function decodeBase64(input: string): string {\n if (BufferCtor) {\n return BufferCtor.from(input, 'base64').toString('utf-8');\n }\n const binary = atob(input);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/** Decode a base64url string (RFC 4648 §5: `-` and `_` instead of `+` and `/`, optional padding). */\nexport function decodeBase64Url(input: string): string {\n const standard = input.replace(/-/g, '+').replace(/_/g, '/');\n const padded = standard + '='.repeat((4 - (standard.length % 4)) % 4);\n return decodeBase64(padded);\n}\n\n/** Encode a UTF-8 string as base64url (no padding). */\nexport function encodeBase64Url(input: string): string {\n return encodeBase64(input)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/** Decode a base64 string to raw bytes. Used for Solana transactions where the payload is binary. */\nexport function base64ToUint8Array(input: string): Uint8Array {\n if (BufferCtor) {\n const buf = BufferCtor.from(input, 'base64') as unknown as Uint8Array;\n return new Uint8Array(buf);\n }\n const binary = atob(input);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\n/** Encode raw bytes as a base64 string. */\nexport function uint8ArrayToBase64(bytes: Uint8Array): string {\n if (BufferCtor) {\n return BufferCtor.from(bytes).toString('base64');\n }\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n","/**\n * Structured error classes used by both Node and Web clients.\n *\n * Every error carries a `code` field so callers can branch on the kind\n * without string-matching `.message`.\n */\n\nexport class MoltsPayError extends Error {\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = 'MoltsPayError';\n this.code = code;\n }\n}\n\nexport class NotInitializedError extends MoltsPayError {\n constructor(message = 'Client not initialized') {\n super('NOT_INITIALIZED', message);\n this.name = 'NotInitializedError';\n }\n}\n\nexport class UnsupportedChainError extends MoltsPayError {\n constructor(public readonly chain: string, message?: string) {\n super('UNSUPPORTED_CHAIN', message ?? `Chain not supported: ${chain}`);\n this.name = 'UnsupportedChainError';\n }\n}\n\nexport interface NeedsApprovalDetails {\n chain: string;\n spender: string;\n token: string;\n currentAllowance: string;\n required: string;\n}\n\nexport class NeedsApprovalError extends MoltsPayError {\n constructor(public readonly details: NeedsApprovalDetails, message?: string) {\n super(\n 'NEEDS_APPROVAL',\n message ??\n `Insufficient allowance for ${details.spender}. Current=${details.currentAllowance}, required=${details.required}.`\n );\n this.name = 'NeedsApprovalError';\n }\n}\n\nexport class InsufficientBalanceError extends MoltsPayError {\n constructor(message: string) {\n super('INSUFFICIENT_BALANCE', message);\n this.name = 'InsufficientBalanceError';\n }\n}\n\nexport class SpendingLimitExceededError extends MoltsPayError {\n constructor(message: string) {\n super('SPENDING_LIMIT_EXCEEDED', message);\n this.name = 'SpendingLimitExceededError';\n }\n}\n\nexport class PaymentRejectedError extends MoltsPayError {\n constructor(message: string) {\n super('PAYMENT_REJECTED', message);\n this.name = 'PaymentRejectedError';\n }\n}\n\nexport class ServerError extends MoltsPayError {\n constructor(public readonly status: number, message: string) {\n super('SERVER_ERROR', message);\n this.name = 'ServerError';\n }\n}\n\nexport class InvalidPaymentHeaderError extends MoltsPayError {\n constructor(message: string) {\n super('INVALID_PAYMENT_HEADER', message);\n this.name = 'InvalidPaymentHeaderError';\n }\n}\n","/**\n * EIP-3009 TransferWithAuthorization typed-data builder (pure).\n *\n * Used for gasless payments on Base / Polygon / Base Sepolia where the token\n * contract (USDC, USDT variants) implements the `transferWithAuthorization`\n * function selector.\n *\n * The builder is runtime-agnostic: it returns the envelope, and the signer\n * (Node `ethers.Wallet` or Web EIP-1193 provider) does the actual signing.\n */\n\nimport type {\n EIP3009Authorization,\n TypedDataEnvelope,\n} from './types.js';\n\nexport interface BuildEIP3009Args {\n /** Payer address (will become `from`). */\n from: string;\n /** Payee address (will become `to`). */\n to: string;\n /** Token amount in the token's smallest unit (e.g. 6 decimals for USDC → 500000 = $0.50). */\n value: string;\n /** Random 32-byte nonce as 0x-prefixed hex. Caller generates with their environment's CSPRNG. */\n nonce: string;\n /** Unix seconds when the authorization starts being valid. Default 0. */\n validAfter?: string;\n /** Unix seconds when the authorization expires. Default now + 3600. */\n validBefore?: string;\n\n /** EIP-155 chain id. */\n chainId: number;\n /** Token contract address. */\n tokenAddress: string;\n /** EIP-712 domain name as declared on-chain (e.g. \"USD Coin\" for USDC Base). */\n tokenName: string;\n /** EIP-712 domain version. Usually \"2\" for USDC, varies for USDT. */\n tokenVersion: string;\n}\n\nexport const EIP3009_TYPES = {\n TransferWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const;\n\nexport function buildEIP3009TypedData(\n args: BuildEIP3009Args\n): TypedDataEnvelope<EIP3009Authorization> {\n const validAfter = args.validAfter ?? '0';\n const validBefore =\n args.validBefore ?? (Math.floor(Date.now() / 1000) + 3600).toString();\n\n const authorization: EIP3009Authorization = {\n from: args.from,\n to: args.to,\n value: args.value,\n validAfter,\n validBefore,\n nonce: args.nonce,\n };\n\n return {\n domain: {\n name: args.tokenName,\n version: args.tokenVersion,\n chainId: args.chainId,\n verifyingContract: args.tokenAddress,\n },\n types: EIP3009_TYPES,\n primaryType: 'TransferWithAuthorization',\n message: authorization,\n };\n}\n","/**\n * EIP-2612 Permit typed-data builder (pure).\n *\n * Used for gasless payments on Tempo Moderato, where the TIP-20 stablecoin\n * contracts (pathUSD, AlphaUSD, BetaUSD, ThetaUSD) implement EIP-2612 permit\n * but not EIP-3009 transferWithAuthorization.\n *\n * Domain values below were verified on-chain against the live\n * `DOMAIN_SEPARATOR()` returned by each token contract on 2026-04-21.\n * See docs/TEMPO-WEB-SUPPORT.md Section 2 and Phase 0 results in\n * docs/WEB-CLIENT-DESIGN.md.\n */\n\nimport type {\n EIP2612PermitMessage,\n TypedDataEnvelope,\n} from './types.js';\n\nexport interface BuildEIP2612PermitArgs {\n owner: string;\n spender: string;\n /** Token amount in the token's smallest unit. */\n value: string;\n /** Current `nonces(owner)` value on the token contract. Caller must read from chain. */\n nonce: string;\n /** Unix seconds until the permit expires. */\n deadline: string;\n\n chainId: number;\n /** Token contract address (the `verifyingContract` for EIP-712 domain). */\n tokenAddress: string;\n /** EIP-712 domain name as declared on-chain. See `TEMPO_EIP2612_DOMAINS` for Tempo tokens. */\n tokenName: string;\n /** EIP-712 domain version. */\n tokenVersion: string;\n}\n\nexport const EIP2612_TYPES = {\n Permit: [\n { name: 'owner', type: 'address' },\n { name: 'spender', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n} as const;\n\nexport function buildEIP2612PermitTypedData(\n args: BuildEIP2612PermitArgs\n): TypedDataEnvelope<EIP2612PermitMessage> {\n const message: EIP2612PermitMessage = {\n owner: args.owner,\n spender: args.spender,\n value: args.value,\n nonce: args.nonce,\n deadline: args.deadline,\n };\n\n return {\n domain: {\n name: args.tokenName,\n version: args.tokenVersion,\n chainId: args.chainId,\n verifyingContract: args.tokenAddress,\n },\n types: EIP2612_TYPES,\n primaryType: 'Permit',\n message,\n };\n}\n\n// ===== Tempo Moderato TIP-20 domain fixtures (verified on-chain 2026-04-21) =====\n\nexport const TEMPO_CHAIN_ID = 42431;\n\nexport interface TempoTokenDomain {\n /** Symbol case-preserved (e.g. \"pathUSD\"). */\n symbol: string;\n /** Token contract address. */\n address: string;\n /** EIP-712 domain name (first letter capitalized, e.g. \"PathUSD\"). */\n name: string;\n /** EIP-712 domain version (uniformly \"1\" across all 4 Tempo TIP-20s). */\n version: string;\n /** Expected on-chain DOMAIN_SEPARATOR. Used as a guardrail fixture in tests. */\n expectedDomainSeparator: string;\n /** Token decimals. */\n decimals: number;\n}\n\nexport const TEMPO_EIP2612_DOMAINS: Record<string, TempoTokenDomain> = {\n pathUSD: {\n symbol: 'pathUSD',\n address: '0x20c0000000000000000000000000000000000000',\n name: 'PathUSD',\n version: '1',\n expectedDomainSeparator:\n '0xc601a8a9918b2bf5076e4a47925ebe14407230ba77dc84e248c15218a46ad6b4',\n decimals: 6,\n },\n AlphaUSD: {\n symbol: 'AlphaUSD',\n address: '0x20c0000000000000000000000000000000000001',\n name: 'AlphaUSD',\n version: '1',\n expectedDomainSeparator:\n '0x32d762f61205377e7b402fe1ef8014637c3b3a18234a5629cfab1982efdc2630',\n decimals: 6,\n },\n BetaUSD: {\n symbol: 'BetaUSD',\n address: '0x20c0000000000000000000000000000000000002',\n name: 'BetaUSD',\n version: '1',\n expectedDomainSeparator:\n '0x99a494a75ff574cc1ff179a3b4f4ec0aff55b51cdd0906994aa8e91bf95137d3',\n decimals: 6,\n },\n ThetaUSD: {\n symbol: 'ThetaUSD',\n address: '0x20c0000000000000000000000000000000000003',\n name: 'ThetaUSD',\n version: '1',\n expectedDomainSeparator:\n '0x657494dec20c65c40c636bb1781412e1dd3eb5aba55cd8dc8346a00753b9a782',\n decimals: 6,\n },\n};\n\n/** Convenience: build a Permit for a Tempo token by symbol. */\nexport function buildTempoPermitTypedData(args: {\n symbol: keyof typeof TEMPO_EIP2612_DOMAINS | string;\n owner: string;\n spender: string;\n value: string;\n nonce: string;\n deadline: string;\n}): TypedDataEnvelope<EIP2612PermitMessage> {\n const token = TEMPO_EIP2612_DOMAINS[args.symbol];\n if (!token) {\n throw new Error(`Unknown Tempo token: ${args.symbol}`);\n }\n return buildEIP2612PermitTypedData({\n owner: args.owner,\n spender: args.spender,\n value: args.value,\n nonce: args.nonce,\n deadline: args.deadline,\n chainId: TEMPO_CHAIN_ID,\n tokenAddress: token.address,\n tokenName: token.name,\n tokenVersion: token.version,\n });\n}\n","/**\n * BNB PaymentIntent typed-data builder (pure).\n *\n * Used on BNB Smart Chain (mainnet 56, testnet 97) where the user has already\n * approved the MoltsPay spender contract (via `npx moltspay approve` on CLI,\n * or `client.approveBnb()` on Web) and now signs an EIP-712 intent that\n * authorizes a specific spender → payTo transfer for a specific service.\n *\n * The server's spender contract verifies the signature in `transferFrom`-like\n * flow and moves tokens from owner → payTo.\n */\n\nimport type { BnbPaymentIntent, TypedDataEnvelope } from './types.js';\n\nexport interface BuildBnbIntentArgs {\n /** Owner address (payer). */\n from: string;\n /** Recipient address (payTo). */\n to: string;\n /** Amount in token's smallest unit (BNB stablecoins use 18 decimals). */\n amount: string;\n /** Token contract address on BNB chain. */\n tokenAddress: string;\n /** Service ID the intent authorizes payment for. */\n service: string;\n /** Intent nonce — any uint256 value unique per (owner, service). Caller supplies. */\n nonce: number;\n /** Unix milliseconds when the intent expires. */\n deadline: number;\n /** Chain id (56 or 97). */\n chainId: number;\n}\n\nexport const BNB_INTENT_TYPES = {\n PaymentIntent: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'amount', type: 'uint256' },\n { name: 'token', type: 'address' },\n { name: 'service', type: 'string' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n} as const;\n\nexport const BNB_DOMAIN_NAME = 'MoltsPay';\nexport const BNB_DOMAIN_VERSION = '1';\n\nexport function buildBnbIntentTypedData(\n args: BuildBnbIntentArgs\n): TypedDataEnvelope<BnbPaymentIntent> {\n const intent: BnbPaymentIntent = {\n from: args.from,\n to: args.to,\n amount: args.amount,\n token: args.tokenAddress,\n service: args.service,\n nonce: args.nonce,\n deadline: args.deadline,\n };\n\n return {\n domain: {\n name: BNB_DOMAIN_NAME,\n version: BNB_DOMAIN_VERSION,\n chainId: args.chainId,\n },\n types: BNB_INTENT_TYPES,\n primaryType: 'PaymentIntent',\n message: intent,\n };\n}\n","/**\n * Solana Facilitator\n * \n * Pay-for-success payment settlement for Solana SPL token transfers.\n * Unlike EVM chains, Solana doesn't have a third-party facilitator - \n * we verify and settle directly on-chain.\n * \n * Flow:\n * 1. Client signs a SPL token transfer authorization\n * 2. Server receives the signed transaction\n * 3. Server verifies the signature and amount\n * 4. Server submits the transaction to settle payment\n */\n\nimport { \n Connection, \n PublicKey, \n Transaction,\n VersionedTransaction,\n sendAndConfirmTransaction,\n Keypair,\n} from '@solana/web3.js';\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getAccount,\n createAssociatedTokenAccountInstruction,\n TOKEN_PROGRAM_ID,\n} from '@solana/spl-token';\nimport { \n BaseFacilitator, \n type X402PaymentPayload, \n type X402PaymentRequirements,\n type VerifyResult,\n type SettleResult,\n type HealthCheckResult,\n} from './interface.js';\nimport { SOLANA_CHAINS, type SolanaChainName } from '../chains/solana.js';\n\n/**\n * Solana payment payload structure\n */\nexport interface SolanaPaymentPayload {\n /** Base58 encoded signed transaction */\n signedTransaction: string;\n /** Sender's public key (Base58) */\n sender: string;\n /** Chain: solana or solana_devnet */\n chain: SolanaChainName;\n}\n\n/**\n * Solana Facilitator configuration\n */\nexport interface SolanaFacilitatorConfig {\n /** Optional fee payer keypair for gasless transactions */\n feePayerKeypair?: Keypair;\n}\n\n/**\n * Solana Facilitator for pay-for-success payments\n * \n * Supports gasless mode: if feePayerKeypair is provided, server pays tx fees\n */\nexport class SolanaFacilitator extends BaseFacilitator {\n readonly name = 'solana';\n readonly displayName = 'Solana Direct';\n readonly supportedNetworks = ['solana:mainnet', 'solana:devnet'];\n\n private connections: Map<SolanaChainName, Connection> = new Map();\n private feePayerKeypair?: Keypair;\n\n constructor(config?: SolanaFacilitatorConfig) {\n super();\n this.feePayerKeypair = config?.feePayerKeypair;\n \n // Initialize connections\n for (const [chain, config] of Object.entries(SOLANA_CHAINS)) {\n this.connections.set(\n chain as SolanaChainName, \n new Connection(config.rpc, 'confirmed')\n );\n }\n \n if (this.feePayerKeypair) {\n console.log(`[SolanaFacilitator] Gasless mode enabled. Fee payer: ${this.feePayerKeypair.publicKey.toBase58()}`);\n }\n }\n \n /**\n * Get fee payer public key (for gasless transactions)\n */\n getFeePayerPubkey(): string | null {\n return this.feePayerKeypair?.publicKey.toBase58() || null;\n }\n\n private getConnection(chain: SolanaChainName): Connection {\n const conn = this.connections.get(chain);\n if (!conn) {\n throw new Error(`No connection for chain: ${chain}`);\n }\n return conn;\n }\n\n /**\n * Convert our chain name to network identifier\n */\n static chainToNetwork(chain: SolanaChainName): string {\n return chain === 'solana' ? 'solana:mainnet' : 'solana:devnet';\n }\n\n /**\n * Convert network identifier to chain name\n */\n static networkToChain(network: string): SolanaChainName | null {\n if (network === 'solana:mainnet') return 'solana';\n if (network === 'solana:devnet') return 'solana_devnet';\n return null;\n }\n\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n try {\n // Check devnet connection\n const conn = this.getConnection('solana_devnet');\n await conn.getSlot();\n return {\n healthy: true,\n latencyMs: Date.now() - start,\n };\n } catch (error: any) {\n return {\n healthy: false,\n error: error.message,\n };\n }\n }\n\n /**\n * Verify a Solana payment\n * \n * Checks:\n * 1. Transaction is valid and properly signed\n * 2. Transfer instruction matches expected amount and recipient\n */\n async verify(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<VerifyResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { valid: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const chainConfig = SOLANA_CHAINS[chain];\n if (!chainConfig) {\n return { valid: false, error: `Invalid chain: ${chain}` };\n }\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n let tx: Transaction | VersionedTransaction;\n \n try {\n // Try legacy transaction first\n tx = Transaction.from(txBuffer);\n } catch {\n // Try versioned transaction\n tx = VersionedTransaction.deserialize(txBuffer);\n }\n\n // Verify at least one signature exists (may be partial in gasless mode)\n if (tx instanceof Transaction) {\n // In gasless mode, fee payer signature is added by server\n // Client only signs for token transfer authority\n const hasAnySignature = tx.signatures.some(sig => \n sig.signature && !sig.signature.every(b => b === 0)\n );\n if (!hasAnySignature) {\n return { valid: false, error: 'Transaction not signed' };\n }\n }\n\n // Parse expected values from requirements\n const expectedAmount = BigInt(requirements.amount);\n const expectedRecipient = new PublicKey(requirements.payTo);\n\n // For now, we trust the transaction structure\n // Full verification happens at settlement time\n return {\n valid: true,\n details: {\n chain,\n sender: solanaPayload.sender,\n recipient: requirements.payTo,\n amount: requirements.amount,\n },\n };\n } catch (error: any) {\n return { valid: false, error: error.message };\n }\n }\n\n /**\n * Settle a Solana payment\n * \n * Submits the signed transaction to the network.\n * In gasless mode, adds fee payer signature before submitting.\n */\n async settle(\n paymentPayload: X402PaymentPayload,\n requirements: X402PaymentRequirements\n ): Promise<SettleResult> {\n try {\n const solanaPayload = paymentPayload.payload as SolanaPaymentPayload;\n if (!solanaPayload || !solanaPayload.signedTransaction) {\n return { success: false, error: 'Missing signed transaction' };\n }\n\n const chain = solanaPayload.chain || 'solana_devnet';\n const connection = this.getConnection(chain);\n\n // Decode the transaction\n const txBuffer = Buffer.from(solanaPayload.signedTransaction, 'base64');\n \n let txToSend: Buffer;\n \n try {\n // Try legacy transaction\n const tx = Transaction.from(txBuffer);\n \n // Check if we need to add fee payer signature (gasless mode)\n if (this.feePayerKeypair && tx.feePayer) {\n const feePayerPubkey = this.feePayerKeypair.publicKey.toBase58();\n const txFeePayer = tx.feePayer.toBase58();\n \n if (txFeePayer === feePayerPubkey) {\n // Gasless mode: add fee payer signature\n console.log(`[SolanaFacilitator] Gasless mode: adding fee payer signature`);\n tx.partialSign(this.feePayerKeypair);\n }\n }\n \n txToSend = tx.serialize();\n } catch (e: any) {\n // Fall back to versioned transaction (no gasless support for versioned yet)\n txToSend = txBuffer;\n }\n\n // Send the transaction\n const signature = await connection.sendRawTransaction(txToSend, {\n skipPreflight: false,\n preflightCommitment: 'confirmed',\n });\n\n // Wait for confirmation\n const confirmation = await connection.confirmTransaction(signature, 'confirmed');\n \n if (confirmation.value.err) {\n return {\n success: false,\n error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,\n transaction: signature,\n };\n }\n\n return {\n success: true,\n transaction: signature,\n status: 'confirmed',\n };\n } catch (error: any) {\n return { success: false, error: error.message };\n }\n }\n\n supportsNetwork(network: string): boolean {\n return this.supportedNetworks.includes(network);\n }\n}\n\n/**\n * Create a Solana payment transaction for signing\n * \n * This is called by the client to create the transaction to sign.\n * \n * @param senderPubkey - The sender's public key (token owner)\n * @param recipientPubkey - The recipient's public key\n * @param amount - Amount in token base units\n * @param chain - Solana chain (solana or solana_devnet)\n * @param feePayerPubkey - Optional fee payer public key for gasless transactions\n */\nexport async function createSolanaPaymentTransaction(\n senderPubkey: PublicKey,\n recipientPubkey: PublicKey,\n amount: bigint,\n chain: SolanaChainName,\n feePayerPubkey?: PublicKey,\n connection?: Connection,\n): Promise<Transaction> {\n const chainConfig = SOLANA_CHAINS[chain];\n const conn = connection ?? new Connection(chainConfig.rpc, 'confirmed');\n const mint = new PublicKey(chainConfig.tokens.USDC.mint);\n\n // Determine who pays fees (gasless mode uses server's fee payer)\n const actualFeePayer = feePayerPubkey || senderPubkey;\n\n // Get ATAs\n const senderATA = await getAssociatedTokenAddress(mint, senderPubkey);\n const recipientATA = await getAssociatedTokenAddress(mint, recipientPubkey);\n\n const transaction = new Transaction();\n\n // Check if recipient ATA exists\n try {\n await getAccount(conn, recipientATA);\n } catch {\n // Create ATA for recipient (fee payer pays rent in gasless mode)\n transaction.add(\n createAssociatedTokenAccountInstruction(\n actualFeePayer, // payer (fee payer in gasless mode)\n recipientATA, // ata to create\n recipientPubkey, // owner\n mint // mint\n )\n );\n }\n\n // Add transfer instruction\n transaction.add(\n createTransferCheckedInstruction(\n senderATA, // source\n mint, // mint\n recipientATA, // destination\n senderPubkey, // owner (sender still authorizes the transfer)\n amount, // amount\n chainConfig.tokens.USDC.decimals // decimals\n )\n );\n\n // Get recent blockhash\n const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();\n transaction.recentBlockhash = blockhash;\n transaction.feePayer = actualFeePayer;\n\n return transaction;\n}\n\nexport default SolanaFacilitator;\n","/**\n * Solana Chain Configuration\n * \n * Solana is NOT an EVM chain - uses different:\n * - Key format: ed25519 (EdDSA) vs secp256k1 (ECDSA)\n * - Address format: Base58 vs 0x hex\n * - Token standard: SPL vs ERC-20\n */\n\nimport { Connection, PublicKey } from '@solana/web3.js';\n\nexport interface SolanaChainConfig {\n name: string;\n cluster: 'mainnet-beta' | 'devnet' | 'testnet';\n rpc: string;\n explorer: string;\n explorerTx: string;\n tokens: {\n USDC: {\n mint: string;\n decimals: number;\n };\n };\n}\n\nexport type SolanaChainName = 'solana' | 'solana_devnet';\n\nexport const SOLANA_CHAINS: Record<SolanaChainName, SolanaChainConfig> = {\n solana: {\n name: 'Solana Mainnet',\n cluster: 'mainnet-beta',\n rpc: 'https://api.mainnet-beta.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // Circle official USDC\n decimals: 6,\n },\n },\n },\n solana_devnet: {\n name: 'Solana Devnet',\n cluster: 'devnet',\n rpc: 'https://api.devnet.solana.com',\n explorer: 'https://solscan.io/account/',\n explorerTx: 'https://solscan.io/tx/',\n tokens: {\n USDC: {\n // Circle's devnet USDC (if not available, we'll deploy our own test token)\n mint: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',\n decimals: 6,\n },\n },\n },\n};\n\n/**\n * Get Solana RPC connection\n */\nexport function getSolanaConnection(chain: SolanaChainName): Connection {\n const config = SOLANA_CHAINS[chain];\n return new Connection(config.rpc, 'confirmed');\n}\n\n/**\n * Get USDC mint public key for a Solana chain\n */\nexport function getUSDCMint(chain: SolanaChainName): PublicKey {\n return new PublicKey(SOLANA_CHAINS[chain].tokens.USDC.mint);\n}\n\n/**\n * Get Solana chain config\n */\nexport function getSolanaChain(name: SolanaChainName): SolanaChainConfig {\n const config = SOLANA_CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported Solana chain: ${name}. Supported: ${Object.keys(SOLANA_CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * Check if a chain name is a Solana chain\n */\nexport function isSolanaChain(chain: string): chain is SolanaChainName {\n return chain === 'solana' || chain === 'solana_devnet';\n}\n\n/**\n * Get explorer URL for a Solana address\n */\nexport function getSolanaExplorerUrl(chain: SolanaChainName, address: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorer}${address}${clusterParam}`;\n}\n\n/**\n * Get explorer URL for a Solana transaction\n */\nexport function getSolanaTxExplorerUrl(chain: SolanaChainName, signature: string): string {\n const config = SOLANA_CHAINS[chain];\n const clusterParam = chain === 'solana_devnet' ? '?cluster=devnet' : '';\n return `${config.explorerTx}${signature}${clusterParam}`;\n}\n","/**\n * x402 protocol primitives — pure, runtime-agnostic.\n *\n * Handles:\n * - Decoding the `X-Payment-Required` header into a list of requirements\n * (supporting v1 array / v2 object-with-accepts / single-object shapes).\n * - Selecting the right requirement for a user-chosen chain.\n * - Assembling and base64-encoding the `X-Payment` request header.\n *\n * All inputs and outputs are JSON-serializable. No network I/O, no signing.\n */\n\nimport {\n decodeBase64,\n encodeBase64,\n} from './base64.js';\nimport {\n networkToChainName,\n chainNameToNetwork,\n type ChainName,\n} from './chain-map.js';\nimport { InvalidPaymentHeaderError, UnsupportedChainError } from './errors.js';\nimport {\n X402_VERSION,\n type X402PaymentRequirements,\n} from './types.js';\n\n/**\n * Decode a base64-encoded `X-Payment-Required` header into an array of\n * requirement entries. Accepts all three observed shapes:\n * - v1: `[req1, req2, ...]`\n * - v2: `{ x402Version: 2, accepts: [req1, req2, ...] }`\n * - single: `{ scheme, network, ... }`\n */\nexport function parsePaymentRequiredHeader(\n header: string\n): X402PaymentRequirements[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(decodeBase64(header));\n } catch {\n throw new InvalidPaymentHeaderError('Invalid x-payment-required header');\n }\n\n if (Array.isArray(parsed)) {\n return parsed as X402PaymentRequirements[];\n }\n if (\n parsed &&\n typeof parsed === 'object' &&\n Array.isArray((parsed as { accepts?: unknown }).accepts)\n ) {\n return (parsed as { accepts: X402PaymentRequirements[] }).accepts;\n }\n // Single requirement object\n return [parsed as X402PaymentRequirements];\n}\n\n/** Collect the set of chain names the server accepts, from its requirements list. */\nexport function serverAcceptedChains(\n requirements: X402PaymentRequirements[]\n): ChainName[] {\n return requirements\n .map(r => networkToChainName(r.network))\n .filter((c): c is ChainName => c !== null);\n}\n\n/**\n * Select the chain to pay on, using the 1.5.x rules:\n * - If the caller specified a chain, it must be in the server's accepted set.\n * - Otherwise, default to `base` **only** when `base` is the sole accepted chain;\n * otherwise require the caller to be explicit.\n */\nexport function selectChain(\n requirements: X402PaymentRequirements[],\n userSpecifiedChain?: ChainName\n): ChainName {\n const accepted = serverAcceptedChains(requirements);\n\n if (userSpecifiedChain) {\n if (!accepted.includes(userSpecifiedChain)) {\n throw new UnsupportedChainError(\n userSpecifiedChain,\n `Server doesn't accept '${userSpecifiedChain}'. Server accepts: ${accepted.join(', ')}`\n );\n }\n return userSpecifiedChain;\n }\n\n if (accepted.length === 1 && accepted[0] === 'base') {\n return 'base';\n }\n\n throw new UnsupportedChainError(\n 'unspecified',\n `Server accepts: ${accepted.join(', ')}. Please specify a chain explicitly.`\n );\n}\n\n/** Find the requirement entry matching a given chain name. */\nexport function findRequirementForChain(\n requirements: X402PaymentRequirements[],\n chain: ChainName\n): X402PaymentRequirements | null {\n const network = chainNameToNetwork(chain);\n return requirements.find(r => r.network === network) ?? null;\n}\n\n/** x402 v2 payment payload envelope, returned by the scheme-specific builders. */\nexport interface X402PaymentPayloadEnvelope {\n x402Version: typeof X402_VERSION;\n scheme: string;\n network: string;\n payload: Record<string, unknown>;\n accepted: X402PaymentRequirements;\n}\n\n/**\n * Assemble an x402 v2 payment payload.\n *\n * `payload` is scheme-specific (EIP-3009 authorization, EIP-2612 permit\n * struct, BNB intent, Solana signed transaction, etc.). `accepted` mirrors\n * the server's requirement so settlement can verify exact match.\n */\nexport function buildPaymentPayload(args: {\n scheme: string;\n network: string;\n payload: Record<string, unknown>;\n accepted: X402PaymentRequirements;\n}): X402PaymentPayloadEnvelope {\n return {\n x402Version: X402_VERSION,\n scheme: args.scheme,\n network: args.network,\n payload: args.payload,\n accepted: args.accepted,\n };\n}\n\n/** Base64-encode a payment payload for the `X-Payment` request header. */\nexport function encodePaymentHeader(\n payload: X402PaymentPayloadEnvelope | Record<string, unknown>\n): string {\n return encodeBase64(JSON.stringify(payload));\n}\n","/**\n * Blockchain Configuration\n */\n\nimport type { ChainConfig, ChainName, EvmChainName, TokenSymbol } from '../types/index.js';\n\nexport const CHAINS: Record<EvmChainName, ChainConfig> = {\n // ============ Mainnet ============\n base: {\n name: 'Base',\n chainId: 8453,\n rpc: 'https://mainnet.base.org',\n tokens: {\n USDC: {\n address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin', // EIP-712 domain name\n },\n USDT: {\n address: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // deprecated, for backward compat\n explorer: 'https://basescan.org/address/',\n explorerTx: 'https://basescan.org/tx/',\n avgBlockTime: 2,\n },\n polygon: {\n name: 'Polygon',\n chainId: 137,\n rpc: 'https://polygon-bor-rpc.publicnode.com',\n tokens: {\n USDC: {\n address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',\n decimals: 6,\n symbol: 'USDT',\n eip712Name: '(PoS) Tether USD', // Polygon uses this name\n },\n },\n usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',\n explorer: 'https://polygonscan.com/address/',\n explorerTx: 'https://polygonscan.com/tx/',\n avgBlockTime: 2,\n },\n // ============ Testnet ============\n base_sepolia: {\n name: 'Base Sepolia',\n chainId: 84532,\n rpc: 'https://sepolia.base.org',\n tokens: {\n USDC: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'USDC', // Testnet USDC uses 'USDC' not 'USD Coin'\n },\n USDT: {\n address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Same as USDC on testnet (no official USDT)\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'USDC', // Uses same contract as USDC\n },\n },\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n explorer: 'https://sepolia.basescan.org/address/',\n explorerTx: 'https://sepolia.basescan.org/tx/',\n avgBlockTime: 2,\n },\n // ============ Tempo Testnet (Moderato) ============\n tempo_moderato: {\n name: 'Tempo Moderato',\n chainId: 42431,\n rpc: 'https://rpc.moderato.tempo.xyz',\n tokens: {\n // TIP-20 stablecoins on Tempo testnet (from mppx SDK)\n // Note: Tempo uses USD as native gas token, not ETH\n USDC: {\n address: '0x20c0000000000000000000000000000000000000', // pathUSD - primary testnet stablecoin\n decimals: 6,\n symbol: 'USDC',\n eip712Name: 'pathUSD',\n },\n USDT: {\n address: '0x20c0000000000000000000000000000000000001', // alphaUSD\n decimals: 6,\n symbol: 'USDT',\n eip712Name: 'alphaUSD',\n },\n },\n usdc: '0x20c0000000000000000000000000000000000000',\n explorer: 'https://explore.testnet.tempo.xyz/address/',\n explorerTx: 'https://explore.testnet.tempo.xyz/tx/',\n avgBlockTime: 0.5, // ~500ms finality\n },\n // ============ BNB Chain Testnet ============\n bnb_testnet: {\n name: 'BNB Testnet',\n chainId: 97,\n rpc: 'https://data-seed-prebsc-1-s1.binance.org:8545',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins (unlike Base/Polygon which use 6)\n // Using official Binance-Peg testnet tokens\n USDC: {\n address: '0x64544969ed7EBf5f083679233325356EbE738930', // Testnet USDC\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x337610d27c682E347C9cD60BD4b3b107C9d34dDd', // Testnet USDT\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x64544969ed7EBf5f083679233325356EbE738930',\n explorer: 'https://testnet.bscscan.com/address/',\n explorerTx: 'https://testnet.bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n // ============ BNB Chain Mainnet ============\n bnb: {\n name: 'BNB Smart Chain',\n chainId: 56,\n rpc: 'https://bsc-dataseed.binance.org',\n tokens: {\n // Note: BNB uses 18 decimals for stablecoins\n USDC: {\n address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n decimals: 18,\n symbol: 'USDC',\n eip712Name: 'USD Coin',\n },\n USDT: {\n address: '0x55d398326f99059fF775485246999027B3197955',\n decimals: 18,\n symbol: 'USDT',\n eip712Name: 'Tether USD',\n },\n },\n usdc: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',\n explorer: 'https://bscscan.com/address/',\n explorerTx: 'https://bscscan.com/tx/',\n avgBlockTime: 3,\n // BNB-specific: requires approval for pay-for-success flow\n requiresApproval: true,\n },\n};\n\n/**\n * Get token address for a chain\n */\nexport function getTokenAddress(chainName: EvmChainName, token: TokenSymbol): string {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n const tokenConfig = chain.tokens[token];\n if (!tokenConfig) {\n throw new Error(`Token ${token} not supported on ${chainName}`);\n }\n return tokenConfig.address;\n}\n\n/**\n * Get token config for a chain\n */\nexport function getTokenConfig(chainName: EvmChainName, token: TokenSymbol) {\n const chain = CHAINS[chainName];\n if (!chain) {\n throw new Error(`Unsupported chain: ${chainName}`);\n }\n return chain.tokens[token];\n}\n\n/**\n * Get chain configuration\n */\nexport function getChain(name: EvmChainName): ChainConfig {\n const config = CHAINS[name];\n if (!config) {\n throw new Error(`Unsupported chain: ${name}. Supported: ${Object.keys(CHAINS).join(', ')}`);\n }\n return config;\n}\n\n/**\n * List all supported EVM chains\n */\nexport function listChains(): EvmChainName[] {\n return Object.keys(CHAINS) as EvmChainName[];\n}\n\n/**\n * Get chain config by chainId\n */\nexport function getChainById(chainId: number): ChainConfig | undefined {\n return Object.values(CHAINS).find(c => c.chainId === chainId);\n}\n\n/**\n * ERC20 ABI (minimal, only required methods)\n */\nexport const ERC20_ABI = [\n 'function balanceOf(address owner) view returns (uint256)',\n 'function transfer(address to, uint256 amount) returns (bool)',\n 'function approve(address spender, uint256 amount) returns (bool)',\n 'function allowance(address owner, address spender) view returns (uint256)',\n 'function decimals() view returns (uint8)',\n 'function symbol() view returns (string)',\n 'function name() view returns (string)',\n 'function nonces(address owner) view returns (uint256)',\n 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)',\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n 'event Approval(address indexed owner, address indexed spender, uint256 value)',\n];\n\nexport type { ChainConfig, ChainName, EvmChainName, TokenSymbol };\n","/**\n * Browser-side spending limits — optional opt-in only. Mirrors the Node\n * CLI's daily-reset ledger but persists to `localStorage` instead of\n * `~/.moltspay/spending.json`.\n *\n * Default: disabled. Rationale (from docs/WEB-CLIENT-DESIGN.md §Spending\n * Limits on Web): external wallets already enforce per-signature policy,\n * per-browser localStorage limits don't sync, and the UX of silently\n * blocking a payment without consulting the wallet is worse than just\n * letting the wallet prompt. Apps that want session-level caps can still\n * opt in.\n */\n\nimport { SpendingLimitExceededError } from '../core/index.js';\n\nexport interface SpendingLimitsConfig {\n /** Maximum per single transaction. Currency-agnostic; measured in service units (USD). */\n maxPerTx: number;\n /** Maximum aggregate across the current calendar day (local time). */\n maxPerDay: number;\n /** `localStorage` key. Default: `moltspay:spending`. */\n storageKey?: string;\n}\n\ninterface PersistedSpending {\n /** `Date.setHours(0,0,0,0)` of the day the counter started. */\n date: number;\n /** Sum of charges recorded today. */\n amount: number;\n updatedAt: number;\n}\n\nconst DEFAULT_STORAGE_KEY = 'moltspay:spending';\n\n/** Minimal shape of `window.localStorage` — the only surface we touch. */\ninterface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/** Returns `globalThis.localStorage` if available, else `null` (non-browser runtime). */\nfunction getStorage(): StorageLike | null {\n const g = globalThis as { localStorage?: StorageLike };\n return g.localStorage ?? null;\n}\n\nfunction todayKey(): number {\n return new Date().setHours(0, 0, 0, 0);\n}\n\nexport class SpendingLedger {\n private readonly storageKey: string;\n private readonly maxPerTx: number;\n private readonly maxPerDay: number;\n\n constructor(config: SpendingLimitsConfig) {\n this.maxPerTx = config.maxPerTx;\n this.maxPerDay = config.maxPerDay;\n this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;\n }\n\n /** Load the persisted total for today. Fails silently on parse errors. */\n private read(): PersistedSpending {\n const storage = getStorage();\n const today = todayKey();\n if (!storage) return { date: today, amount: 0, updatedAt: Date.now() };\n try {\n const raw = storage.getItem(this.storageKey);\n if (!raw) return { date: today, amount: 0, updatedAt: Date.now() };\n const parsed = JSON.parse(raw) as PersistedSpending;\n if (parsed.date !== today) {\n // Stale day — reset in-memory. Disk is rewritten on next record.\n return { date: today, amount: 0, updatedAt: Date.now() };\n }\n return parsed;\n } catch {\n return { date: today, amount: 0, updatedAt: Date.now() };\n }\n }\n\n private write(entry: PersistedSpending): void {\n const storage = getStorage();\n if (!storage) return;\n try {\n storage.setItem(this.storageKey, JSON.stringify(entry));\n } catch {\n // Quota / disabled storage — non-fatal. Limits degrade to per-session only.\n }\n }\n\n /**\n * Throw `SpendingLimitExceededError` if the requested charge would push\n * per-tx or per-day limits over. Does NOT mutate state — callers record\n * only after the payment clears via {@link record}.\n */\n check(amount: number): void {\n if (amount > this.maxPerTx) {\n throw new SpendingLimitExceededError(\n `Amount $${amount} exceeds max per transaction ($${this.maxPerTx})`\n );\n }\n const current = this.read();\n if (current.amount + amount > this.maxPerDay) {\n throw new SpendingLimitExceededError(\n `Would exceed daily limit ($${current.amount} + $${amount} > $${this.maxPerDay})`\n );\n }\n }\n\n /** Persist a successful charge. Silently no-ops in non-browser runtimes. */\n record(amount: number): void {\n const current = this.read();\n this.write({\n date: current.date,\n amount: current.amount + amount,\n updatedAt: Date.now(),\n });\n }\n\n /** Current aggregate for the active day. Intended for UI display. */\n get todaySpending(): number {\n return this.read().amount;\n }\n\n /** Manually reset — mainly useful for tests. */\n reset(): void {\n this.write({ date: todayKey(), amount: 0, updatedAt: Date.now() });\n }\n}\n","/**\n * EIP-1193 signer adapter — wraps `window.ethereum`-style providers into the\n * runtime-agnostic `PaymentSigner` interface.\n *\n * Works with any EIP-1193 provider: MetaMask, Coinbase Wallet, Rainbow,\n * Frame, a WalletConnect transport, etc. The caller supplies the provider;\n * this module never imports a specific wallet connector.\n *\n * Methods used:\n * - `eth_requestAccounts` (getEvmAddress)\n * - `eth_signTypedData_v4` (signTypedData)\n * - `wallet_switchEthereumChain` (sendEvmTransaction pre-flight; EIP-3326)\n * - `wallet_addEthereumChain` (fallback when target chain is unknown; EIP-3085)\n * - `eth_sendTransaction` (sendEvmTransaction)\n */\n\nimport type { PaymentSigner } from '../../signer.js';\nimport type { TypedDataEnvelope } from '../../core/index.js';\nimport { PaymentRejectedError } from '../../core/index.js';\n\n/** Minimal EIP-1193 provider shape. */\nexport interface Eip1193Provider {\n request(args: { method: string; params?: unknown[] | object }): Promise<unknown>;\n}\n\n/**\n * Chain metadata passed alongside `sendEvmTransaction` so the signer can add\n * the chain to the wallet if it is not already known. Required for Tempo /\n * BNB which MetaMask does not ship preconfigured.\n *\n * Keyed by decimal chainId. The signer consults this table only when\n * `wallet_switchEthereumChain` returns the \"unknown chain\" error (code 4902).\n */\nexport interface Eip1193ChainMetadata {\n chainName: string;\n rpcUrls: string[];\n nativeCurrency: { name: string; symbol: string; decimals: number };\n blockExplorerUrls?: string[];\n}\n\nexport interface Eip1193SignerOptions {\n /** Optional chain registry for `wallet_addEthereumChain` fallbacks. */\n addChainMetadata?: Record<number, Eip1193ChainMetadata>;\n}\n\n/**\n * User-rejection EIP-1193 error codes. Mapped to `PaymentRejectedError`\n * so callers can distinguish explicit cancel from genuine failure.\n */\nconst USER_REJECTED_CODES = new Set([4001, -32603]);\n\nfunction isUserRejection(err: unknown): boolean {\n const code = (err as { code?: number })?.code;\n return code !== undefined && USER_REJECTED_CODES.has(code);\n}\n\nfunction toHexChainId(chainId: number): string {\n return '0x' + chainId.toString(16);\n}\n\n/**\n * Build an EIP-1193-backed `PaymentSigner`.\n *\n * The returned signer is stateless — every method issues a fresh provider\n * request so account changes (user switches wallet, locks, etc.) are picked\n * up on the next call. The caller is free to wrap this in their own cache if\n * account drift mid-session is a concern.\n */\nexport function eip1193Signer(\n provider: Eip1193Provider,\n options: Eip1193SignerOptions = {}\n): PaymentSigner {\n async function getEvmAddress(): Promise<string> {\n const accounts = (await provider.request({\n method: 'eth_requestAccounts',\n })) as string[];\n if (!accounts || accounts.length === 0) {\n throw new PaymentRejectedError('No EVM account available from provider');\n }\n return accounts[0];\n }\n\n async function ensureChainId(chainId: number): Promise<void> {\n const hexId = toHexChainId(chainId);\n try {\n const current = (await provider.request({ method: 'eth_chainId' })) as string;\n if (current?.toLowerCase() === hexId.toLowerCase()) return;\n await provider.request({\n method: 'wallet_switchEthereumChain',\n params: [{ chainId: hexId }],\n });\n } catch (err) {\n // EIP-3085: unknown chain — try to add it if the caller supplied metadata.\n const code = (err as { code?: number })?.code;\n if (code === 4902 || code === -32603) {\n const meta = options.addChainMetadata?.[chainId];\n if (!meta) {\n throw new Error(\n `Wallet does not know chainId ${chainId}. Provide addChainMetadata to eip1193Signer for automatic addition.`\n );\n }\n await provider.request({\n method: 'wallet_addEthereumChain',\n params: [\n {\n chainId: hexId,\n chainName: meta.chainName,\n rpcUrls: meta.rpcUrls,\n nativeCurrency: meta.nativeCurrency,\n blockExplorerUrls: meta.blockExplorerUrls,\n },\n ],\n });\n // After adding, MetaMask switches automatically; no second switch call needed.\n return;\n }\n if (isUserRejection(err)) {\n throw new PaymentRejectedError('User rejected chain switch');\n }\n throw err;\n }\n }\n\n return {\n getEvmAddress,\n\n async signTypedData<TMessage>(envelope: TypedDataEnvelope<TMessage>): Promise<string> {\n // Signing is chain-independent at the crypto layer, but MetaMask (and\n // some other EIP-1193 providers) throw \"Provider is not connected to\n // the requested chain\" on eth_signTypedData_v4 when the active chain\n // differs from envelope.domain.chainId. Switch first so the user's\n // wallet matches the x402 EIP-712 domain before the signature prompt.\n if (typeof envelope.domain?.chainId === 'number') {\n await ensureChainId(envelope.domain.chainId);\n }\n\n const from = await getEvmAddress();\n\n // eth_signTypedData_v4 expects a JSON envelope that includes the\n // implicit EIP712Domain type. Our core envelopes omit it, so we add\n // the minimal four-field form here. `salt` is not used anywhere in\n // the x402 domain set, so we don't include it in the type list.\n const typesForWire: Record<string, { name: string; type: string }[]> = {\n EIP712Domain: [\n { name: 'name', type: 'string' },\n { name: 'version', type: 'string' },\n { name: 'chainId', type: 'uint256' },\n { name: 'verifyingContract', type: 'address' },\n ],\n };\n for (const [key, fields] of Object.entries(envelope.types)) {\n typesForWire[key] = [...fields];\n }\n\n const payload = JSON.stringify({\n domain: envelope.domain,\n types: typesForWire,\n primaryType: envelope.primaryType,\n message: envelope.message,\n });\n\n try {\n return (await provider.request({\n method: 'eth_signTypedData_v4',\n params: [from, payload],\n })) as string;\n } catch (err) {\n if (isUserRejection(err)) {\n throw new PaymentRejectedError('User rejected signature');\n }\n throw err;\n }\n },\n\n async sendEvmTransaction(args: {\n chainId: number;\n to: string;\n data: string;\n value?: string;\n }): Promise<string> {\n await ensureChainId(args.chainId);\n const from = await getEvmAddress();\n try {\n return (await provider.request({\n method: 'eth_sendTransaction',\n params: [\n {\n from,\n to: args.to,\n data: args.data,\n ...(args.value ? { value: args.value } : {}),\n },\n ],\n })) as string;\n } catch (err) {\n if (isUserRejection(err)) {\n throw new PaymentRejectedError('User rejected transaction');\n }\n throw err;\n }\n },\n };\n}\n","/**\n * Solana wallet-adapter signer — wraps a wallet adapter (Phantom, Solflare,\n * Backpack, any `@solana/wallet-adapter` compatible object) into a\n * `PaymentSigner`.\n *\n * We intentionally accept only the subset of the WalletAdapter interface we\n * actually use (`publicKey` + `signTransaction`) so the caller doesn't have\n * to pull in the full `@solana/wallet-adapter-base` type transitively.\n *\n * Behavior matches `NodeSigner.signSolanaTransaction`:\n * - The wallet signs the serialized transaction and returns it base64-encoded.\n * - We never submit — the server does, after the x402 payload lands.\n * - `partialSign: true` is passed through to the adapter so the server's fee\n * payer signature is preserved (gasless Solana mode).\n */\n\nimport { Transaction } from '@solana/web3.js';\nimport type { PublicKey } from '@solana/web3.js';\nimport type { PaymentSigner } from '../../signer.js';\nimport {\n base64ToUint8Array,\n uint8ArrayToBase64,\n PaymentRejectedError,\n} from '../../core/index.js';\n\n/** Minimal Solana wallet-adapter shape the signer needs. */\nexport interface SolanaSignerAdapter {\n /** Connected account's public key, or `null` if disconnected. */\n publicKey: PublicKey | null;\n /**\n * Signs a legacy Solana `Transaction` and returns the same transaction with\n * signatures attached. Wallet adapters already implement this — we simply\n * delegate. We do NOT use `signAllTransactions` or `signMessage`.\n */\n signTransaction(tx: Transaction): Promise<Transaction>;\n}\n\n/**\n * Wallet-rejection heuristic for Solana adapters. Adapters raise\n * `WalletSignTransactionError` with `name` like `WalletSignTransactionError`\n * or messages containing \"reject\"; different wallets vary, so we err on\n * the side of propagating the original error and only surface\n * `PaymentRejectedError` when the signal is clear.\n */\nfunction isUserRejection(err: unknown): boolean {\n const name = (err as { name?: string })?.name ?? '';\n const message = String((err as { message?: string })?.message ?? '');\n return (\n name.toLowerCase().includes('reject') ||\n /user rejected|user denied|rejected by user/i.test(message)\n );\n}\n\n/**\n * Build a `PaymentSigner` backed by a Solana wallet adapter.\n *\n * The returned signer exposes only Solana capabilities — `getEvmAddress`\n * throws, because the Web Client dispatches by chain and never asks a Solana\n * signer to sign EVM data. To support both EVM and Solana in one client,\n * compose this with `eip1193Signer` via `composeSigners`.\n */\nexport function solanaSigner(adapter: SolanaSignerAdapter): PaymentSigner {\n return {\n async getEvmAddress(): Promise<string> {\n throw new Error(\n 'solanaSigner does not support EVM. Compose with eip1193Signer for multi-chain.'\n );\n },\n\n async getSolanaAddress(): Promise<string | null> {\n return adapter.publicKey ? adapter.publicKey.toBase58() : null;\n },\n\n async signTypedData(): Promise<string> {\n throw new Error('solanaSigner does not support EIP-712 signing.');\n },\n\n async signSolanaTransaction(args: {\n transactionBase64: string;\n partialSign: boolean;\n }): Promise<string> {\n if (!adapter.publicKey) {\n throw new PaymentRejectedError('Solana wallet not connected');\n }\n const tx = Transaction.from(base64ToUint8Array(args.transactionBase64));\n let signed: Transaction;\n try {\n // Wallet adapters themselves decide between full-sign and partial-sign\n // behavior based on the transaction's declared feePayer. We rely on\n // the caller passing `partialSign: true` when a feePayer is set so\n // the server-side signature slot is preserved.\n signed = await adapter.signTransaction(tx);\n } catch (err) {\n if (isUserRejection(err)) {\n throw new PaymentRejectedError('User rejected Solana signature');\n }\n throw err;\n }\n const serialized = signed.serialize({\n requireAllSignatures: false,\n verifySignatures: false,\n });\n return uint8ArrayToBase64(serialized);\n },\n };\n}\n","/**\n * `composeSigners` — merge multiple `PaymentSigner`s into one, routing each\n * method to the first underlying signer that can service it.\n *\n * Typical use: a dApp wants to pay on both EVM chains (via MetaMask) and\n * Solana (via Phantom) from the same `MoltsPayWebClient` instance. Rather\n * than re-instantiating the client per chain, the app composes the two:\n *\n * const client = new MoltsPayWebClient({\n * signer: composeSigners(\n * eip1193Signer(window.ethereum),\n * solanaSigner(phantomAdapter),\n * ),\n * });\n *\n * Routing rule: for each method (`getEvmAddress`, `signTypedData`,\n * `sendEvmTransaction`, `getSolanaAddress`, `signSolanaTransaction`), pick\n * the first signer in argument order whose method returns a successful\n * result. \"Successful\" = the method is defined on the signer AND does not\n * throw the sentinel `NOT_SUPPORTED` error the adapters emit for off-chain\n * methods (`solanaSigner.getEvmAddress` throws plainly, and that throw is\n * caught and tried on the next signer).\n *\n * Falls through to the last signer's error if none match, so callers still\n * see an actionable message.\n */\n\nimport type { PaymentSigner } from '../../signer.js';\nimport type { TypedDataEnvelope } from '../../core/index.js';\n\nexport function composeSigners(...signers: PaymentSigner[]): PaymentSigner {\n if (signers.length === 0) {\n throw new Error('composeSigners requires at least one signer');\n }\n if (signers.length === 1) {\n return signers[0];\n }\n\n async function tryEach<T>(\n pick: (s: PaymentSigner) => Promise<T> | undefined\n ): Promise<T> {\n let lastError: unknown = new Error('composeSigners: no signer supported the operation');\n for (const signer of signers) {\n const candidate = pick(signer);\n if (candidate === undefined) continue;\n try {\n return await candidate;\n } catch (err) {\n lastError = err;\n }\n }\n throw lastError;\n }\n\n return {\n getEvmAddress: () => tryEach(s => s.getEvmAddress?.()),\n\n getSolanaAddress: () =>\n tryEach<string | null>(s =>\n s.getSolanaAddress ? s.getSolanaAddress() : undefined\n ),\n\n signTypedData: <TMessage>(envelope: TypedDataEnvelope<TMessage>) =>\n tryEach(s => s.signTypedData?.(envelope)),\n\n sendEvmTransaction: (args) =>\n tryEach<string>(s =>\n s.sendEvmTransaction ? s.sendEvmTransaction(args) : undefined\n ),\n\n signSolanaTransaction: (args) =>\n tryEach<string>(s =>\n s.signSolanaTransaction ? s.signSolanaTransaction(args) : undefined\n ),\n };\n}\n"],"mappings":"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","names":["Connection","PublicKey","Connection","PublicKey","Connection","PublicKey","Transaction","isUserRejection","Transaction","Connection","owner","PublicKey"]}
@@ -1,7 +1,7 @@
1
1
  import { B as BaseFacilitator, H as HealthCheckResult, X as X402PaymentPayload, f as X402PaymentRequirements, V as VerifyResult, e as SettleResult } from '../registry-OsEO2dOu.mjs';
2
2
  export { F as Facilitator, a as FacilitatorConfig, b as FacilitatorFee, c as FacilitatorRegistry, d as FacilitatorSelection, S as SelectionStrategy, g as createRegistry, h as getDefaultRegistry } from '../registry-OsEO2dOu.mjs';
3
3
  export { C as CDPFacilitator, a as CDPFacilitatorConfig } from '../cdp-p_eHuQpb.mjs';
4
- import { Keypair, PublicKey, Transaction } from '@solana/web3.js';
4
+ import { Keypair, PublicKey, Connection, Transaction } from '@solana/web3.js';
5
5
 
6
6
  /**
7
7
  * Tempo Testnet Facilitator
@@ -20,10 +20,32 @@ declare class TempoFacilitator extends BaseFacilitator {
20
20
  readonly displayName = "Tempo Testnet";
21
21
  readonly supportedNetworks: string[];
22
22
  private rpcUrl;
23
+ private settlerWallet;
23
24
  constructor();
25
+ /**
26
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
27
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
28
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
29
+ */
30
+ getSpenderAddress(): string | null;
24
31
  healthCheck(): Promise<HealthCheckResult>;
25
32
  verify(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<VerifyResult>;
33
+ /**
34
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
35
+ * anything on-chain — actual submission happens in settlePermit().
36
+ */
37
+ private verifyPermit;
38
+ private verifyTxHash;
26
39
  settle(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<SettleResult>;
40
+ /**
41
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
42
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
43
+ * 2. pathUSD.transferFrom(owner, payTo, value)
44
+ *
45
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
46
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
47
+ */
48
+ private settlePermit;
27
49
  private getTransactionReceipt;
28
50
  }
29
51
 
@@ -243,6 +265,6 @@ declare class SolanaFacilitator extends BaseFacilitator {
243
265
  * @param chain - Solana chain (solana or solana_devnet)
244
266
  * @param feePayerPubkey - Optional fee payer public key for gasless transactions
245
267
  */
246
- declare function createSolanaPaymentTransaction(senderPubkey: PublicKey, recipientPubkey: PublicKey, amount: bigint, chain: SolanaChainName, feePayerPubkey?: PublicKey): Promise<Transaction>;
268
+ declare function createSolanaPaymentTransaction(senderPubkey: PublicKey, recipientPubkey: PublicKey, amount: bigint, chain: SolanaChainName, feePayerPubkey?: PublicKey, connection?: Connection): Promise<Transaction>;
247
269
 
248
270
  export { BNBFacilitator, type BNBPaymentIntent, BaseFacilitator, HealthCheckResult, SettleResult, SolanaFacilitator, type SolanaPaymentPayload, TempoFacilitator, VerifyResult, X402PaymentPayload, X402PaymentRequirements, createIntentTypedData, createSolanaPaymentTransaction };
@@ -1,7 +1,7 @@
1
1
  import { B as BaseFacilitator, H as HealthCheckResult, X as X402PaymentPayload, f as X402PaymentRequirements, V as VerifyResult, e as SettleResult } from '../registry-OsEO2dOu.js';
2
2
  export { F as Facilitator, a as FacilitatorConfig, b as FacilitatorFee, c as FacilitatorRegistry, d as FacilitatorSelection, S as SelectionStrategy, g as createRegistry, h as getDefaultRegistry } from '../registry-OsEO2dOu.js';
3
3
  export { C as CDPFacilitator, a as CDPFacilitatorConfig } from '../cdp-DeohBe1o.js';
4
- import { Keypair, PublicKey, Transaction } from '@solana/web3.js';
4
+ import { Keypair, PublicKey, Connection, Transaction } from '@solana/web3.js';
5
5
 
6
6
  /**
7
7
  * Tempo Testnet Facilitator
@@ -20,10 +20,32 @@ declare class TempoFacilitator extends BaseFacilitator {
20
20
  readonly displayName = "Tempo Testnet";
21
21
  readonly supportedNetworks: string[];
22
22
  private rpcUrl;
23
+ private settlerWallet;
23
24
  constructor();
25
+ /**
26
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
27
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
28
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
29
+ */
30
+ getSpenderAddress(): string | null;
24
31
  healthCheck(): Promise<HealthCheckResult>;
25
32
  verify(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<VerifyResult>;
33
+ /**
34
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
35
+ * anything on-chain — actual submission happens in settlePermit().
36
+ */
37
+ private verifyPermit;
38
+ private verifyTxHash;
26
39
  settle(paymentPayload: X402PaymentPayload, requirements: X402PaymentRequirements): Promise<SettleResult>;
40
+ /**
41
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
42
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
43
+ * 2. pathUSD.transferFrom(owner, payTo, value)
44
+ *
45
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
46
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
47
+ */
48
+ private settlePermit;
27
49
  private getTransactionReceipt;
28
50
  }
29
51
 
@@ -243,6 +265,6 @@ declare class SolanaFacilitator extends BaseFacilitator {
243
265
  * @param chain - Solana chain (solana or solana_devnet)
244
266
  * @param feePayerPubkey - Optional fee payer public key for gasless transactions
245
267
  */
246
- declare function createSolanaPaymentTransaction(senderPubkey: PublicKey, recipientPubkey: PublicKey, amount: bigint, chain: SolanaChainName, feePayerPubkey?: PublicKey): Promise<Transaction>;
268
+ declare function createSolanaPaymentTransaction(senderPubkey: PublicKey, recipientPubkey: PublicKey, amount: bigint, chain: SolanaChainName, feePayerPubkey?: PublicKey, connection?: Connection): Promise<Transaction>;
247
269
 
248
270
  export { BNBFacilitator, type BNBPaymentIntent, BaseFacilitator, HealthCheckResult, SettleResult, SolanaFacilitator, type SolanaPaymentPayload, TempoFacilitator, VerifyResult, X402PaymentPayload, X402PaymentRequirements, createIntentTypedData, createSolanaPaymentTransaction };
@@ -269,6 +269,9 @@ var CDPFacilitator = class extends BaseFacilitator {
269
269
  }
270
270
  };
271
271
 
272
+ // src/facilitators/tempo.ts
273
+ var import_ethers = require("ethers");
274
+
272
275
  // src/chains/index.ts
273
276
  var CHAINS = {
274
277
  // ============ Mainnet ============
@@ -438,15 +441,38 @@ var CHAINS = {
438
441
 
439
442
  // src/facilitators/tempo.ts
440
443
  var TRANSFER_EVENT_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
444
+ var TIP20_PERMIT_ABI = [
445
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
446
+ "function transferFrom(address from, address to, uint256 value) returns (bool)"
447
+ ];
441
448
  var TempoFacilitator = class extends BaseFacilitator {
442
449
  name = "tempo";
443
450
  displayName = "Tempo Testnet";
444
451
  supportedNetworks = ["eip155:42431"];
445
452
  // Tempo Moderato
446
453
  rpcUrl;
454
+ settlerWallet = null;
447
455
  constructor() {
448
456
  super();
449
457
  this.rpcUrl = CHAINS.tempo_moderato.rpc;
458
+ const settlerKey = process.env.TEMPO_SETTLER_KEY;
459
+ if (settlerKey) {
460
+ try {
461
+ const provider = new import_ethers.ethers.JsonRpcProvider(this.rpcUrl);
462
+ this.settlerWallet = new import_ethers.ethers.Wallet(settlerKey, provider);
463
+ } catch (err) {
464
+ console.warn("[TempoFacilitator] Invalid TEMPO_SETTLER_KEY, permit settlement disabled:", err);
465
+ this.settlerWallet = null;
466
+ }
467
+ }
468
+ }
469
+ /**
470
+ * Settler EOA address advertised to clients via `X-Payment-Required.extra.tempoSpender`.
471
+ * Web Client uses this as the `spender` field in the signed EIP-2612 Permit.
472
+ * Returns null if no TEMPO_SETTLER_KEY is configured — permit settlement unavailable.
473
+ */
474
+ getSpenderAddress() {
475
+ return this.settlerWallet?.address ?? null;
450
476
  }
451
477
  async healthCheck() {
452
478
  const start = Date.now();
@@ -472,6 +498,44 @@ var TempoFacilitator = class extends BaseFacilitator {
472
498
  }
473
499
  }
474
500
  async verify(paymentPayload, requirements) {
501
+ const inner = paymentPayload.payload;
502
+ if (inner && "permit" in inner && inner.permit) {
503
+ return this.verifyPermit(inner, requirements);
504
+ }
505
+ return this.verifyTxHash(paymentPayload, requirements);
506
+ }
507
+ /**
508
+ * Structural validation of an EIP-2612 permit payload. Does NOT submit
509
+ * anything on-chain — actual submission happens in settlePermit().
510
+ */
511
+ async verifyPermit(payload, requirements) {
512
+ if (!this.settlerWallet) {
513
+ return { valid: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
514
+ }
515
+ const p = payload.permit;
516
+ if (!p || !p.owner || !p.spender || !p.value || !p.deadline) {
517
+ return { valid: false, error: "Invalid permit payload: missing fields" };
518
+ }
519
+ if (p.spender.toLowerCase() !== this.settlerWallet.address.toLowerCase()) {
520
+ return {
521
+ valid: false,
522
+ error: `Permit spender ${p.spender} does not match configured settler ${this.settlerWallet.address}`
523
+ };
524
+ }
525
+ const deadline = BigInt(p.deadline);
526
+ const now = BigInt(Math.floor(Date.now() / 1e3));
527
+ if (deadline <= now) {
528
+ return { valid: false, error: "Permit deadline has expired" };
529
+ }
530
+ if (BigInt(p.value) < BigInt(requirements.amount || "0")) {
531
+ return {
532
+ valid: false,
533
+ error: `Permit value ${p.value} is less than required ${requirements.amount}`
534
+ };
535
+ }
536
+ return { valid: true, details: { scheme: "permit", owner: p.owner } };
537
+ }
538
+ async verifyTxHash(paymentPayload, requirements) {
475
539
  try {
476
540
  const tempoPayload = paymentPayload.payload;
477
541
  if (!tempoPayload?.txHash) {
@@ -529,7 +593,11 @@ var TempoFacilitator = class extends BaseFacilitator {
529
593
  }
530
594
  }
531
595
  async settle(paymentPayload, requirements) {
532
- const verifyResult = await this.verify(paymentPayload, requirements);
596
+ const inner = paymentPayload.payload;
597
+ if (inner && "permit" in inner && inner.permit) {
598
+ return this.settlePermit(inner, requirements);
599
+ }
600
+ const verifyResult = await this.verifyTxHash(paymentPayload, requirements);
533
601
  if (!verifyResult.valid) {
534
602
  return { success: false, error: verifyResult.error };
535
603
  }
@@ -540,6 +608,52 @@ var TempoFacilitator = class extends BaseFacilitator {
540
608
  status: "settled"
541
609
  };
542
610
  }
611
+ /**
612
+ * EIP-2612 permit settlement path. Submits two transactions on Tempo:
613
+ * 1. pathUSD.permit(owner, spender=settler, value, deadline, v, r, s)
614
+ * 2. pathUSD.transferFrom(owner, payTo, value)
615
+ *
616
+ * The settler EOA pays Tempo gas (via the TIP-20 `feeToken` mechanism — no
617
+ * native tTEMPO required; any held TIP-20 token balance covers fees).
618
+ */
619
+ async settlePermit(payload, requirements) {
620
+ if (!this.settlerWallet) {
621
+ return { success: false, error: "Permit settlement not configured (TEMPO_SETTLER_KEY missing)" };
622
+ }
623
+ if (!requirements.asset || !requirements.payTo) {
624
+ return { success: false, error: "Missing asset or payTo in requirements" };
625
+ }
626
+ const verifyResult = await this.verifyPermit(payload, requirements);
627
+ if (!verifyResult.valid) {
628
+ return { success: false, error: verifyResult.error };
629
+ }
630
+ const token = new import_ethers.ethers.Contract(requirements.asset, TIP20_PERMIT_ABI, this.settlerWallet);
631
+ const p = payload.permit;
632
+ try {
633
+ const permitTx = await token.permit(
634
+ p.owner,
635
+ p.spender,
636
+ p.value,
637
+ p.deadline,
638
+ p.v,
639
+ p.r,
640
+ p.s
641
+ );
642
+ await permitTx.wait();
643
+ const transferTx = await token.transferFrom(p.owner, requirements.payTo, p.value);
644
+ await transferTx.wait();
645
+ return {
646
+ success: true,
647
+ transaction: transferTx.hash,
648
+ status: "settled"
649
+ };
650
+ } catch (err) {
651
+ return {
652
+ success: false,
653
+ error: `Tempo permit settlement failed: ${err.message}`
654
+ };
655
+ }
656
+ }
543
657
  async getTransactionReceipt(txHash) {
544
658
  const response = await fetch(this.rpcUrl, {
545
659
  method: "POST",
@@ -782,12 +896,12 @@ var BNBFacilitator = class extends BaseFacilitator {
782
896
  return this.spenderAddress;
783
897
  }
784
898
  async getServerAddress() {
785
- const { ethers } = await import("ethers");
786
- const wallet = new ethers.Wallet(this.serverPrivateKey);
899
+ const { ethers: ethers2 } = await import("ethers");
900
+ const wallet = new ethers2.Wallet(this.serverPrivateKey);
787
901
  return wallet.address;
788
902
  }
789
903
  async recoverIntentSigner(intent, chainId) {
790
- const { ethers } = await import("ethers");
904
+ const { ethers: ethers2 } = await import("ethers");
791
905
  const domain = {
792
906
  ...EIP712_DOMAIN,
793
907
  chainId
@@ -801,7 +915,7 @@ var BNBFacilitator = class extends BaseFacilitator {
801
915
  nonce: intent.nonce,
802
916
  deadline: intent.deadline
803
917
  };
804
- const recoveredAddress = ethers.verifyTypedData(
918
+ const recoveredAddress = ethers2.verifyTypedData(
805
919
  domain,
806
920
  INTENT_TYPES,
807
921
  message,
@@ -845,10 +959,10 @@ var BNBFacilitator = class extends BaseFacilitator {
845
959
  return result.result || "0x0";
846
960
  }
847
961
  async executeTransferFrom(from, to, amount, token, rpcUrl) {
848
- const { ethers } = await import("ethers");
849
- const provider = new ethers.JsonRpcProvider(rpcUrl);
850
- const wallet = new ethers.Wallet(this.serverPrivateKey, provider);
851
- const tokenContract = new ethers.Contract(token, [
962
+ const { ethers: ethers2 } = await import("ethers");
963
+ const provider = new ethers2.JsonRpcProvider(rpcUrl);
964
+ const wallet = new ethers2.Wallet(this.serverPrivateKey, provider);
965
+ const tokenContract = new ethers2.Contract(token, [
852
966
  "function transferFrom(address from, address to, uint256 amount) returns (bool)"
853
967
  ], wallet);
854
968
  const tx = await tokenContract.transferFrom(from, to, amount);
@@ -1093,16 +1207,16 @@ var SolanaFacilitator = class extends BaseFacilitator {
1093
1207
  return this.supportedNetworks.includes(network);
1094
1208
  }
1095
1209
  };
1096
- async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
1210
+ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
1097
1211
  const chainConfig = SOLANA_CHAINS[chain];
1098
- const connection = new import_web32.Connection(chainConfig.rpc, "confirmed");
1212
+ const conn = connection ?? new import_web32.Connection(chainConfig.rpc, "confirmed");
1099
1213
  const mint = new import_web32.PublicKey(chainConfig.tokens.USDC.mint);
1100
1214
  const actualFeePayer = feePayerPubkey || senderPubkey;
1101
1215
  const senderATA = await (0, import_spl_token.getAssociatedTokenAddress)(mint, senderPubkey);
1102
1216
  const recipientATA = await (0, import_spl_token.getAssociatedTokenAddress)(mint, recipientPubkey);
1103
1217
  const transaction = new import_web32.Transaction();
1104
1218
  try {
1105
- await (0, import_spl_token.getAccount)(connection, recipientATA);
1219
+ await (0, import_spl_token.getAccount)(conn, recipientATA);
1106
1220
  } catch {
1107
1221
  transaction.add(
1108
1222
  (0, import_spl_token.createAssociatedTokenAccountInstruction)(
@@ -1133,7 +1247,7 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
1133
1247
  // decimals
1134
1248
  )
1135
1249
  );
1136
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
1250
+ const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
1137
1251
  transaction.recentBlockhash = blockhash;
1138
1252
  transaction.feePayer = actualFeePayer;
1139
1253
  return transaction;