@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/react/index.mjs
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
1
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
2
5
|
var __esm = (fn, res) => function __init() {
|
|
3
6
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
7
|
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
5
21
|
|
|
6
22
|
// src/types/common.ts
|
|
7
23
|
var PROD_BACK_URL;
|
|
@@ -79,6 +95,28 @@ var init_types = __esm({
|
|
|
79
95
|
});
|
|
80
96
|
|
|
81
97
|
// src/utils/wallet.ts
|
|
98
|
+
var wallet_exports = {};
|
|
99
|
+
__export(wallet_exports, {
|
|
100
|
+
clearAllWalletAddresses: () => clearAllWalletAddresses,
|
|
101
|
+
clearWalletDisconnection: () => clearWalletDisconnection,
|
|
102
|
+
formatAddress: () => formatAddress,
|
|
103
|
+
getAllConnectedWalletIds: () => getAllConnectedWalletIds,
|
|
104
|
+
getAllWalletAddresses: () => getAllWalletAddresses,
|
|
105
|
+
getCachedWalletAddress: () => getCachedWalletAddress,
|
|
106
|
+
getConnectedNetworkType: () => getConnectedNetworkType,
|
|
107
|
+
getConnectedWalletId: () => getConnectedWalletId,
|
|
108
|
+
getWalletDisplayName: () => getWalletDisplayName,
|
|
109
|
+
getWalletInstallUrl: () => getWalletInstallUrl,
|
|
110
|
+
getWalletProvider: () => getWalletProvider,
|
|
111
|
+
isWalletInstalled: () => isWalletInstalled,
|
|
112
|
+
isWalletManuallyDisconnected: () => isWalletManuallyDisconnected,
|
|
113
|
+
markWalletDisconnected: () => markWalletDisconnected,
|
|
114
|
+
removeConnectedWalletId: () => removeConnectedWalletId,
|
|
115
|
+
removeWalletAddress: () => removeWalletAddress,
|
|
116
|
+
saveConnectedNetworkType: () => saveConnectedNetworkType,
|
|
117
|
+
saveConnectedWalletId: () => saveConnectedWalletId,
|
|
118
|
+
saveWalletAddress: () => saveWalletAddress
|
|
119
|
+
});
|
|
82
120
|
function isWalletInstalled(networkType) {
|
|
83
121
|
if (typeof window === "undefined") {
|
|
84
122
|
return false;
|
|
@@ -93,6 +131,20 @@ function isWalletInstalled(networkType) {
|
|
|
93
131
|
return false;
|
|
94
132
|
}
|
|
95
133
|
}
|
|
134
|
+
function getWalletProvider(networkType) {
|
|
135
|
+
if (typeof window === "undefined") {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
switch (networkType) {
|
|
139
|
+
case "evm" /* EVM */:
|
|
140
|
+
return window.ethereum;
|
|
141
|
+
case "solana" /* SOLANA */:
|
|
142
|
+
case "svm" /* SVM */:
|
|
143
|
+
return window.solana || window.phantom;
|
|
144
|
+
default:
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
96
148
|
function formatAddress(address) {
|
|
97
149
|
if (!address || address.length < 10) {
|
|
98
150
|
return address;
|
|
@@ -167,6 +219,17 @@ function getWalletInstallUrl(networkType) {
|
|
|
167
219
|
return "#";
|
|
168
220
|
}
|
|
169
221
|
}
|
|
222
|
+
function getWalletDisplayName(networkType) {
|
|
223
|
+
switch (networkType) {
|
|
224
|
+
case "evm" /* EVM */:
|
|
225
|
+
return "MetaMask";
|
|
226
|
+
case "solana" /* SOLANA */:
|
|
227
|
+
case "svm" /* SVM */:
|
|
228
|
+
return "Phantom";
|
|
229
|
+
default:
|
|
230
|
+
return "Unknown Wallet";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
170
233
|
function getAllWalletAddresses() {
|
|
171
234
|
if (typeof window === "undefined") {
|
|
172
235
|
return {};
|
|
@@ -199,7 +262,44 @@ function removeWalletAddress(networkType) {
|
|
|
199
262
|
delete addresses[networkType];
|
|
200
263
|
localStorage.setItem(WALLET_ADDRESSES_KEY, JSON.stringify(addresses));
|
|
201
264
|
}
|
|
202
|
-
|
|
265
|
+
function clearAllWalletAddresses() {
|
|
266
|
+
if (typeof window !== "undefined") {
|
|
267
|
+
localStorage.removeItem(WALLET_ADDRESSES_KEY);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function getAllConnectedWalletIds() {
|
|
271
|
+
if (typeof window === "undefined") {
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const cached = localStorage.getItem(CONNECTED_WALLET_IDS_KEY);
|
|
276
|
+
return cached ? JSON.parse(cached) : {};
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error("Failed to parse connected wallet IDs:", error);
|
|
279
|
+
return {};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function saveConnectedWalletId(networkType, walletId) {
|
|
283
|
+
if (typeof window === "undefined") {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const walletIds = getAllConnectedWalletIds();
|
|
287
|
+
walletIds[networkType] = walletId;
|
|
288
|
+
localStorage.setItem(CONNECTED_WALLET_IDS_KEY, JSON.stringify(walletIds));
|
|
289
|
+
}
|
|
290
|
+
function getConnectedWalletId(networkType) {
|
|
291
|
+
const walletIds = getAllConnectedWalletIds();
|
|
292
|
+
return walletIds[networkType] || null;
|
|
293
|
+
}
|
|
294
|
+
function removeConnectedWalletId(networkType) {
|
|
295
|
+
if (typeof window === "undefined") {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const walletIds = getAllConnectedWalletIds();
|
|
299
|
+
delete walletIds[networkType];
|
|
300
|
+
localStorage.setItem(CONNECTED_WALLET_IDS_KEY, JSON.stringify(walletIds));
|
|
301
|
+
}
|
|
302
|
+
var WALLET_DISCONNECTED_KEY, WALLET_DISCONNECTED_NETWORKS_KEY, CONNECTED_NETWORK_TYPE_KEY, WALLET_ADDRESSES_KEY, CONNECTED_WALLET_IDS_KEY;
|
|
203
303
|
var init_wallet = __esm({
|
|
204
304
|
"src/utils/wallet.ts"() {
|
|
205
305
|
"use strict";
|
|
@@ -207,6 +307,7 @@ var init_wallet = __esm({
|
|
|
207
307
|
WALLET_DISCONNECTED_NETWORKS_KEY = "wallet_disconnected_networks";
|
|
208
308
|
CONNECTED_NETWORK_TYPE_KEY = "connected_network_type";
|
|
209
309
|
WALLET_ADDRESSES_KEY = "wallet_addresses_cache";
|
|
310
|
+
CONNECTED_WALLET_IDS_KEY = "connected_wallet_ids";
|
|
210
311
|
}
|
|
211
312
|
});
|
|
212
313
|
|
|
@@ -221,45 +322,143 @@ init_wallet();
|
|
|
221
322
|
|
|
222
323
|
// src/utils/wallet-connect.ts
|
|
223
324
|
init_wallet();
|
|
224
|
-
async function connectWallet(networkType) {
|
|
325
|
+
async function connectWallet(networkType, forceSelect = false) {
|
|
225
326
|
if (typeof window === "undefined") {
|
|
226
|
-
throw new Error("
|
|
327
|
+
throw new Error("Please use in browser environment");
|
|
227
328
|
}
|
|
228
329
|
let address;
|
|
229
330
|
switch (networkType) {
|
|
230
331
|
case "evm" /* EVM */: {
|
|
231
332
|
if (!window.ethereum) {
|
|
232
|
-
throw new Error("
|
|
333
|
+
throw new Error("Please install MetaMask or another Ethereum wallet");
|
|
233
334
|
}
|
|
234
335
|
const ethereum = window.ethereum;
|
|
336
|
+
if (forceSelect) {
|
|
337
|
+
try {
|
|
338
|
+
const permissions = await ethereum.request({
|
|
339
|
+
method: "wallet_requestPermissions",
|
|
340
|
+
params: [{ eth_accounts: {} }]
|
|
341
|
+
});
|
|
342
|
+
const accountsPermission = permissions?.find(
|
|
343
|
+
(p) => p.parentCapability === "eth_accounts"
|
|
344
|
+
);
|
|
345
|
+
if (accountsPermission?.caveats?.[0]?.value?.length > 0) {
|
|
346
|
+
address = accountsPermission.caveats[0].value[0];
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
} catch (err) {
|
|
350
|
+
if (err.code === 4001) {
|
|
351
|
+
throw new Error("User cancelled wallet connection");
|
|
352
|
+
}
|
|
353
|
+
console.warn("wallet_requestPermissions failed, falling back to eth_requestAccounts");
|
|
354
|
+
}
|
|
355
|
+
}
|
|
235
356
|
const accounts = await ethereum.request({
|
|
236
357
|
method: "eth_requestAccounts",
|
|
237
358
|
params: []
|
|
238
359
|
});
|
|
239
360
|
if (!accounts || accounts.length === 0) {
|
|
240
|
-
throw new Error("
|
|
361
|
+
throw new Error("Failed to get wallet address");
|
|
241
362
|
}
|
|
242
363
|
address = accounts[0];
|
|
243
364
|
break;
|
|
244
365
|
}
|
|
245
366
|
case "solana" /* SOLANA */:
|
|
246
367
|
case "svm" /* SVM */: {
|
|
247
|
-
const
|
|
368
|
+
const phantom = window.phantom?.solana || window.solana;
|
|
369
|
+
const solflare = window.solflare;
|
|
370
|
+
let solana = phantom;
|
|
371
|
+
if (!solana && solflare?.isSolflare) {
|
|
372
|
+
solana = solflare;
|
|
373
|
+
}
|
|
248
374
|
if (!solana) {
|
|
249
|
-
throw new Error("
|
|
375
|
+
throw new Error("Please install Phantom or another Solana wallet");
|
|
376
|
+
}
|
|
377
|
+
if (forceSelect) {
|
|
378
|
+
try {
|
|
379
|
+
if (phantom?.isConnected) {
|
|
380
|
+
await phantom.disconnect();
|
|
381
|
+
}
|
|
382
|
+
if (solflare?.isConnected) {
|
|
383
|
+
await solflare.disconnect();
|
|
384
|
+
}
|
|
385
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
386
|
+
} catch (err) {
|
|
387
|
+
console.warn("Failed to disconnect Solana wallet:", err);
|
|
388
|
+
}
|
|
389
|
+
} else if (solana.isConnected) {
|
|
390
|
+
try {
|
|
391
|
+
await solana.disconnect();
|
|
392
|
+
} catch (err) {
|
|
393
|
+
console.warn("Failed to disconnect Solana wallet:", err);
|
|
394
|
+
}
|
|
250
395
|
}
|
|
251
396
|
const response = await solana.connect();
|
|
252
397
|
address = response.publicKey.toString();
|
|
253
398
|
break;
|
|
254
399
|
}
|
|
255
400
|
default:
|
|
256
|
-
throw new Error("
|
|
401
|
+
throw new Error("Unsupported network type");
|
|
257
402
|
}
|
|
258
403
|
clearWalletDisconnection(networkType);
|
|
259
404
|
saveConnectedNetworkType(networkType);
|
|
260
405
|
saveWalletAddress(networkType, address);
|
|
261
406
|
return address;
|
|
262
407
|
}
|
|
408
|
+
async function disconnectAllSolanaWallets() {
|
|
409
|
+
if (typeof window === "undefined") return;
|
|
410
|
+
const phantom = window.phantom?.solana || window.solana;
|
|
411
|
+
const solflare = window.solflare;
|
|
412
|
+
const disconnectPromises = [];
|
|
413
|
+
if (phantom?.isConnected) {
|
|
414
|
+
disconnectPromises.push(
|
|
415
|
+
phantom.disconnect().catch(
|
|
416
|
+
(err) => console.warn("Failed to disconnect Phantom:", err)
|
|
417
|
+
)
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
if (solflare?.isConnected) {
|
|
421
|
+
disconnectPromises.push(
|
|
422
|
+
solflare.disconnect().catch(
|
|
423
|
+
(err) => console.warn("Failed to disconnect Solflare:", err)
|
|
424
|
+
)
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
await Promise.all(disconnectPromises);
|
|
428
|
+
}
|
|
429
|
+
async function disconnectWallet(networkType, clearAll = false) {
|
|
430
|
+
const targetNetwork = networkType || getConnectedNetworkType();
|
|
431
|
+
if (targetNetwork && typeof window !== "undefined") {
|
|
432
|
+
try {
|
|
433
|
+
switch (targetNetwork) {
|
|
434
|
+
case "solana" /* SOLANA */:
|
|
435
|
+
case "svm" /* SVM */: {
|
|
436
|
+
await disconnectAllSolanaWallets();
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
// EVM wallets (like MetaMask) don't have a real disconnect API
|
|
440
|
+
// Only clear local state, will request permissions again on next connection
|
|
441
|
+
case "evm" /* EVM */:
|
|
442
|
+
default:
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
} catch (err) {
|
|
446
|
+
console.warn("Failed to disconnect wallet:", err);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (clearAll) {
|
|
450
|
+
const { clearAllWalletAddresses: clearAllWalletAddresses2 } = (init_wallet(), __toCommonJS(wallet_exports));
|
|
451
|
+
clearAllWalletAddresses2();
|
|
452
|
+
markWalletDisconnected();
|
|
453
|
+
await disconnectAllSolanaWallets();
|
|
454
|
+
} else if (networkType) {
|
|
455
|
+
removeWalletAddress(networkType);
|
|
456
|
+
} else {
|
|
457
|
+
if (targetNetwork) {
|
|
458
|
+
removeWalletAddress(targetNetwork);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
263
462
|
async function getCurrentWallet(networkType) {
|
|
264
463
|
if (typeof window === "undefined") {
|
|
265
464
|
return null;
|
|
@@ -269,36 +468,32 @@ async function getCurrentWallet(networkType) {
|
|
|
269
468
|
return null;
|
|
270
469
|
}
|
|
271
470
|
const cachedAddress = getCachedWalletAddress(type);
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
471
|
+
if (type === "evm" /* EVM */) {
|
|
472
|
+
if (cachedAddress) {
|
|
473
|
+
return cachedAddress;
|
|
474
|
+
}
|
|
475
|
+
if (window.ethereum) {
|
|
476
|
+
try {
|
|
277
477
|
const accounts = await window.ethereum.request({
|
|
278
478
|
method: "eth_accounts",
|
|
279
479
|
params: []
|
|
280
480
|
});
|
|
281
|
-
|
|
282
|
-
|
|
481
|
+
return accounts && accounts.length > 0 ? accounts[0] : null;
|
|
482
|
+
} catch (error) {
|
|
483
|
+
console.error("Failed to get EVM accounts:", error);
|
|
484
|
+
return null;
|
|
283
485
|
}
|
|
284
|
-
case "solana" /* SOLANA */:
|
|
285
|
-
case "svm" /* SVM */: {
|
|
286
|
-
const solana = window.solana;
|
|
287
|
-
if (!solana || !solana.isConnected) return cachedAddress;
|
|
288
|
-
currentAddress = solana.publicKey?.toString() || null;
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
default:
|
|
292
|
-
return cachedAddress;
|
|
293
486
|
}
|
|
294
|
-
|
|
295
|
-
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
if (type === "solana" /* SOLANA */ || type === "svm" /* SVM */) {
|
|
490
|
+
const solana = window.solana;
|
|
491
|
+
if (!solana || !solana.isConnected) {
|
|
492
|
+
return cachedAddress;
|
|
296
493
|
}
|
|
297
|
-
return
|
|
298
|
-
} catch (error) {
|
|
299
|
-
console.error("Failed to get current wallet:", error);
|
|
300
|
-
return cachedAddress;
|
|
494
|
+
return solana.publicKey?.toString() || cachedAddress;
|
|
301
495
|
}
|
|
496
|
+
return cachedAddress;
|
|
302
497
|
}
|
|
303
498
|
function onAccountsChanged(callback) {
|
|
304
499
|
if (typeof window === "undefined" || !window.ethereum) {
|
|
@@ -361,24 +556,257 @@ async function switchNetwork(networkType) {
|
|
|
361
556
|
return null;
|
|
362
557
|
}
|
|
363
558
|
|
|
559
|
+
// src/utils/wallet-discovery.ts
|
|
560
|
+
init_wallet();
|
|
561
|
+
var SOLANA_WALLETS = [
|
|
562
|
+
{
|
|
563
|
+
id: "phantom",
|
|
564
|
+
name: "Phantom",
|
|
565
|
+
// Phantom official icon
|
|
566
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNBQjlGRjIiLz4KPHBhdGggZD0iTTExMC41IDY0QzExMC41IDg5Ljk1NjggODkuMjAxIDExMSA2My41IDExMUg0MS41QzM2LjI1MzMgMTExIDMyIDEwNi43NDcgMzIgMTAxLjVWNTQuNUMzMiAzMS4wMjggNTEuMDI4IDEyIDc0LjUgMTJDOTcuOTcyIDEyIDExNyAzMS4wMjggMTE3IDU0LjVWNTUuNUMxMTcgNTguNTM3NiAxMTQuNTM4IDYxIDExMS41IDYxSDEwOS41QzEwNi40NjIgNjEgMTA0IDYzLjQ2MjQgMTA0IDY2LjVWNjhDMTA0IDcxLjg2NiAxMDcuMTM0IDc1IDExMSA3NUgxMTEuNUMxMTQuNTM4IDc1IDExNyA3Mi41Mzc2IDExNyA2OS41VjY0SDExMC41WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzEyOF8xMjgpIi8+CjxwYXRoIGQ9Ik00OC41IDY3QzUxLjUzNzYgNjcgNTQgNjQuNTM3NiA1NCA2MS41QzU0IDU4LjQ2MjQgNTEuNTM3NiA1NiA0OC41IDU2QzQ1LjQ2MjQgNTYgNDMgNTguNDYyNCA0MyA2MS41QzQzIDY0LjUzNzYgNDUuNDYyNCA2NyA0OC41IDY3WiIgZmlsbD0iIzFCMUIxQiIvPgo8cGF0aCBkPSJNNzMuNSA2N0M3Ni41Mzc2IDY3IDc5IDY0LjUzNzYgNzkgNjEuNUM3OSA1OC40NjI0IDc2LjUzNzYgNTYgNzMuNSA1NkM3MC40NjI0IDU2IDY4IDU4LjQ2MjQgNjggNjEuNUM2OCA2NC41Mzc2IDcwLjQ2MjQgNjcgNzMuNSA2N1oiIGZpbGw9IiMxQjFCMUIiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xMjhfMTI4IiB4MT0iMTE3IiB5MT0iMTIiIHgyPSIxMTciIHkyPSIxMTEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRkZGRiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkZGRkYiIHN0b3Atb3BhY2l0eT0iMC44MiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg==",
|
|
567
|
+
detect: () => window.phantom?.solana
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
id: "solflare",
|
|
571
|
+
name: "Solflare",
|
|
572
|
+
// Solflare icon
|
|
573
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNGQzZEMDEiLz4KPHBhdGggZD0iTTk2IDY0TDY0IDMyTDMyIDY0TDY0IDk2TDk2IDY0WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+",
|
|
574
|
+
detect: () => window.solflare
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
id: "backpack",
|
|
578
|
+
name: "Backpack",
|
|
579
|
+
// Backpack icon (red coral color)
|
|
580
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiNFMzM0MzAiLz4KPHBhdGggZD0iTTQwIDQ4SDg4VjgwQzg4IDg4LjgzNjYgODAuODM2NiA5NiA3MiA5Nkg1NkM0Ny4xNjM0IDk2IDQwIDg4LjgzNjYgNDAgODBWNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNTIgMzJINzZWNDhINTJWMzJaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
|
|
581
|
+
detect: () => window.backpack
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
id: "okx-solana",
|
|
585
|
+
name: "OKX Wallet",
|
|
586
|
+
// OKX icon
|
|
587
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9ImJsYWNrIi8+CjxyZWN0IHg9IjI0IiB5PSIyNCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iNTIiIHk9IjI0IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSI4MCIgeT0iMjQiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjI0IiB5PSI1MiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjUyIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSIyNCIgeT0iODAiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgcng9IjQiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjUyIiB5PSI4MCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iODAiIHk9IjgwIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4=",
|
|
588
|
+
detect: () => window.okxwallet?.solana
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
id: "coinbase-solana",
|
|
592
|
+
name: "Coinbase Wallet",
|
|
593
|
+
// Coinbase icon (blue)
|
|
594
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwMDUyRkYiLz4KPGNpcmNsZSBjeD0iNjQiIGN5PSI2NCIgcj0iMzYiIGZpbGw9IndoaXRlIi8+CjxyZWN0IHg9IjQ4IiB5PSI1NiIgd2lkdGg9IjMyIiBoZWlnaHQ9IjE2IiByeD0iNCIgZmlsbD0iIzAwNTJGRiIvPgo8L3N2Zz4=",
|
|
595
|
+
detect: () => window.coinbaseSolana
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
id: "trust-solana",
|
|
599
|
+
name: "Trust Wallet",
|
|
600
|
+
// Trust Wallet icon
|
|
601
|
+
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iMjYiIGZpbGw9IiMwNTAwRkYiLz4KPHBhdGggZD0iTTY0IDI0QzY0IDI0IDk2IDQwIDk2IDY0Qzk2IDg4IDY0IDEwNCA2NCAxMDRDNjQgMTA0IDMyIDg4IDMyIDY0QzMyIDQwIDY0IDI0IDY0IDI0WiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSI2IiBmaWxsPSJub25lIi8+Cjwvc3ZnPg==",
|
|
602
|
+
detect: () => window.trustwallet?.solana
|
|
603
|
+
}
|
|
604
|
+
];
|
|
605
|
+
var evmWallets = /* @__PURE__ */ new Map();
|
|
606
|
+
var evmDiscoveryListeners = /* @__PURE__ */ new Set();
|
|
607
|
+
var evmDiscoveryInitialized = false;
|
|
608
|
+
var currentConnectedWallet = null;
|
|
609
|
+
function initEVMWalletDiscovery() {
|
|
610
|
+
if (typeof window === "undefined" || evmDiscoveryInitialized) return;
|
|
611
|
+
evmDiscoveryInitialized = true;
|
|
612
|
+
window.addEventListener("eip6963:announceProvider", ((event) => {
|
|
613
|
+
const { info, provider } = event.detail;
|
|
614
|
+
evmWallets.set(info.uuid, { info, provider });
|
|
615
|
+
evmDiscoveryListeners.forEach((listener) => listener());
|
|
616
|
+
}));
|
|
617
|
+
window.dispatchEvent(new Event("eip6963:requestProvider"));
|
|
618
|
+
}
|
|
619
|
+
function getEVMWallets() {
|
|
620
|
+
const wallets = [];
|
|
621
|
+
const detectedNames = /* @__PURE__ */ new Set();
|
|
622
|
+
evmWallets.forEach((detail, uuid) => {
|
|
623
|
+
if (!detectedNames.has(detail.info.name)) {
|
|
624
|
+
wallets.push({
|
|
625
|
+
id: uuid,
|
|
626
|
+
name: detail.info.name,
|
|
627
|
+
icon: detail.info.icon,
|
|
628
|
+
networkType: "evm" /* EVM */,
|
|
629
|
+
provider: detail.provider,
|
|
630
|
+
installed: true
|
|
631
|
+
});
|
|
632
|
+
detectedNames.add(detail.info.name);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
if (wallets.length === 0 && typeof window !== "undefined" && window.ethereum) {
|
|
636
|
+
const ethereum = window.ethereum;
|
|
637
|
+
const walletName = ethereum.isMetaMask ? "MetaMask" : ethereum.isCoinbaseWallet ? "Coinbase Wallet" : ethereum.isOkxWallet ? "OKX Wallet" : "Browser Wallet";
|
|
638
|
+
if (!detectedNames.has(walletName)) {
|
|
639
|
+
wallets.push({
|
|
640
|
+
id: "injected",
|
|
641
|
+
name: walletName,
|
|
642
|
+
icon: "",
|
|
643
|
+
// Will use first letter as avatar
|
|
644
|
+
networkType: "evm" /* EVM */,
|
|
645
|
+
provider: ethereum,
|
|
646
|
+
installed: true
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return wallets;
|
|
651
|
+
}
|
|
652
|
+
function onEVMWalletsChanged(callback) {
|
|
653
|
+
evmDiscoveryListeners.add(callback);
|
|
654
|
+
return () => {
|
|
655
|
+
evmDiscoveryListeners.delete(callback);
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function getSolanaWallets() {
|
|
659
|
+
if (typeof window === "undefined") return [];
|
|
660
|
+
const wallets = [];
|
|
661
|
+
const detectedProviders = /* @__PURE__ */ new Set();
|
|
662
|
+
const detectedNames = /* @__PURE__ */ new Set();
|
|
663
|
+
for (const wallet of SOLANA_WALLETS) {
|
|
664
|
+
const provider = wallet.detect();
|
|
665
|
+
if (provider && !detectedNames.has(wallet.name)) {
|
|
666
|
+
wallets.push({
|
|
667
|
+
id: wallet.id,
|
|
668
|
+
name: wallet.name,
|
|
669
|
+
icon: wallet.icon,
|
|
670
|
+
networkType: "solana" /* SOLANA */,
|
|
671
|
+
provider,
|
|
672
|
+
installed: true
|
|
673
|
+
});
|
|
674
|
+
detectedProviders.add(provider);
|
|
675
|
+
detectedNames.add(wallet.name);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const windowSolana = window.solana;
|
|
679
|
+
if (windowSolana && !detectedProviders.has(windowSolana)) {
|
|
680
|
+
const walletName = windowSolana.isPhantom ? "Phantom" : windowSolana.isSolflare ? "Solflare" : windowSolana.isBackpack ? "Backpack" : windowSolana.walletName || "Solana Wallet";
|
|
681
|
+
if (!detectedNames.has(walletName)) {
|
|
682
|
+
wallets.push({
|
|
683
|
+
id: "solana-unknown",
|
|
684
|
+
name: walletName,
|
|
685
|
+
icon: "",
|
|
686
|
+
// Will use first letter as avatar
|
|
687
|
+
networkType: "solana" /* SOLANA */,
|
|
688
|
+
provider: windowSolana,
|
|
689
|
+
installed: true
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return wallets;
|
|
694
|
+
}
|
|
695
|
+
function getWalletsForNetwork(networkType) {
|
|
696
|
+
switch (networkType) {
|
|
697
|
+
case "evm" /* EVM */:
|
|
698
|
+
return getEVMWallets();
|
|
699
|
+
case "solana" /* SOLANA */:
|
|
700
|
+
case "svm" /* SVM */:
|
|
701
|
+
return getSolanaWallets();
|
|
702
|
+
default:
|
|
703
|
+
return [];
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function getWalletByName(name, networkType) {
|
|
707
|
+
const wallets = getWalletsForNetwork(networkType);
|
|
708
|
+
return wallets.find((w) => w.name === name) || null;
|
|
709
|
+
}
|
|
710
|
+
async function connectEVMWallet(wallet) {
|
|
711
|
+
if (!wallet.provider) {
|
|
712
|
+
throw new Error(`Wallet ${wallet.name} is not available`);
|
|
713
|
+
}
|
|
714
|
+
const accounts = await wallet.provider.request({
|
|
715
|
+
method: "eth_requestAccounts",
|
|
716
|
+
params: []
|
|
717
|
+
});
|
|
718
|
+
if (!accounts || accounts.length === 0) {
|
|
719
|
+
throw new Error("Failed to get wallet address");
|
|
720
|
+
}
|
|
721
|
+
return accounts[0];
|
|
722
|
+
}
|
|
723
|
+
async function connectSolanaWallet(wallet) {
|
|
724
|
+
if (!wallet.provider) {
|
|
725
|
+
throw new Error(`Wallet ${wallet.name} is not available`);
|
|
726
|
+
}
|
|
727
|
+
if (wallet.provider.isConnected) {
|
|
728
|
+
try {
|
|
729
|
+
await wallet.provider.disconnect();
|
|
730
|
+
} catch (err) {
|
|
731
|
+
console.warn("Failed to disconnect before connecting:", err);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const response = await wallet.provider.connect();
|
|
735
|
+
return response.publicKey.toString();
|
|
736
|
+
}
|
|
737
|
+
async function connectToWallet(wallet) {
|
|
738
|
+
let address;
|
|
739
|
+
switch (wallet.networkType) {
|
|
740
|
+
case "evm" /* EVM */:
|
|
741
|
+
address = await connectEVMWallet(wallet);
|
|
742
|
+
break;
|
|
743
|
+
case "solana" /* SOLANA */:
|
|
744
|
+
case "svm" /* SVM */:
|
|
745
|
+
address = await connectSolanaWallet(wallet);
|
|
746
|
+
break;
|
|
747
|
+
default:
|
|
748
|
+
throw new Error("Unsupported network type");
|
|
749
|
+
}
|
|
750
|
+
currentConnectedWallet = wallet;
|
|
751
|
+
saveConnectedWalletId(wallet.networkType, wallet.name);
|
|
752
|
+
return address;
|
|
753
|
+
}
|
|
754
|
+
function setCurrentConnectedWallet(wallet) {
|
|
755
|
+
currentConnectedWallet = wallet;
|
|
756
|
+
}
|
|
757
|
+
function clearConnectedWallet(networkType) {
|
|
758
|
+
if (networkType) {
|
|
759
|
+
removeConnectedWalletId(networkType);
|
|
760
|
+
}
|
|
761
|
+
currentConnectedWallet = null;
|
|
762
|
+
}
|
|
763
|
+
function restoreConnectedWallet(networkType) {
|
|
764
|
+
const savedWalletName = getConnectedWalletId(networkType);
|
|
765
|
+
if (!savedWalletName) return null;
|
|
766
|
+
const wallet = getWalletByName(savedWalletName, networkType);
|
|
767
|
+
if (wallet) {
|
|
768
|
+
currentConnectedWallet = wallet;
|
|
769
|
+
console.log(`\u2705 Restored wallet provider: ${wallet.name}`);
|
|
770
|
+
return wallet;
|
|
771
|
+
}
|
|
772
|
+
console.warn(`\u26A0\uFE0F Could not find wallet with name: ${savedWalletName}`);
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
function getWalletProviderForPayment(networkType) {
|
|
776
|
+
if (currentConnectedWallet && currentConnectedWallet.networkType === networkType) {
|
|
777
|
+
return currentConnectedWallet.provider;
|
|
778
|
+
}
|
|
779
|
+
const restoredWallet = restoreConnectedWallet(networkType);
|
|
780
|
+
if (restoredWallet) {
|
|
781
|
+
return restoredWallet.provider;
|
|
782
|
+
}
|
|
783
|
+
if (typeof window === "undefined") return null;
|
|
784
|
+
switch (networkType) {
|
|
785
|
+
case "evm" /* EVM */:
|
|
786
|
+
return window.ethereum;
|
|
787
|
+
case "solana" /* SOLANA */:
|
|
788
|
+
case "svm" /* SVM */:
|
|
789
|
+
return window.phantom?.solana || window.solana;
|
|
790
|
+
default:
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (typeof window !== "undefined") {
|
|
795
|
+
initEVMWalletDiscovery();
|
|
796
|
+
}
|
|
797
|
+
|
|
364
798
|
// src/services/svm/payment-header.ts
|
|
365
|
-
import {
|
|
366
|
-
ComputeBudgetProgram,
|
|
367
|
-
Connection,
|
|
368
|
-
PublicKey,
|
|
369
|
-
TransactionMessage,
|
|
370
|
-
VersionedTransaction
|
|
371
|
-
} from "@solana/web3.js";
|
|
799
|
+
import { ComputeBudgetProgram, Connection, PublicKey, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
|
372
800
|
import {
|
|
373
801
|
createTransferCheckedInstruction,
|
|
374
|
-
|
|
802
|
+
getAssociatedTokenAddressSync,
|
|
375
803
|
getMint,
|
|
376
804
|
TOKEN_2022_PROGRAM_ID,
|
|
377
805
|
TOKEN_PROGRAM_ID
|
|
378
806
|
} from "@solana/spl-token";
|
|
379
807
|
async function createSvmPaymentHeader(params) {
|
|
380
808
|
const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
|
|
381
|
-
const connection = new Connection(rpcUrl
|
|
809
|
+
const connection = new Connection(rpcUrl);
|
|
382
810
|
const feePayer = paymentRequirements?.extra?.feePayer;
|
|
383
811
|
if (typeof feePayer !== "string" || !feePayer) {
|
|
384
812
|
throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
|
|
@@ -392,83 +820,85 @@ async function createSvmPaymentHeader(params) {
|
|
|
392
820
|
if (!paymentRequirements?.payTo) {
|
|
393
821
|
throw new Error("Missing payTo in payment requirements");
|
|
394
822
|
}
|
|
395
|
-
const
|
|
396
|
-
const instructions = [];
|
|
397
|
-
instructions.push(
|
|
398
|
-
ComputeBudgetProgram.setComputeUnitLimit({
|
|
399
|
-
units: 7e3
|
|
400
|
-
// Sufficient for SPL token transfer
|
|
401
|
-
})
|
|
402
|
-
);
|
|
403
|
-
instructions.push(
|
|
404
|
-
ComputeBudgetProgram.setComputeUnitPrice({
|
|
405
|
-
microLamports: 1
|
|
406
|
-
// Minimal price
|
|
407
|
-
})
|
|
408
|
-
);
|
|
823
|
+
const destinationPubkey = new PublicKey(paymentRequirements.payTo);
|
|
409
824
|
if (!paymentRequirements.asset) {
|
|
410
825
|
throw new Error("Missing token mint for SPL transfer");
|
|
411
826
|
}
|
|
412
827
|
const mintPubkey = new PublicKey(paymentRequirements.asset);
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
828
|
+
const mintAccountInfo = await connection.getAccountInfo(mintPubkey);
|
|
829
|
+
if (!mintAccountInfo) {
|
|
830
|
+
throw new Error(`Mint account ${mintPubkey.toBase58()} not found`);
|
|
831
|
+
}
|
|
832
|
+
const tokenProgramId = mintAccountInfo.owner.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
|
|
833
|
+
const mint = await getMint(connection, mintPubkey, void 0, tokenProgramId);
|
|
834
|
+
const sourceAta = getAssociatedTokenAddressSync(
|
|
417
835
|
mintPubkey,
|
|
418
836
|
userPubkey,
|
|
419
837
|
false,
|
|
420
|
-
|
|
838
|
+
tokenProgramId
|
|
421
839
|
);
|
|
422
|
-
const destinationAta =
|
|
840
|
+
const destinationAta = getAssociatedTokenAddressSync(
|
|
423
841
|
mintPubkey,
|
|
424
|
-
|
|
842
|
+
destinationPubkey,
|
|
425
843
|
false,
|
|
426
|
-
|
|
844
|
+
tokenProgramId
|
|
427
845
|
);
|
|
428
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta
|
|
846
|
+
const sourceAtaInfo = await connection.getAccountInfo(sourceAta);
|
|
429
847
|
if (!sourceAtaInfo) {
|
|
430
848
|
throw new Error(
|
|
431
849
|
`User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
|
|
432
850
|
);
|
|
433
851
|
}
|
|
434
|
-
const destAtaInfo = await connection.getAccountInfo(destinationAta
|
|
852
|
+
const destAtaInfo = await connection.getAccountInfo(destinationAta);
|
|
435
853
|
if (!destAtaInfo) {
|
|
436
854
|
throw new Error(
|
|
437
855
|
`Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
|
|
438
856
|
);
|
|
439
857
|
}
|
|
440
|
-
const
|
|
441
|
-
|
|
858
|
+
const instructions = [
|
|
859
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
860
|
+
units: 7e3
|
|
861
|
+
// Sufficient for SPL token transfer
|
|
862
|
+
}),
|
|
863
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
864
|
+
microLamports: 1
|
|
865
|
+
// Minimal price
|
|
866
|
+
}),
|
|
442
867
|
createTransferCheckedInstruction(
|
|
443
868
|
sourceAta,
|
|
444
869
|
mintPubkey,
|
|
445
870
|
destinationAta,
|
|
446
871
|
userPubkey,
|
|
447
|
-
|
|
872
|
+
BigInt(paymentRequirements.maxAmountRequired),
|
|
448
873
|
mint.decimals,
|
|
449
874
|
[],
|
|
450
|
-
|
|
875
|
+
tokenProgramId
|
|
451
876
|
)
|
|
452
|
-
|
|
453
|
-
const { blockhash } = await connection.getLatestBlockhash(
|
|
454
|
-
const
|
|
877
|
+
];
|
|
878
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
879
|
+
const messageV0 = new TransactionMessage({
|
|
455
880
|
payerKey: feePayerPubkey,
|
|
456
881
|
recentBlockhash: blockhash,
|
|
457
882
|
instructions
|
|
458
883
|
}).compileToV0Message();
|
|
459
|
-
const transaction = new VersionedTransaction(
|
|
884
|
+
const transaction = new VersionedTransaction(messageV0);
|
|
460
885
|
if (typeof wallet?.signTransaction !== "function") {
|
|
461
886
|
throw new Error("Connected wallet does not support signTransaction");
|
|
462
887
|
}
|
|
463
|
-
let
|
|
888
|
+
let signedTransaction;
|
|
464
889
|
try {
|
|
465
|
-
|
|
890
|
+
signedTransaction = await wallet.signTransaction(transaction);
|
|
466
891
|
console.log("\u2705 Transaction signed successfully");
|
|
467
892
|
} catch (error) {
|
|
468
893
|
console.error("\u274C Failed to sign transaction:", error);
|
|
469
894
|
throw wrapPaymentError(error);
|
|
470
895
|
}
|
|
471
|
-
const
|
|
896
|
+
const serializedBytes = signedTransaction.serialize();
|
|
897
|
+
let binary = "";
|
|
898
|
+
for (let i = 0; i < serializedBytes.length; i++) {
|
|
899
|
+
binary += String.fromCharCode(serializedBytes[i]);
|
|
900
|
+
}
|
|
901
|
+
const serializedTransaction = btoa(binary);
|
|
472
902
|
const paymentPayload = {
|
|
473
903
|
x402Version,
|
|
474
904
|
scheme: paymentRequirements.scheme,
|
|
@@ -477,7 +907,7 @@ async function createSvmPaymentHeader(params) {
|
|
|
477
907
|
transaction: serializedTransaction
|
|
478
908
|
}
|
|
479
909
|
};
|
|
480
|
-
const paymentHeader =
|
|
910
|
+
const paymentHeader = btoa(JSON.stringify(paymentPayload));
|
|
481
911
|
return paymentHeader;
|
|
482
912
|
}
|
|
483
913
|
function getDefaultSolanaRpcUrl(network) {
|
|
@@ -493,7 +923,7 @@ function getDefaultSolanaRpcUrl(network) {
|
|
|
493
923
|
// src/services/svm/payment-handler.ts
|
|
494
924
|
init_types();
|
|
495
925
|
async function handleSvmPayment(endpoint, config, requestInit) {
|
|
496
|
-
const { wallet,
|
|
926
|
+
const { wallet, rpcUrl, maxPaymentAmount } = config;
|
|
497
927
|
const initialResponse = await fetch(endpoint, {
|
|
498
928
|
...requestInit,
|
|
499
929
|
method: requestInit?.method || "POST"
|
|
@@ -502,26 +932,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
502
932
|
return initialResponse;
|
|
503
933
|
}
|
|
504
934
|
const rawResponse = await initialResponse.json();
|
|
505
|
-
|
|
506
|
-
"X-PAYMENT header is required",
|
|
507
|
-
"missing X-PAYMENT header",
|
|
508
|
-
"payment_required"
|
|
509
|
-
];
|
|
510
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
935
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
511
936
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
"invalid_signature": "Invalid payment signature",
|
|
515
|
-
"expired": "Payment authorization has expired",
|
|
516
|
-
"already_used": "This payment has already been used",
|
|
517
|
-
"network_mismatch": "Payment network does not match",
|
|
518
|
-
"invalid_payment": "Invalid payment data",
|
|
519
|
-
"verification_failed": "Payment verification failed",
|
|
520
|
-
"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."
|
|
521
|
-
};
|
|
522
|
-
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
523
|
-
const error = new Error(errorMessage);
|
|
524
|
-
throw wrapPaymentError(error);
|
|
937
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
938
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
525
939
|
}
|
|
526
940
|
const x402Version = rawResponse.x402Version;
|
|
527
941
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -571,26 +985,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
571
985
|
if (retryResponse.status === 402) {
|
|
572
986
|
try {
|
|
573
987
|
const retryData = await retryResponse.json();
|
|
574
|
-
|
|
575
|
-
"X-PAYMENT header is required",
|
|
576
|
-
"missing X-PAYMENT header",
|
|
577
|
-
"payment_required"
|
|
578
|
-
];
|
|
579
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
988
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
580
989
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
"invalid_signature": "Invalid payment signature",
|
|
584
|
-
"expired": "Payment authorization has expired",
|
|
585
|
-
"already_used": "This payment has already been used",
|
|
586
|
-
"network_mismatch": "Payment network does not match",
|
|
587
|
-
"invalid_payment": "Invalid payment data",
|
|
588
|
-
"verification_failed": "Payment verification failed",
|
|
589
|
-
"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."
|
|
590
|
-
};
|
|
591
|
-
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
592
|
-
const error = new Error(errorMessage);
|
|
593
|
-
throw wrapPaymentError(error);
|
|
990
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
991
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
594
992
|
}
|
|
595
993
|
} catch (error) {
|
|
596
994
|
if (error instanceof PaymentOperationError) {
|
|
@@ -717,6 +1115,15 @@ function getChainIdFromNetwork(network) {
|
|
|
717
1115
|
|
|
718
1116
|
// src/services/evm/payment-handler.ts
|
|
719
1117
|
init_types();
|
|
1118
|
+
var NETWORK_NAMES = {
|
|
1119
|
+
1: "Ethereum Mainnet",
|
|
1120
|
+
11155111: "Sepolia Testnet",
|
|
1121
|
+
8453: "Base Mainnet",
|
|
1122
|
+
84532: "Base Sepolia Testnet",
|
|
1123
|
+
137: "Polygon Mainnet",
|
|
1124
|
+
42161: "Arbitrum One",
|
|
1125
|
+
10: "Optimism Mainnet"
|
|
1126
|
+
};
|
|
720
1127
|
async function handleEvmPayment(endpoint, config, requestInit) {
|
|
721
1128
|
const { wallet, network, maxPaymentAmount } = config;
|
|
722
1129
|
const initialResponse = await fetch(endpoint, {
|
|
@@ -727,25 +1134,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
727
1134
|
return initialResponse;
|
|
728
1135
|
}
|
|
729
1136
|
const rawResponse = await initialResponse.json();
|
|
730
|
-
|
|
731
|
-
"X-PAYMENT header is required",
|
|
732
|
-
"missing X-PAYMENT header",
|
|
733
|
-
"payment_required"
|
|
734
|
-
];
|
|
735
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
1137
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
736
1138
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
"invalid_signature": "Invalid payment signature",
|
|
740
|
-
"expired": "Payment authorization has expired",
|
|
741
|
-
"already_used": "This payment has already been used",
|
|
742
|
-
"network_mismatch": "Payment network does not match",
|
|
743
|
-
"invalid_payment": "Invalid payment data",
|
|
744
|
-
"verification_failed": "Payment verification failed"
|
|
745
|
-
};
|
|
746
|
-
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
747
|
-
const error = new Error(errorMessage);
|
|
748
|
-
throw wrapPaymentError(error);
|
|
1139
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
1140
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
749
1141
|
}
|
|
750
1142
|
const x402Version = rawResponse.x402Version;
|
|
751
1143
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -777,19 +1169,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
777
1169
|
console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
|
|
778
1170
|
}
|
|
779
1171
|
}
|
|
780
|
-
const networkNames = {
|
|
781
|
-
1: "Ethereum Mainnet",
|
|
782
|
-
11155111: "Sepolia Testnet",
|
|
783
|
-
8453: "Base Mainnet",
|
|
784
|
-
84532: "Base Sepolia Testnet",
|
|
785
|
-
137: "Polygon Mainnet",
|
|
786
|
-
42161: "Arbitrum One",
|
|
787
|
-
10: "Optimism Mainnet"
|
|
788
|
-
};
|
|
789
1172
|
if (currentChainId && currentChainId !== targetChainId) {
|
|
790
1173
|
if (!wallet.switchChain) {
|
|
791
|
-
const currentNetworkName =
|
|
792
|
-
const targetNetworkName =
|
|
1174
|
+
const currentNetworkName = NETWORK_NAMES[currentChainId] || `Chain ${currentChainId}`;
|
|
1175
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
793
1176
|
const error = new Error(
|
|
794
1177
|
`Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
|
|
795
1178
|
);
|
|
@@ -801,7 +1184,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
801
1184
|
console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
|
|
802
1185
|
} catch (error) {
|
|
803
1186
|
console.error("\u274C Failed to switch chain:", error);
|
|
804
|
-
const targetNetworkName =
|
|
1187
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
805
1188
|
const wrappedError = wrapPaymentError(error);
|
|
806
1189
|
let finalError;
|
|
807
1190
|
if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
|
|
@@ -855,25 +1238,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
855
1238
|
if (retryResponse.status === 402) {
|
|
856
1239
|
try {
|
|
857
1240
|
const retryData = await retryResponse.json();
|
|
858
|
-
|
|
859
|
-
"X-PAYMENT header is required",
|
|
860
|
-
"missing X-PAYMENT header",
|
|
861
|
-
"payment_required"
|
|
862
|
-
];
|
|
863
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
1241
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
864
1242
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
"invalid_signature": "Invalid payment signature",
|
|
868
|
-
"expired": "Payment authorization has expired",
|
|
869
|
-
"already_used": "This payment has already been used",
|
|
870
|
-
"network_mismatch": "Payment network does not match",
|
|
871
|
-
"invalid_payment": "Invalid payment data",
|
|
872
|
-
"verification_failed": "Payment verification failed"
|
|
873
|
-
};
|
|
874
|
-
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
875
|
-
const error = new Error(errorMessage);
|
|
876
|
-
throw wrapPaymentError(error);
|
|
1243
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
1244
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
877
1245
|
}
|
|
878
1246
|
} catch (error) {
|
|
879
1247
|
if (error instanceof PaymentOperationError) {
|
|
@@ -911,7 +1279,7 @@ function getSupportedNetworkTypes(paymentRequirements) {
|
|
|
911
1279
|
});
|
|
912
1280
|
return Array.from(networkTypes);
|
|
913
1281
|
}
|
|
914
|
-
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams) {
|
|
1282
|
+
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams, expectedAddress) {
|
|
915
1283
|
const fullEndpoint = `${endpoint}/${merchantId}`;
|
|
916
1284
|
let response;
|
|
917
1285
|
const requestInit = additionalParams && Object.keys(additionalParams).length > 0 ? {
|
|
@@ -921,26 +1289,41 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
921
1289
|
}
|
|
922
1290
|
} : {};
|
|
923
1291
|
if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
|
|
924
|
-
const solana =
|
|
1292
|
+
const solana = getWalletProviderForPayment(networkType);
|
|
925
1293
|
if (!solana) {
|
|
926
|
-
throw new Error("
|
|
1294
|
+
throw new Error("Please connect your Solana wallet first.");
|
|
927
1295
|
}
|
|
928
1296
|
if (!solana.isConnected) {
|
|
929
1297
|
await solana.connect();
|
|
930
1298
|
}
|
|
1299
|
+
if (expectedAddress && solana.publicKey) {
|
|
1300
|
+
const currentAddress = solana.publicKey.toString();
|
|
1301
|
+
if (currentAddress !== expectedAddress) {
|
|
1302
|
+
throw new Error(
|
|
1303
|
+
`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.`
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
931
1307
|
response = await handleSvmPayment(fullEndpoint, {
|
|
932
1308
|
wallet: solana,
|
|
933
1309
|
network: "solana"
|
|
934
1310
|
// Will use backend's network configuration
|
|
935
1311
|
}, requestInit);
|
|
936
1312
|
} else if (networkType === "evm" /* EVM */) {
|
|
937
|
-
|
|
938
|
-
|
|
1313
|
+
const ethereum = getWalletProviderForPayment(networkType);
|
|
1314
|
+
if (!ethereum) {
|
|
1315
|
+
throw new Error("Please connect the EVM wallet first");
|
|
939
1316
|
}
|
|
940
|
-
const provider = new ethers2.BrowserProvider(
|
|
1317
|
+
const provider = new ethers2.BrowserProvider(ethereum);
|
|
941
1318
|
const signer = await provider.getSigner();
|
|
1319
|
+
const currentAddress = await signer.getAddress();
|
|
1320
|
+
if (expectedAddress && currentAddress.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
1321
|
+
throw new Error(
|
|
1322
|
+
`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.`
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
942
1325
|
const wallet = {
|
|
943
|
-
address:
|
|
1326
|
+
address: currentAddress,
|
|
944
1327
|
signTypedData: async (domain, types, message2) => {
|
|
945
1328
|
return await signer.signTypedData(domain, types, message2);
|
|
946
1329
|
},
|
|
@@ -951,7 +1334,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
951
1334
|
},
|
|
952
1335
|
// Switch to a different chain
|
|
953
1336
|
switchChain: async (chainId) => {
|
|
954
|
-
await
|
|
1337
|
+
await ethereum.request({
|
|
955
1338
|
method: "wallet_switchEthereumChain",
|
|
956
1339
|
params: [{ chainId }]
|
|
957
1340
|
});
|
|
@@ -963,7 +1346,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
963
1346
|
// Will use backend's network configuration
|
|
964
1347
|
}, requestInit);
|
|
965
1348
|
} else {
|
|
966
|
-
throw new Error(
|
|
1349
|
+
throw new Error(`Unsupported network types: ${networkType}`);
|
|
967
1350
|
}
|
|
968
1351
|
return response;
|
|
969
1352
|
}
|
|
@@ -1006,6 +1389,21 @@ function getNetworkDisplayName(network) {
|
|
|
1006
1389
|
}
|
|
1007
1390
|
|
|
1008
1391
|
// src/utils/payment-error-handler.ts
|
|
1392
|
+
var IGNORED_402_ERRORS = [
|
|
1393
|
+
"X-PAYMENT header is required",
|
|
1394
|
+
"missing X-PAYMENT header",
|
|
1395
|
+
"payment_required"
|
|
1396
|
+
];
|
|
1397
|
+
var PAYMENT_ERROR_MESSAGES = {
|
|
1398
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
1399
|
+
"invalid_signature": "Invalid payment signature",
|
|
1400
|
+
"expired": "Payment authorization has expired",
|
|
1401
|
+
"already_used": "This payment has already been used",
|
|
1402
|
+
"network_mismatch": "Payment network does not match",
|
|
1403
|
+
"invalid_payment": "Invalid payment data",
|
|
1404
|
+
"verification_failed": "Payment verification failed",
|
|
1405
|
+
"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."
|
|
1406
|
+
};
|
|
1009
1407
|
function parsePaymentError(error) {
|
|
1010
1408
|
if (!error) {
|
|
1011
1409
|
return {
|
|
@@ -1209,13 +1607,18 @@ var WalletStore = class {
|
|
|
1209
1607
|
});
|
|
1210
1608
|
}
|
|
1211
1609
|
// Connect wallet
|
|
1212
|
-
|
|
1610
|
+
// @param forceSelect - 强制弹出钱包选择界面,用于切换账户
|
|
1611
|
+
async connect(type, forceSelect = false) {
|
|
1213
1612
|
if (this.state.address && this.state.networkType && this.state.networkType !== type) {
|
|
1214
1613
|
saveWalletAddress(this.state.networkType, this.state.address);
|
|
1215
1614
|
}
|
|
1216
1615
|
this.setState({ isConnecting: true, error: null });
|
|
1217
1616
|
try {
|
|
1218
|
-
const walletAddress = await connectWallet(type);
|
|
1617
|
+
const walletAddress = await connectWallet(type, forceSelect);
|
|
1618
|
+
const wallets = getWalletsForNetwork(type);
|
|
1619
|
+
if (wallets.length > 0) {
|
|
1620
|
+
setCurrentConnectedWallet(wallets[0]);
|
|
1621
|
+
}
|
|
1219
1622
|
this.setState({
|
|
1220
1623
|
address: walletAddress,
|
|
1221
1624
|
networkType: type,
|
|
@@ -1229,6 +1632,30 @@ var WalletStore = class {
|
|
|
1229
1632
|
throw err;
|
|
1230
1633
|
}
|
|
1231
1634
|
}
|
|
1635
|
+
// Connect to a specific wallet (from wallet discovery)
|
|
1636
|
+
async connectWithWallet(wallet) {
|
|
1637
|
+
if (this.state.address && this.state.networkType && this.state.networkType !== wallet.networkType) {
|
|
1638
|
+
saveWalletAddress(this.state.networkType, this.state.address);
|
|
1639
|
+
}
|
|
1640
|
+
this.setState({ isConnecting: true, error: null });
|
|
1641
|
+
try {
|
|
1642
|
+
const walletAddress = await connectToWallet(wallet);
|
|
1643
|
+
clearWalletDisconnection(wallet.networkType);
|
|
1644
|
+
saveConnectedNetworkType(wallet.networkType);
|
|
1645
|
+
saveWalletAddress(wallet.networkType, walletAddress);
|
|
1646
|
+
this.setState({
|
|
1647
|
+
address: walletAddress,
|
|
1648
|
+
networkType: wallet.networkType,
|
|
1649
|
+
isConnecting: false
|
|
1650
|
+
});
|
|
1651
|
+
} catch (err) {
|
|
1652
|
+
this.setState({
|
|
1653
|
+
error: err.message || "Failed to connect wallet",
|
|
1654
|
+
isConnecting: false
|
|
1655
|
+
});
|
|
1656
|
+
throw err;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1232
1659
|
// Switch network (use cached wallet if available)
|
|
1233
1660
|
async switchNetwork(type) {
|
|
1234
1661
|
if (this.state.address && this.state.networkType) {
|
|
@@ -1260,9 +1687,15 @@ var WalletStore = class {
|
|
|
1260
1687
|
}
|
|
1261
1688
|
}
|
|
1262
1689
|
// Disconnect wallet
|
|
1263
|
-
disconnect() {
|
|
1690
|
+
async disconnect() {
|
|
1264
1691
|
const currentNetwork = this.state.networkType;
|
|
1692
|
+
clearConnectedWallet(currentNetwork || void 0);
|
|
1265
1693
|
if (currentNetwork) {
|
|
1694
|
+
try {
|
|
1695
|
+
await disconnectWallet(currentNetwork);
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
console.warn("Failed to disconnect wallet provider:", err);
|
|
1698
|
+
}
|
|
1266
1699
|
this.handleDisconnect(currentNetwork);
|
|
1267
1700
|
} else {
|
|
1268
1701
|
this.setState({
|
|
@@ -1306,7 +1739,8 @@ function useWallet() {
|
|
|
1306
1739
|
);
|
|
1307
1740
|
return {
|
|
1308
1741
|
...state,
|
|
1309
|
-
connect: (type) => walletStore.connect(type),
|
|
1742
|
+
connect: (type, forceSelect) => walletStore.connect(type, forceSelect),
|
|
1743
|
+
connectWithWallet: (wallet) => walletStore.connectWithWallet(wallet),
|
|
1310
1744
|
switchNetwork: (type) => walletStore.switchNetwork(type),
|
|
1311
1745
|
ensureNetwork: (type) => walletStore.ensureNetwork(type),
|
|
1312
1746
|
disconnect: () => walletStore.disconnect(),
|
|
@@ -1414,45 +1848,327 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL, additionalParams)
|
|
|
1414
1848
|
};
|
|
1415
1849
|
}
|
|
1416
1850
|
|
|
1417
|
-
// src/react/
|
|
1418
|
-
import
|
|
1851
|
+
// src/react/hooks/useToast.tsx
|
|
1852
|
+
import React2, { useCallback as useCallback2, useRef, useState as useState3 } from "react";
|
|
1419
1853
|
|
|
1420
|
-
// src/react/
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1854
|
+
// src/react/components/ui/Toast.tsx
|
|
1855
|
+
import React, { useEffect as useEffect3 } from "react";
|
|
1856
|
+
import { createPortal } from "react-dom";
|
|
1857
|
+
var Toast = ({ message: message2, type, onClose }) => {
|
|
1858
|
+
useEffect3(() => {
|
|
1859
|
+
const timer = setTimeout(onClose, 3e3);
|
|
1860
|
+
return () => clearTimeout(timer);
|
|
1861
|
+
}, [onClose]);
|
|
1862
|
+
const bgColor = type === "success" ? "#22c55e" : type === "error" ? "#ef4444" : "#3b82f6";
|
|
1863
|
+
const icon = type === "success" ? "\u2713" : type === "error" ? "\u2717" : "\u2139";
|
|
1864
|
+
return createPortal(
|
|
1865
|
+
/* @__PURE__ */ React.createElement(
|
|
1866
|
+
"div",
|
|
1867
|
+
{
|
|
1868
|
+
className: "fixed top-4 right-4 z-[99999] animate-slide-in-right",
|
|
1869
|
+
style: {
|
|
1870
|
+
animation: "slideInRight 0.3s ease-out"
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
/* @__PURE__ */ React.createElement(
|
|
1874
|
+
"div",
|
|
1875
|
+
{
|
|
1876
|
+
className: "flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg text-white font-mono text-sm",
|
|
1877
|
+
style: { backgroundColor: bgColor, minWidth: "280px" }
|
|
1878
|
+
},
|
|
1879
|
+
/* @__PURE__ */ React.createElement("span", { className: "text-lg" }, icon),
|
|
1880
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1" }, message2),
|
|
1881
|
+
/* @__PURE__ */ React.createElement(
|
|
1882
|
+
"button",
|
|
1883
|
+
{
|
|
1884
|
+
onClick: onClose,
|
|
1885
|
+
className: "text-white/80 hover:text-white transition-colors"
|
|
1886
|
+
},
|
|
1887
|
+
"\xD7"
|
|
1888
|
+
)
|
|
1889
|
+
)
|
|
1890
|
+
),
|
|
1891
|
+
document.body
|
|
1892
|
+
);
|
|
1424
1893
|
};
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1894
|
+
|
|
1895
|
+
// src/react/hooks/useToast.tsx
|
|
1896
|
+
var useToast = () => {
|
|
1897
|
+
const [toasts, setToasts] = useState3([]);
|
|
1898
|
+
const toastIdRef = useRef(0);
|
|
1899
|
+
const showToast = useCallback2((message2, type) => {
|
|
1900
|
+
const id = ++toastIdRef.current;
|
|
1901
|
+
setToasts((prev) => [...prev, { id, message: message2, type }]);
|
|
1902
|
+
}, []);
|
|
1903
|
+
const removeToast = useCallback2((id) => {
|
|
1904
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1905
|
+
}, []);
|
|
1906
|
+
const ToastContainer = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, toasts.map((toast, index) => /* @__PURE__ */ React2.createElement(
|
|
1907
|
+
"div",
|
|
1908
|
+
{
|
|
1909
|
+
key: toast.id,
|
|
1910
|
+
style: { top: `${16 + index * 60}px` },
|
|
1911
|
+
className: "fixed right-4 z-[99999]"
|
|
1912
|
+
},
|
|
1913
|
+
React2.createElement(Toast, {
|
|
1914
|
+
message: toast.message,
|
|
1915
|
+
type: toast.type,
|
|
1916
|
+
onClose: () => removeToast(toast.id)
|
|
1917
|
+
})
|
|
1918
|
+
)));
|
|
1919
|
+
return { showToast, ToastContainer };
|
|
1920
|
+
};
|
|
1921
|
+
|
|
1922
|
+
// src/react/components/wallet/WalletConnect.tsx
|
|
1923
|
+
import React4, { useState as useState5 } from "react";
|
|
1924
|
+
|
|
1925
|
+
// src/react/components/wallet/WalletSelectModal.tsx
|
|
1926
|
+
import React3, { useEffect as useEffect4, useState as useState4 } from "react";
|
|
1927
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
1928
|
+
var overlayStyle = {
|
|
1929
|
+
position: "fixed",
|
|
1930
|
+
top: 0,
|
|
1931
|
+
left: 0,
|
|
1932
|
+
right: 0,
|
|
1933
|
+
bottom: 0,
|
|
1934
|
+
backgroundColor: "rgba(0, 0, 0, 0.4)",
|
|
1935
|
+
display: "flex",
|
|
1936
|
+
alignItems: "center",
|
|
1937
|
+
justifyContent: "center",
|
|
1938
|
+
zIndex: 99999
|
|
1939
|
+
};
|
|
1940
|
+
var modalStyle = {
|
|
1941
|
+
backgroundColor: "#ffffff",
|
|
1942
|
+
borderRadius: "12px",
|
|
1943
|
+
padding: "16px",
|
|
1944
|
+
maxWidth: "320px",
|
|
1945
|
+
width: "90%",
|
|
1946
|
+
maxHeight: "70vh",
|
|
1947
|
+
overflow: "auto",
|
|
1948
|
+
boxShadow: "0 10px 25px rgba(0, 0, 0, 0.15)"
|
|
1949
|
+
};
|
|
1950
|
+
var headerStyle = {
|
|
1951
|
+
display: "flex",
|
|
1952
|
+
alignItems: "center",
|
|
1953
|
+
justifyContent: "space-between",
|
|
1954
|
+
marginBottom: "12px"
|
|
1955
|
+
};
|
|
1956
|
+
var titleStyle = {
|
|
1957
|
+
fontSize: "15px",
|
|
1958
|
+
fontWeight: 600,
|
|
1959
|
+
color: "#1a1a1a",
|
|
1960
|
+
margin: 0
|
|
1961
|
+
};
|
|
1962
|
+
var closeButtonStyle = {
|
|
1963
|
+
background: "none",
|
|
1964
|
+
border: "none",
|
|
1965
|
+
fontSize: "20px",
|
|
1966
|
+
cursor: "pointer",
|
|
1967
|
+
color: "#666",
|
|
1968
|
+
padding: "2px 6px",
|
|
1969
|
+
lineHeight: 1,
|
|
1970
|
+
borderRadius: "4px"
|
|
1971
|
+
};
|
|
1972
|
+
var subtitleStyle = {
|
|
1973
|
+
fontSize: "12px",
|
|
1974
|
+
color: "#666",
|
|
1975
|
+
marginBottom: "12px"
|
|
1976
|
+
};
|
|
1977
|
+
var walletListStyle = {
|
|
1978
|
+
display: "flex",
|
|
1979
|
+
flexDirection: "column",
|
|
1980
|
+
gap: "6px"
|
|
1981
|
+
};
|
|
1982
|
+
var getWalletItemStyle = (isHovered) => ({
|
|
1983
|
+
display: "flex",
|
|
1984
|
+
alignItems: "center",
|
|
1985
|
+
gap: "10px",
|
|
1986
|
+
padding: "10px 12px",
|
|
1987
|
+
border: "1px solid #e5e5e5",
|
|
1988
|
+
borderRadius: "8px",
|
|
1989
|
+
cursor: "pointer",
|
|
1990
|
+
transition: "all 0.15s ease",
|
|
1991
|
+
backgroundColor: isHovered ? "#f5f5f5" : "#ffffff",
|
|
1992
|
+
borderColor: isHovered ? "#d0d0d0" : "#e5e5e5"
|
|
1993
|
+
});
|
|
1994
|
+
var walletIconStyle = {
|
|
1995
|
+
width: "32px",
|
|
1996
|
+
height: "32px",
|
|
1997
|
+
borderRadius: "8px",
|
|
1998
|
+
objectFit: "contain",
|
|
1999
|
+
backgroundColor: "#f5f5f5"
|
|
2000
|
+
};
|
|
2001
|
+
var getAvatarColor = (name) => {
|
|
2002
|
+
const colors2 = [
|
|
2003
|
+
"#6366f1",
|
|
2004
|
+
// indigo
|
|
2005
|
+
"#8b5cf6",
|
|
2006
|
+
// violet
|
|
2007
|
+
"#ec4899",
|
|
2008
|
+
// pink
|
|
2009
|
+
"#f43f5e",
|
|
2010
|
+
// rose
|
|
2011
|
+
"#f97316",
|
|
2012
|
+
// orange
|
|
2013
|
+
"#eab308",
|
|
2014
|
+
// yellow
|
|
2015
|
+
"#22c55e",
|
|
2016
|
+
// green
|
|
2017
|
+
"#14b8a6",
|
|
2018
|
+
// teal
|
|
2019
|
+
"#06b6d4",
|
|
2020
|
+
// cyan
|
|
2021
|
+
"#3b82f6"
|
|
2022
|
+
// blue
|
|
2023
|
+
];
|
|
2024
|
+
const index = name.charCodeAt(0) % colors2.length;
|
|
2025
|
+
return colors2[index];
|
|
2026
|
+
};
|
|
2027
|
+
var getWalletIconPlaceholderStyle = (walletName) => ({
|
|
2028
|
+
width: "32px",
|
|
2029
|
+
height: "32px",
|
|
2030
|
+
borderRadius: "8px",
|
|
2031
|
+
backgroundColor: getAvatarColor(walletName),
|
|
2032
|
+
display: "flex",
|
|
2033
|
+
alignItems: "center",
|
|
2034
|
+
justifyContent: "center",
|
|
2035
|
+
fontSize: "14px",
|
|
2036
|
+
fontWeight: 600,
|
|
2037
|
+
color: "#ffffff"
|
|
2038
|
+
});
|
|
2039
|
+
var walletNameStyle = {
|
|
2040
|
+
fontSize: "14px",
|
|
2041
|
+
fontWeight: 500,
|
|
2042
|
+
color: "#1a1a1a"
|
|
2043
|
+
};
|
|
2044
|
+
var emptyStateStyle = {
|
|
2045
|
+
textAlign: "center",
|
|
2046
|
+
padding: "24px 12px",
|
|
2047
|
+
color: "#666"
|
|
2048
|
+
};
|
|
2049
|
+
var emptyTitleStyle = {
|
|
2050
|
+
fontSize: "14px",
|
|
2051
|
+
fontWeight: 500,
|
|
2052
|
+
marginBottom: "6px",
|
|
2053
|
+
color: "#1a1a1a"
|
|
2054
|
+
};
|
|
2055
|
+
var emptyDescStyle = {
|
|
2056
|
+
fontSize: "12px",
|
|
2057
|
+
color: "#888"
|
|
2058
|
+
};
|
|
2059
|
+
function WalletItem({
|
|
2060
|
+
wallet,
|
|
2061
|
+
isHovered,
|
|
2062
|
+
onSelect,
|
|
2063
|
+
onHover
|
|
2064
|
+
}) {
|
|
2065
|
+
const [iconError, setIconError] = useState4(false);
|
|
2066
|
+
return /* @__PURE__ */ React3.createElement(
|
|
2067
|
+
"div",
|
|
2068
|
+
{
|
|
2069
|
+
style: getWalletItemStyle(isHovered),
|
|
2070
|
+
onClick: onSelect,
|
|
2071
|
+
onMouseEnter: () => onHover(true),
|
|
2072
|
+
onMouseLeave: () => onHover(false)
|
|
2073
|
+
},
|
|
2074
|
+
wallet.icon && !iconError ? /* @__PURE__ */ React3.createElement(
|
|
2075
|
+
"img",
|
|
2076
|
+
{
|
|
2077
|
+
src: wallet.icon,
|
|
2078
|
+
alt: wallet.name,
|
|
2079
|
+
style: walletIconStyle,
|
|
2080
|
+
onError: () => setIconError(true)
|
|
2081
|
+
}
|
|
2082
|
+
) : /* @__PURE__ */ React3.createElement("div", { style: getWalletIconPlaceholderStyle(wallet.name) }, wallet.name.charAt(0).toUpperCase()),
|
|
2083
|
+
/* @__PURE__ */ React3.createElement("span", { style: walletNameStyle }, wallet.name)
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
function WalletSelectModal({
|
|
2087
|
+
isOpen,
|
|
2088
|
+
networkType,
|
|
2089
|
+
onSelect,
|
|
2090
|
+
onClose
|
|
2091
|
+
}) {
|
|
2092
|
+
const [wallets, setWallets] = useState4([]);
|
|
2093
|
+
const [hoveredWallet, setHoveredWallet] = useState4(null);
|
|
2094
|
+
const [mounted, setMounted] = useState4(false);
|
|
2095
|
+
useEffect4(() => {
|
|
2096
|
+
setMounted(true);
|
|
2097
|
+
return () => setMounted(false);
|
|
2098
|
+
}, []);
|
|
2099
|
+
useEffect4(() => {
|
|
2100
|
+
if (!isOpen) return;
|
|
2101
|
+
initEVMWalletDiscovery();
|
|
2102
|
+
const updateWallets = () => {
|
|
2103
|
+
setWallets(getWalletsForNetwork(networkType));
|
|
2104
|
+
};
|
|
2105
|
+
updateWallets();
|
|
2106
|
+
const unsubscribe = onEVMWalletsChanged(updateWallets);
|
|
2107
|
+
const timer = setTimeout(updateWallets, 500);
|
|
2108
|
+
return () => {
|
|
2109
|
+
unsubscribe();
|
|
2110
|
+
clearTimeout(timer);
|
|
2111
|
+
};
|
|
2112
|
+
}, [isOpen, networkType]);
|
|
2113
|
+
if (!isOpen || !mounted) return null;
|
|
2114
|
+
const handleOverlayClick = (e) => {
|
|
2115
|
+
if (e.target === e.currentTarget) {
|
|
2116
|
+
onClose();
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
const networkName = getNetworkDisplayName(networkType);
|
|
2120
|
+
const modalContent = /* @__PURE__ */ React3.createElement("div", { style: overlayStyle, onClick: handleOverlayClick }, /* @__PURE__ */ React3.createElement("div", { style: modalStyle }, /* @__PURE__ */ React3.createElement("div", { style: headerStyle }, /* @__PURE__ */ React3.createElement("h3", { style: titleStyle }, "Select Wallet"), /* @__PURE__ */ React3.createElement("button", { style: closeButtonStyle, onClick: onClose }, "\xD7")), /* @__PURE__ */ React3.createElement("p", { style: subtitleStyle }, "Connect a ", networkName, " wallet"), wallets.length > 0 ? /* @__PURE__ */ React3.createElement("div", { style: walletListStyle }, wallets.map((wallet) => /* @__PURE__ */ React3.createElement(
|
|
2121
|
+
WalletItem,
|
|
2122
|
+
{
|
|
2123
|
+
key: wallet.id,
|
|
2124
|
+
wallet,
|
|
2125
|
+
isHovered: hoveredWallet === wallet.id,
|
|
2126
|
+
onSelect: () => onSelect(wallet),
|
|
2127
|
+
onHover: (hovered) => setHoveredWallet(hovered ? wallet.id : null)
|
|
2128
|
+
}
|
|
2129
|
+
))) : /* @__PURE__ */ React3.createElement("div", { style: emptyStateStyle }, /* @__PURE__ */ React3.createElement("p", { style: emptyTitleStyle }, "No wallets found"), /* @__PURE__ */ React3.createElement("p", { style: emptyDescStyle }, "Please install a ", networkName, " wallet extension."))));
|
|
2130
|
+
if (typeof document !== "undefined") {
|
|
2131
|
+
return createPortal2(modalContent, document.body);
|
|
2132
|
+
}
|
|
2133
|
+
return modalContent;
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// src/react/styles/inline-styles.ts
|
|
2137
|
+
var isDarkMode = () => {
|
|
2138
|
+
if (typeof window === "undefined") return false;
|
|
2139
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
|
|
2140
|
+
};
|
|
2141
|
+
var colors = {
|
|
2142
|
+
// Light mode
|
|
2143
|
+
light: {
|
|
2144
|
+
background: "#fafafa",
|
|
2145
|
+
cardBg: "#ffffff",
|
|
2146
|
+
text: "#0a0a0a",
|
|
2147
|
+
textSecondary: "#737373",
|
|
2148
|
+
primary: "#000000",
|
|
2149
|
+
primaryHover: "#262626",
|
|
2150
|
+
danger: "#ef4444",
|
|
2151
|
+
dangerHover: "#dc2626",
|
|
2152
|
+
success: "#10b981",
|
|
2153
|
+
successHover: "#059669",
|
|
2154
|
+
disabled: "#e5e5e5",
|
|
2155
|
+
disabledText: "#a3a3a3",
|
|
2156
|
+
errorBg: "#fef2f2",
|
|
2157
|
+
errorText: "#dc2626"
|
|
2158
|
+
},
|
|
2159
|
+
// Dark mode
|
|
2160
|
+
dark: {
|
|
2161
|
+
background: "#0a0a0a",
|
|
2162
|
+
cardBg: "#171717",
|
|
2163
|
+
text: "#fafafa",
|
|
2164
|
+
textSecondary: "#a3a3a3",
|
|
2165
|
+
primary: "#ffffff",
|
|
2166
|
+
primaryHover: "#e5e5e5",
|
|
2167
|
+
danger: "#f87171",
|
|
2168
|
+
dangerHover: "#ef4444",
|
|
2169
|
+
success: "#34d399",
|
|
2170
|
+
successHover: "#10b981",
|
|
2171
|
+
disabled: "#262626",
|
|
1456
2172
|
disabledText: "#525252",
|
|
1457
2173
|
errorBg: "#1c1917",
|
|
1458
2174
|
errorText: "#f87171"
|
|
@@ -1532,18 +2248,6 @@ var getDisconnectButtonStyle = (isHovered) => {
|
|
|
1532
2248
|
color: "#ffffff"
|
|
1533
2249
|
};
|
|
1534
2250
|
};
|
|
1535
|
-
var getInstallLinkStyle = (isHovered) => {
|
|
1536
|
-
const c = getColors();
|
|
1537
|
-
return {
|
|
1538
|
-
display: "inline-block",
|
|
1539
|
-
padding: "0.5rem",
|
|
1540
|
-
fontSize: "0.8125rem",
|
|
1541
|
-
color: c.textSecondary,
|
|
1542
|
-
textDecoration: isHovered ? "underline" : "none",
|
|
1543
|
-
textAlign: "center",
|
|
1544
|
-
fontWeight: 500
|
|
1545
|
-
};
|
|
1546
|
-
};
|
|
1547
2251
|
var walletAddressStyle = {
|
|
1548
2252
|
display: "flex",
|
|
1549
2253
|
flexDirection: "column",
|
|
@@ -1596,51 +2300,60 @@ var getErrorStyle = () => {
|
|
|
1596
2300
|
};
|
|
1597
2301
|
};
|
|
1598
2302
|
|
|
1599
|
-
// src/react/components/WalletConnect.tsx
|
|
2303
|
+
// src/react/components/wallet/WalletConnect.tsx
|
|
1600
2304
|
function WalletConnect({
|
|
1601
2305
|
supportedNetworks = ["solana" /* SOLANA */, "evm" /* EVM */],
|
|
1602
2306
|
className = "",
|
|
1603
2307
|
onConnect,
|
|
1604
|
-
onDisconnect
|
|
2308
|
+
onDisconnect,
|
|
2309
|
+
showSwitchWallet = true
|
|
1605
2310
|
}) {
|
|
1606
|
-
const { address, networkType, isConnecting, error,
|
|
1607
|
-
const [hoveredButton, setHoveredButton] =
|
|
1608
|
-
const [
|
|
1609
|
-
const
|
|
2311
|
+
const { address, networkType, isConnecting, error, connectWithWallet, disconnect } = useWallet();
|
|
2312
|
+
const [hoveredButton, setHoveredButton] = useState5(null);
|
|
2313
|
+
const [walletSelectOpen, setWalletSelectOpen] = useState5(false);
|
|
2314
|
+
const [selectedNetworkType, setSelectedNetworkType] = useState5(null);
|
|
2315
|
+
const handleOpenWalletSelect = (network) => {
|
|
2316
|
+
setSelectedNetworkType(network);
|
|
2317
|
+
setWalletSelectOpen(true);
|
|
2318
|
+
};
|
|
2319
|
+
const handleWalletSelect = async (wallet) => {
|
|
2320
|
+
setWalletSelectOpen(false);
|
|
1610
2321
|
try {
|
|
1611
|
-
await
|
|
2322
|
+
await connectWithWallet(wallet);
|
|
1612
2323
|
} catch (err) {
|
|
1613
2324
|
}
|
|
1614
2325
|
};
|
|
1615
|
-
const handleDisconnect = () => {
|
|
1616
|
-
disconnect();
|
|
2326
|
+
const handleDisconnect = async () => {
|
|
2327
|
+
await disconnect();
|
|
1617
2328
|
onDisconnect?.();
|
|
1618
2329
|
};
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2330
|
+
const handleSwitchWallet = () => {
|
|
2331
|
+
if (networkType) {
|
|
2332
|
+
setSelectedNetworkType(networkType);
|
|
2333
|
+
setWalletSelectOpen(true);
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("div", { style: { ...containerStyle, ...className ? {} : {} }, className }, !address ? /* @__PURE__ */ React4.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ React4.createElement("h3", { style: getTitleStyle() }, "Connect Wallet"), supportedNetworks.length === 0 ? /* @__PURE__ */ React4.createElement("p", { style: getHintStyle() }, "Please install a supported wallet extension") : /* @__PURE__ */ React4.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => /* @__PURE__ */ React4.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ React4.createElement(
|
|
2337
|
+
"button",
|
|
2338
|
+
{
|
|
2339
|
+
style: getConnectButtonStyle(isConnecting, hoveredButton === network),
|
|
2340
|
+
onClick: () => handleOpenWalletSelect(network),
|
|
2341
|
+
disabled: isConnecting,
|
|
2342
|
+
onMouseEnter: () => setHoveredButton(network),
|
|
2343
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
2344
|
+
},
|
|
2345
|
+
isConnecting ? "Connecting..." : getNetworkDisplayName(network)
|
|
2346
|
+
)))), error && /* @__PURE__ */ React4.createElement("p", { style: getErrorStyle() }, error), /* @__PURE__ */ React4.createElement("p", { style: getHintStyle() }, "Select a network to see available wallets")) : /* @__PURE__ */ React4.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ React4.createElement("div", { style: walletAddressStyle }, /* @__PURE__ */ React4.createElement("span", { style: getLabelStyle() }, "Connected ", networkType && `(${getNetworkDisplayName(networkType)})`), /* @__PURE__ */ React4.createElement("span", { style: getAddressStyle() }, formatAddress(address))), /* @__PURE__ */ React4.createElement("div", { style: walletActionsStyle }, showSwitchWallet && /* @__PURE__ */ React4.createElement(
|
|
2347
|
+
"button",
|
|
2348
|
+
{
|
|
2349
|
+
style: getConnectButtonStyle(isConnecting, hoveredButton === "switch"),
|
|
2350
|
+
onClick: handleSwitchWallet,
|
|
2351
|
+
disabled: isConnecting,
|
|
2352
|
+
onMouseEnter: () => setHoveredButton("switch"),
|
|
2353
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
2354
|
+
},
|
|
2355
|
+
isConnecting ? "Switching..." : "Switch Wallet"
|
|
2356
|
+
), /* @__PURE__ */ React4.createElement(
|
|
1644
2357
|
"button",
|
|
1645
2358
|
{
|
|
1646
2359
|
style: getDisconnectButtonStyle(hoveredButton === "disconnect"),
|
|
@@ -1649,11 +2362,19 @@ function WalletConnect({
|
|
|
1649
2362
|
onMouseLeave: () => setHoveredButton(null)
|
|
1650
2363
|
},
|
|
1651
2364
|
"Disconnect"
|
|
1652
|
-
)), /* @__PURE__ */
|
|
2365
|
+
)), /* @__PURE__ */ React4.createElement("p", { style: getHintStyle() }, 'Click "Switch Wallet" to change wallet or account'))), selectedNetworkType && /* @__PURE__ */ React4.createElement(
|
|
2366
|
+
WalletSelectModal,
|
|
2367
|
+
{
|
|
2368
|
+
isOpen: walletSelectOpen,
|
|
2369
|
+
networkType: selectedNetworkType,
|
|
2370
|
+
onSelect: handleWalletSelect,
|
|
2371
|
+
onClose: () => setWalletSelectOpen(false)
|
|
2372
|
+
}
|
|
2373
|
+
));
|
|
1653
2374
|
}
|
|
1654
2375
|
|
|
1655
|
-
// src/react/components/V402Checkout.tsx
|
|
1656
|
-
import
|
|
2376
|
+
// src/react/components/checkout/V402Checkout.tsx
|
|
2377
|
+
import React6, { useEffect as useEffect5, useState as useState6 } from "react";
|
|
1657
2378
|
import { Button, Card, Divider, message, Spin, Tooltip, Typography } from "antd";
|
|
1658
2379
|
import {
|
|
1659
2380
|
DisconnectOutlined,
|
|
@@ -1666,9 +2387,9 @@ import {
|
|
|
1666
2387
|
init_common();
|
|
1667
2388
|
|
|
1668
2389
|
// src/react/utils/CryptoIcons.tsx
|
|
1669
|
-
import
|
|
2390
|
+
import React5 from "react";
|
|
1670
2391
|
var SolanaIcon = ({ width = 16, height = 16, className, style }) => {
|
|
1671
|
-
return /* @__PURE__ */
|
|
2392
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1672
2393
|
"svg",
|
|
1673
2394
|
{
|
|
1674
2395
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1678,14 +2399,14 @@ var SolanaIcon = ({ width = 16, height = 16, className, style }) => {
|
|
|
1678
2399
|
className,
|
|
1679
2400
|
style
|
|
1680
2401
|
},
|
|
1681
|
-
/* @__PURE__ */
|
|
1682
|
-
/* @__PURE__ */
|
|
2402
|
+
/* @__PURE__ */ React5.createElement("desc", null, "Solana SOL Fill Streamline Icon: https://streamlinehq.com"),
|
|
2403
|
+
/* @__PURE__ */ React5.createElement("g", { fill: "none", fillRule: "evenodd" }, /* @__PURE__ */ React5.createElement(
|
|
1683
2404
|
"path",
|
|
1684
2405
|
{
|
|
1685
2406
|
d: "M16 0v16H0V0h16ZM8.395333333333333 15.505333333333333l-0.007333333333333332 0.0013333333333333333 -0.047333333333333324 0.023333333333333334 -0.013333333333333332 0.0026666666666666666 -0.009333333333333332 -0.0026666666666666666 -0.047333333333333324 -0.023333333333333334c-0.006666666666666666 -0.0026666666666666666 -0.012666666666666666 -0.0006666666666666666 -0.016 0.003333333333333333l-0.0026666666666666666 0.006666666666666666 -0.011333333333333334 0.2853333333333333 0.003333333333333333 0.013333333333333332 0.006666666666666666 0.008666666666666666 0.06933333333333333 0.049333333333333326 0.009999999999999998 0.0026666666666666666 0.008 -0.0026666666666666666 0.06933333333333333 -0.049333333333333326 0.008 -0.010666666666666666 0.0026666666666666666 -0.011333333333333334 -0.011333333333333334 -0.2846666666666666c-0.0013333333333333333 -0.006666666666666666 -0.005999999999999999 -0.011333333333333334 -0.011333333333333334 -0.011999999999999999Zm0.17666666666666667 -0.07533333333333334 -0.008666666666666666 0.0013333333333333333 -0.12333333333333332 0.062 -0.006666666666666666 0.006666666666666666 -0.002 0.007333333333333332 0.011999999999999999 0.2866666666666666 0.003333333333333333 0.008 0.005333333333333333 0.004666666666666666 0.134 0.062c0.008 0.0026666666666666666 0.015333333333333332 0 0.019333333333333334 -0.005333333333333333l0.0026666666666666666 -0.009333333333333332 -0.02266666666666667 -0.4093333333333333c-0.002 -0.008 -0.006666666666666666 -0.013333333333333332 -0.013333333333333332 -0.014666666666666665Zm-0.4766666666666666 0.0013333333333333333a0.015333333333333332 0.015333333333333332 0 0 0 -0.018 0.004l-0.004 0.009333333333333332 -0.02266666666666667 0.4093333333333333c0 0.008 0.004666666666666666 0.013333333333333332 0.011333333333333334 0.016l0.009999999999999998 -0.0013333333333333333 0.134 -0.062 0.006666666666666666 -0.005333333333333333 0.0026666666666666666 -0.007333333333333332 0.011333333333333334 -0.2866666666666666 -0.002 -0.008 -0.006666666666666666 -0.006666666666666666 -0.12266666666666666 -0.06133333333333333Z",
|
|
1686
2407
|
strokeWidth: "0.6667"
|
|
1687
2408
|
}
|
|
1688
|
-
), /* @__PURE__ */
|
|
2409
|
+
), /* @__PURE__ */ React5.createElement(
|
|
1689
2410
|
"path",
|
|
1690
2411
|
{
|
|
1691
2412
|
fill: "#000000",
|
|
@@ -1696,7 +2417,7 @@ var SolanaIcon = ({ width = 16, height = 16, className, style }) => {
|
|
|
1696
2417
|
);
|
|
1697
2418
|
};
|
|
1698
2419
|
var BaseIcon = ({ width = 24, height = 24, className, style }) => {
|
|
1699
|
-
return /* @__PURE__ */
|
|
2420
|
+
return /* @__PURE__ */ React5.createElement(
|
|
1700
2421
|
"svg",
|
|
1701
2422
|
{
|
|
1702
2423
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -1710,8 +2431,8 @@ var BaseIcon = ({ width = 24, height = 24, className, style }) => {
|
|
|
1710
2431
|
className,
|
|
1711
2432
|
style
|
|
1712
2433
|
},
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
2434
|
+
/* @__PURE__ */ React5.createElement("desc", null, "Brand Coinbase Streamline Icon: https://streamlinehq.com"),
|
|
2435
|
+
/* @__PURE__ */ React5.createElement(
|
|
1715
2436
|
"path",
|
|
1716
2437
|
{
|
|
1717
2438
|
d: "M12.95 22c-4.503 0 -8.445 -3.04 -9.61 -7.413 -1.165 -4.373 0.737 -8.988 4.638 -11.25a9.906 9.906 0 0 1 12.008 1.598l-3.335 3.367a5.185 5.185 0 0 0 -7.354 0.013 5.252 5.252 0 0 0 0 7.393 5.185 5.185 0 0 0 7.354 0.013L20 19.088A9.887 9.887 0 0 1 12.95 22z",
|
|
@@ -1731,7 +2452,7 @@ var getNetworkIcon = (network) => {
|
|
|
1731
2452
|
return BaseIcon;
|
|
1732
2453
|
};
|
|
1733
2454
|
|
|
1734
|
-
// src/react/components/V402Checkout.tsx
|
|
2455
|
+
// src/react/components/checkout/V402Checkout.tsx
|
|
1735
2456
|
var { Title, Text } = Typography;
|
|
1736
2457
|
var notify = {
|
|
1737
2458
|
success: (title, msg) => {
|
|
@@ -1769,14 +2490,14 @@ function V402Checkout({
|
|
|
1769
2490
|
{ autoSwitch: !!targetNetwork, switchOnMount: true }
|
|
1770
2491
|
);
|
|
1771
2492
|
const { isProcessing, setIsProcessing, result, setResult, error, setError } = usePayment();
|
|
1772
|
-
const [paymentDetails, setPaymentDetails] =
|
|
2493
|
+
const [paymentDetails, setPaymentDetails] = useState6(null);
|
|
1773
2494
|
const handleDisconnect = () => {
|
|
1774
2495
|
disconnect();
|
|
1775
2496
|
setResult(null);
|
|
1776
2497
|
setError(null);
|
|
1777
2498
|
notify.info("Wallet Disconnected", "Your wallet has been disconnected successfully.");
|
|
1778
2499
|
};
|
|
1779
|
-
|
|
2500
|
+
useEffect5(() => {
|
|
1780
2501
|
if (paymentInfo && paymentInfo.length > 0) {
|
|
1781
2502
|
const firstPayment = paymentInfo[0];
|
|
1782
2503
|
const rawAmount = firstPayment.maxAmountRequired?.toString() || "0";
|
|
@@ -1791,7 +2512,7 @@ function V402Checkout({
|
|
|
1791
2512
|
});
|
|
1792
2513
|
}
|
|
1793
2514
|
}, [paymentInfo]);
|
|
1794
|
-
|
|
2515
|
+
useEffect5(() => {
|
|
1795
2516
|
if (targetNetwork && !fetchingPaymentInfo && ensureNetwork) {
|
|
1796
2517
|
ensureNetwork(targetNetwork).catch((err) => {
|
|
1797
2518
|
console.error("Failed to ensure network:", err);
|
|
@@ -1807,7 +2528,7 @@ function V402Checkout({
|
|
|
1807
2528
|
setError(null);
|
|
1808
2529
|
setIsProcessing(true);
|
|
1809
2530
|
try {
|
|
1810
|
-
const response = await makePayment(networkType, checkoutId, endpoint, additionalParams);
|
|
2531
|
+
const response = await makePayment(networkType, checkoutId, endpoint, additionalParams, address || void 0);
|
|
1811
2532
|
const data = await response.json();
|
|
1812
2533
|
setResult(data);
|
|
1813
2534
|
notify.success("Payment Successful!", "Your payment has been processed successfully.");
|
|
@@ -1831,12 +2552,12 @@ function V402Checkout({
|
|
|
1831
2552
|
const networkColor = paymentDetails ? getNetworkColor(paymentDetails.network) : "#8c8c8c";
|
|
1832
2553
|
const loadingColor = "#8c8c8c";
|
|
1833
2554
|
const hasInvalidCheckoutId = !fetchingPaymentInfo && (!paymentInfo || paymentInfo.length === 0);
|
|
1834
|
-
return /* @__PURE__ */
|
|
2555
|
+
return /* @__PURE__ */ React6.createElement(
|
|
1835
2556
|
"div",
|
|
1836
2557
|
{
|
|
1837
2558
|
className: isModal ? "bg-white" : "h-screen bg-white flex items-center justify-center p-4 overflow-hidden"
|
|
1838
2559
|
},
|
|
1839
|
-
/* @__PURE__ */
|
|
2560
|
+
/* @__PURE__ */ React6.createElement(
|
|
1840
2561
|
"div",
|
|
1841
2562
|
{
|
|
1842
2563
|
className: "flex gap-4 items-center justify-center",
|
|
@@ -1846,7 +2567,7 @@ function V402Checkout({
|
|
|
1846
2567
|
width: "100%"
|
|
1847
2568
|
}
|
|
1848
2569
|
},
|
|
1849
|
-
/* @__PURE__ */
|
|
2570
|
+
/* @__PURE__ */ React6.createElement(
|
|
1850
2571
|
Card,
|
|
1851
2572
|
{
|
|
1852
2573
|
className: "flex-shrink-0",
|
|
@@ -1862,7 +2583,7 @@ function V402Checkout({
|
|
|
1862
2583
|
},
|
|
1863
2584
|
styles: { body: { padding: isModal ? "0px" : "32px 24px" } }
|
|
1864
2585
|
},
|
|
1865
|
-
/* @__PURE__ */
|
|
2586
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-3 mb-4" }, /* @__PURE__ */ React6.createElement(
|
|
1866
2587
|
"div",
|
|
1867
2588
|
{
|
|
1868
2589
|
className: "w-12 h-12 rounded-xl flex items-center justify-center",
|
|
@@ -1871,22 +2592,22 @@ function V402Checkout({
|
|
|
1871
2592
|
transition: "background 0.3s ease"
|
|
1872
2593
|
}
|
|
1873
2594
|
},
|
|
1874
|
-
hasInvalidCheckoutId ? /* @__PURE__ */
|
|
1875
|
-
), /* @__PURE__ */
|
|
2595
|
+
hasInvalidCheckoutId ? /* @__PURE__ */ React6.createElement("span", { style: { fontSize: "20px", color: "white", fontWeight: "bold" } }, "\u2717") : paymentDetails && NetworkIcon ? /* @__PURE__ */ React6.createElement(NetworkIcon, { width: 24, height: 24 }) : /* @__PURE__ */ React6.createElement(LoadingOutlined, { style: { fontSize: "20px", color: "white" }, spin: true })
|
|
2596
|
+
), /* @__PURE__ */ React6.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React6.createElement(Title, { level: 4, style: { margin: 0, fontSize: "18px", fontWeight: 600 } }, title || "Echo Payment OnVoyage"), !hasInvalidCheckoutId && /* @__PURE__ */ React6.createElement(
|
|
1876
2597
|
Tooltip,
|
|
1877
2598
|
{
|
|
1878
2599
|
title: tooltipText,
|
|
1879
2600
|
placement: "top"
|
|
1880
2601
|
},
|
|
1881
|
-
/* @__PURE__ */
|
|
2602
|
+
/* @__PURE__ */ React6.createElement(
|
|
1882
2603
|
InfoCircleOutlined,
|
|
1883
2604
|
{
|
|
1884
2605
|
style: { fontSize: "14px", color: "#8c8c8c", cursor: "help" }
|
|
1885
2606
|
}
|
|
1886
2607
|
)
|
|
1887
|
-
)), /* @__PURE__ */
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
hasInvalidCheckoutId && /* @__PURE__ */
|
|
2608
|
+
)), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, subtitle))),
|
|
2609
|
+
/* @__PURE__ */ React6.createElement("div", { className: "text-center mb-5" }, /* @__PURE__ */ React6.createElement("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-gray-50 mb-3" }, /* @__PURE__ */ React6.createElement(LockOutlined, { style: { fontSize: "20px", color: "#595959" } })), /* @__PURE__ */ React6.createElement(Title, { level: 3, style: { margin: "0 0 6px 0", fontSize: "20px", fontWeight: 600 } }, "Payment Required"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Pay ", paymentDetails ? `$${paymentDetails.amount} ${paymentDetails.currency}` : "the required amount", " to access")),
|
|
2610
|
+
hasInvalidCheckoutId && /* @__PURE__ */ React6.createElement("div", { className: "text-center py-6" }, /* @__PURE__ */ React6.createElement(
|
|
1890
2611
|
"div",
|
|
1891
2612
|
{
|
|
1892
2613
|
className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
|
|
@@ -1895,15 +2616,15 @@ function V402Checkout({
|
|
|
1895
2616
|
boxShadow: "0 4px 20px rgba(239, 68, 68, 0.3)"
|
|
1896
2617
|
}
|
|
1897
2618
|
},
|
|
1898
|
-
/* @__PURE__ */
|
|
1899
|
-
), /* @__PURE__ */
|
|
2619
|
+
/* @__PURE__ */ React6.createElement("span", { style: { fontSize: "32px", color: "white" } }, "!")
|
|
2620
|
+
), /* @__PURE__ */ React6.createElement(
|
|
1900
2621
|
Title,
|
|
1901
2622
|
{
|
|
1902
2623
|
level: 4,
|
|
1903
2624
|
style: { margin: "0 0 12px 0", fontSize: "18px", fontWeight: 600, color: "#262626" }
|
|
1904
2625
|
},
|
|
1905
2626
|
"Invalid Checkout ID"
|
|
1906
|
-
), /* @__PURE__ */
|
|
2627
|
+
), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c", display: "block", marginBottom: "16px" } }, "The checkout ID you provided is invalid or has expired."), /* @__PURE__ */ React6.createElement(
|
|
1907
2628
|
"div",
|
|
1908
2629
|
{
|
|
1909
2630
|
style: {
|
|
@@ -1914,33 +2635,33 @@ function V402Checkout({
|
|
|
1914
2635
|
marginTop: "16px"
|
|
1915
2636
|
}
|
|
1916
2637
|
},
|
|
1917
|
-
/* @__PURE__ */
|
|
2638
|
+
/* @__PURE__ */ React6.createElement(Text, { style: {
|
|
1918
2639
|
fontSize: "13px",
|
|
1919
2640
|
color: "#dc2626",
|
|
1920
2641
|
lineHeight: "1.6",
|
|
1921
2642
|
fontWeight: 500
|
|
1922
2643
|
} }, "Failed to load payment information. Please check your checkout ID.")
|
|
1923
2644
|
)),
|
|
1924
|
-
!hasInvalidCheckoutId && fetchingPaymentInfo && /* @__PURE__ */
|
|
1925
|
-
!hasInvalidCheckoutId && !fetchingPaymentInfo && !address && /* @__PURE__ */
|
|
1926
|
-
!hasInvalidCheckoutId && address && /* @__PURE__ */
|
|
2645
|
+
!hasInvalidCheckoutId && fetchingPaymentInfo && /* @__PURE__ */ React6.createElement("div", { className: "text-center py-6" }, /* @__PURE__ */ React6.createElement(Text, { style: { color: "#8c8c8c" } }, "Loading payment information...")),
|
|
2646
|
+
!hasInvalidCheckoutId && !fetchingPaymentInfo && !address && /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement(WalletConnect, { supportedNetworks })),
|
|
2647
|
+
!hasInvalidCheckoutId && address && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
|
|
1927
2648
|
"div",
|
|
1928
2649
|
{
|
|
1929
2650
|
className: "bg-gray-50 rounded-lg p-3 mb-4",
|
|
1930
2651
|
style: { border: "1px solid #f0f0f0" }
|
|
1931
2652
|
},
|
|
1932
|
-
/* @__PURE__ */
|
|
2653
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-3 flex-1" }, /* @__PURE__ */ React6.createElement(
|
|
1933
2654
|
"div",
|
|
1934
2655
|
{
|
|
1935
2656
|
className: "w-10 h-10 rounded-full bg-black flex items-center justify-center text-white text-sm font-semibold"
|
|
1936
2657
|
},
|
|
1937
2658
|
address.slice(0, 2).toUpperCase()
|
|
1938
|
-
), /* @__PURE__ */
|
|
2659
|
+
), /* @__PURE__ */ React6.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React6.createElement(Text, { style: {
|
|
1939
2660
|
display: "block",
|
|
1940
2661
|
fontSize: "12px",
|
|
1941
2662
|
color: "#8c8c8c",
|
|
1942
2663
|
marginBottom: "2px"
|
|
1943
|
-
} }, "Connected Wallet"), /* @__PURE__ */
|
|
2664
|
+
} }, "Connected Wallet"), /* @__PURE__ */ React6.createElement(
|
|
1944
2665
|
Text,
|
|
1945
2666
|
{
|
|
1946
2667
|
style: {
|
|
@@ -1950,17 +2671,17 @@ function V402Checkout({
|
|
|
1950
2671
|
}
|
|
1951
2672
|
},
|
|
1952
2673
|
formatAddress(address)
|
|
1953
|
-
))), /* @__PURE__ */
|
|
2674
|
+
))), /* @__PURE__ */ React6.createElement(
|
|
1954
2675
|
Button,
|
|
1955
2676
|
{
|
|
1956
2677
|
type: "text",
|
|
1957
2678
|
size: "small",
|
|
1958
|
-
icon: /* @__PURE__ */
|
|
2679
|
+
icon: /* @__PURE__ */ React6.createElement(DisconnectOutlined, null),
|
|
1959
2680
|
onClick: handleDisconnect,
|
|
1960
2681
|
style: { color: "#ff4d4f" }
|
|
1961
2682
|
}
|
|
1962
2683
|
))
|
|
1963
|
-
), paymentDetails && /* @__PURE__ */
|
|
2684
|
+
), paymentDetails && /* @__PURE__ */ React6.createElement("div", { className: "bg-gray-50 rounded-lg p-3 mb-4", style: { border: "1px solid #f0f0f0" } }, /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Payment Amount"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "18px", fontWeight: 600 } }, "$", paymentDetails.amount)), /* @__PURE__ */ React6.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Currency"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", fontWeight: 500 } }, paymentDetails.currency)), /* @__PURE__ */ React6.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Network"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", fontWeight: 500 } }, paymentDetails.network)), /* @__PURE__ */ React6.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React6.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Wallet Address"), /* @__PURE__ */ React6.createElement(Text, { style: {
|
|
1964
2685
|
fontSize: "11px",
|
|
1965
2686
|
fontWeight: 500,
|
|
1966
2687
|
fontFamily: "Monaco, monospace",
|
|
@@ -1968,15 +2689,15 @@ function V402Checkout({
|
|
|
1968
2689
|
textAlign: "right",
|
|
1969
2690
|
maxWidth: "60%",
|
|
1970
2691
|
lineHeight: 1.4
|
|
1971
|
-
} }, address))), /* @__PURE__ */
|
|
2692
|
+
} }, address))), /* @__PURE__ */ React6.createElement(
|
|
1972
2693
|
"div",
|
|
1973
2694
|
{
|
|
1974
2695
|
className: "flex items-center justify-center gap-2 mb-3 p-2 rounded-lg",
|
|
1975
2696
|
style: { background: "#f6ffed", border: "1px solid #d9f7be" }
|
|
1976
2697
|
},
|
|
1977
|
-
/* @__PURE__ */
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
), /* @__PURE__ */
|
|
2698
|
+
/* @__PURE__ */ React6.createElement(SafetyOutlined, { style: { color: "#52c41a", fontSize: "13px" } }),
|
|
2699
|
+
/* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "12px", color: "#52c41a", fontWeight: 500 } }, "Secure payment powered by v402pay")
|
|
2700
|
+
), /* @__PURE__ */ React6.createElement(
|
|
1980
2701
|
Button,
|
|
1981
2702
|
{
|
|
1982
2703
|
type: "primary",
|
|
@@ -1998,7 +2719,7 @@ function V402Checkout({
|
|
|
1998
2719
|
}
|
|
1999
2720
|
},
|
|
2000
2721
|
isProcessing ? "Processing..." : !paymentDetails ? "Loading..." : `Pay $${paymentDetails.amount} ${paymentDetails.currency}`
|
|
2001
|
-
), /* @__PURE__ */
|
|
2722
|
+
), /* @__PURE__ */ React6.createElement("div", { className: "text-center" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Don't have USDC?", " "), /* @__PURE__ */ React6.createElement(
|
|
2002
2723
|
"a",
|
|
2003
2724
|
{
|
|
2004
2725
|
href: "https://faucet.circle.com/",
|
|
@@ -2007,26 +2728,26 @@ function V402Checkout({
|
|
|
2007
2728
|
className: "text-blue-600 hover:text-blue-700 text-sm font-medium inline-flex items-center gap-1"
|
|
2008
2729
|
},
|
|
2009
2730
|
"Get it here ",
|
|
2010
|
-
/* @__PURE__ */
|
|
2011
|
-
)), isModal && result && /* @__PURE__ */
|
|
2731
|
+
/* @__PURE__ */ React6.createElement(LinkOutlined, { style: { fontSize: "12px" } })
|
|
2732
|
+
)), isModal && result && /* @__PURE__ */ React6.createElement(
|
|
2012
2733
|
"div",
|
|
2013
2734
|
{
|
|
2014
2735
|
className: "mt-4 p-4 rounded-lg",
|
|
2015
2736
|
style: { background: "#f6ffed", border: "1px solid #b7eb8f" }
|
|
2016
2737
|
},
|
|
2017
|
-
/* @__PURE__ */
|
|
2738
|
+
/* @__PURE__ */ React6.createElement("div", { className: "text-center" }, /* @__PURE__ */ React6.createElement("span", { style: { fontSize: "20px" } }, "\u2713"), /* @__PURE__ */ React6.createElement(Text, { style: {
|
|
2018
2739
|
fontSize: "14px",
|
|
2019
2740
|
color: "#52c41a",
|
|
2020
2741
|
fontWeight: 600,
|
|
2021
2742
|
marginLeft: "8px"
|
|
2022
2743
|
} }, "Payment Successful!"))
|
|
2023
|
-
), isModal && error && /* @__PURE__ */
|
|
2744
|
+
), isModal && error && /* @__PURE__ */ React6.createElement(
|
|
2024
2745
|
"div",
|
|
2025
2746
|
{
|
|
2026
2747
|
className: "mt-4 p-4 rounded-lg",
|
|
2027
2748
|
style: { background: "#fff2f0", border: "1px solid #ffccc7" }
|
|
2028
2749
|
},
|
|
2029
|
-
/* @__PURE__ */
|
|
2750
|
+
/* @__PURE__ */ React6.createElement("div", { className: "text-center mb-3" }, /* @__PURE__ */ React6.createElement("span", { style: { fontSize: "20px" } }, "\u2717"), /* @__PURE__ */ React6.createElement(Text, { style: {
|
|
2030
2751
|
fontSize: "14px",
|
|
2031
2752
|
color: "#ff4d4f",
|
|
2032
2753
|
fontWeight: 600,
|
|
@@ -2034,7 +2755,7 @@ function V402Checkout({
|
|
|
2034
2755
|
display: "block",
|
|
2035
2756
|
marginTop: "4px"
|
|
2036
2757
|
} }, "Payment Failed")),
|
|
2037
|
-
/* @__PURE__ */
|
|
2758
|
+
/* @__PURE__ */ React6.createElement(Text, { style: {
|
|
2038
2759
|
fontSize: "13px",
|
|
2039
2760
|
color: "#ff4d4f",
|
|
2040
2761
|
display: "block",
|
|
@@ -2042,11 +2763,11 @@ function V402Checkout({
|
|
|
2042
2763
|
} }, error)
|
|
2043
2764
|
))
|
|
2044
2765
|
),
|
|
2045
|
-
!isModal && (isProcessing || result || error) && /* @__PURE__ */
|
|
2766
|
+
!isModal && (isProcessing || result || error) && /* @__PURE__ */ React6.createElement(
|
|
2046
2767
|
Card,
|
|
2047
2768
|
{
|
|
2048
|
-
title: /* @__PURE__ */
|
|
2049
|
-
extra: !isProcessing && /* @__PURE__ */
|
|
2769
|
+
title: /* @__PURE__ */ React6.createElement("div", { className: "flex items-center gap-2" }, isProcessing && !result && !error ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(LoadingOutlined, { style: { color: "#14b8a6", fontSize: "16px" } }), /* @__PURE__ */ React6.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Processing Payment")) : result ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement("span", { style: { color: "#52c41a", fontSize: "18px" } }, "\u2713"), /* @__PURE__ */ React6.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Payment Successful")) : /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement("span", { style: { color: "#ff4d4f", fontSize: "18px" } }, "\u2717"), /* @__PURE__ */ React6.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Payment Failed"))),
|
|
2770
|
+
extra: !isProcessing && /* @__PURE__ */ React6.createElement(
|
|
2050
2771
|
Button,
|
|
2051
2772
|
{
|
|
2052
2773
|
type: "text",
|
|
@@ -2074,7 +2795,7 @@ function V402Checkout({
|
|
|
2074
2795
|
}
|
|
2075
2796
|
}
|
|
2076
2797
|
},
|
|
2077
|
-
isProcessing && !result && !error && /* @__PURE__ */
|
|
2798
|
+
isProcessing && !result && !error && /* @__PURE__ */ React6.createElement("div", { className: "text-center py-10" }, /* @__PURE__ */ React6.createElement("div", { className: "relative inline-block" }, /* @__PURE__ */ React6.createElement(
|
|
2078
2799
|
"div",
|
|
2079
2800
|
{
|
|
2080
2801
|
className: "absolute inset-0 rounded-full blur-xl opacity-40",
|
|
@@ -2083,12 +2804,12 @@ function V402Checkout({
|
|
|
2083
2804
|
animation: "pulse 2s ease-in-out infinite"
|
|
2084
2805
|
}
|
|
2085
2806
|
}
|
|
2086
|
-
), /* @__PURE__ */
|
|
2807
|
+
), /* @__PURE__ */ React6.createElement(
|
|
2087
2808
|
Spin,
|
|
2088
2809
|
{
|
|
2089
|
-
indicator: /* @__PURE__ */
|
|
2810
|
+
indicator: /* @__PURE__ */ React6.createElement(LoadingOutlined, { style: { fontSize: 56, color: "#14b8a6" } })
|
|
2090
2811
|
}
|
|
2091
|
-
)), /* @__PURE__ */
|
|
2812
|
+
)), /* @__PURE__ */ React6.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React6.createElement(Text, { strong: true, style: { fontSize: "18px", color: "#262626", letterSpacing: "-0.02em" } }, "Verifying Payment")), /* @__PURE__ */ React6.createElement("div", { className: "mt-2 mb-6" }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c", lineHeight: "1.6" } }, "Please wait while we confirm your transaction")), /* @__PURE__ */ React6.createElement(
|
|
2092
2813
|
"div",
|
|
2093
2814
|
{
|
|
2094
2815
|
className: "mt-4 p-4 rounded-xl",
|
|
@@ -2097,9 +2818,9 @@ function V402Checkout({
|
|
|
2097
2818
|
border: "1px solid #ccfbf1"
|
|
2098
2819
|
}
|
|
2099
2820
|
},
|
|
2100
|
-
/* @__PURE__ */
|
|
2821
|
+
/* @__PURE__ */ React6.createElement("div", { className: "flex items-center justify-center gap-2" }, /* @__PURE__ */ React6.createElement("span", { style: { fontSize: "16px" } }, "\u23F1\uFE0F"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "13px", color: "#0f766e", fontWeight: 500 } }, "This may take a few moments"))
|
|
2101
2822
|
)),
|
|
2102
|
-
result && /* @__PURE__ */
|
|
2823
|
+
result && /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("div", { className: "text-center mb-6" }, /* @__PURE__ */ React6.createElement(
|
|
2103
2824
|
"div",
|
|
2104
2825
|
{
|
|
2105
2826
|
className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
|
|
@@ -2108,13 +2829,13 @@ function V402Checkout({
|
|
|
2108
2829
|
boxShadow: "0 4px 20px rgba(16, 185, 129, 0.3)"
|
|
2109
2830
|
}
|
|
2110
2831
|
},
|
|
2111
|
-
/* @__PURE__ */
|
|
2112
|
-
), /* @__PURE__ */
|
|
2832
|
+
/* @__PURE__ */ React6.createElement("span", { style: { fontSize: "32px", color: "white" } }, "\u2713")
|
|
2833
|
+
), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement(Text, { strong: true, style: {
|
|
2113
2834
|
fontSize: "20px",
|
|
2114
2835
|
color: "#262626",
|
|
2115
2836
|
display: "block",
|
|
2116
2837
|
marginBottom: "8px"
|
|
2117
|
-
} }, "Payment Successful!"), /* @__PURE__ */
|
|
2838
|
+
} }, "Payment Successful!"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c" } }, "Your transaction has been confirmed"))), /* @__PURE__ */ React6.createElement(Divider, { style: { margin: "20px 0", borderColor: "#f0f0f0" } }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "12px", color: "#8c8c8c", fontWeight: 500 } }, "RESPONSE DATA")), /* @__PURE__ */ React6.createElement(
|
|
2118
2839
|
"pre",
|
|
2119
2840
|
{
|
|
2120
2841
|
style: {
|
|
@@ -2134,7 +2855,7 @@ function V402Checkout({
|
|
|
2134
2855
|
},
|
|
2135
2856
|
JSON.stringify(result, null, 2)
|
|
2136
2857
|
)),
|
|
2137
|
-
error && /* @__PURE__ */
|
|
2858
|
+
error && /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement("div", { className: "text-center mb-6" }, /* @__PURE__ */ React6.createElement(
|
|
2138
2859
|
"div",
|
|
2139
2860
|
{
|
|
2140
2861
|
className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
|
|
@@ -2143,13 +2864,13 @@ function V402Checkout({
|
|
|
2143
2864
|
boxShadow: "0 4px 20px rgba(239, 68, 68, 0.3)"
|
|
2144
2865
|
}
|
|
2145
2866
|
},
|
|
2146
|
-
/* @__PURE__ */
|
|
2147
|
-
), /* @__PURE__ */
|
|
2867
|
+
/* @__PURE__ */ React6.createElement("span", { style: { fontSize: "32px", color: "white" } }, "\u2717")
|
|
2868
|
+
), /* @__PURE__ */ React6.createElement("div", null, /* @__PURE__ */ React6.createElement(Text, { strong: true, style: {
|
|
2148
2869
|
fontSize: "20px",
|
|
2149
2870
|
color: "#262626",
|
|
2150
2871
|
display: "block",
|
|
2151
2872
|
marginBottom: "8px"
|
|
2152
|
-
} }, "Payment Failed"), /* @__PURE__ */
|
|
2873
|
+
} }, "Payment Failed"), /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c" } }, "Something went wrong with your transaction"))), /* @__PURE__ */ React6.createElement(Divider, { style: { margin: "20px 0", borderColor: "#f0f0f0" } }, /* @__PURE__ */ React6.createElement(Text, { style: { fontSize: "12px", color: "#8c8c8c", fontWeight: 500 } }, "ERROR DETAILS")), /* @__PURE__ */ React6.createElement(
|
|
2153
2874
|
"div",
|
|
2154
2875
|
{
|
|
2155
2876
|
style: {
|
|
@@ -2159,13 +2880,13 @@ function V402Checkout({
|
|
|
2159
2880
|
border: "1px solid #fee2e2"
|
|
2160
2881
|
}
|
|
2161
2882
|
},
|
|
2162
|
-
/* @__PURE__ */
|
|
2883
|
+
/* @__PURE__ */ React6.createElement(Text, { style: {
|
|
2163
2884
|
fontSize: "14px",
|
|
2164
2885
|
color: "#dc2626",
|
|
2165
2886
|
lineHeight: "1.6",
|
|
2166
2887
|
fontWeight: 500
|
|
2167
2888
|
} }, error)
|
|
2168
|
-
), /* @__PURE__ */
|
|
2889
|
+
), /* @__PURE__ */ React6.createElement("div", { className: "mt-4 text-center" }, /* @__PURE__ */ React6.createElement(
|
|
2169
2890
|
Button,
|
|
2170
2891
|
{
|
|
2171
2892
|
size: "large",
|
|
@@ -2186,7 +2907,7 @@ function V402Checkout({
|
|
|
2186
2907
|
)))
|
|
2187
2908
|
)
|
|
2188
2909
|
),
|
|
2189
|
-
/* @__PURE__ */
|
|
2910
|
+
/* @__PURE__ */ React6.createElement("style", { dangerouslySetInnerHTML: {
|
|
2190
2911
|
__html: `
|
|
2191
2912
|
@keyframes slideInRight {
|
|
2192
2913
|
from {
|
|
@@ -2213,12 +2934,698 @@ function V402Checkout({
|
|
|
2213
2934
|
} })
|
|
2214
2935
|
);
|
|
2215
2936
|
}
|
|
2937
|
+
|
|
2938
|
+
// src/react/components/checkout/V402CheckoutV2.tsx
|
|
2939
|
+
import React11, { useEffect as useEffect7, useState as useState8 } from "react";
|
|
2940
|
+
init_common();
|
|
2941
|
+
|
|
2942
|
+
// src/react/styles/animations.tsx
|
|
2943
|
+
import React7 from "react";
|
|
2944
|
+
var checkoutAnimations = `
|
|
2945
|
+
@keyframes spin {
|
|
2946
|
+
from { transform: rotate(0deg); }
|
|
2947
|
+
to { transform: rotate(360deg); }
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
@keyframes receiptShake {
|
|
2951
|
+
0%, 100% { transform: rotate(-0.3deg); }
|
|
2952
|
+
50% { transform: rotate(0.3deg); }
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
@keyframes slideInRight {
|
|
2956
|
+
from {
|
|
2957
|
+
opacity: 0;
|
|
2958
|
+
transform: translateX(100px);
|
|
2959
|
+
}
|
|
2960
|
+
to {
|
|
2961
|
+
opacity: 1;
|
|
2962
|
+
transform: translateX(0);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
@keyframes pulse {
|
|
2967
|
+
0%, 100% { opacity: 1; }
|
|
2968
|
+
50% { opacity: 0.4; }
|
|
2969
|
+
}
|
|
2970
|
+
`;
|
|
2971
|
+
var AnimationStyles = () => /* @__PURE__ */ React7.createElement("style", { dangerouslySetInnerHTML: { __html: checkoutAnimations } });
|
|
2972
|
+
|
|
2973
|
+
// src/react/components/checkout/Receipt.tsx
|
|
2974
|
+
import React8, { useEffect as useEffect6, useState as useState7 } from "react";
|
|
2975
|
+
var Receipt = ({
|
|
2976
|
+
isLoading,
|
|
2977
|
+
isVisible,
|
|
2978
|
+
result,
|
|
2979
|
+
error,
|
|
2980
|
+
paymentDetails,
|
|
2981
|
+
address,
|
|
2982
|
+
onClose,
|
|
2983
|
+
primaryColor,
|
|
2984
|
+
receiptTitle,
|
|
2985
|
+
tempReceiptId
|
|
2986
|
+
}) => {
|
|
2987
|
+
const [animationState, setAnimationState] = useState7("hidden");
|
|
2988
|
+
useEffect6(() => {
|
|
2989
|
+
if (isLoading) {
|
|
2990
|
+
setAnimationState("printing");
|
|
2991
|
+
} else if (isVisible && (result || error)) {
|
|
2992
|
+
setAnimationState("visible");
|
|
2993
|
+
const timer = setTimeout(() => setAnimationState("bounce"), 50);
|
|
2994
|
+
return () => clearTimeout(timer);
|
|
2995
|
+
} else if (!isVisible) {
|
|
2996
|
+
setAnimationState("hidden");
|
|
2997
|
+
}
|
|
2998
|
+
}, [isLoading, isVisible, result, error]);
|
|
2999
|
+
useEffect6(() => {
|
|
3000
|
+
if (animationState === "bounce") {
|
|
3001
|
+
const timer = setTimeout(() => setAnimationState("visible"), 150);
|
|
3002
|
+
return () => clearTimeout(timer);
|
|
3003
|
+
}
|
|
3004
|
+
}, [animationState]);
|
|
3005
|
+
const now = /* @__PURE__ */ new Date();
|
|
3006
|
+
const dateStr = `${String(now.getMonth() + 1).padStart(2, "0")}/${String(now.getDate()).padStart(2, "0")}/${String(now.getFullYear()).slice(-2)}`;
|
|
3007
|
+
const timeStr = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
|
|
3008
|
+
const receiptId = result?.transactionHash ? result.transactionHash.slice(-8).toUpperCase() : tempReceiptId;
|
|
3009
|
+
const getAnimationStyles = () => {
|
|
3010
|
+
const baseTransition = animationState === "bounce" ? "all 0.15s cubic-bezier(0.34, 1.56, 0.64, 1)" : "all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)";
|
|
3011
|
+
switch (animationState) {
|
|
3012
|
+
case "hidden":
|
|
3013
|
+
return {
|
|
3014
|
+
opacity: 0,
|
|
3015
|
+
transform: "translateY(50px)",
|
|
3016
|
+
marginBottom: 0,
|
|
3017
|
+
transition: baseTransition
|
|
3018
|
+
};
|
|
3019
|
+
case "printing":
|
|
3020
|
+
return {
|
|
3021
|
+
opacity: 1,
|
|
3022
|
+
transform: "translateY(0)",
|
|
3023
|
+
marginBottom: "-4px",
|
|
3024
|
+
// 负边距让小票贴着机器,还没撕开的感觉
|
|
3025
|
+
animation: "receiptShake 0.12s ease-in-out infinite",
|
|
3026
|
+
transition: baseTransition
|
|
3027
|
+
};
|
|
3028
|
+
case "visible":
|
|
3029
|
+
return {
|
|
3030
|
+
opacity: 1,
|
|
3031
|
+
transform: "translateY(0)",
|
|
3032
|
+
marginBottom: "8px",
|
|
3033
|
+
transition: baseTransition
|
|
3034
|
+
};
|
|
3035
|
+
case "bounce":
|
|
3036
|
+
return {
|
|
3037
|
+
opacity: 1,
|
|
3038
|
+
transform: "translateY(-8px)",
|
|
3039
|
+
marginBottom: "8px",
|
|
3040
|
+
transition: baseTransition
|
|
3041
|
+
};
|
|
3042
|
+
default:
|
|
3043
|
+
return {};
|
|
3044
|
+
}
|
|
3045
|
+
};
|
|
3046
|
+
const getContentStyles = () => {
|
|
3047
|
+
const baseTransition = "all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)";
|
|
3048
|
+
switch (animationState) {
|
|
3049
|
+
case "hidden":
|
|
3050
|
+
return {
|
|
3051
|
+
maxHeight: 0,
|
|
3052
|
+
overflow: "hidden",
|
|
3053
|
+
transition: baseTransition
|
|
3054
|
+
};
|
|
3055
|
+
case "printing":
|
|
3056
|
+
return {
|
|
3057
|
+
maxHeight: "80px",
|
|
3058
|
+
overflow: "hidden",
|
|
3059
|
+
transition: baseTransition
|
|
3060
|
+
};
|
|
3061
|
+
case "visible":
|
|
3062
|
+
case "bounce":
|
|
3063
|
+
return {
|
|
3064
|
+
maxHeight: "600px",
|
|
3065
|
+
overflow: "visible",
|
|
3066
|
+
transition: baseTransition
|
|
3067
|
+
};
|
|
3068
|
+
default:
|
|
3069
|
+
return {};
|
|
3070
|
+
}
|
|
3071
|
+
};
|
|
3072
|
+
if (animationState === "hidden" && !isLoading) return null;
|
|
3073
|
+
return /* @__PURE__ */ React8.createElement(
|
|
3074
|
+
"div",
|
|
3075
|
+
{
|
|
3076
|
+
className: "w-full flex justify-center",
|
|
3077
|
+
style: getAnimationStyles()
|
|
3078
|
+
},
|
|
3079
|
+
/* @__PURE__ */ React8.createElement(
|
|
3080
|
+
"div",
|
|
3081
|
+
{
|
|
3082
|
+
className: "relative bg-white shadow-2xl",
|
|
3083
|
+
style: {
|
|
3084
|
+
width: "75%",
|
|
3085
|
+
maxWidth: "280px",
|
|
3086
|
+
backgroundImage: `
|
|
3087
|
+
repeating-linear-gradient(
|
|
3088
|
+
0deg,
|
|
3089
|
+
transparent,
|
|
3090
|
+
transparent 1px,
|
|
3091
|
+
rgba(0,0,0,0.02) 1px,
|
|
3092
|
+
rgba(0,0,0,0.02) 2px
|
|
3093
|
+
)
|
|
3094
|
+
`
|
|
3095
|
+
}
|
|
3096
|
+
},
|
|
3097
|
+
/* @__PURE__ */ React8.createElement(
|
|
3098
|
+
"div",
|
|
3099
|
+
{
|
|
3100
|
+
className: "absolute top-0 left-0 right-0",
|
|
3101
|
+
style: {
|
|
3102
|
+
height: "8px",
|
|
3103
|
+
transform: "translateY(-100%)",
|
|
3104
|
+
background: `radial-gradient(circle at 50% 100%, white 5px, transparent 5px)`,
|
|
3105
|
+
backgroundSize: "12px 8px",
|
|
3106
|
+
backgroundPosition: "6px 0",
|
|
3107
|
+
backgroundRepeat: "repeat-x"
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
),
|
|
3111
|
+
/* @__PURE__ */ React8.createElement(
|
|
3112
|
+
"div",
|
|
3113
|
+
{
|
|
3114
|
+
className: "absolute left-0 right-0 bg-white",
|
|
3115
|
+
style: {
|
|
3116
|
+
height: "4px",
|
|
3117
|
+
top: "-4px"
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
),
|
|
3121
|
+
!isLoading && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
|
|
3122
|
+
"div",
|
|
3123
|
+
{
|
|
3124
|
+
className: "absolute bottom-0 left-0 right-0",
|
|
3125
|
+
style: {
|
|
3126
|
+
height: "8px",
|
|
3127
|
+
transform: "translateY(100%)",
|
|
3128
|
+
background: `radial-gradient(circle at 50% 0%, white 5px, transparent 5px)`,
|
|
3129
|
+
backgroundSize: "12px 8px",
|
|
3130
|
+
backgroundPosition: "6px 0",
|
|
3131
|
+
backgroundRepeat: "repeat-x"
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
), /* @__PURE__ */ React8.createElement(
|
|
3135
|
+
"div",
|
|
3136
|
+
{
|
|
3137
|
+
className: "absolute left-0 right-0 bg-white",
|
|
3138
|
+
style: {
|
|
3139
|
+
height: "4px",
|
|
3140
|
+
bottom: "-4px"
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
)),
|
|
3144
|
+
!isLoading && (result || error) && /* @__PURE__ */ React8.createElement(
|
|
3145
|
+
"button",
|
|
3146
|
+
{
|
|
3147
|
+
onClick: onClose,
|
|
3148
|
+
className: "absolute top-3 right-3 text-gray-300 hover:text-gray-500 transition-colors bg-transparent border-none outline-none p-0 cursor-pointer",
|
|
3149
|
+
style: { background: "none", border: "none" }
|
|
3150
|
+
},
|
|
3151
|
+
/* @__PURE__ */ React8.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" }, /* @__PURE__ */ React8.createElement("path", { d: "M18 6L6 18M6 6l12 12" }))
|
|
3152
|
+
),
|
|
3153
|
+
/* @__PURE__ */ React8.createElement("div", { className: "p-4 font-mono text-sm", style: getContentStyles() }, /* @__PURE__ */ React8.createElement("div", { className: "text-center mb-3" }, /* @__PURE__ */ React8.createElement("div", { className: "text-base font-bold tracking-wider text-gray-800" }, receiptTitle), /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between text-xs text-gray-500 mt-1" }, /* @__PURE__ */ React8.createElement("span", null, "ID: ", receiptId), /* @__PURE__ */ React8.createElement("span", null, dateStr, " ", timeStr))), /* @__PURE__ */ React8.createElement("div", { className: "border-t border-dashed border-gray-300 my-2" }), /* @__PURE__ */ React8.createElement(
|
|
3154
|
+
"div",
|
|
3155
|
+
{
|
|
3156
|
+
className: "max-h-64 overflow-y-auto pr-1",
|
|
3157
|
+
style: {
|
|
3158
|
+
scrollbarWidth: "thin",
|
|
3159
|
+
scrollbarColor: "#d1d5db transparent"
|
|
3160
|
+
}
|
|
3161
|
+
},
|
|
3162
|
+
isLoading ? /* @__PURE__ */ React8.createElement(LoadingContent, { primaryColor }) : error ? /* @__PURE__ */ React8.createElement(ErrorContent, { error }) : result ? /* @__PURE__ */ React8.createElement(
|
|
3163
|
+
SuccessContent,
|
|
3164
|
+
{
|
|
3165
|
+
result,
|
|
3166
|
+
paymentDetails,
|
|
3167
|
+
address,
|
|
3168
|
+
primaryColor
|
|
3169
|
+
}
|
|
3170
|
+
) : null
|
|
3171
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "border-t border-dashed border-gray-300 my-2" }), /* @__PURE__ */ React8.createElement(Barcode, null), /* @__PURE__ */ React8.createElement("div", { className: "text-center text-xs text-gray-400 mt-1 tracking-widest" }, "POWERED BY", " ", /* @__PURE__ */ React8.createElement(
|
|
3172
|
+
"a",
|
|
3173
|
+
{
|
|
3174
|
+
href: "https://v402pay.onvoyage.ai",
|
|
3175
|
+
target: "_blank",
|
|
3176
|
+
rel: "noopener noreferrer",
|
|
3177
|
+
className: "text-gray-500 hover:text-gray-700 underline transition-colors"
|
|
3178
|
+
},
|
|
3179
|
+
"V402PAY"
|
|
3180
|
+
)))
|
|
3181
|
+
)
|
|
3182
|
+
);
|
|
3183
|
+
};
|
|
3184
|
+
var LoadingContent = ({ primaryColor }) => /* @__PURE__ */ React8.createElement("div", { className: "text-center py-4" }, /* @__PURE__ */ React8.createElement(
|
|
3185
|
+
"div",
|
|
3186
|
+
{
|
|
3187
|
+
className: "inline-block w-8 h-8 border-2 border-gray-200 rounded-full mb-2",
|
|
3188
|
+
style: {
|
|
3189
|
+
borderTopColor: primaryColor,
|
|
3190
|
+
animation: "spin 0.8s linear infinite"
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "text-gray-700 font-semibold text-sm" }, "Processing..."), /* @__PURE__ */ React8.createElement("div", { className: "text-gray-400 text-xs mt-1" }, "Please wait"));
|
|
3194
|
+
var ErrorContent = ({ error }) => /* @__PURE__ */ React8.createElement("div", { className: "text-center py-3" }, /* @__PURE__ */ React8.createElement("div", { className: "text-red-500 text-2xl mb-2" }, "\u2717"), /* @__PURE__ */ React8.createElement("div", { className: "text-red-600 font-semibold mb-1 text-sm" }, "FAILED"), /* @__PURE__ */ React8.createElement("div", { className: "text-red-500 text-xs break-words px-2" }, error));
|
|
3195
|
+
var SuccessContent = ({
|
|
3196
|
+
result,
|
|
3197
|
+
paymentDetails,
|
|
3198
|
+
address,
|
|
3199
|
+
primaryColor
|
|
3200
|
+
}) => {
|
|
3201
|
+
const [copied, setCopied] = useState7(false);
|
|
3202
|
+
const handleCopy = async () => {
|
|
3203
|
+
try {
|
|
3204
|
+
await navigator.clipboard.writeText(JSON.stringify(result, null, 2));
|
|
3205
|
+
setCopied(true);
|
|
3206
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
3207
|
+
} catch (err) {
|
|
3208
|
+
console.error("Failed to copy:", err);
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
return /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("div", { className: "text-center mb-2" }, /* @__PURE__ */ React8.createElement("div", { className: "text-2xl mb-1", style: { color: primaryColor } }, "\u2713"), /* @__PURE__ */ React8.createElement("div", { className: "font-semibold text-sm", style: { color: primaryColor } }, "SUCCESS")), /* @__PURE__ */ React8.createElement("div", { className: "space-y-1 text-xs" }, paymentDetails && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-gray-500" }, "Amount:"), /* @__PURE__ */ React8.createElement("span", { className: "font-semibold" }, "$", paymentDetails.amount, " ", paymentDetails.currency)), /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-gray-500" }, "Network:"), /* @__PURE__ */ React8.createElement("span", { className: "font-semibold" }, paymentDetails.network))), address && /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-gray-500" }, "From:"), /* @__PURE__ */ React8.createElement("span", { className: "font-semibold" }, formatAddress(address))), result.transactionHash && /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-gray-500" }, "TX:"), /* @__PURE__ */ React8.createElement("span", { className: "font-semibold" }, formatAddress(result.transactionHash)))), /* @__PURE__ */ React8.createElement("div", { className: "border-t border-dashed border-gray-300 my-2" }), /* @__PURE__ */ React8.createElement("div", { className: "text-xs" }, /* @__PURE__ */ React8.createElement("div", { className: "flex justify-between items-center mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "text-gray-500" }, "Response:"), /* @__PURE__ */ React8.createElement(
|
|
3212
|
+
"button",
|
|
3213
|
+
{
|
|
3214
|
+
onClick: handleCopy,
|
|
3215
|
+
className: "text-gray-300 hover:text-gray-500 transition-colors flex items-center gap-1 bg-transparent border-none outline-none p-0 cursor-pointer",
|
|
3216
|
+
style: { background: "none", border: "none" }
|
|
3217
|
+
},
|
|
3218
|
+
copied ? /* @__PURE__ */ React8.createElement("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" }, /* @__PURE__ */ React8.createElement("path", { d: "M20 6L9 17l-5-5" })) : /* @__PURE__ */ React8.createElement("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React8.createElement("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), /* @__PURE__ */ React8.createElement("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" }))
|
|
3219
|
+
)), /* @__PURE__ */ React8.createElement(
|
|
3220
|
+
"pre",
|
|
3221
|
+
{
|
|
3222
|
+
className: "bg-gray-50 p-2 rounded text-xs overflow-auto whitespace-pre-wrap break-words",
|
|
3223
|
+
style: { maxHeight: "80px", fontSize: "10px" }
|
|
3224
|
+
},
|
|
3225
|
+
JSON.stringify(result, null, 2)
|
|
3226
|
+
)));
|
|
3227
|
+
};
|
|
3228
|
+
var Barcode = () => {
|
|
3229
|
+
const pattern = [2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1];
|
|
3230
|
+
const heights = [10, 12, 11, 13, 10, 14, 11, 12, 13, 10, 11, 14, 12, 10, 13, 11, 12, 14, 10, 11];
|
|
3231
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-center gap-0.5 h-4 opacity-60" }, pattern.map((width, i) => /* @__PURE__ */ React8.createElement(
|
|
3232
|
+
"div",
|
|
3233
|
+
{
|
|
3234
|
+
key: i,
|
|
3235
|
+
className: "bg-gray-800",
|
|
3236
|
+
style: {
|
|
3237
|
+
width: `${width}px`,
|
|
3238
|
+
height: `${heights[i]}px`
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
)));
|
|
3242
|
+
};
|
|
3243
|
+
|
|
3244
|
+
// src/react/components/checkout/TerminalScreen.tsx
|
|
3245
|
+
import React9 from "react";
|
|
3246
|
+
var TerminalScreen = ({
|
|
3247
|
+
title,
|
|
3248
|
+
tooltipText,
|
|
3249
|
+
hasInvalidCheckoutId,
|
|
3250
|
+
fetchingPaymentInfo,
|
|
3251
|
+
address,
|
|
3252
|
+
paymentDetails,
|
|
3253
|
+
screenText,
|
|
3254
|
+
supportedNetworks
|
|
3255
|
+
}) => {
|
|
3256
|
+
return /* @__PURE__ */ React9.createElement(
|
|
3257
|
+
"div",
|
|
3258
|
+
{
|
|
3259
|
+
className: "rounded-xl p-3 mb-3",
|
|
3260
|
+
style: {
|
|
3261
|
+
backgroundColor: "#0a1a0a",
|
|
3262
|
+
boxShadow: "inset 0 3px 16px rgba(0,0,0,0.5)",
|
|
3263
|
+
border: "3px solid rgba(0,0,0,0.3)"
|
|
3264
|
+
}
|
|
3265
|
+
},
|
|
3266
|
+
/* @__PURE__ */ React9.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React9.createElement("div", { className: "flex items-center gap-1.5 flex-1 min-w-0" }, /* @__PURE__ */ React9.createElement("div", { className: "w-2.5 h-2.5 rounded border border-green-700 flex-shrink-0" }), title ? /* @__PURE__ */ React9.createElement(
|
|
3267
|
+
"span",
|
|
3268
|
+
{
|
|
3269
|
+
className: "text-xs font-mono",
|
|
3270
|
+
style: { color: "#22c55e80" },
|
|
3271
|
+
title
|
|
3272
|
+
},
|
|
3273
|
+
title.length > 26 ? `${title.slice(0, 13)}...${title.slice(-13)}` : title
|
|
3274
|
+
) : /* @__PURE__ */ React9.createElement("span", { className: "text-xs font-mono", style: { color: "#22c55e80" } }, "CHECKOUT")), /* @__PURE__ */ React9.createElement(
|
|
3275
|
+
"div",
|
|
3276
|
+
{
|
|
3277
|
+
className: "flex gap-0.5 flex-shrink-0 cursor-help",
|
|
3278
|
+
title: tooltipText
|
|
3279
|
+
},
|
|
3280
|
+
/* @__PURE__ */ React9.createElement(
|
|
3281
|
+
"div",
|
|
3282
|
+
{
|
|
3283
|
+
className: "w-1 h-1.5 rounded-sm",
|
|
3284
|
+
style: { backgroundColor: address ? "#22c55e80" : "#22c55e30" }
|
|
3285
|
+
}
|
|
3286
|
+
),
|
|
3287
|
+
/* @__PURE__ */ React9.createElement(
|
|
3288
|
+
"div",
|
|
3289
|
+
{
|
|
3290
|
+
className: "w-1 h-1.5 rounded-sm",
|
|
3291
|
+
style: { backgroundColor: address ? "#22c55e80" : "#22c55e30" }
|
|
3292
|
+
}
|
|
3293
|
+
),
|
|
3294
|
+
/* @__PURE__ */ React9.createElement("div", { className: "w-1 h-1.5 rounded-sm", style: { backgroundColor: "#22c55e80" } })
|
|
3295
|
+
)),
|
|
3296
|
+
/* @__PURE__ */ React9.createElement("div", { className: "min-h-[120px]" }, hasInvalidCheckoutId ? /* @__PURE__ */ React9.createElement(InvalidIdContent, null) : fetchingPaymentInfo ? /* @__PURE__ */ React9.createElement(LoadingContent2, null) : !address ? /* @__PURE__ */ React9.createElement(ConnectWalletContent, { supportedNetworks }) : /* @__PURE__ */ React9.createElement(
|
|
3297
|
+
PaymentInfoContent,
|
|
3298
|
+
{
|
|
3299
|
+
screenText,
|
|
3300
|
+
paymentDetails,
|
|
3301
|
+
address
|
|
3302
|
+
}
|
|
3303
|
+
))
|
|
3304
|
+
);
|
|
3305
|
+
};
|
|
3306
|
+
var InvalidIdContent = () => /* @__PURE__ */ React9.createElement("div", { className: "text-center py-3" }, /* @__PURE__ */ React9.createElement("div", { className: "text-red-500 text-xl mb-1" }, "\u2717"), /* @__PURE__ */ React9.createElement("div", { className: "text-red-500 font-mono text-sm mb-1" }, "INVALID ID"), /* @__PURE__ */ React9.createElement("div", { className: "text-red-400 font-mono text-xs" }, "Check your checkout ID"));
|
|
3307
|
+
var LoadingContent2 = () => /* @__PURE__ */ React9.createElement("div", { className: "text-center py-4" }, /* @__PURE__ */ React9.createElement(
|
|
3308
|
+
"div",
|
|
3309
|
+
{
|
|
3310
|
+
className: "inline-block w-5 h-5 border-2 rounded-full mb-2",
|
|
3311
|
+
style: {
|
|
3312
|
+
borderColor: "#22c55e40",
|
|
3313
|
+
borderTopColor: "#22c55e",
|
|
3314
|
+
animation: "spin 1s linear infinite"
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
), /* @__PURE__ */ React9.createElement("div", { className: "font-mono text-sm", style: { color: "#22c55e" } }, "LOADING..."));
|
|
3318
|
+
var ConnectWalletContent = ({ supportedNetworks }) => /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement(
|
|
3319
|
+
"div",
|
|
3320
|
+
{
|
|
3321
|
+
className: "font-mono text-base mb-3 tracking-wider",
|
|
3322
|
+
style: { color: "#f97316", textShadow: "0 0 10px #f9731640" }
|
|
3323
|
+
},
|
|
3324
|
+
"CONNECT WALLET..."
|
|
3325
|
+
), /* @__PURE__ */ React9.createElement(WalletConnect, { supportedNetworks, showSwitchWallet: false }));
|
|
3326
|
+
var PaymentInfoContent = ({
|
|
3327
|
+
screenText,
|
|
3328
|
+
paymentDetails,
|
|
3329
|
+
address
|
|
3330
|
+
}) => /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement(
|
|
3331
|
+
"div",
|
|
3332
|
+
{
|
|
3333
|
+
className: "font-mono text-base mb-3 tracking-wider",
|
|
3334
|
+
style: { color: "#f97316", textShadow: "0 0 10px #f9731640" }
|
|
3335
|
+
},
|
|
3336
|
+
screenText
|
|
3337
|
+
), paymentDetails && /* @__PURE__ */ React9.createElement("div", { className: "text-xs font-mono" }, /* @__PURE__ */ React9.createElement("div", { className: "grid grid-cols-2 gap-1.5 mb-1.5" }, /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e60" } }, "AMOUNT"), /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e" } }, "$", paymentDetails.amount)), /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e60" } }, "CURRENCY"), /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e" } }, paymentDetails.currency))), /* @__PURE__ */ React9.createElement("div", null, /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e60" } }, "WALLET"), /* @__PURE__ */ React9.createElement("div", { style: { color: "#22c55e", wordBreak: "break-all" } }, address))));
|
|
3338
|
+
|
|
3339
|
+
// src/react/components/checkout/TerminalButtons.tsx
|
|
3340
|
+
import React10 from "react";
|
|
3341
|
+
var TerminalButtons = ({
|
|
3342
|
+
address,
|
|
3343
|
+
showReceipt,
|
|
3344
|
+
isProcessing,
|
|
3345
|
+
paymentDetails,
|
|
3346
|
+
hasInvalidCheckoutId,
|
|
3347
|
+
onDisconnect,
|
|
3348
|
+
onClearReceipt,
|
|
3349
|
+
onPayment
|
|
3350
|
+
}) => {
|
|
3351
|
+
const isPayDisabled = isProcessing || !paymentDetails || !address || hasInvalidCheckoutId;
|
|
3352
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "flex items-center justify-between px-1" }, /* @__PURE__ */ React10.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React10.createElement(
|
|
3353
|
+
CircleButton,
|
|
3354
|
+
{
|
|
3355
|
+
onClick: () => address && onDisconnect(),
|
|
3356
|
+
disabled: !address,
|
|
3357
|
+
title: "Disconnect",
|
|
3358
|
+
size: "small"
|
|
3359
|
+
},
|
|
3360
|
+
/* @__PURE__ */ React10.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2" }, /* @__PURE__ */ React10.createElement("path", { d: "M18 6L6 18M6 6l12 12" }))
|
|
3361
|
+
), /* @__PURE__ */ React10.createElement(
|
|
3362
|
+
CircleButton,
|
|
3363
|
+
{
|
|
3364
|
+
onClick: onClearReceipt,
|
|
3365
|
+
disabled: !showReceipt || isProcessing,
|
|
3366
|
+
title: "Clear",
|
|
3367
|
+
size: "small"
|
|
3368
|
+
},
|
|
3369
|
+
/* @__PURE__ */ React10.createElement("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2" }, /* @__PURE__ */ React10.createElement("path", { d: "M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6h14z" }))
|
|
3370
|
+
)), /* @__PURE__ */ React10.createElement("div", { className: "flex flex-col gap-0.5 opacity-40" }, /* @__PURE__ */ React10.createElement("div", { className: "w-6 h-0.5 rounded", style: { backgroundColor: "rgba(0,0,0,0.3)" } }), /* @__PURE__ */ React10.createElement("div", { className: "w-6 h-0.5 rounded", style: { backgroundColor: "rgba(0,0,0,0.3)" } }), /* @__PURE__ */ React10.createElement("div", { className: "w-6 h-0.5 rounded", style: { backgroundColor: "rgba(0,0,0,0.3)" } })), /* @__PURE__ */ React10.createElement(
|
|
3371
|
+
"button",
|
|
3372
|
+
{
|
|
3373
|
+
onClick: onPayment,
|
|
3374
|
+
disabled: isPayDisabled,
|
|
3375
|
+
className: "px-5 py-2.5 rounded-xl font-bold text-white flex items-center gap-2 transition-all active:scale-95",
|
|
3376
|
+
style: {
|
|
3377
|
+
backgroundColor: isPayDisabled ? "#9ca3af" : "#ea580c",
|
|
3378
|
+
boxShadow: isPayDisabled ? "none" : "0 4px 12px rgba(234,88,12,0.4), inset 0 -2px 4px rgba(0,0,0,0.2)",
|
|
3379
|
+
cursor: isPayDisabled ? "not-allowed" : "pointer"
|
|
3380
|
+
}
|
|
3381
|
+
},
|
|
3382
|
+
isProcessing ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
|
|
3383
|
+
"div",
|
|
3384
|
+
{
|
|
3385
|
+
className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full",
|
|
3386
|
+
style: { animation: "spin 0.8s linear infinite" }
|
|
3387
|
+
}
|
|
3388
|
+
), /* @__PURE__ */ React10.createElement("span", { className: "font-mono tracking-wider text-sm" }, "PAYING...")) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement("span", { className: "font-mono tracking-wider text-sm" }, "PAY"), /* @__PURE__ */ React10.createElement(
|
|
3389
|
+
"svg",
|
|
3390
|
+
{
|
|
3391
|
+
width: "18",
|
|
3392
|
+
height: "18",
|
|
3393
|
+
viewBox: "0 0 24 24",
|
|
3394
|
+
fill: "none",
|
|
3395
|
+
stroke: "currentColor",
|
|
3396
|
+
strokeWidth: "2"
|
|
3397
|
+
},
|
|
3398
|
+
/* @__PURE__ */ React10.createElement("path", { d: "M12 2v20M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6" })
|
|
3399
|
+
))
|
|
3400
|
+
));
|
|
3401
|
+
};
|
|
3402
|
+
var CircleButton = ({ onClick, disabled, title, size = "normal", children }) => {
|
|
3403
|
+
const sizeClass = size === "small" ? "w-10 h-10" : "w-12 h-12";
|
|
3404
|
+
return /* @__PURE__ */ React10.createElement(
|
|
3405
|
+
"button",
|
|
3406
|
+
{
|
|
3407
|
+
onClick,
|
|
3408
|
+
disabled,
|
|
3409
|
+
className: `${sizeClass} rounded-full flex items-center justify-center transition-transform active:scale-95`,
|
|
3410
|
+
style: {
|
|
3411
|
+
backgroundColor: "#374151",
|
|
3412
|
+
boxShadow: "inset 0 -2px 4px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.2)",
|
|
3413
|
+
opacity: disabled ? 0.5 : 1
|
|
3414
|
+
},
|
|
3415
|
+
title
|
|
3416
|
+
},
|
|
3417
|
+
children
|
|
3418
|
+
);
|
|
3419
|
+
};
|
|
3420
|
+
|
|
3421
|
+
// src/react/components/checkout/V402CheckoutV2.tsx
|
|
3422
|
+
var generateRandomId = () => {
|
|
3423
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
3424
|
+
return Array.from({ length: 8 }, () => chars[Math.floor(Math.random() * chars.length)]).join("");
|
|
3425
|
+
};
|
|
3426
|
+
function V402CheckoutV2({
|
|
3427
|
+
checkoutId,
|
|
3428
|
+
headerInfo = {},
|
|
3429
|
+
primaryColor = "#84cc16",
|
|
3430
|
+
isModal = false,
|
|
3431
|
+
onPaymentComplete,
|
|
3432
|
+
additionalParams = {},
|
|
3433
|
+
expectedNetwork
|
|
3434
|
+
}) {
|
|
3435
|
+
const {
|
|
3436
|
+
title = "V402Pay Checkout",
|
|
3437
|
+
brandName = "V402PAY",
|
|
3438
|
+
receiptTitle = "V402 PAYMENT",
|
|
3439
|
+
tooltipText = "V402Pay - Accept Crypto Payments Easier"
|
|
3440
|
+
} = headerInfo;
|
|
3441
|
+
const endpoint = PROD_BACK_URL;
|
|
3442
|
+
const {
|
|
3443
|
+
supportedNetworks,
|
|
3444
|
+
isLoading: fetchingPaymentInfo,
|
|
3445
|
+
paymentInfo
|
|
3446
|
+
} = usePaymentInfo(checkoutId, endpoint, additionalParams);
|
|
3447
|
+
const targetNetwork = expectedNetwork || supportedNetworks[0];
|
|
3448
|
+
const { address, networkType, disconnect, ensureNetwork } = usePageNetwork(
|
|
3449
|
+
targetNetwork,
|
|
3450
|
+
{ autoSwitch: !!targetNetwork, switchOnMount: true }
|
|
3451
|
+
);
|
|
3452
|
+
const { isProcessing, setIsProcessing, result, setResult, error, setError } = usePayment();
|
|
3453
|
+
const [paymentDetails, setPaymentDetails] = useState8(null);
|
|
3454
|
+
const [showReceipt, setShowReceipt] = useState8(false);
|
|
3455
|
+
const [tempReceiptId, setTempReceiptId] = useState8(() => generateRandomId());
|
|
3456
|
+
const handleDisconnect = () => {
|
|
3457
|
+
disconnect();
|
|
3458
|
+
setResult(null);
|
|
3459
|
+
setError(null);
|
|
3460
|
+
setShowReceipt(false);
|
|
3461
|
+
};
|
|
3462
|
+
const handlePayment = async () => {
|
|
3463
|
+
if (!networkType) return;
|
|
3464
|
+
setTempReceiptId(generateRandomId());
|
|
3465
|
+
setResult(null);
|
|
3466
|
+
setError(null);
|
|
3467
|
+
setIsProcessing(true);
|
|
3468
|
+
setShowReceipt(true);
|
|
3469
|
+
try {
|
|
3470
|
+
const response = await makePayment(networkType, checkoutId, endpoint, additionalParams, address || void 0);
|
|
3471
|
+
const data = await response.json();
|
|
3472
|
+
setResult(data);
|
|
3473
|
+
if (onPaymentComplete) {
|
|
3474
|
+
onPaymentComplete(data);
|
|
3475
|
+
}
|
|
3476
|
+
} catch (err) {
|
|
3477
|
+
setError(err.message || "Payment failed");
|
|
3478
|
+
} finally {
|
|
3479
|
+
setIsProcessing(false);
|
|
3480
|
+
}
|
|
3481
|
+
};
|
|
3482
|
+
const handleCloseReceipt = () => {
|
|
3483
|
+
setShowReceipt(false);
|
|
3484
|
+
setResult(null);
|
|
3485
|
+
setError(null);
|
|
3486
|
+
};
|
|
3487
|
+
useEffect7(() => {
|
|
3488
|
+
if (paymentInfo && paymentInfo.length > 0) {
|
|
3489
|
+
const firstPayment = paymentInfo[0];
|
|
3490
|
+
const rawAmount = firstPayment.maxAmountRequired?.toString() || "0";
|
|
3491
|
+
const decimals = 6;
|
|
3492
|
+
const humanReadableAmount = (Number(rawAmount) / Math.pow(10, decimals)).toFixed(2);
|
|
3493
|
+
const network = firstPayment.network || "Unknown";
|
|
3494
|
+
const currency = "USDC";
|
|
3495
|
+
setPaymentDetails({ amount: humanReadableAmount, currency, network });
|
|
3496
|
+
}
|
|
3497
|
+
}, [paymentInfo]);
|
|
3498
|
+
useEffect7(() => {
|
|
3499
|
+
if (targetNetwork && !fetchingPaymentInfo && ensureNetwork) {
|
|
3500
|
+
ensureNetwork(targetNetwork).catch((err) => {
|
|
3501
|
+
console.error("Failed to ensure network:", err);
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
}, [targetNetwork, fetchingPaymentInfo]);
|
|
3505
|
+
useEffect7(() => {
|
|
3506
|
+
if (isProcessing || result || error) {
|
|
3507
|
+
setShowReceipt(true);
|
|
3508
|
+
}
|
|
3509
|
+
}, [isProcessing, result, error]);
|
|
3510
|
+
const hasInvalidCheckoutId = !fetchingPaymentInfo && (!paymentInfo || paymentInfo.length === 0);
|
|
3511
|
+
const NetworkIcon = paymentDetails ? getNetworkIcon(paymentDetails.network) : null;
|
|
3512
|
+
const screenText = paymentDetails ? `PAY $${paymentDetails.amount} ${paymentDetails.currency}` : "AWAITING...";
|
|
3513
|
+
const getStatusText = () => {
|
|
3514
|
+
if (hasInvalidCheckoutId) return "ERROR";
|
|
3515
|
+
if (fetchingPaymentInfo) return "LOADING";
|
|
3516
|
+
if (!address) return "CONNECT";
|
|
3517
|
+
if (isProcessing) return "PAYING";
|
|
3518
|
+
return "READY";
|
|
3519
|
+
};
|
|
3520
|
+
return /* @__PURE__ */ React11.createElement("div", { className: isModal ? "bg-transparent" : "min-h-screen bg-gray-100 flex items-center justify-center p-4" }, /* @__PURE__ */ React11.createElement(
|
|
3521
|
+
"div",
|
|
3522
|
+
{
|
|
3523
|
+
className: "flex flex-col items-center",
|
|
3524
|
+
style: { width: isModal ? "100%" : "380px", maxWidth: "100%" }
|
|
3525
|
+
},
|
|
3526
|
+
/* @__PURE__ */ React11.createElement(
|
|
3527
|
+
Receipt,
|
|
3528
|
+
{
|
|
3529
|
+
isLoading: isProcessing,
|
|
3530
|
+
isVisible: showReceipt,
|
|
3531
|
+
result,
|
|
3532
|
+
error,
|
|
3533
|
+
paymentDetails,
|
|
3534
|
+
address,
|
|
3535
|
+
onClose: handleCloseReceipt,
|
|
3536
|
+
primaryColor,
|
|
3537
|
+
receiptTitle,
|
|
3538
|
+
tempReceiptId
|
|
3539
|
+
}
|
|
3540
|
+
),
|
|
3541
|
+
/* @__PURE__ */ React11.createElement(
|
|
3542
|
+
"div",
|
|
3543
|
+
{
|
|
3544
|
+
className: "relative rounded-2xl p-3 shadow-2xl w-full",
|
|
3545
|
+
style: {
|
|
3546
|
+
backgroundColor: primaryColor,
|
|
3547
|
+
boxShadow: `0 16px 48px -8px ${primaryColor}66, 0 8px 24px -4px rgba(0,0,0,0.3);padding-bottom: 0px`
|
|
3548
|
+
}
|
|
3549
|
+
},
|
|
3550
|
+
/* @__PURE__ */ React11.createElement(
|
|
3551
|
+
"div",
|
|
3552
|
+
{
|
|
3553
|
+
className: "absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-2.5 rounded-b-lg",
|
|
3554
|
+
style: { backgroundColor: "rgba(0,0,0,0.4)" }
|
|
3555
|
+
}
|
|
3556
|
+
),
|
|
3557
|
+
/* @__PURE__ */ React11.createElement("div", { className: "flex justify-between items-center mb-2 mt-1 px-1" }, /* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React11.createElement(
|
|
3558
|
+
"div",
|
|
3559
|
+
{
|
|
3560
|
+
className: "w-2 h-2 rounded-full",
|
|
3561
|
+
style: {
|
|
3562
|
+
backgroundColor: address ? "#22c55e" : "#ef4444",
|
|
3563
|
+
animation: "pulse 2s ease-in-out infinite"
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
), /* @__PURE__ */ React11.createElement(
|
|
3567
|
+
"span",
|
|
3568
|
+
{
|
|
3569
|
+
className: "text-xs font-mono font-bold tracking-wider",
|
|
3570
|
+
style: { color: "rgba(0,0,0,0.7)" }
|
|
3571
|
+
},
|
|
3572
|
+
getStatusText()
|
|
3573
|
+
)), /* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-1.5" }, paymentDetails && NetworkIcon && /* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React11.createElement(NetworkIcon, { width: 12, height: 12, style: { color: "rgba(0,0,0,0.7)" } }), /* @__PURE__ */ React11.createElement("span", { className: "text-xs font-mono font-bold", style: { color: "rgba(0,0,0,0.7)" } }, paymentDetails.network)))),
|
|
3574
|
+
/* @__PURE__ */ React11.createElement(
|
|
3575
|
+
TerminalScreen,
|
|
3576
|
+
{
|
|
3577
|
+
title,
|
|
3578
|
+
tooltipText,
|
|
3579
|
+
hasInvalidCheckoutId,
|
|
3580
|
+
fetchingPaymentInfo,
|
|
3581
|
+
address,
|
|
3582
|
+
paymentDetails,
|
|
3583
|
+
screenText,
|
|
3584
|
+
supportedNetworks
|
|
3585
|
+
}
|
|
3586
|
+
),
|
|
3587
|
+
/* @__PURE__ */ React11.createElement(
|
|
3588
|
+
TerminalButtons,
|
|
3589
|
+
{
|
|
3590
|
+
address,
|
|
3591
|
+
showReceipt,
|
|
3592
|
+
isProcessing,
|
|
3593
|
+
paymentDetails,
|
|
3594
|
+
hasInvalidCheckoutId,
|
|
3595
|
+
onDisconnect: handleDisconnect,
|
|
3596
|
+
onClearReceipt: handleCloseReceipt,
|
|
3597
|
+
onPayment: handlePayment
|
|
3598
|
+
}
|
|
3599
|
+
),
|
|
3600
|
+
brandName && /* @__PURE__ */ React11.createElement("div", { className: "text-center mt-0 mb-0" }, /* @__PURE__ */ React11.createElement(
|
|
3601
|
+
"div",
|
|
3602
|
+
{
|
|
3603
|
+
className: "inline-block px-2 py-0.5 rounded text-[10px] font-mono font-bold tracking-[0.2em]",
|
|
3604
|
+
style: {
|
|
3605
|
+
backgroundColor: "#1a1a1a",
|
|
3606
|
+
color: "#9acd32",
|
|
3607
|
+
boxShadow: "inset 0 1px 3px rgba(0,0,0,0.8), 0 1px 0 rgba(255,255,255,0.1)",
|
|
3608
|
+
border: "1px solid rgba(0,0,0,0.5)",
|
|
3609
|
+
textShadow: "0 0 4px #9acd3280"
|
|
3610
|
+
}
|
|
3611
|
+
},
|
|
3612
|
+
brandName
|
|
3613
|
+
))
|
|
3614
|
+
)
|
|
3615
|
+
), /* @__PURE__ */ React11.createElement(AnimationStyles, null));
|
|
3616
|
+
}
|
|
2216
3617
|
export {
|
|
3618
|
+
AnimationStyles,
|
|
3619
|
+
Toast,
|
|
2217
3620
|
V402Checkout,
|
|
3621
|
+
V402CheckoutV2,
|
|
2218
3622
|
WalletConnect,
|
|
3623
|
+
WalletSelectModal,
|
|
3624
|
+
checkoutAnimations,
|
|
2219
3625
|
usePageNetwork,
|
|
2220
3626
|
usePayment,
|
|
2221
3627
|
usePaymentInfo,
|
|
3628
|
+
useToast,
|
|
2222
3629
|
useWallet
|
|
2223
3630
|
};
|
|
2224
3631
|
//# sourceMappingURL=index.mjs.map
|