@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/README.md +8 -4
- package/dist/index.d.mts +9 -7
- package/dist/index.d.ts +9 -7
- package/dist/index.js +301 -133
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +303 -141
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +140 -11
- package/dist/react/index.d.ts +140 -11
- package/dist/react/index.js +1717 -314
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1734 -327
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/styles.css +1 -1
- package/package.json +9 -2
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
|
-
|
|
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
|
|
367
|
-
|
|
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 =
|
|
421
|
-
const targetNetworkName =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
495
|
-
|
|
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 =
|
|
704
|
+
const solana = getWalletProviderForPayment(networkType);
|
|
537
705
|
if (!solana) {
|
|
538
|
-
throw new Error("
|
|
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
|
-
|
|
550
|
-
|
|
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(
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
1014
|
+
tokenProgramId
|
|
827
1015
|
);
|
|
828
|
-
const destinationAta =
|
|
1016
|
+
const destinationAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
829
1017
|
mintPubkey,
|
|
830
|
-
|
|
1018
|
+
destinationPubkey,
|
|
831
1019
|
false,
|
|
832
|
-
|
|
1020
|
+
tokenProgramId
|
|
833
1021
|
);
|
|
834
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta
|
|
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
|
|
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
|
|
847
|
-
|
|
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
|
-
|
|
1048
|
+
BigInt(paymentRequirements.maxAmountRequired),
|
|
854
1049
|
mint.decimals,
|
|
855
1050
|
[],
|
|
856
|
-
|
|
1051
|
+
tokenProgramId
|
|
857
1052
|
)
|
|
858
|
-
|
|
859
|
-
const { blockhash } = await connection.getLatestBlockhash(
|
|
860
|
-
const
|
|
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(
|
|
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
|
|
1064
|
+
let signedTransaction;
|
|
870
1065
|
try {
|
|
871
|
-
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
|
919
|
-
|
|
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
|
-
|
|
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
|
|
988
|
-
|
|
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) {
|