@voyage_ai/v402-web-ts 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -182,9 +182,27 @@ function getWalletDisplayName(networkType) {
182
182
  return "Unknown Wallet";
183
183
  }
184
184
  }
185
+ function getAllConnectedWalletIds() {
186
+ if (typeof window === "undefined") {
187
+ return {};
188
+ }
189
+ try {
190
+ const cached = localStorage.getItem(CONNECTED_WALLET_IDS_KEY);
191
+ return cached ? JSON.parse(cached) : {};
192
+ } catch (error) {
193
+ console.error("Failed to parse connected wallet IDs:", error);
194
+ return {};
195
+ }
196
+ }
197
+ function getConnectedWalletId(networkType) {
198
+ const walletIds = getAllConnectedWalletIds();
199
+ return walletIds[networkType] || null;
200
+ }
201
+ var CONNECTED_WALLET_IDS_KEY;
185
202
  var init_wallet = __esm({
186
203
  "src/utils/wallet.ts"() {
187
204
  "use strict";
205
+ CONNECTED_WALLET_IDS_KEY = "connected_wallet_ids";
188
206
  }
189
207
  });
190
208
 
@@ -231,6 +249,186 @@ var import_spl_token = require("@solana/spl-token");
231
249
  // src/utils/index.ts
232
250
  init_wallet();
233
251
 
252
+ // src/utils/wallet-discovery.ts
253
+ init_wallet();
254
+ var SOLANA_WALLETS = [
255
+ {
256
+ id: "phantom",
257
+ name: "Phantom",
258
+ // Phantom official icon
259
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZD0iTTExMC41IDY0QzExMC41IDg5Ljk1NjggODkuMjAxIDExMSA2My41IDExMUg0MS41QzM2LjI1MzMgMTExIDMyIDEwNi43NDcgMzIgMTAxLjVWNTQuNUMzMiAzMS4wMjggNTEuMDI4IDEyIDc0LjUgMTJDOTcuOTcyIDEyIDExNyAzMS4wMjggMTE3IDU0LjVWNTUuNUMxMTcgNTguNTM3NiAxMTQuNTM4IDYxIDExMS41IDYxSDEwOS41QzEwNi40NjIgNjEgMTA0IDYzLjQ2MjQgMTA0IDY2LjVWNjhDMTA0IDcxLjg2NiAxMDcuMTM0IDc1IDExMSA3NUgxMTEuNUMxMTQuNTM4IDc1IDExNyA3Mi41Mzc2IDExNyA2OS41VjY0SDExMC41WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzEyOF8xMjgpIi8+CjxwYXRoIGQ9Ik00OC41IDY3QzUxLjUzNzYgNjcgNTQgNjQuNTM3NiA1NCA2MS41QzU0IDU4LjQ2MjQgNTEuNTM3NiA1NiA0OC41IDU2QzQ1LjQ2MjQgNTYgNDMgNTguNDYyNCA0MyA2MS41QzQzIDY0LjUzNzYgNDUuNDYyNCA2NyA0OC41IDY3WiIgZmlsbD0iIzFCMUIxQiIvPgo8cGF0aCBkPSJNNzMuNSA2N0M3Ni41Mzc2IDY3IDc5IDY0LjUzNzYgNzkgNjEuNUM3OSA1OC40NjI0IDc2LjUzNzYgNTYgNzMuNSA1NkM3MC40NjI0IDU2IDY4IDU4LjQ2MjQgNjggNjEuNUM2OCA2NC41Mzc2IDcwLjQ2MjQgNjcgNzMuNSA2N1oiIGZpbGw9IiMxQjFCMUIiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xMjhfMTI4IiB4MT0iMTE3IiB5MT0iMTIiIHgyPSIxMTciIHkyPSIxMTEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRkZGRiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkZGRkYiIHN0b3Atb3BhY2l0eT0iMC44MiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg==",
260
+ detect: () => window.phantom?.solana
261
+ },
262
+ {
263
+ id: "solflare",
264
+ name: "Solflare",
265
+ // Solflare icon
266
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNGQzZEMDEiLz4KPHBhdGggZD0iTTk2IDY0TDY0IDMyTDMyIDY0TDY0IDk2TDk2IDY0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+",
267
+ detect: () => window.solflare
268
+ },
269
+ {
270
+ id: "backpack",
271
+ name: "Backpack",
272
+ // Backpack icon (red coral color)
273
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNFMzM0MzAiLz4KPHBhdGggZD0iTTQwIDQ4SDg4VjgwQzg4IDg4LjgzNjYgODAuODM2NiA5NiA3MiA5Nkg1NkM0Ny4xNjM0IDk2IDQwIDg4LjgzNjYgNDAgODBWNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNTIgMzJINzZWNDhINTJWMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
274
+ detect: () => window.backpack
275
+ },
276
+ {
277
+ id: "okx-solana",
278
+ name: "OKX Wallet",
279
+ // OKX icon
280
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iNTIiIHk9IjI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSI4MCIgeT0iMjQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjI0IiB5PSI1MiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjUyIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSIyNCIgeT0iODAiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjUyIiB5PSI4MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjgwIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
281
+ detect: () => window.okxwallet?.solana
282
+ },
283
+ {
284
+ id: "coinbase-solana",
285
+ name: "Coinbase Wallet",
286
+ // Coinbase icon (blue)
287
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwMDUyRkYiLz4KPGNpcmNsZSBjeD0iNjQiIGN5PSI2NCIgcj0iMzYiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjQ4IiB5PSI1NiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0iIzAwNTJGRiIvPgo8L3N2Zz4=",
288
+ detect: () => window.coinbaseSolana
289
+ },
290
+ {
291
+ id: "trust-solana",
292
+ name: "Trust Wallet",
293
+ // Trust Wallet icon
294
+ icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwNTAwRkYiLz4KPHBhdGggZD0iTTY0IDI0QzY0IDI0IDk2IDQwIDk2IDY0Qzk2IDg4IDY0IDEwNCA2NCAxMDRDNjQgMTA0IDMyIDg4IDMyIDY0QzMyIDQwIDY0IDI0IDY0IDI0WiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSI2IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==",
295
+ detect: () => window.trustwallet?.solana
296
+ }
297
+ ];
298
+ var evmWallets = /* @__PURE__ */ new Map();
299
+ var evmDiscoveryListeners = /* @__PURE__ */ new Set();
300
+ var evmDiscoveryInitialized = false;
301
+ var currentConnectedWallet = null;
302
+ function initEVMWalletDiscovery() {
303
+ if (typeof window === "undefined" || evmDiscoveryInitialized) return;
304
+ evmDiscoveryInitialized = true;
305
+ window.addEventListener("eip6963:announceProvider", ((event) => {
306
+ const { info, provider } = event.detail;
307
+ evmWallets.set(info.uuid, { info, provider });
308
+ evmDiscoveryListeners.forEach((listener) => listener());
309
+ }));
310
+ window.dispatchEvent(new Event("eip6963:requestProvider"));
311
+ }
312
+ function getEVMWallets() {
313
+ const wallets = [];
314
+ const detectedNames = /* @__PURE__ */ new Set();
315
+ evmWallets.forEach((detail, uuid) => {
316
+ if (!detectedNames.has(detail.info.name)) {
317
+ wallets.push({
318
+ id: uuid,
319
+ name: detail.info.name,
320
+ icon: detail.info.icon,
321
+ networkType: "evm" /* EVM */,
322
+ provider: detail.provider,
323
+ installed: true
324
+ });
325
+ detectedNames.add(detail.info.name);
326
+ }
327
+ });
328
+ if (wallets.length === 0 && typeof window !== "undefined" && window.ethereum) {
329
+ const ethereum = window.ethereum;
330
+ const walletName = ethereum.isMetaMask ? "MetaMask" : ethereum.isCoinbaseWallet ? "Coinbase Wallet" : ethereum.isOkxWallet ? "OKX Wallet" : "Browser Wallet";
331
+ if (!detectedNames.has(walletName)) {
332
+ wallets.push({
333
+ id: "injected",
334
+ name: walletName,
335
+ icon: "",
336
+ // Will use first letter as avatar
337
+ networkType: "evm" /* EVM */,
338
+ provider: ethereum,
339
+ installed: true
340
+ });
341
+ }
342
+ }
343
+ return wallets;
344
+ }
345
+ function getSolanaWallets() {
346
+ if (typeof window === "undefined") return [];
347
+ const wallets = [];
348
+ const detectedProviders = /* @__PURE__ */ new Set();
349
+ const detectedNames = /* @__PURE__ */ new Set();
350
+ for (const wallet of SOLANA_WALLETS) {
351
+ const provider = wallet.detect();
352
+ if (provider && !detectedNames.has(wallet.name)) {
353
+ wallets.push({
354
+ id: wallet.id,
355
+ name: wallet.name,
356
+ icon: wallet.icon,
357
+ networkType: "solana" /* SOLANA */,
358
+ provider,
359
+ installed: true
360
+ });
361
+ detectedProviders.add(provider);
362
+ detectedNames.add(wallet.name);
363
+ }
364
+ }
365
+ const windowSolana = window.solana;
366
+ if (windowSolana && !detectedProviders.has(windowSolana)) {
367
+ const walletName = windowSolana.isPhantom ? "Phantom" : windowSolana.isSolflare ? "Solflare" : windowSolana.isBackpack ? "Backpack" : windowSolana.walletName || "Solana Wallet";
368
+ if (!detectedNames.has(walletName)) {
369
+ wallets.push({
370
+ id: "solana-unknown",
371
+ name: walletName,
372
+ icon: "",
373
+ // Will use first letter as avatar
374
+ networkType: "solana" /* SOLANA */,
375
+ provider: windowSolana,
376
+ installed: true
377
+ });
378
+ }
379
+ }
380
+ return wallets;
381
+ }
382
+ function getWalletsForNetwork(networkType) {
383
+ switch (networkType) {
384
+ case "evm" /* EVM */:
385
+ return getEVMWallets();
386
+ case "solana" /* SOLANA */:
387
+ case "svm" /* SVM */:
388
+ return getSolanaWallets();
389
+ default:
390
+ return [];
391
+ }
392
+ }
393
+ function getWalletByName(name, networkType) {
394
+ const wallets = getWalletsForNetwork(networkType);
395
+ return wallets.find((w) => w.name === name) || null;
396
+ }
397
+ function restoreConnectedWallet(networkType) {
398
+ const savedWalletName = getConnectedWalletId(networkType);
399
+ if (!savedWalletName) return null;
400
+ const wallet = getWalletByName(savedWalletName, networkType);
401
+ if (wallet) {
402
+ currentConnectedWallet = wallet;
403
+ console.log(`\u2705 Restored wallet provider: ${wallet.name}`);
404
+ return wallet;
405
+ }
406
+ console.warn(`\u26A0\uFE0F Could not find wallet with name: ${savedWalletName}`);
407
+ return null;
408
+ }
409
+ function getWalletProviderForPayment(networkType) {
410
+ if (currentConnectedWallet && currentConnectedWallet.networkType === networkType) {
411
+ return currentConnectedWallet.provider;
412
+ }
413
+ const restoredWallet = restoreConnectedWallet(networkType);
414
+ if (restoredWallet) {
415
+ return restoredWallet.provider;
416
+ }
417
+ if (typeof window === "undefined") return null;
418
+ switch (networkType) {
419
+ case "evm" /* EVM */:
420
+ return window.ethereum;
421
+ case "solana" /* SOLANA */:
422
+ case "svm" /* SVM */:
423
+ return window.phantom?.solana || window.solana;
424
+ default:
425
+ return null;
426
+ }
427
+ }
428
+ if (typeof window !== "undefined") {
429
+ initEVMWalletDiscovery();
430
+ }
431
+
234
432
  // src/services/evm/payment-header.ts
235
433
  var import_ethers = require("ethers");
236
434
  async function createEvmPaymentHeader(params) {
@@ -346,6 +544,15 @@ function getChainIdFromNetwork(network) {
346
544
 
347
545
  // src/services/evm/payment-handler.ts
348
546
  init_types();
547
+ var NETWORK_NAMES = {
548
+ 1: "Ethereum Mainnet",
549
+ 11155111: "Sepolia Testnet",
550
+ 8453: "Base Mainnet",
551
+ 84532: "Base Sepolia Testnet",
552
+ 137: "Polygon Mainnet",
553
+ 42161: "Arbitrum One",
554
+ 10: "Optimism Mainnet"
555
+ };
349
556
  async function handleEvmPayment(endpoint, config, requestInit) {
350
557
  const { wallet, network, maxPaymentAmount } = config;
351
558
  const initialResponse = await fetch(endpoint, {
@@ -356,25 +563,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
356
563
  return initialResponse;
357
564
  }
358
565
  const rawResponse = await initialResponse.json();
359
- const IGNORED_ERRORS = [
360
- "X-PAYMENT header is required",
361
- "missing X-PAYMENT header",
362
- "payment_required"
363
- ];
364
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
566
+ if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
365
567
  console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
366
- const ERROR_MESSAGES = {
367
- "insufficient_funds": "Insufficient balance to complete this payment",
368
- "invalid_signature": "Invalid payment signature",
369
- "expired": "Payment authorization has expired",
370
- "already_used": "This payment has already been used",
371
- "network_mismatch": "Payment network does not match",
372
- "invalid_payment": "Invalid payment data",
373
- "verification_failed": "Payment verification failed"
374
- };
375
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
376
- const error = new Error(errorMessage);
377
- throw wrapPaymentError(error);
568
+ const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
569
+ throw wrapPaymentError(new Error(errorMessage));
378
570
  }
379
571
  const x402Version = rawResponse.x402Version;
380
572
  const parsedPaymentRequirements = rawResponse.accepts || [];
@@ -406,19 +598,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
406
598
  console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
407
599
  }
408
600
  }
409
- const networkNames = {
410
- 1: "Ethereum Mainnet",
411
- 11155111: "Sepolia Testnet",
412
- 8453: "Base Mainnet",
413
- 84532: "Base Sepolia Testnet",
414
- 137: "Polygon Mainnet",
415
- 42161: "Arbitrum One",
416
- 10: "Optimism Mainnet"
417
- };
418
601
  if (currentChainId && currentChainId !== targetChainId) {
419
602
  if (!wallet.switchChain) {
420
- const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
421
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
603
+ const currentNetworkName = NETWORK_NAMES[currentChainId] || `Chain ${currentChainId}`;
604
+ const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
422
605
  const error = new Error(
423
606
  `Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
424
607
  );
@@ -430,7 +613,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
430
613
  console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
431
614
  } catch (error) {
432
615
  console.error("\u274C Failed to switch chain:", error);
433
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
616
+ const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
434
617
  const wrappedError = wrapPaymentError(error);
435
618
  let finalError;
436
619
  if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
@@ -484,25 +667,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
484
667
  if (retryResponse.status === 402) {
485
668
  try {
486
669
  const retryData = await retryResponse.json();
487
- const IGNORED_ERRORS2 = [
488
- "X-PAYMENT header is required",
489
- "missing X-PAYMENT header",
490
- "payment_required"
491
- ];
492
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
670
+ if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
493
671
  console.error(`\u274C Payment verification failed: ${retryData.error}`);
494
- const ERROR_MESSAGES = {
495
- "insufficient_funds": "Insufficient balance to complete this payment",
496
- "invalid_signature": "Invalid payment signature",
497
- "expired": "Payment authorization has expired",
498
- "already_used": "This payment has already been used",
499
- "network_mismatch": "Payment network does not match",
500
- "invalid_payment": "Invalid payment data",
501
- "verification_failed": "Payment verification failed"
502
- };
503
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
504
- const error = new Error(errorMessage);
505
- throw wrapPaymentError(error);
672
+ const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
673
+ throw wrapPaymentError(new Error(errorMessage));
506
674
  }
507
675
  } catch (error) {
508
676
  if (error instanceof PaymentOperationError) {
@@ -523,7 +691,7 @@ function createEvmPaymentFetch(config) {
523
691
  // src/utils/payment-helpers.ts
524
692
  var import_ethers2 = require("ethers");
525
693
  init_common();
526
- async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams) {
694
+ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams, expectedAddress) {
527
695
  const fullEndpoint = `${endpoint}/${merchantId}`;
528
696
  let response;
529
697
  const requestInit = additionalParams && Object.keys(additionalParams).length > 0 ? {
@@ -533,26 +701,41 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
533
701
  }
534
702
  } : {};
535
703
  if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
536
- const solana = window.solana;
704
+ const solana = getWalletProviderForPayment(networkType);
537
705
  if (!solana) {
538
- throw new Error("\u8BF7\u5B89\u88C5 Phantom \u94B1\u5305");
706
+ throw new Error("Please connect your Solana wallet first.");
539
707
  }
540
708
  if (!solana.isConnected) {
541
709
  await solana.connect();
542
710
  }
711
+ if (expectedAddress && solana.publicKey) {
712
+ const currentAddress = solana.publicKey.toString();
713
+ if (currentAddress !== expectedAddress) {
714
+ throw new Error(
715
+ `Wallet account mismatch: the current wallet account is ${currentAddress.slice(0, 8)}...\uFF0CBut the desired account is ${expectedAddress.slice(0, 8)}.... Please switch to the correct account in your wallet.`
716
+ );
717
+ }
718
+ }
543
719
  response = await handleSvmPayment(fullEndpoint, {
544
720
  wallet: solana,
545
721
  network: "solana"
546
722
  // Will use backend's network configuration
547
723
  }, requestInit);
548
724
  } else if (networkType === "evm" /* EVM */) {
549
- if (!window.ethereum) {
550
- throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
725
+ const ethereum = getWalletProviderForPayment(networkType);
726
+ if (!ethereum) {
727
+ throw new Error("Please connect the EVM wallet first");
551
728
  }
552
- const provider = new import_ethers2.ethers.BrowserProvider(window.ethereum);
729
+ const provider = new import_ethers2.ethers.BrowserProvider(ethereum);
553
730
  const signer = await provider.getSigner();
731
+ const currentAddress = await signer.getAddress();
732
+ if (expectedAddress && currentAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
733
+ throw new Error(
734
+ `Wallet account mismatch: the current wallet account is ${currentAddress.slice(0, 8)}...\uFF0CBut the desired account is ${expectedAddress.slice(0, 8)}.... Please switch to the correct account in your wallet.`
735
+ );
736
+ }
554
737
  const wallet = {
555
- address: await signer.getAddress(),
738
+ address: currentAddress,
556
739
  signTypedData: async (domain, types, message) => {
557
740
  return await signer.signTypedData(domain, types, message);
558
741
  },
@@ -563,7 +746,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
563
746
  },
564
747
  // Switch to a different chain
565
748
  switchChain: async (chainId) => {
566
- await window.ethereum.request({
749
+ await ethereum.request({
567
750
  method: "wallet_switchEthereumChain",
568
751
  params: [{ chainId }]
569
752
  });
@@ -575,7 +758,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
575
758
  // Will use backend's network configuration
576
759
  }, requestInit);
577
760
  } else {
578
- throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
761
+ throw new Error(`Unsupported network types: ${networkType}`);
579
762
  }
580
763
  return response;
581
764
  }
@@ -650,6 +833,21 @@ function is402Response(response) {
650
833
  }
651
834
 
652
835
  // src/utils/payment-error-handler.ts
836
+ var IGNORED_402_ERRORS = [
837
+ "X-PAYMENT header is required",
838
+ "missing X-PAYMENT header",
839
+ "payment_required"
840
+ ];
841
+ var PAYMENT_ERROR_MESSAGES = {
842
+ "insufficient_funds": "Insufficient balance to complete this payment",
843
+ "invalid_signature": "Invalid payment signature",
844
+ "expired": "Payment authorization has expired",
845
+ "already_used": "This payment has already been used",
846
+ "network_mismatch": "Payment network does not match",
847
+ "invalid_payment": "Invalid payment data",
848
+ "verification_failed": "Payment verification failed",
849
+ "invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
850
+ };
653
851
  function parsePaymentError(error) {
654
852
  if (!error) {
655
853
  return {
@@ -784,7 +982,7 @@ function wrapPaymentError(error) {
784
982
  // src/services/svm/payment-header.ts
785
983
  async function createSvmPaymentHeader(params) {
786
984
  const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
787
- const connection = new import_web3.Connection(rpcUrl, "confirmed");
985
+ const connection = new import_web3.Connection(rpcUrl);
788
986
  const feePayer = paymentRequirements?.extra?.feePayer;
789
987
  if (typeof feePayer !== "string" || !feePayer) {
790
988
  throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
@@ -798,83 +996,85 @@ async function createSvmPaymentHeader(params) {
798
996
  if (!paymentRequirements?.payTo) {
799
997
  throw new Error("Missing payTo in payment requirements");
800
998
  }
801
- const destination = new import_web3.PublicKey(paymentRequirements.payTo);
802
- const instructions = [];
803
- instructions.push(
804
- import_web3.ComputeBudgetProgram.setComputeUnitLimit({
805
- units: 7e3
806
- // Sufficient for SPL token transfer
807
- })
808
- );
809
- instructions.push(
810
- import_web3.ComputeBudgetProgram.setComputeUnitPrice({
811
- microLamports: 1
812
- // Minimal price
813
- })
814
- );
999
+ const destinationPubkey = new import_web3.PublicKey(paymentRequirements.payTo);
815
1000
  if (!paymentRequirements.asset) {
816
1001
  throw new Error("Missing token mint for SPL transfer");
817
1002
  }
818
1003
  const mintPubkey = new import_web3.PublicKey(paymentRequirements.asset);
819
- const mintInfo = await connection.getAccountInfo(mintPubkey, "confirmed");
820
- const programId = mintInfo?.owner?.toBase58() === import_spl_token.TOKEN_2022_PROGRAM_ID.toBase58() ? import_spl_token.TOKEN_2022_PROGRAM_ID : import_spl_token.TOKEN_PROGRAM_ID;
821
- const mint = await (0, import_spl_token.getMint)(connection, mintPubkey, void 0, programId);
822
- const sourceAta = await (0, import_spl_token.getAssociatedTokenAddress)(
1004
+ const mintAccountInfo = await connection.getAccountInfo(mintPubkey);
1005
+ if (!mintAccountInfo) {
1006
+ throw new Error(`Mint account ${mintPubkey.toBase58()} not found`);
1007
+ }
1008
+ const tokenProgramId = mintAccountInfo.owner.equals(import_spl_token.TOKEN_2022_PROGRAM_ID) ? import_spl_token.TOKEN_2022_PROGRAM_ID : import_spl_token.TOKEN_PROGRAM_ID;
1009
+ const mint = await (0, import_spl_token.getMint)(connection, mintPubkey, void 0, tokenProgramId);
1010
+ const sourceAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
823
1011
  mintPubkey,
824
1012
  userPubkey,
825
1013
  false,
826
- programId
1014
+ tokenProgramId
827
1015
  );
828
- const destinationAta = await (0, import_spl_token.getAssociatedTokenAddress)(
1016
+ const destinationAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
829
1017
  mintPubkey,
830
- destination,
1018
+ destinationPubkey,
831
1019
  false,
832
- programId
1020
+ tokenProgramId
833
1021
  );
834
- const sourceAtaInfo = await connection.getAccountInfo(sourceAta, "confirmed");
1022
+ const sourceAtaInfo = await connection.getAccountInfo(sourceAta);
835
1023
  if (!sourceAtaInfo) {
836
1024
  throw new Error(
837
1025
  `User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
838
1026
  );
839
1027
  }
840
- const destAtaInfo = await connection.getAccountInfo(destinationAta, "confirmed");
1028
+ const destAtaInfo = await connection.getAccountInfo(destinationAta);
841
1029
  if (!destAtaInfo) {
842
1030
  throw new Error(
843
1031
  `Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
844
1032
  );
845
1033
  }
846
- const amount = BigInt(paymentRequirements.maxAmountRequired);
847
- instructions.push(
1034
+ const instructions = [
1035
+ import_web3.ComputeBudgetProgram.setComputeUnitLimit({
1036
+ units: 7e3
1037
+ // Sufficient for SPL token transfer
1038
+ }),
1039
+ import_web3.ComputeBudgetProgram.setComputeUnitPrice({
1040
+ microLamports: 1
1041
+ // Minimal price
1042
+ }),
848
1043
  (0, import_spl_token.createTransferCheckedInstruction)(
849
1044
  sourceAta,
850
1045
  mintPubkey,
851
1046
  destinationAta,
852
1047
  userPubkey,
853
- amount,
1048
+ BigInt(paymentRequirements.maxAmountRequired),
854
1049
  mint.decimals,
855
1050
  [],
856
- programId
1051
+ tokenProgramId
857
1052
  )
858
- );
859
- const { blockhash } = await connection.getLatestBlockhash("confirmed");
860
- const message = new import_web3.TransactionMessage({
1053
+ ];
1054
+ const { blockhash } = await connection.getLatestBlockhash();
1055
+ const messageV0 = new import_web3.TransactionMessage({
861
1056
  payerKey: feePayerPubkey,
862
1057
  recentBlockhash: blockhash,
863
1058
  instructions
864
1059
  }).compileToV0Message();
865
- const transaction = new import_web3.VersionedTransaction(message);
1060
+ const transaction = new import_web3.VersionedTransaction(messageV0);
866
1061
  if (typeof wallet?.signTransaction !== "function") {
867
1062
  throw new Error("Connected wallet does not support signTransaction");
868
1063
  }
869
- let userSignedTx;
1064
+ let signedTransaction;
870
1065
  try {
871
- userSignedTx = await wallet.signTransaction(transaction);
1066
+ signedTransaction = await wallet.signTransaction(transaction);
872
1067
  console.log("\u2705 Transaction signed successfully");
873
1068
  } catch (error) {
874
1069
  console.error("\u274C Failed to sign transaction:", error);
875
1070
  throw wrapPaymentError(error);
876
1071
  }
877
- const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
1072
+ const serializedBytes = signedTransaction.serialize();
1073
+ let binary = "";
1074
+ for (let i = 0; i < serializedBytes.length; i++) {
1075
+ binary += String.fromCharCode(serializedBytes[i]);
1076
+ }
1077
+ const serializedTransaction = btoa(binary);
878
1078
  const paymentPayload = {
879
1079
  x402Version,
880
1080
  scheme: paymentRequirements.scheme,
@@ -883,7 +1083,7 @@ async function createSvmPaymentHeader(params) {
883
1083
  transaction: serializedTransaction
884
1084
  }
885
1085
  };
886
- const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
1086
+ const paymentHeader = btoa(JSON.stringify(paymentPayload));
887
1087
  return paymentHeader;
888
1088
  }
889
1089
  function getDefaultSolanaRpcUrl(network) {
@@ -899,7 +1099,7 @@ function getDefaultSolanaRpcUrl(network) {
899
1099
  // src/services/svm/payment-handler.ts
900
1100
  init_types();
901
1101
  async function handleSvmPayment(endpoint, config, requestInit) {
902
- const { wallet, network, rpcUrl, maxPaymentAmount } = config;
1102
+ const { wallet, rpcUrl, maxPaymentAmount } = config;
903
1103
  const initialResponse = await fetch(endpoint, {
904
1104
  ...requestInit,
905
1105
  method: requestInit?.method || "POST"
@@ -908,26 +1108,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
908
1108
  return initialResponse;
909
1109
  }
910
1110
  const rawResponse = await initialResponse.json();
911
- const IGNORED_ERRORS = [
912
- "X-PAYMENT header is required",
913
- "missing X-PAYMENT header",
914
- "payment_required"
915
- ];
916
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
1111
+ if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
917
1112
  console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
918
- const ERROR_MESSAGES = {
919
- "insufficient_funds": "Insufficient balance to complete this payment",
920
- "invalid_signature": "Invalid payment signature",
921
- "expired": "Payment authorization has expired",
922
- "already_used": "This payment has already been used",
923
- "network_mismatch": "Payment network does not match",
924
- "invalid_payment": "Invalid payment data",
925
- "verification_failed": "Payment verification failed",
926
- "invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
927
- };
928
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
929
- const error = new Error(errorMessage);
930
- throw wrapPaymentError(error);
1113
+ const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
1114
+ throw wrapPaymentError(new Error(errorMessage));
931
1115
  }
932
1116
  const x402Version = rawResponse.x402Version;
933
1117
  const parsedPaymentRequirements = rawResponse.accepts || [];
@@ -977,26 +1161,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
977
1161
  if (retryResponse.status === 402) {
978
1162
  try {
979
1163
  const retryData = await retryResponse.json();
980
- const IGNORED_ERRORS2 = [
981
- "X-PAYMENT header is required",
982
- "missing X-PAYMENT header",
983
- "payment_required"
984
- ];
985
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
1164
+ if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
986
1165
  console.error(`\u274C Payment verification failed: ${retryData.error}`);
987
- const ERROR_MESSAGES = {
988
- "insufficient_funds": "Insufficient balance to complete this payment",
989
- "invalid_signature": "Invalid payment signature",
990
- "expired": "Payment authorization has expired",
991
- "already_used": "This payment has already been used",
992
- "network_mismatch": "Payment network does not match",
993
- "invalid_payment": "Invalid payment data",
994
- "verification_failed": "Payment verification failed",
995
- "invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
996
- };
997
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
998
- const error = new Error(errorMessage);
999
- throw wrapPaymentError(error);
1166
+ const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
1167
+ throw wrapPaymentError(new Error(errorMessage));
1000
1168
  }
1001
1169
  } catch (error) {
1002
1170
  if (error instanceof PaymentOperationError) {