@voyage_ai/v402-web-ts 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -183,16 +183,10 @@ var init_wallet = __esm({
183
183
  init_types();
184
184
 
185
185
  // src/services/svm/payment-header.ts
186
- import {
187
- ComputeBudgetProgram,
188
- Connection,
189
- PublicKey,
190
- TransactionMessage,
191
- VersionedTransaction
192
- } from "@solana/web3.js";
186
+ import { ComputeBudgetProgram, Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
193
187
  import {
194
188
  createTransferCheckedInstruction,
195
- getAssociatedTokenAddress,
189
+ getAssociatedTokenAddressSync,
196
190
  getMint,
197
191
  TOKEN_2022_PROGRAM_ID,
198
192
  TOKEN_PROGRAM_ID
@@ -201,6 +195,40 @@ import {
201
195
  // src/utils/index.ts
202
196
  init_wallet();
203
197
 
198
+ // src/utils/wallet-discovery.ts
199
+ var evmWallets = /* @__PURE__ */ new Map();
200
+ var evmDiscoveryListeners = /* @__PURE__ */ new Set();
201
+ var evmDiscoveryInitialized = false;
202
+ var currentConnectedWallet = null;
203
+ function initEVMWalletDiscovery() {
204
+ if (typeof window === "undefined" || evmDiscoveryInitialized) return;
205
+ evmDiscoveryInitialized = true;
206
+ window.addEventListener("eip6963:announceProvider", ((event) => {
207
+ const { info, provider } = event.detail;
208
+ evmWallets.set(info.uuid, { info, provider });
209
+ evmDiscoveryListeners.forEach((listener) => listener());
210
+ }));
211
+ window.dispatchEvent(new Event("eip6963:requestProvider"));
212
+ }
213
+ function getWalletProviderForPayment(networkType) {
214
+ if (currentConnectedWallet && currentConnectedWallet.networkType === networkType) {
215
+ return currentConnectedWallet.provider;
216
+ }
217
+ if (typeof window === "undefined") return null;
218
+ switch (networkType) {
219
+ case "evm" /* EVM */:
220
+ return window.ethereum;
221
+ case "solana" /* SOLANA */:
222
+ case "svm" /* SVM */:
223
+ return window.phantom?.solana || window.solana;
224
+ default:
225
+ return null;
226
+ }
227
+ }
228
+ if (typeof window !== "undefined") {
229
+ initEVMWalletDiscovery();
230
+ }
231
+
204
232
  // src/services/evm/payment-header.ts
205
233
  import { ethers } from "ethers";
206
234
  async function createEvmPaymentHeader(params) {
@@ -316,6 +344,15 @@ function getChainIdFromNetwork(network) {
316
344
 
317
345
  // src/services/evm/payment-handler.ts
318
346
  init_types();
347
+ var NETWORK_NAMES = {
348
+ 1: "Ethereum Mainnet",
349
+ 11155111: "Sepolia Testnet",
350
+ 8453: "Base Mainnet",
351
+ 84532: "Base Sepolia Testnet",
352
+ 137: "Polygon Mainnet",
353
+ 42161: "Arbitrum One",
354
+ 10: "Optimism Mainnet"
355
+ };
319
356
  async function handleEvmPayment(endpoint, config, requestInit) {
320
357
  const { wallet, network, maxPaymentAmount } = config;
321
358
  const initialResponse = await fetch(endpoint, {
@@ -326,25 +363,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
326
363
  return initialResponse;
327
364
  }
328
365
  const rawResponse = await initialResponse.json();
329
- const IGNORED_ERRORS = [
330
- "X-PAYMENT header is required",
331
- "missing X-PAYMENT header",
332
- "payment_required"
333
- ];
334
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
366
+ if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
335
367
  console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
336
- const ERROR_MESSAGES = {
337
- "insufficient_funds": "Insufficient balance to complete this payment",
338
- "invalid_signature": "Invalid payment signature",
339
- "expired": "Payment authorization has expired",
340
- "already_used": "This payment has already been used",
341
- "network_mismatch": "Payment network does not match",
342
- "invalid_payment": "Invalid payment data",
343
- "verification_failed": "Payment verification failed"
344
- };
345
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
346
- const error = new Error(errorMessage);
347
- throw wrapPaymentError(error);
368
+ const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
369
+ throw wrapPaymentError(new Error(errorMessage));
348
370
  }
349
371
  const x402Version = rawResponse.x402Version;
350
372
  const parsedPaymentRequirements = rawResponse.accepts || [];
@@ -376,19 +398,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
376
398
  console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
377
399
  }
378
400
  }
379
- const networkNames = {
380
- 1: "Ethereum Mainnet",
381
- 11155111: "Sepolia Testnet",
382
- 8453: "Base Mainnet",
383
- 84532: "Base Sepolia Testnet",
384
- 137: "Polygon Mainnet",
385
- 42161: "Arbitrum One",
386
- 10: "Optimism Mainnet"
387
- };
388
401
  if (currentChainId && currentChainId !== targetChainId) {
389
402
  if (!wallet.switchChain) {
390
- const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
391
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
403
+ const currentNetworkName = NETWORK_NAMES[currentChainId] || `Chain ${currentChainId}`;
404
+ const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
392
405
  const error = new Error(
393
406
  `Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
394
407
  );
@@ -400,7 +413,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
400
413
  console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
401
414
  } catch (error) {
402
415
  console.error("\u274C Failed to switch chain:", error);
403
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
416
+ const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
404
417
  const wrappedError = wrapPaymentError(error);
405
418
  let finalError;
406
419
  if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
@@ -454,25 +467,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
454
467
  if (retryResponse.status === 402) {
455
468
  try {
456
469
  const retryData = await retryResponse.json();
457
- const IGNORED_ERRORS2 = [
458
- "X-PAYMENT header is required",
459
- "missing X-PAYMENT header",
460
- "payment_required"
461
- ];
462
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
470
+ if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
463
471
  console.error(`\u274C Payment verification failed: ${retryData.error}`);
464
- const ERROR_MESSAGES = {
465
- "insufficient_funds": "Insufficient balance to complete this payment",
466
- "invalid_signature": "Invalid payment signature",
467
- "expired": "Payment authorization has expired",
468
- "already_used": "This payment has already been used",
469
- "network_mismatch": "Payment network does not match",
470
- "invalid_payment": "Invalid payment data",
471
- "verification_failed": "Payment verification failed"
472
- };
473
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
474
- const error = new Error(errorMessage);
475
- throw wrapPaymentError(error);
472
+ const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
473
+ throw wrapPaymentError(new Error(errorMessage));
476
474
  }
477
475
  } catch (error) {
478
476
  if (error instanceof PaymentOperationError) {
@@ -503,9 +501,9 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
503
501
  }
504
502
  } : {};
505
503
  if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
506
- const solana = window.solana;
504
+ const solana = getWalletProviderForPayment(networkType);
507
505
  if (!solana) {
508
- throw new Error("\u8BF7\u5B89\u88C5 Phantom \u94B1\u5305");
506
+ throw new Error("\u8BF7\u5148\u8FDE\u63A5 Solana \u94B1\u5305");
509
507
  }
510
508
  if (!solana.isConnected) {
511
509
  await solana.connect();
@@ -516,10 +514,11 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
516
514
  // Will use backend's network configuration
517
515
  }, requestInit);
518
516
  } else if (networkType === "evm" /* EVM */) {
519
- if (!window.ethereum) {
520
- throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
517
+ const ethereum = getWalletProviderForPayment(networkType);
518
+ if (!ethereum) {
519
+ throw new Error("\u8BF7\u5148\u8FDE\u63A5 EVM \u94B1\u5305");
521
520
  }
522
- const provider = new ethers2.BrowserProvider(window.ethereum);
521
+ const provider = new ethers2.BrowserProvider(ethereum);
523
522
  const signer = await provider.getSigner();
524
523
  const wallet = {
525
524
  address: await signer.getAddress(),
@@ -533,7 +532,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
533
532
  },
534
533
  // Switch to a different chain
535
534
  switchChain: async (chainId) => {
536
- await window.ethereum.request({
535
+ await ethereum.request({
537
536
  method: "wallet_switchEthereumChain",
538
537
  params: [{ chainId }]
539
538
  });
@@ -620,6 +619,21 @@ function is402Response(response) {
620
619
  }
621
620
 
622
621
  // src/utils/payment-error-handler.ts
622
+ var IGNORED_402_ERRORS = [
623
+ "X-PAYMENT header is required",
624
+ "missing X-PAYMENT header",
625
+ "payment_required"
626
+ ];
627
+ var PAYMENT_ERROR_MESSAGES = {
628
+ "insufficient_funds": "Insufficient balance to complete this payment",
629
+ "invalid_signature": "Invalid payment signature",
630
+ "expired": "Payment authorization has expired",
631
+ "already_used": "This payment has already been used",
632
+ "network_mismatch": "Payment network does not match",
633
+ "invalid_payment": "Invalid payment data",
634
+ "verification_failed": "Payment verification failed",
635
+ "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."
636
+ };
623
637
  function parsePaymentError(error) {
624
638
  if (!error) {
625
639
  return {
@@ -754,7 +768,7 @@ function wrapPaymentError(error) {
754
768
  // src/services/svm/payment-header.ts
755
769
  async function createSvmPaymentHeader(params) {
756
770
  const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
757
- const connection = new Connection(rpcUrl, "confirmed");
771
+ const connection = new Connection(rpcUrl);
758
772
  const feePayer = paymentRequirements?.extra?.feePayer;
759
773
  if (typeof feePayer !== "string" || !feePayer) {
760
774
  throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
@@ -768,83 +782,85 @@ async function createSvmPaymentHeader(params) {
768
782
  if (!paymentRequirements?.payTo) {
769
783
  throw new Error("Missing payTo in payment requirements");
770
784
  }
771
- const destination = new PublicKey(paymentRequirements.payTo);
772
- const instructions = [];
773
- instructions.push(
774
- ComputeBudgetProgram.setComputeUnitLimit({
775
- units: 7e3
776
- // Sufficient for SPL token transfer
777
- })
778
- );
779
- instructions.push(
780
- ComputeBudgetProgram.setComputeUnitPrice({
781
- microLamports: 1
782
- // Minimal price
783
- })
784
- );
785
+ const destinationPubkey = new PublicKey(paymentRequirements.payTo);
785
786
  if (!paymentRequirements.asset) {
786
787
  throw new Error("Missing token mint for SPL transfer");
787
788
  }
788
789
  const mintPubkey = new PublicKey(paymentRequirements.asset);
789
- const mintInfo = await connection.getAccountInfo(mintPubkey, "confirmed");
790
- const programId = mintInfo?.owner?.toBase58() === TOKEN_2022_PROGRAM_ID.toBase58() ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
791
- const mint = await getMint(connection, mintPubkey, void 0, programId);
792
- const sourceAta = await getAssociatedTokenAddress(
790
+ const mintAccountInfo = await connection.getAccountInfo(mintPubkey);
791
+ if (!mintAccountInfo) {
792
+ throw new Error(`Mint account ${mintPubkey.toBase58()} not found`);
793
+ }
794
+ const tokenProgramId = mintAccountInfo.owner.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
795
+ const mint = await getMint(connection, mintPubkey, void 0, tokenProgramId);
796
+ const sourceAta = getAssociatedTokenAddressSync(
793
797
  mintPubkey,
794
798
  userPubkey,
795
799
  false,
796
- programId
800
+ tokenProgramId
797
801
  );
798
- const destinationAta = await getAssociatedTokenAddress(
802
+ const destinationAta = getAssociatedTokenAddressSync(
799
803
  mintPubkey,
800
- destination,
804
+ destinationPubkey,
801
805
  false,
802
- programId
806
+ tokenProgramId
803
807
  );
804
- const sourceAtaInfo = await connection.getAccountInfo(sourceAta, "confirmed");
808
+ const sourceAtaInfo = await connection.getAccountInfo(sourceAta);
805
809
  if (!sourceAtaInfo) {
806
810
  throw new Error(
807
811
  `User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
808
812
  );
809
813
  }
810
- const destAtaInfo = await connection.getAccountInfo(destinationAta, "confirmed");
814
+ const destAtaInfo = await connection.getAccountInfo(destinationAta);
811
815
  if (!destAtaInfo) {
812
816
  throw new Error(
813
817
  `Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
814
818
  );
815
819
  }
816
- const amount = BigInt(paymentRequirements.maxAmountRequired);
817
- instructions.push(
820
+ const instructions = [
821
+ ComputeBudgetProgram.setComputeUnitLimit({
822
+ units: 7e3
823
+ // Sufficient for SPL token transfer
824
+ }),
825
+ ComputeBudgetProgram.setComputeUnitPrice({
826
+ microLamports: 1
827
+ // Minimal price
828
+ }),
818
829
  createTransferCheckedInstruction(
819
830
  sourceAta,
820
831
  mintPubkey,
821
832
  destinationAta,
822
833
  userPubkey,
823
- amount,
834
+ BigInt(paymentRequirements.maxAmountRequired),
824
835
  mint.decimals,
825
836
  [],
826
- programId
837
+ tokenProgramId
827
838
  )
828
- );
829
- const { blockhash } = await connection.getLatestBlockhash("confirmed");
830
- const message = new TransactionMessage({
839
+ ];
840
+ const { blockhash } = await connection.getLatestBlockhash();
841
+ const messageV0 = new TransactionMessage({
831
842
  payerKey: feePayerPubkey,
832
843
  recentBlockhash: blockhash,
833
844
  instructions
834
845
  }).compileToV0Message();
835
- const transaction = new VersionedTransaction(message);
846
+ const transaction = new VersionedTransaction(messageV0);
836
847
  if (typeof wallet?.signTransaction !== "function") {
837
848
  throw new Error("Connected wallet does not support signTransaction");
838
849
  }
839
- let userSignedTx;
850
+ let signedTransaction;
840
851
  try {
841
- userSignedTx = await wallet.signTransaction(transaction);
852
+ signedTransaction = await wallet.signTransaction(transaction);
842
853
  console.log("\u2705 Transaction signed successfully");
843
854
  } catch (error) {
844
855
  console.error("\u274C Failed to sign transaction:", error);
845
856
  throw wrapPaymentError(error);
846
857
  }
847
- const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
858
+ const serializedBytes = signedTransaction.serialize();
859
+ let binary = "";
860
+ for (let i = 0; i < serializedBytes.length; i++) {
861
+ binary += String.fromCharCode(serializedBytes[i]);
862
+ }
863
+ const serializedTransaction = btoa(binary);
848
864
  const paymentPayload = {
849
865
  x402Version,
850
866
  scheme: paymentRequirements.scheme,
@@ -853,7 +869,7 @@ async function createSvmPaymentHeader(params) {
853
869
  transaction: serializedTransaction
854
870
  }
855
871
  };
856
- const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
872
+ const paymentHeader = btoa(JSON.stringify(paymentPayload));
857
873
  return paymentHeader;
858
874
  }
859
875
  function getDefaultSolanaRpcUrl(network) {
@@ -869,7 +885,7 @@ function getDefaultSolanaRpcUrl(network) {
869
885
  // src/services/svm/payment-handler.ts
870
886
  init_types();
871
887
  async function handleSvmPayment(endpoint, config, requestInit) {
872
- const { wallet, network, rpcUrl, maxPaymentAmount } = config;
888
+ const { wallet, rpcUrl, maxPaymentAmount } = config;
873
889
  const initialResponse = await fetch(endpoint, {
874
890
  ...requestInit,
875
891
  method: requestInit?.method || "POST"
@@ -878,26 +894,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
878
894
  return initialResponse;
879
895
  }
880
896
  const rawResponse = await initialResponse.json();
881
- const IGNORED_ERRORS = [
882
- "X-PAYMENT header is required",
883
- "missing X-PAYMENT header",
884
- "payment_required"
885
- ];
886
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
897
+ if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
887
898
  console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
888
- const ERROR_MESSAGES = {
889
- "insufficient_funds": "Insufficient balance to complete this payment",
890
- "invalid_signature": "Invalid payment signature",
891
- "expired": "Payment authorization has expired",
892
- "already_used": "This payment has already been used",
893
- "network_mismatch": "Payment network does not match",
894
- "invalid_payment": "Invalid payment data",
895
- "verification_failed": "Payment verification failed",
896
- "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."
897
- };
898
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
899
- const error = new Error(errorMessage);
900
- throw wrapPaymentError(error);
899
+ const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
900
+ throw wrapPaymentError(new Error(errorMessage));
901
901
  }
902
902
  const x402Version = rawResponse.x402Version;
903
903
  const parsedPaymentRequirements = rawResponse.accepts || [];
@@ -947,26 +947,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
947
947
  if (retryResponse.status === 402) {
948
948
  try {
949
949
  const retryData = await retryResponse.json();
950
- const IGNORED_ERRORS2 = [
951
- "X-PAYMENT header is required",
952
- "missing X-PAYMENT header",
953
- "payment_required"
954
- ];
955
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
950
+ if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
956
951
  console.error(`\u274C Payment verification failed: ${retryData.error}`);
957
- const ERROR_MESSAGES = {
958
- "insufficient_funds": "Insufficient balance to complete this payment",
959
- "invalid_signature": "Invalid payment signature",
960
- "expired": "Payment authorization has expired",
961
- "already_used": "This payment has already been used",
962
- "network_mismatch": "Payment network does not match",
963
- "invalid_payment": "Invalid payment data",
964
- "verification_failed": "Payment verification failed",
965
- "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."
966
- };
967
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
968
- const error = new Error(errorMessage);
969
- throw wrapPaymentError(error);
952
+ const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
953
+ throw wrapPaymentError(new Error(errorMessage));
970
954
  }
971
955
  } catch (error) {
972
956
  if (error instanceof PaymentOperationError) {