@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.mjs
CHANGED
|
@@ -173,9 +173,27 @@ function getWalletDisplayName(networkType) {
|
|
|
173
173
|
return "Unknown Wallet";
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
+
function getAllConnectedWalletIds() {
|
|
177
|
+
if (typeof window === "undefined") {
|
|
178
|
+
return {};
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const cached = localStorage.getItem(CONNECTED_WALLET_IDS_KEY);
|
|
182
|
+
return cached ? JSON.parse(cached) : {};
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error("Failed to parse connected wallet IDs:", error);
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function getConnectedWalletId(networkType) {
|
|
189
|
+
const walletIds = getAllConnectedWalletIds();
|
|
190
|
+
return walletIds[networkType] || null;
|
|
191
|
+
}
|
|
192
|
+
var CONNECTED_WALLET_IDS_KEY;
|
|
176
193
|
var init_wallet = __esm({
|
|
177
194
|
"src/utils/wallet.ts"() {
|
|
178
195
|
"use strict";
|
|
196
|
+
CONNECTED_WALLET_IDS_KEY = "connected_wallet_ids";
|
|
179
197
|
}
|
|
180
198
|
});
|
|
181
199
|
|
|
@@ -183,16 +201,10 @@ var init_wallet = __esm({
|
|
|
183
201
|
init_types();
|
|
184
202
|
|
|
185
203
|
// src/services/svm/payment-header.ts
|
|
186
|
-
import {
|
|
187
|
-
ComputeBudgetProgram,
|
|
188
|
-
Connection,
|
|
189
|
-
PublicKey,
|
|
190
|
-
TransactionMessage,
|
|
191
|
-
VersionedTransaction
|
|
192
|
-
} from "@solana/web3.js";
|
|
204
|
+
import { ComputeBudgetProgram, Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
|
193
205
|
import {
|
|
194
206
|
createTransferCheckedInstruction,
|
|
195
|
-
|
|
207
|
+
getAssociatedTokenAddressSync,
|
|
196
208
|
getMint,
|
|
197
209
|
TOKEN_2022_PROGRAM_ID,
|
|
198
210
|
TOKEN_PROGRAM_ID
|
|
@@ -201,6 +213,186 @@ import {
|
|
|
201
213
|
// src/utils/index.ts
|
|
202
214
|
init_wallet();
|
|
203
215
|
|
|
216
|
+
// src/utils/wallet-discovery.ts
|
|
217
|
+
init_wallet();
|
|
218
|
+
var SOLANA_WALLETS = [
|
|
219
|
+
{
|
|
220
|
+
id: "phantom",
|
|
221
|
+
name: "Phantom",
|
|
222
|
+
// Phantom official icon
|
|
223
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZD0iTTExMC41IDY0QzExMC41IDg5Ljk1NjggODkuMjAxIDExMSA2My41IDExMUg0MS41QzM2LjI1MzMgMTExIDMyIDEwNi43NDcgMzIgMTAxLjVWNTQuNUMzMiAzMS4wMjggNTEuMDI4IDEyIDc0LjUgMTJDOTcuOTcyIDEyIDExNyAzMS4wMjggMTE3IDU0LjVWNTUuNUMxMTcgNTguNTM3NiAxMTQuNTM4IDYxIDExMS41IDYxSDEwOS41QzEwNi40NjIgNjEgMTA0IDYzLjQ2MjQgMTA0IDY2LjVWNjhDMTA0IDcxLjg2NiAxMDcuMTM0IDc1IDExMSA3NUgxMTEuNUMxMTQuNTM4IDc1IDExNyA3Mi41Mzc2IDExNyA2OS41VjY0SDExMC41WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzEyOF8xMjgpIi8+CjxwYXRoIGQ9Ik00OC41IDY3QzUxLjUzNzYgNjcgNTQgNjQuNTM3NiA1NCA2MS41QzU0IDU4LjQ2MjQgNTEuNTM3NiA1NiA0OC41IDU2QzQ1LjQ2MjQgNTYgNDMgNTguNDYyNCA0MyA2MS41QzQzIDY0LjUzNzYgNDUuNDYyNCA2NyA0OC41IDY3WiIgZmlsbD0iIzFCMUIxQiIvPgo8cGF0aCBkPSJNNzMuNSA2N0M3Ni41Mzc2IDY3IDc5IDY0LjUzNzYgNzkgNjEuNUM3OSA1OC40NjI0IDc2LjUzNzYgNTYgNzMuNSA1NkM3MC40NjI0IDU2IDY4IDU4LjQ2MjQgNjggNjEuNUM2OCA2NC41Mzc2IDcwLjQ2MjQgNjcgNzMuNSA2N1oiIGZpbGw9IiMxQjFCMUIiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xMjhfMTI4IiB4MT0iMTE3IiB5MT0iMTIiIHgyPSIxMTciIHkyPSIxMTEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRkZGRiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkZGRkYiIHN0b3Atb3BhY2l0eT0iMC44MiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg==",
|
|
224
|
+
detect: () => window.phantom?.solana
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "solflare",
|
|
228
|
+
name: "Solflare",
|
|
229
|
+
// Solflare icon
|
|
230
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNGQzZEMDEiLz4KPHBhdGggZD0iTTk2IDY0TDY0IDMyTDMyIDY0TDY0IDk2TDk2IDY0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+",
|
|
231
|
+
detect: () => window.solflare
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: "backpack",
|
|
235
|
+
name: "Backpack",
|
|
236
|
+
// Backpack icon (red coral color)
|
|
237
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNFMzM0MzAiLz4KPHBhdGggZD0iTTQwIDQ4SDg4VjgwQzg4IDg4LjgzNjYgODAuODM2NiA5NiA3MiA5Nkg1NkM0Ny4xNjM0IDk2IDQwIDg4LjgzNjYgNDAgODBWNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNTIgMzJINzZWNDhINTJWMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
|
|
238
|
+
detect: () => window.backpack
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: "okx-solana",
|
|
242
|
+
name: "OKX Wallet",
|
|
243
|
+
// OKX icon
|
|
244
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iNTIiIHk9IjI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSI4MCIgeT0iMjQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjI0IiB5PSI1MiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjUyIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSIyNCIgeT0iODAiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjUyIiB5PSI4MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjgwIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
|
|
245
|
+
detect: () => window.okxwallet?.solana
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "coinbase-solana",
|
|
249
|
+
name: "Coinbase Wallet",
|
|
250
|
+
// Coinbase icon (blue)
|
|
251
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwMDUyRkYiLz4KPGNpcmNsZSBjeD0iNjQiIGN5PSI2NCIgcj0iMzYiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjQ4IiB5PSI1NiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0iIzAwNTJGRiIvPgo8L3N2Zz4=",
|
|
252
|
+
detect: () => window.coinbaseSolana
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: "trust-solana",
|
|
256
|
+
name: "Trust Wallet",
|
|
257
|
+
// Trust Wallet icon
|
|
258
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwNTAwRkYiLz4KPHBhdGggZD0iTTY0IDI0QzY0IDI0IDk2IDQwIDk2IDY0Qzk2IDg4IDY0IDEwNCA2NCAxMDRDNjQgMTA0IDMyIDg4IDMyIDY0QzMyIDQwIDY0IDI0IDY0IDI0WiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSI2IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==",
|
|
259
|
+
detect: () => window.trustwallet?.solana
|
|
260
|
+
}
|
|
261
|
+
];
|
|
262
|
+
var evmWallets = /* @__PURE__ */ new Map();
|
|
263
|
+
var evmDiscoveryListeners = /* @__PURE__ */ new Set();
|
|
264
|
+
var evmDiscoveryInitialized = false;
|
|
265
|
+
var currentConnectedWallet = null;
|
|
266
|
+
function initEVMWalletDiscovery() {
|
|
267
|
+
if (typeof window === "undefined" || evmDiscoveryInitialized) return;
|
|
268
|
+
evmDiscoveryInitialized = true;
|
|
269
|
+
window.addEventListener("eip6963:announceProvider", ((event) => {
|
|
270
|
+
const { info, provider } = event.detail;
|
|
271
|
+
evmWallets.set(info.uuid, { info, provider });
|
|
272
|
+
evmDiscoveryListeners.forEach((listener) => listener());
|
|
273
|
+
}));
|
|
274
|
+
window.dispatchEvent(new Event("eip6963:requestProvider"));
|
|
275
|
+
}
|
|
276
|
+
function getEVMWallets() {
|
|
277
|
+
const wallets = [];
|
|
278
|
+
const detectedNames = /* @__PURE__ */ new Set();
|
|
279
|
+
evmWallets.forEach((detail, uuid) => {
|
|
280
|
+
if (!detectedNames.has(detail.info.name)) {
|
|
281
|
+
wallets.push({
|
|
282
|
+
id: uuid,
|
|
283
|
+
name: detail.info.name,
|
|
284
|
+
icon: detail.info.icon,
|
|
285
|
+
networkType: "evm" /* EVM */,
|
|
286
|
+
provider: detail.provider,
|
|
287
|
+
installed: true
|
|
288
|
+
});
|
|
289
|
+
detectedNames.add(detail.info.name);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
if (wallets.length === 0 && typeof window !== "undefined" && window.ethereum) {
|
|
293
|
+
const ethereum = window.ethereum;
|
|
294
|
+
const walletName = ethereum.isMetaMask ? "MetaMask" : ethereum.isCoinbaseWallet ? "Coinbase Wallet" : ethereum.isOkxWallet ? "OKX Wallet" : "Browser Wallet";
|
|
295
|
+
if (!detectedNames.has(walletName)) {
|
|
296
|
+
wallets.push({
|
|
297
|
+
id: "injected",
|
|
298
|
+
name: walletName,
|
|
299
|
+
icon: "",
|
|
300
|
+
// Will use first letter as avatar
|
|
301
|
+
networkType: "evm" /* EVM */,
|
|
302
|
+
provider: ethereum,
|
|
303
|
+
installed: true
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return wallets;
|
|
308
|
+
}
|
|
309
|
+
function getSolanaWallets() {
|
|
310
|
+
if (typeof window === "undefined") return [];
|
|
311
|
+
const wallets = [];
|
|
312
|
+
const detectedProviders = /* @__PURE__ */ new Set();
|
|
313
|
+
const detectedNames = /* @__PURE__ */ new Set();
|
|
314
|
+
for (const wallet of SOLANA_WALLETS) {
|
|
315
|
+
const provider = wallet.detect();
|
|
316
|
+
if (provider && !detectedNames.has(wallet.name)) {
|
|
317
|
+
wallets.push({
|
|
318
|
+
id: wallet.id,
|
|
319
|
+
name: wallet.name,
|
|
320
|
+
icon: wallet.icon,
|
|
321
|
+
networkType: "solana" /* SOLANA */,
|
|
322
|
+
provider,
|
|
323
|
+
installed: true
|
|
324
|
+
});
|
|
325
|
+
detectedProviders.add(provider);
|
|
326
|
+
detectedNames.add(wallet.name);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const windowSolana = window.solana;
|
|
330
|
+
if (windowSolana && !detectedProviders.has(windowSolana)) {
|
|
331
|
+
const walletName = windowSolana.isPhantom ? "Phantom" : windowSolana.isSolflare ? "Solflare" : windowSolana.isBackpack ? "Backpack" : windowSolana.walletName || "Solana Wallet";
|
|
332
|
+
if (!detectedNames.has(walletName)) {
|
|
333
|
+
wallets.push({
|
|
334
|
+
id: "solana-unknown",
|
|
335
|
+
name: walletName,
|
|
336
|
+
icon: "",
|
|
337
|
+
// Will use first letter as avatar
|
|
338
|
+
networkType: "solana" /* SOLANA */,
|
|
339
|
+
provider: windowSolana,
|
|
340
|
+
installed: true
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return wallets;
|
|
345
|
+
}
|
|
346
|
+
function getWalletsForNetwork(networkType) {
|
|
347
|
+
switch (networkType) {
|
|
348
|
+
case "evm" /* EVM */:
|
|
349
|
+
return getEVMWallets();
|
|
350
|
+
case "solana" /* SOLANA */:
|
|
351
|
+
case "svm" /* SVM */:
|
|
352
|
+
return getSolanaWallets();
|
|
353
|
+
default:
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function getWalletByName(name, networkType) {
|
|
358
|
+
const wallets = getWalletsForNetwork(networkType);
|
|
359
|
+
return wallets.find((w) => w.name === name) || null;
|
|
360
|
+
}
|
|
361
|
+
function restoreConnectedWallet(networkType) {
|
|
362
|
+
const savedWalletName = getConnectedWalletId(networkType);
|
|
363
|
+
if (!savedWalletName) return null;
|
|
364
|
+
const wallet = getWalletByName(savedWalletName, networkType);
|
|
365
|
+
if (wallet) {
|
|
366
|
+
currentConnectedWallet = wallet;
|
|
367
|
+
console.log(`\u2705 Restored wallet provider: ${wallet.name}`);
|
|
368
|
+
return wallet;
|
|
369
|
+
}
|
|
370
|
+
console.warn(`\u26A0\uFE0F Could not find wallet with name: ${savedWalletName}`);
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
function getWalletProviderForPayment(networkType) {
|
|
374
|
+
if (currentConnectedWallet && currentConnectedWallet.networkType === networkType) {
|
|
375
|
+
return currentConnectedWallet.provider;
|
|
376
|
+
}
|
|
377
|
+
const restoredWallet = restoreConnectedWallet(networkType);
|
|
378
|
+
if (restoredWallet) {
|
|
379
|
+
return restoredWallet.provider;
|
|
380
|
+
}
|
|
381
|
+
if (typeof window === "undefined") return null;
|
|
382
|
+
switch (networkType) {
|
|
383
|
+
case "evm" /* EVM */:
|
|
384
|
+
return window.ethereum;
|
|
385
|
+
case "solana" /* SOLANA */:
|
|
386
|
+
case "svm" /* SVM */:
|
|
387
|
+
return window.phantom?.solana || window.solana;
|
|
388
|
+
default:
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (typeof window !== "undefined") {
|
|
393
|
+
initEVMWalletDiscovery();
|
|
394
|
+
}
|
|
395
|
+
|
|
204
396
|
// src/services/evm/payment-header.ts
|
|
205
397
|
import { ethers } from "ethers";
|
|
206
398
|
async function createEvmPaymentHeader(params) {
|
|
@@ -316,6 +508,15 @@ function getChainIdFromNetwork(network) {
|
|
|
316
508
|
|
|
317
509
|
// src/services/evm/payment-handler.ts
|
|
318
510
|
init_types();
|
|
511
|
+
var NETWORK_NAMES = {
|
|
512
|
+
1: "Ethereum Mainnet",
|
|
513
|
+
11155111: "Sepolia Testnet",
|
|
514
|
+
8453: "Base Mainnet",
|
|
515
|
+
84532: "Base Sepolia Testnet",
|
|
516
|
+
137: "Polygon Mainnet",
|
|
517
|
+
42161: "Arbitrum One",
|
|
518
|
+
10: "Optimism Mainnet"
|
|
519
|
+
};
|
|
319
520
|
async function handleEvmPayment(endpoint, config, requestInit) {
|
|
320
521
|
const { wallet, network, maxPaymentAmount } = config;
|
|
321
522
|
const initialResponse = await fetch(endpoint, {
|
|
@@ -326,25 +527,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
326
527
|
return initialResponse;
|
|
327
528
|
}
|
|
328
529
|
const rawResponse = await initialResponse.json();
|
|
329
|
-
|
|
330
|
-
"X-PAYMENT header is required",
|
|
331
|
-
"missing X-PAYMENT header",
|
|
332
|
-
"payment_required"
|
|
333
|
-
];
|
|
334
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
530
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
335
531
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
336
|
-
const
|
|
337
|
-
|
|
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);
|
|
532
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
533
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
348
534
|
}
|
|
349
535
|
const x402Version = rawResponse.x402Version;
|
|
350
536
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -376,19 +562,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
376
562
|
console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
|
|
377
563
|
}
|
|
378
564
|
}
|
|
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
565
|
if (currentChainId && currentChainId !== targetChainId) {
|
|
389
566
|
if (!wallet.switchChain) {
|
|
390
|
-
const currentNetworkName =
|
|
391
|
-
const targetNetworkName =
|
|
567
|
+
const currentNetworkName = NETWORK_NAMES[currentChainId] || `Chain ${currentChainId}`;
|
|
568
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
392
569
|
const error = new Error(
|
|
393
570
|
`Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
|
|
394
571
|
);
|
|
@@ -400,7 +577,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
400
577
|
console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
|
|
401
578
|
} catch (error) {
|
|
402
579
|
console.error("\u274C Failed to switch chain:", error);
|
|
403
|
-
const targetNetworkName =
|
|
580
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
404
581
|
const wrappedError = wrapPaymentError(error);
|
|
405
582
|
let finalError;
|
|
406
583
|
if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
|
|
@@ -454,25 +631,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
454
631
|
if (retryResponse.status === 402) {
|
|
455
632
|
try {
|
|
456
633
|
const retryData = await retryResponse.json();
|
|
457
|
-
|
|
458
|
-
"X-PAYMENT header is required",
|
|
459
|
-
"missing X-PAYMENT header",
|
|
460
|
-
"payment_required"
|
|
461
|
-
];
|
|
462
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
634
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
463
635
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
464
|
-
const
|
|
465
|
-
|
|
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);
|
|
636
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
637
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
476
638
|
}
|
|
477
639
|
} catch (error) {
|
|
478
640
|
if (error instanceof PaymentOperationError) {
|
|
@@ -493,7 +655,7 @@ function createEvmPaymentFetch(config) {
|
|
|
493
655
|
// src/utils/payment-helpers.ts
|
|
494
656
|
init_common();
|
|
495
657
|
import { ethers as ethers2 } from "ethers";
|
|
496
|
-
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams) {
|
|
658
|
+
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams, expectedAddress) {
|
|
497
659
|
const fullEndpoint = `${endpoint}/${merchantId}`;
|
|
498
660
|
let response;
|
|
499
661
|
const requestInit = additionalParams && Object.keys(additionalParams).length > 0 ? {
|
|
@@ -503,26 +665,41 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
503
665
|
}
|
|
504
666
|
} : {};
|
|
505
667
|
if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
|
|
506
|
-
const solana =
|
|
668
|
+
const solana = getWalletProviderForPayment(networkType);
|
|
507
669
|
if (!solana) {
|
|
508
|
-
throw new Error("
|
|
670
|
+
throw new Error("Please connect your Solana wallet first.");
|
|
509
671
|
}
|
|
510
672
|
if (!solana.isConnected) {
|
|
511
673
|
await solana.connect();
|
|
512
674
|
}
|
|
675
|
+
if (expectedAddress && solana.publicKey) {
|
|
676
|
+
const currentAddress = solana.publicKey.toString();
|
|
677
|
+
if (currentAddress !== expectedAddress) {
|
|
678
|
+
throw new Error(
|
|
679
|
+
`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.`
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
513
683
|
response = await handleSvmPayment(fullEndpoint, {
|
|
514
684
|
wallet: solana,
|
|
515
685
|
network: "solana"
|
|
516
686
|
// Will use backend's network configuration
|
|
517
687
|
}, requestInit);
|
|
518
688
|
} else if (networkType === "evm" /* EVM */) {
|
|
519
|
-
|
|
520
|
-
|
|
689
|
+
const ethereum = getWalletProviderForPayment(networkType);
|
|
690
|
+
if (!ethereum) {
|
|
691
|
+
throw new Error("Please connect the EVM wallet first");
|
|
521
692
|
}
|
|
522
|
-
const provider = new ethers2.BrowserProvider(
|
|
693
|
+
const provider = new ethers2.BrowserProvider(ethereum);
|
|
523
694
|
const signer = await provider.getSigner();
|
|
695
|
+
const currentAddress = await signer.getAddress();
|
|
696
|
+
if (expectedAddress && currentAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
697
|
+
throw new Error(
|
|
698
|
+
`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.`
|
|
699
|
+
);
|
|
700
|
+
}
|
|
524
701
|
const wallet = {
|
|
525
|
-
address:
|
|
702
|
+
address: currentAddress,
|
|
526
703
|
signTypedData: async (domain, types, message) => {
|
|
527
704
|
return await signer.signTypedData(domain, types, message);
|
|
528
705
|
},
|
|
@@ -533,7 +710,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
533
710
|
},
|
|
534
711
|
// Switch to a different chain
|
|
535
712
|
switchChain: async (chainId) => {
|
|
536
|
-
await
|
|
713
|
+
await ethereum.request({
|
|
537
714
|
method: "wallet_switchEthereumChain",
|
|
538
715
|
params: [{ chainId }]
|
|
539
716
|
});
|
|
@@ -545,7 +722,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
545
722
|
// Will use backend's network configuration
|
|
546
723
|
}, requestInit);
|
|
547
724
|
} else {
|
|
548
|
-
throw new Error(
|
|
725
|
+
throw new Error(`Unsupported network types: ${networkType}`);
|
|
549
726
|
}
|
|
550
727
|
return response;
|
|
551
728
|
}
|
|
@@ -620,6 +797,21 @@ function is402Response(response) {
|
|
|
620
797
|
}
|
|
621
798
|
|
|
622
799
|
// src/utils/payment-error-handler.ts
|
|
800
|
+
var IGNORED_402_ERRORS = [
|
|
801
|
+
"X-PAYMENT header is required",
|
|
802
|
+
"missing X-PAYMENT header",
|
|
803
|
+
"payment_required"
|
|
804
|
+
];
|
|
805
|
+
var PAYMENT_ERROR_MESSAGES = {
|
|
806
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
807
|
+
"invalid_signature": "Invalid payment signature",
|
|
808
|
+
"expired": "Payment authorization has expired",
|
|
809
|
+
"already_used": "This payment has already been used",
|
|
810
|
+
"network_mismatch": "Payment network does not match",
|
|
811
|
+
"invalid_payment": "Invalid payment data",
|
|
812
|
+
"verification_failed": "Payment verification failed",
|
|
813
|
+
"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."
|
|
814
|
+
};
|
|
623
815
|
function parsePaymentError(error) {
|
|
624
816
|
if (!error) {
|
|
625
817
|
return {
|
|
@@ -754,7 +946,7 @@ function wrapPaymentError(error) {
|
|
|
754
946
|
// src/services/svm/payment-header.ts
|
|
755
947
|
async function createSvmPaymentHeader(params) {
|
|
756
948
|
const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
|
|
757
|
-
const connection = new Connection(rpcUrl
|
|
949
|
+
const connection = new Connection(rpcUrl);
|
|
758
950
|
const feePayer = paymentRequirements?.extra?.feePayer;
|
|
759
951
|
if (typeof feePayer !== "string" || !feePayer) {
|
|
760
952
|
throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
|
|
@@ -768,83 +960,85 @@ async function createSvmPaymentHeader(params) {
|
|
|
768
960
|
if (!paymentRequirements?.payTo) {
|
|
769
961
|
throw new Error("Missing payTo in payment requirements");
|
|
770
962
|
}
|
|
771
|
-
const
|
|
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
|
-
);
|
|
963
|
+
const destinationPubkey = new PublicKey(paymentRequirements.payTo);
|
|
785
964
|
if (!paymentRequirements.asset) {
|
|
786
965
|
throw new Error("Missing token mint for SPL transfer");
|
|
787
966
|
}
|
|
788
967
|
const mintPubkey = new PublicKey(paymentRequirements.asset);
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
968
|
+
const mintAccountInfo = await connection.getAccountInfo(mintPubkey);
|
|
969
|
+
if (!mintAccountInfo) {
|
|
970
|
+
throw new Error(`Mint account ${mintPubkey.toBase58()} not found`);
|
|
971
|
+
}
|
|
972
|
+
const tokenProgramId = mintAccountInfo.owner.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
|
|
973
|
+
const mint = await getMint(connection, mintPubkey, void 0, tokenProgramId);
|
|
974
|
+
const sourceAta = getAssociatedTokenAddressSync(
|
|
793
975
|
mintPubkey,
|
|
794
976
|
userPubkey,
|
|
795
977
|
false,
|
|
796
|
-
|
|
978
|
+
tokenProgramId
|
|
797
979
|
);
|
|
798
|
-
const destinationAta =
|
|
980
|
+
const destinationAta = getAssociatedTokenAddressSync(
|
|
799
981
|
mintPubkey,
|
|
800
|
-
|
|
982
|
+
destinationPubkey,
|
|
801
983
|
false,
|
|
802
|
-
|
|
984
|
+
tokenProgramId
|
|
803
985
|
);
|
|
804
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta
|
|
986
|
+
const sourceAtaInfo = await connection.getAccountInfo(sourceAta);
|
|
805
987
|
if (!sourceAtaInfo) {
|
|
806
988
|
throw new Error(
|
|
807
989
|
`User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
|
|
808
990
|
);
|
|
809
991
|
}
|
|
810
|
-
const destAtaInfo = await connection.getAccountInfo(destinationAta
|
|
992
|
+
const destAtaInfo = await connection.getAccountInfo(destinationAta);
|
|
811
993
|
if (!destAtaInfo) {
|
|
812
994
|
throw new Error(
|
|
813
995
|
`Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
|
|
814
996
|
);
|
|
815
997
|
}
|
|
816
|
-
const
|
|
817
|
-
|
|
998
|
+
const instructions = [
|
|
999
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
1000
|
+
units: 7e3
|
|
1001
|
+
// Sufficient for SPL token transfer
|
|
1002
|
+
}),
|
|
1003
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
1004
|
+
microLamports: 1
|
|
1005
|
+
// Minimal price
|
|
1006
|
+
}),
|
|
818
1007
|
createTransferCheckedInstruction(
|
|
819
1008
|
sourceAta,
|
|
820
1009
|
mintPubkey,
|
|
821
1010
|
destinationAta,
|
|
822
1011
|
userPubkey,
|
|
823
|
-
|
|
1012
|
+
BigInt(paymentRequirements.maxAmountRequired),
|
|
824
1013
|
mint.decimals,
|
|
825
1014
|
[],
|
|
826
|
-
|
|
1015
|
+
tokenProgramId
|
|
827
1016
|
)
|
|
828
|
-
|
|
829
|
-
const { blockhash } = await connection.getLatestBlockhash(
|
|
830
|
-
const
|
|
1017
|
+
];
|
|
1018
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
1019
|
+
const messageV0 = new TransactionMessage({
|
|
831
1020
|
payerKey: feePayerPubkey,
|
|
832
1021
|
recentBlockhash: blockhash,
|
|
833
1022
|
instructions
|
|
834
1023
|
}).compileToV0Message();
|
|
835
|
-
const transaction = new VersionedTransaction(
|
|
1024
|
+
const transaction = new VersionedTransaction(messageV0);
|
|
836
1025
|
if (typeof wallet?.signTransaction !== "function") {
|
|
837
1026
|
throw new Error("Connected wallet does not support signTransaction");
|
|
838
1027
|
}
|
|
839
|
-
let
|
|
1028
|
+
let signedTransaction;
|
|
840
1029
|
try {
|
|
841
|
-
|
|
1030
|
+
signedTransaction = await wallet.signTransaction(transaction);
|
|
842
1031
|
console.log("\u2705 Transaction signed successfully");
|
|
843
1032
|
} catch (error) {
|
|
844
1033
|
console.error("\u274C Failed to sign transaction:", error);
|
|
845
1034
|
throw wrapPaymentError(error);
|
|
846
1035
|
}
|
|
847
|
-
const
|
|
1036
|
+
const serializedBytes = signedTransaction.serialize();
|
|
1037
|
+
let binary = "";
|
|
1038
|
+
for (let i = 0; i < serializedBytes.length; i++) {
|
|
1039
|
+
binary += String.fromCharCode(serializedBytes[i]);
|
|
1040
|
+
}
|
|
1041
|
+
const serializedTransaction = btoa(binary);
|
|
848
1042
|
const paymentPayload = {
|
|
849
1043
|
x402Version,
|
|
850
1044
|
scheme: paymentRequirements.scheme,
|
|
@@ -853,7 +1047,7 @@ async function createSvmPaymentHeader(params) {
|
|
|
853
1047
|
transaction: serializedTransaction
|
|
854
1048
|
}
|
|
855
1049
|
};
|
|
856
|
-
const paymentHeader =
|
|
1050
|
+
const paymentHeader = btoa(JSON.stringify(paymentPayload));
|
|
857
1051
|
return paymentHeader;
|
|
858
1052
|
}
|
|
859
1053
|
function getDefaultSolanaRpcUrl(network) {
|
|
@@ -869,7 +1063,7 @@ function getDefaultSolanaRpcUrl(network) {
|
|
|
869
1063
|
// src/services/svm/payment-handler.ts
|
|
870
1064
|
init_types();
|
|
871
1065
|
async function handleSvmPayment(endpoint, config, requestInit) {
|
|
872
|
-
const { wallet,
|
|
1066
|
+
const { wallet, rpcUrl, maxPaymentAmount } = config;
|
|
873
1067
|
const initialResponse = await fetch(endpoint, {
|
|
874
1068
|
...requestInit,
|
|
875
1069
|
method: requestInit?.method || "POST"
|
|
@@ -878,26 +1072,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
878
1072
|
return initialResponse;
|
|
879
1073
|
}
|
|
880
1074
|
const rawResponse = await initialResponse.json();
|
|
881
|
-
|
|
882
|
-
"X-PAYMENT header is required",
|
|
883
|
-
"missing X-PAYMENT header",
|
|
884
|
-
"payment_required"
|
|
885
|
-
];
|
|
886
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
1075
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
887
1076
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
888
|
-
const
|
|
889
|
-
|
|
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);
|
|
1077
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
1078
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
901
1079
|
}
|
|
902
1080
|
const x402Version = rawResponse.x402Version;
|
|
903
1081
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -947,26 +1125,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
947
1125
|
if (retryResponse.status === 402) {
|
|
948
1126
|
try {
|
|
949
1127
|
const retryData = await retryResponse.json();
|
|
950
|
-
|
|
951
|
-
"X-PAYMENT header is required",
|
|
952
|
-
"missing X-PAYMENT header",
|
|
953
|
-
"payment_required"
|
|
954
|
-
];
|
|
955
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
1128
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
956
1129
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
957
|
-
const
|
|
958
|
-
|
|
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);
|
|
1130
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
1131
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
970
1132
|
}
|
|
971
1133
|
} catch (error) {
|
|
972
1134
|
if (error instanceof PaymentOperationError) {
|