@cromix-dev/payment-component 0.0.11 → 0.0.13

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.
Files changed (33) hide show
  1. package/dist/{src/components → components}/CryptoPayment.d.ts +6 -1
  2. package/dist/components/CryptoPayment.d.ts.map +1 -0
  3. package/dist/components/CryptoPayment.js +753 -0
  4. package/dist/components/CryptoPayment.js.map +1 -0
  5. package/dist/components/PaymentProvider.d.ts.map +1 -0
  6. package/dist/components/PaymentProvider.js +69 -0
  7. package/dist/components/PaymentProvider.js.map +1 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/styles/Crypto.css.d.ts +37 -0
  13. package/dist/styles/Crypto.css.d.ts.map +1 -0
  14. package/dist/styles/Crypto.css.js +339 -0
  15. package/dist/styles/Crypto.css.js.map +1 -0
  16. package/dist/types/index.d.ts.map +1 -0
  17. package/dist/types/index.js.map +1 -0
  18. package/package.json +13 -4
  19. package/dist/src/components/CryptoPayment.d.ts.map +0 -1
  20. package/dist/src/components/CryptoPayment.js +0 -260
  21. package/dist/src/components/CryptoPayment.js.map +0 -1
  22. package/dist/src/components/PaymentProvider.d.ts.map +0 -1
  23. package/dist/src/components/PaymentProvider.js +0 -62
  24. package/dist/src/components/PaymentProvider.js.map +0 -1
  25. package/dist/src/index.d.ts +0 -4
  26. package/dist/src/index.d.ts.map +0 -1
  27. package/dist/src/index.js +0 -3
  28. package/dist/src/index.js.map +0 -1
  29. package/dist/src/types/index.d.ts.map +0 -1
  30. package/dist/src/types/index.js.map +0 -1
  31. /package/dist/{src/components → components}/PaymentProvider.d.ts +0 -0
  32. /package/dist/{src/types → types}/index.d.ts +0 -0
  33. /package/dist/{src/types → types}/index.js +0 -0
@@ -0,0 +1,753 @@
1
+ import { useState, useEffect, useCallback, useRef, useMemo } from "react";
2
+ import { useAccount, useWalletClient, usePublicClient, useSwitchChain, useDisconnect, useChains, useBalance, useReadContract, useEstimateFeesPerGas } from "wagmi";
3
+ import { useAppKit } from "@reown/appkit/react";
4
+ import { mainnet } from "@reown/appkit/networks";
5
+ import { erc20Abi, formatUnits, maxUint256 } from "viem";
6
+ import { toast } from "react-toastify";
7
+ import { PaymentRouterAbi, ETH, mainnetUSDTAbi, USDT_ADDRESSES } from "@cromix-dev/evm";
8
+ import * as styles from "../styles/Crypto.css.js";
9
+
10
+ // checkers for native coins
11
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
12
+ const isNativeCoin = tokenAddress => tokenAddress.toLowerCase() === ETH.toLowerCase();
13
+
14
+ // checkers for non-standard ERC20 tokens
15
+ const isMainnetUSDT = (chainId, tokenAddress) => chainId === mainnet.id && tokenAddress.toLowerCase() === USDT_ADDRESSES[mainnet.id].toLowerCase();
16
+ export default function CryptoPayment({
17
+ paymentConfig,
18
+ usdAmount,
19
+ onEstimateTokenAmount,
20
+ onCreatePayment,
21
+ onGetSignature,
22
+ onSuccess,
23
+ onCancel
24
+ }) {
25
+ const {
26
+ address,
27
+ chain,
28
+ isConnected
29
+ } = useAccount();
30
+ const publicClient = usePublicClient();
31
+ const {
32
+ data: walletClient
33
+ } = useWalletClient();
34
+ const {
35
+ switchChain
36
+ } = useSwitchChain();
37
+ const {
38
+ disconnect
39
+ } = useDisconnect();
40
+ const {
41
+ open
42
+ } = useAppKit();
43
+ const chains = useChains();
44
+ const [estimatedTokenAmount, setEstimatedTokenAmount] = useState(0);
45
+ const [status, setStatus] = useState("idle");
46
+ const [selectedPaymentRouter, setSelectedPaymentRouter] = useState(null);
47
+ const [selectedPaymentSetting, setSelectedPaymentSetting] = useState(null);
48
+ const [needsApproval, setNeedsApproval] = useState(null);
49
+ const [currentAllowance, setCurrentAllowance] = useState(0n);
50
+ const [error, setError] = useState(null);
51
+ const availableNetworks = useMemo(() => {
52
+ if (!selectedPaymentSetting) return [];
53
+ return paymentConfig.paymentSettings.filter(s => s.tokenSymbol === selectedPaymentSetting.tokenSymbol);
54
+ }, [paymentConfig.paymentSettings, selectedPaymentSetting]);
55
+ const {
56
+ balance,
57
+ isSupported,
58
+ isLoading
59
+ } = useTokenBalance({
60
+ tokenAddress: selectedPaymentSetting?.tokenAddress,
61
+ decimals: selectedPaymentSetting?.tokenDecimals,
62
+ chainId: Number(selectedPaymentSetting?.chainId)
63
+ }, address);
64
+ const {
65
+ data: estimatedFeesPerGas
66
+ } = useEstimateFeesPerGas({
67
+ chainId: selectedPaymentSetting ? Number(selectedPaymentSetting.chainId) : undefined,
68
+ query: {
69
+ enabled: Boolean(selectedPaymentSetting?.chainId)
70
+ }
71
+ });
72
+ const estimatedFee = useMemo(() => {
73
+ if (!estimatedFeesPerGas) return "0";
74
+ const maxFeePerGas = estimatedFeesPerGas.maxFeePerGas ?? estimatedFeesPerGas.gasPrice;
75
+ if (!maxFeePerGas) return "0";
76
+ const estimatedGas = 21000n;
77
+ return formatUnits(estimatedGas * maxFeePerGas, 18);
78
+ }, [estimatedFeesPerGas]);
79
+ const nativeCoinSymbol = useMemo(() => {
80
+ if (!selectedPaymentSetting) return "";
81
+ const chain = chains.find(c => c.id === Number(selectedPaymentSetting.chainId));
82
+ return chain?.nativeCurrency?.symbol ?? "ETH";
83
+ }, [selectedPaymentSetting, chains]);
84
+ useEffect(() => {
85
+ const linkPre = document.createElement("link");
86
+ linkPre.href = "https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.6/dist/web/static/pretendard-dynamic-subset.css";
87
+ linkPre.rel = "stylesheet";
88
+ document.head.appendChild(linkPre);
89
+ const link = document.createElement("link");
90
+ link.href = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@600&family=Inter:opsz,wght@14..32,500&display=swap";
91
+ link.rel = "stylesheet";
92
+ document.head.appendChild(link);
93
+ return () => {
94
+ document.head.removeChild(linkPre);
95
+ document.head.removeChild(link);
96
+ };
97
+ }, []);
98
+ useEffect(() => {
99
+ if (paymentConfig.paymentSettings.length === 0) {
100
+ setSelectedPaymentSetting(null);
101
+ return;
102
+ }
103
+ const firstToken = paymentConfig.paymentSettings[0];
104
+ setSelectedPaymentSetting(firstToken);
105
+ }, [paymentConfig]);
106
+ useEffect(() => {
107
+ if (!selectedPaymentSetting) return;
108
+ async function fetchEstimatedTokenAmount(paymentSetting) {
109
+ const estimatedTokenAmount = await onEstimateTokenAmount(Number(usdAmount), {
110
+ chainId: paymentSetting.chainId,
111
+ tokenSymbol: paymentSetting.tokenSymbol,
112
+ tokenAddress: paymentSetting.tokenAddress
113
+ });
114
+ setEstimatedTokenAmount(estimatedTokenAmount);
115
+ }
116
+ fetchEstimatedTokenAmount(selectedPaymentSetting);
117
+ }, [selectedPaymentSetting, onEstimateTokenAmount, usdAmount]);
118
+ useEffect(() => {
119
+ if (!selectedPaymentSetting) {
120
+ setSelectedPaymentRouter(null);
121
+ return;
122
+ }
123
+ const selectedRouter = paymentConfig.paymentRouters.find(router => router.chainId === selectedPaymentSetting.chainId);
124
+ setSelectedPaymentRouter(selectedRouter ?? null);
125
+ }, [selectedPaymentSetting, paymentConfig.paymentRouters]);
126
+ useEffect(() => {
127
+ if (isConnected && address) {
128
+ toast.success(/*#__PURE__*/_jsxs("div", {
129
+ className: styles.walletConnectSuccess,
130
+ children: ["Connected to Wallet ", address.slice(0, 5), "...", address.slice(-5)]
131
+ }), {
132
+ containerId: "payment-component"
133
+ });
134
+ }
135
+ }, [isConnected, address]);
136
+ const getNetworkName = chainId => {
137
+ const chain = chains.find(c => c.id === Number(chainId));
138
+ return chain ? chain.name : chainId;
139
+ };
140
+ const handleSwitch = async () => {
141
+ disconnect();
142
+ await new Promise(res => setTimeout(res, 500));
143
+ open();
144
+ };
145
+ const handleConnectWallet = () => {
146
+ open();
147
+ };
148
+ const checkAllowance = useCallback(async () => {
149
+ if (!isConnected || !address || !chain || !publicClient || !selectedPaymentRouter || !selectedPaymentSetting || chain.id !== parseInt(selectedPaymentSetting.chainId)) {
150
+ setNeedsApproval(null);
151
+ setCurrentAllowance(0n);
152
+ return;
153
+ }
154
+ if (isNativeCoin(selectedPaymentSetting.tokenAddress)) {
155
+ setNeedsApproval(false);
156
+ setCurrentAllowance(0n);
157
+ return;
158
+ }
159
+ try {
160
+ setStatus("checking_allowance");
161
+ const allowance = await publicClient.readContract({
162
+ address: selectedPaymentSetting.tokenAddress,
163
+ abi: erc20Abi,
164
+ functionName: "allowance",
165
+ args: [address, selectedPaymentRouter.address]
166
+ });
167
+ setNeedsApproval(allowance === 0n);
168
+ setCurrentAllowance(allowance);
169
+ setStatus("idle");
170
+ } catch (err) {
171
+ console.error("Failed to check allowance:", err);
172
+ setNeedsApproval(true);
173
+ setCurrentAllowance(0n);
174
+ setStatus("idle");
175
+ }
176
+ }, [isConnected, address, chain, publicClient, selectedPaymentRouter, selectedPaymentSetting]);
177
+ useEffect(() => {
178
+ checkAllowance();
179
+ }, [checkAllowance]);
180
+ const handleSwitchNetwork = () => {
181
+ if (!isConnected) return;
182
+ if (!selectedPaymentRouter) return;
183
+ setError(null);
184
+ try {
185
+ setStatus("switching");
186
+ switchChain({
187
+ chainId: parseInt(selectedPaymentRouter.chainId)
188
+ });
189
+ setStatus("idle");
190
+ } catch (err) {
191
+ console.error("Failed to switch network:", err);
192
+ setError(err instanceof Error ? err.message : "Failed to switch network");
193
+ setStatus("idle");
194
+ }
195
+ };
196
+ const handleApprove = async () => {
197
+ if (!isConnected) return;
198
+ if (!address) return;
199
+ if (!chain) return;
200
+ if (!publicClient) return;
201
+ if (!walletClient) return;
202
+ if (!selectedPaymentRouter) return;
203
+ if (!selectedPaymentSetting) return;
204
+ if (chain.id !== parseInt(selectedPaymentRouter.chainId)) {
205
+ setError(`Please switch to Chain ID ${selectedPaymentRouter.chainId}`);
206
+ return;
207
+ }
208
+
209
+ // setError(null);
210
+ try {
211
+ setStatus("approving");
212
+ // if the token is ethereum USDT, then revoke existing allowance first
213
+ if (isMainnetUSDT(chain.id, selectedPaymentSetting.tokenAddress) && currentAllowance > 0n) {
214
+ const {
215
+ request: revokeRequest
216
+ } = await publicClient.simulateContract({
217
+ account: address,
218
+ address: selectedPaymentSetting.tokenAddress,
219
+ abi: mainnetUSDTAbi,
220
+ functionName: "approve",
221
+ args: [selectedPaymentRouter.address, 0n]
222
+ });
223
+ const revokeHash = await walletClient.writeContract(revokeRequest);
224
+ await publicClient.waitForTransactionReceipt({
225
+ hash: revokeHash
226
+ });
227
+ }
228
+
229
+ // approve infinite
230
+ const {
231
+ request: approveRequest
232
+ } = await publicClient.simulateContract({
233
+ account: address,
234
+ address: selectedPaymentSetting.tokenAddress,
235
+ abi: isMainnetUSDT(chain.id, selectedPaymentSetting.tokenAddress) ? mainnetUSDTAbi : erc20Abi,
236
+ functionName: "approve",
237
+ args: [selectedPaymentRouter.address, maxUint256]
238
+ });
239
+ const approveHash = await walletClient.writeContract(approveRequest);
240
+ await publicClient.waitForTransactionReceipt({
241
+ hash: approveHash
242
+ });
243
+ // re-check allowance after approval
244
+ await checkAllowance();
245
+ setStatus("idle");
246
+ } catch (err) {
247
+ console.error("Failed to approve token:", err);
248
+ toast.error(`Approval failed. Please ensure your wallet has enough ${selectedPaymentSetting.tokenSymbol} to complete payment and ${chains.find(c => c.id === chain.id)?.nativeCurrency?.symbol ?? "native coin"} to cover gas fees.`, {
249
+ containerId: "payment-component"
250
+ });
251
+ setStatus("idle");
252
+ }
253
+ };
254
+ const handlePay = async () => {
255
+ if (!isConnected) return;
256
+ if (!address) return;
257
+ if (!chain) return;
258
+ if (!publicClient) return;
259
+ if (!walletClient) return;
260
+ if (!selectedPaymentRouter) return;
261
+ if (!selectedPaymentSetting) return;
262
+ if (chain.id !== parseInt(selectedPaymentSetting.chainId)) {
263
+ setError(`Please switch to Chain ID ${selectedPaymentRouter.chainId}`);
264
+ return;
265
+ }
266
+
267
+ // setError(null);
268
+ try {
269
+ setStatus("creating");
270
+ // Create payment via merchant's API
271
+ const {
272
+ id: createdPaymentId
273
+ } = await onCreatePayment({
274
+ usdAmount: usdAmount.toString(),
275
+ chainId: selectedPaymentSetting.chainId,
276
+ tokenAddress: selectedPaymentSetting.tokenAddress,
277
+ sender: address
278
+ });
279
+ setStatus("signing");
280
+ // Get signature from merchant's API
281
+ const {
282
+ paymentData,
283
+ signature
284
+ } = await onGetSignature(createdPaymentId);
285
+ setStatus("sending");
286
+ // Send transaction to PaymentRouter contract
287
+ let hash;
288
+ if (isNativeCoin(paymentData.erc20)) {
289
+ const {
290
+ request
291
+ } = await publicClient.simulateContract({
292
+ account: address,
293
+ address: selectedPaymentRouter.address,
294
+ abi: PaymentRouterAbi,
295
+ functionName: "payETH",
296
+ args: [BigInt(paymentData.id), paymentData.receiver, BigInt(paymentData.feeRate), BigInt(paymentData.expirationTimestamp), signature],
297
+ value: BigInt(paymentData.amount)
298
+ });
299
+ hash = await walletClient.writeContract(request);
300
+ } else {
301
+ const {
302
+ request
303
+ } = await publicClient.simulateContract({
304
+ account: address,
305
+ address: selectedPaymentRouter.address,
306
+ abi: PaymentRouterAbi,
307
+ functionName: "payERC20",
308
+ args: [BigInt(paymentData.id), paymentData.erc20, paymentData.receiver, BigInt(paymentData.amount), BigInt(paymentData.feeRate), BigInt(paymentData.expirationTimestamp), signature]
309
+ });
310
+ hash = await walletClient.writeContract(request);
311
+ }
312
+ console.debug("Payment transaction sent:", hash);
313
+ setStatus("confirming");
314
+ // Confirm payment transaction
315
+ const receipt = await publicClient.waitForTransactionReceipt({
316
+ hash
317
+ });
318
+ if (!receipt) {
319
+ throw new Error("Transaction receipt not found");
320
+ }
321
+ console.debug("Payment transaction confirmed:", receipt);
322
+ setStatus("success");
323
+ // Call onSuccess callback
324
+ onSuccess(createdPaymentId);
325
+ } catch (err) {
326
+ console.error("Failed to process payment:", err);
327
+ toast.error(`Payment failed. Please ensure your wallet has enough ${selectedPaymentSetting.tokenSymbol} to complete payment and ${chains.find(c => c.id === chain.id)?.nativeCurrency?.symbol ?? "native coin"} to cover gas fees.`, {
328
+ containerId: "payment-component"
329
+ });
330
+ setStatus("idle");
331
+ }
332
+ };
333
+ return /*#__PURE__*/_jsx("div", {
334
+ className: styles.overlay,
335
+ children: /*#__PURE__*/_jsxs("div", {
336
+ className: styles.wrapper,
337
+ style: {
338
+ height: isConnected ? "570px" : "575px"
339
+ },
340
+ children: [/*#__PURE__*/_jsxs("div", {
341
+ className: styles.wallet,
342
+ children: [/*#__PURE__*/_jsxs("div", {
343
+ className: styles.walletInput,
344
+ children: [/*#__PURE__*/_jsx("div", {
345
+ className: styles.walletLink,
346
+ style: {
347
+ cursor: isConnected ? "pointer" : "default"
348
+ },
349
+ onClick: () => isConnected ? disconnect() : undefined,
350
+ children: isConnected ? /*#__PURE__*/_jsx(UnLinkSVG, {}) : /*#__PURE__*/_jsx(LinkSVG, {})
351
+ }), /*#__PURE__*/_jsx("div", {
352
+ className: styles.walletAddress,
353
+ children: address ? `${address.slice(0, 5)}...${address.slice(-5)}` : "Wallet Not Connected"
354
+ }), isConnected && /*#__PURE__*/_jsx("div", {
355
+ className: styles.switchBtn,
356
+ onClick: handleSwitch,
357
+ children: "Switch"
358
+ })]
359
+ }), /*#__PURE__*/_jsx("div", {
360
+ className: styles.closeBtn,
361
+ onClick: onCancel,
362
+ children: /*#__PURE__*/_jsx(CloseLargeSVG, {})
363
+ })]
364
+ }), /*#__PURE__*/_jsxs("div", {
365
+ className: styles.paymentWrapper,
366
+ children: [/*#__PURE__*/_jsx("h4", {
367
+ className: styles.paymentTitle,
368
+ children: "Select Payment Currency"
369
+ }), /*#__PURE__*/_jsx(Dropdown, {
370
+ type: "token",
371
+ chains: chains,
372
+ active: selectedPaymentSetting,
373
+ balance: Math.round(Number(balance)).toString(),
374
+ items: Array.from(new Map(paymentConfig.paymentSettings.map(item => [item.tokenSymbol, item])).values()),
375
+ onSelect: item => setSelectedPaymentSetting(item)
376
+ }), /*#__PURE__*/_jsx(Dropdown, {
377
+ type: "network",
378
+ chains: chains,
379
+ active: selectedPaymentSetting,
380
+ balance: `${estimatedFee} ${nativeCoinSymbol}`,
381
+ items: availableNetworks,
382
+ onSelect: item => setSelectedPaymentSetting(item)
383
+ }), /*#__PURE__*/_jsxs("div", {
384
+ className: styles.paymentAmountWrapper,
385
+ children: [/*#__PURE__*/_jsx("p", {
386
+ className: styles.paymentAmount,
387
+ children: "Payment amount"
388
+ }), /*#__PURE__*/_jsx("p", {
389
+ className: styles.paymentAmountTotal,
390
+ children: `${usdAmount} $`
391
+ })]
392
+ })]
393
+ }), /*#__PURE__*/_jsx("div", {
394
+ className: styles.chevronDownLarge,
395
+ children: /*#__PURE__*/_jsx(ChevronDownLargeSVG, {})
396
+ }), /*#__PURE__*/_jsxs("div", {
397
+ className: styles.summaryWrapper,
398
+ children: [/*#__PURE__*/_jsxs("div", {
399
+ className: styles.summaryItemWrapper,
400
+ style: {
401
+ marginBottom: "4px"
402
+ },
403
+ children: [/*#__PURE__*/_jsx("p", {
404
+ className: styles.summaryItemTitle,
405
+ children: "Token"
406
+ }), /*#__PURE__*/_jsx("p", {
407
+ className: styles.summaryItemValue,
408
+ children: selectedPaymentSetting?.tokenSymbol
409
+ })]
410
+ }), /*#__PURE__*/_jsxs("div", {
411
+ className: styles.summaryItemWrapper,
412
+ style: {
413
+ marginBottom: "12px"
414
+ },
415
+ children: [/*#__PURE__*/_jsx("p", {
416
+ className: styles.summaryItemTitle,
417
+ children: "Network"
418
+ }), /*#__PURE__*/_jsx("p", {
419
+ className: styles.summaryItemValue,
420
+ children: getNetworkName(selectedPaymentSetting?.chainId)
421
+ })]
422
+ }), /*#__PURE__*/_jsxs("div", {
423
+ className: styles.summaryItemWrapper,
424
+ children: [/*#__PURE__*/_jsx("p", {
425
+ className: styles.summaryItemValue,
426
+ style: {
427
+ fontSize: "14px"
428
+ },
429
+ children: "Amount"
430
+ }), /*#__PURE__*/_jsxs("p", {
431
+ className: styles.summaryTotal,
432
+ children: [estimatedTokenAmount, " ", selectedPaymentSetting?.tokenSymbol]
433
+ })]
434
+ })]
435
+ }), !isConnected && /*#__PURE__*/_jsx("button", {
436
+ className: styles.connectWallet,
437
+ onClick: handleConnectWallet,
438
+ disabled: status !== "idle",
439
+ children: "Wallet Connect"
440
+ }), isConnected && selectedPaymentRouter && selectedPaymentSetting && /*#__PURE__*/_jsxs(_Fragment, {
441
+ children: [chain && chain.id !== parseInt(selectedPaymentRouter.chainId) && /*#__PURE__*/_jsx("button", {
442
+ onClick: handleSwitchNetwork,
443
+ disabled: status === "switching",
444
+ className: styles.switchNetworkButton,
445
+ children: status === "switching" ? "Switching..." : "Switch Network"
446
+ }), chain && chain.id === parseInt(selectedPaymentRouter.chainId) && /*#__PURE__*/_jsx(_Fragment, {
447
+ children: needsApproval ? /*#__PURE__*/_jsx("button", {
448
+ onClick: handleApprove,
449
+ disabled: status !== "idle" || !address || !chain || chain.id !== parseInt(selectedPaymentRouter.chainId),
450
+ className: styles.payButton,
451
+ style: {
452
+ fontFamily: '"Inter", sans-serif',
453
+ fontWeight: "500",
454
+ backgroundColor: "#646464",
455
+ color: "#FFFFFF"
456
+ },
457
+ children: status === "idle" ? "Approve" : "Processing..."
458
+ }) : /*#__PURE__*/_jsx("button", {
459
+ onClick: handlePay,
460
+ disabled: status !== "idle" || !address || !chain || chain.id !== parseInt(selectedPaymentRouter.chainId)
461
+ // || needsApproval === null
462
+ ,
463
+ className: styles.payButton,
464
+ style: {
465
+ fontFamily: '"IBM Plex Mono", monospace',
466
+ fontWeight: "600",
467
+ backgroundColor: "#FFED2B",
468
+ color: "#000000"
469
+ },
470
+ children: status === "idle" ? "Pay" : "Processing..."
471
+ })
472
+ })]
473
+ })]
474
+ })
475
+ });
476
+ }
477
+ const useTokenBalance = (token, evmWallet) => {
478
+ const isErc20 = Boolean(token.tokenAddress) && !isNativeCoin(token.tokenAddress);
479
+
480
+ // Native balance
481
+ const evmNative = useBalance({
482
+ address: evmWallet,
483
+ chainId: token.chainId,
484
+ query: {
485
+ enabled: !isErc20 && Boolean(evmWallet)
486
+ }
487
+ });
488
+
489
+ // ERC-20 balance
490
+ const evmErc20 = useReadContract({
491
+ address: isErc20 ? token.tokenAddress : undefined,
492
+ abi: erc20Abi,
493
+ functionName: "balanceOf",
494
+ args: evmWallet ? [evmWallet] : undefined,
495
+ chainId: token.chainId,
496
+ query: {
497
+ enabled: isErc20 && Boolean(evmWallet)
498
+ }
499
+ });
500
+ const isLoading = isErc20 ? evmErc20.isLoading : evmNative.isLoading;
501
+ const raw = isErc20 ? evmErc20.data ?? 0n : evmNative.data?.value ?? 0n;
502
+ return {
503
+ isSupported: Boolean(token),
504
+ isLoading: isLoading,
505
+ balance: formatUnits(raw, token.decimals)
506
+ };
507
+ };
508
+ const Dropdown = ({
509
+ type,
510
+ chains,
511
+ active,
512
+ items,
513
+ balance,
514
+ onSelect
515
+ }) => {
516
+ const [lists, setLists] = useState([]);
517
+ const [open, setOpen] = useState(false);
518
+ const [search, setSearch] = useState("");
519
+ const ref = useRef(null);
520
+ useEffect(() => {
521
+ setLists(items);
522
+ const handleClickOutside = e => {
523
+ if (ref.current && !ref.current.contains(e.target)) {
524
+ setOpen(false);
525
+ setSearch("");
526
+ }
527
+ };
528
+ document.addEventListener("mousedown", handleClickOutside);
529
+ return () => document.removeEventListener("mousedown", handleClickOutside);
530
+ }, []);
531
+ useEffect(() => {
532
+ setLists(items);
533
+ }, [items]);
534
+ useEffect(() => {
535
+ if (search.trim().length === 0) {
536
+ setLists(items);
537
+ }
538
+ }, [search]);
539
+ const getNetworkName = chainId => {
540
+ const chain = chains.find(c => c.id === Number(chainId));
541
+ return chain ? chain.name : chainId;
542
+ };
543
+ const handleSearch = () => {
544
+ if (search.trim().length === 0) {
545
+ setLists(items);
546
+ return;
547
+ }
548
+ const q = search.trim().toLowerCase();
549
+ setLists(items.filter(item => item.tokenSymbol.toLowerCase().includes(q)));
550
+ };
551
+ const iconSrc = type === "token" ? active?.tokenIcon : type === "network" ? active?.chainIcon : null;
552
+ const altText = type === "token" ? active?.tokenName : type === "network" ? getNetworkName(active?.chainId) : "";
553
+ return /*#__PURE__*/_jsxs("div", {
554
+ ref: ref,
555
+ className: styles.paymentTokenNetworkWrapper,
556
+ children: [/*#__PURE__*/_jsx("div", {
557
+ className: styles.paymentTokenNetwork,
558
+ style: {
559
+ color: open ? "#FFFFFF" : "#8D8D8D"
560
+ },
561
+ onClick: () => setOpen(!open),
562
+ children: active && /*#__PURE__*/_jsxs("div", {
563
+ className: styles.dropdownItem,
564
+ style: {
565
+ height: "auto",
566
+ paddingLeft: 0,
567
+ paddingRight: 0
568
+ },
569
+ children: [iconSrc && /*#__PURE__*/_jsx("div", {
570
+ className: styles.icon24,
571
+ children: /*#__PURE__*/_jsx("img", {
572
+ src: iconSrc,
573
+ alt: altText
574
+ })
575
+ }), /*#__PURE__*/_jsxs("div", {
576
+ className: styles.dropdownItemMiddle,
577
+ children: [/*#__PURE__*/_jsx("div", {
578
+ className: styles.dropdownItemText,
579
+ children: type === "token" ? active?.tokenSymbol : getNetworkName(active.chainId).slice(0, 16)
580
+ }), /*#__PURE__*/_jsx("div", {
581
+ className: styles.dropdownItemBalance,
582
+ children: balance
583
+ })]
584
+ }), /*#__PURE__*/_jsx("div", {
585
+ className: styles.chevronDown,
586
+ style: {
587
+ transform: open ? "rotate(180deg)" : "rotate(0deg)"
588
+ },
589
+ children: /*#__PURE__*/_jsx(ChevronDownSVG, {})
590
+ })]
591
+ })
592
+ }), /*#__PURE__*/_jsxs("div", {
593
+ className: styles.dropdownWrapper,
594
+ style: {
595
+ display: open ? "flex" : "none"
596
+ },
597
+ children: [type === "token" && /*#__PURE__*/_jsxs("div", {
598
+ className: styles.dropdownSearch,
599
+ children: [/*#__PURE__*/_jsx("input", {
600
+ type: "text",
601
+ name: "search",
602
+ placeholder: "Search Token",
603
+ value: search,
604
+ className: styles.dropdownInput,
605
+ onChange: e => setSearch(e.target.value),
606
+ onKeyDown: e => {
607
+ if (e.key === "Enter") {
608
+ handleSearch();
609
+ }
610
+ }
611
+ }), /*#__PURE__*/_jsx("div", {
612
+ className: styles.icon24,
613
+ style: {
614
+ cursor: "pointer"
615
+ },
616
+ onClick: handleSearch,
617
+ children: /*#__PURE__*/_jsx(SearchSVG, {})
618
+ })]
619
+ }), /*#__PURE__*/_jsx("div", {
620
+ className: styles.dropdownItemWrapper,
621
+ style: {
622
+ maxHeight: type === "token" ? "257px" : "202px"
623
+ },
624
+ children: lists.map((item, index) => {
625
+ const iconSrc = type === "token" ? item.tokenIcon : type === "network" ? item.chainIcon : null;
626
+ const altText = type === "token" ? item.tokenName : type === "network" ? getNetworkName(item.chainId) : "";
627
+ return /*#__PURE__*/_jsxs("div", {
628
+ className: styles.dropdownItem,
629
+ style: {
630
+ backgroundColor: active?.id === item.id ? "#1D1D1D" : "transparent"
631
+ },
632
+ onClick: () => {
633
+ onSelect?.(item);
634
+ setOpen(false);
635
+ setSearch("");
636
+ setLists(items);
637
+ },
638
+ children: [iconSrc && /*#__PURE__*/_jsx("div", {
639
+ className: styles.icon24,
640
+ children: /*#__PURE__*/_jsx("img", {
641
+ src: iconSrc,
642
+ alt: altText
643
+ })
644
+ }), /*#__PURE__*/_jsx("div", {
645
+ className: styles.dropdownItemText,
646
+ children: type === "token" ? item.tokenSymbol : getNetworkName(item.chainId)
647
+ })]
648
+ }, index);
649
+ })
650
+ })]
651
+ })]
652
+ });
653
+ };
654
+ const LinkSVG = () => {
655
+ return /*#__PURE__*/_jsx("svg", {
656
+ width: "24",
657
+ height: "24",
658
+ viewBox: "0 0 24 24",
659
+ fill: "none",
660
+ xmlns: "http://www.w3.org/2000/svg",
661
+ children: /*#__PURE__*/_jsx("path", {
662
+ d: "M14.8284 9.17158L9.17156 14.8284M12.7071 16.9498L11.2929 18.364C9.73078 19.9261 7.19812 19.9261 5.63603 18.364C4.07393 16.8019 4.07393 14.2692 5.63603 12.7071L7.05024 11.2929M16.9497 12.7071L18.3639 11.2929C19.926 9.7308 19.926 7.19814 18.3639 5.63604C16.8019 4.07395 14.2692 4.07395 12.7071 5.63604L11.2929 7.05026",
663
+ stroke: "#707070",
664
+ "stroke-width": "2",
665
+ "stroke-linecap": "round",
666
+ "stroke-linejoin": "round"
667
+ })
668
+ });
669
+ };
670
+ const UnLinkSVG = () => {
671
+ return /*#__PURE__*/_jsx("svg", {
672
+ width: "24",
673
+ height: "24",
674
+ viewBox: "0 0 24 24",
675
+ fill: "none",
676
+ xmlns: "http://www.w3.org/2000/svg",
677
+ children: /*#__PURE__*/_jsx("path", {
678
+ d: "M14.8284 9.17158L9.17156 14.8284M12.7071 16.9498L11.2929 18.364C9.73078 19.9261 7.19812 19.9261 5.63603 18.364C4.07393 16.8019 4.07393 14.2692 5.63603 12.7071L7.05024 11.2929M16.9497 12.7071L18.3639 11.2929C19.926 9.7308 19.926 7.19814 18.3639 5.63604C16.8019 4.07395 14.2692 4.07395 12.7071 5.63604L11.2929 7.05026",
679
+ stroke: "#FFED2B",
680
+ "stroke-width": "2",
681
+ "stroke-linecap": "round",
682
+ "stroke-linejoin": "round"
683
+ })
684
+ });
685
+ };
686
+ const CloseLargeSVG = () => {
687
+ return /*#__PURE__*/_jsx("svg", {
688
+ width: "24",
689
+ height: "24",
690
+ viewBox: "0 0 24 24",
691
+ fill: "none",
692
+ xmlns: "http://www.w3.org/2000/svg",
693
+ children: /*#__PURE__*/_jsx("path", {
694
+ d: "M21 21L12 12M12 12L3 3M12 12L21.0001 3M12 12L3 21.0001",
695
+ stroke: "white",
696
+ "stroke-width": "2",
697
+ "stroke-linecap": "round",
698
+ "stroke-linejoin": "round"
699
+ })
700
+ });
701
+ };
702
+ const SearchSVG = () => {
703
+ return /*#__PURE__*/_jsx("svg", {
704
+ width: "24",
705
+ height: "24",
706
+ viewBox: "0 0 24 24",
707
+ fill: "none",
708
+ xmlns: "http://www.w3.org/2000/svg",
709
+ children: /*#__PURE__*/_jsx("g", {
710
+ opacity: "0.2",
711
+ children: /*#__PURE__*/_jsx("path", {
712
+ d: "M15 15L21 21M10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10C17 13.866 13.866 17 10 17Z",
713
+ stroke: "white",
714
+ "stroke-width": "2",
715
+ "stroke-linecap": "round",
716
+ "stroke-linejoin": "round"
717
+ })
718
+ })
719
+ });
720
+ };
721
+ const ChevronDownSVG = () => {
722
+ return /*#__PURE__*/_jsx("svg", {
723
+ width: "24",
724
+ height: "24",
725
+ viewBox: "0 0 24 24",
726
+ fill: "none",
727
+ xmlns: "http://www.w3.org/2000/svg",
728
+ children: /*#__PURE__*/_jsx("path", {
729
+ d: "M7.92004 9.75L11.9985 14.511L16.0815 9.75",
730
+ stroke: "white",
731
+ "stroke-miterlimit": "10",
732
+ "stroke-linecap": "round",
733
+ "stroke-linejoin": "round"
734
+ })
735
+ });
736
+ };
737
+ const ChevronDownLargeSVG = () => {
738
+ return /*#__PURE__*/_jsx("svg", {
739
+ width: "24",
740
+ height: "24",
741
+ viewBox: "0 0 24 24",
742
+ fill: "none",
743
+ xmlns: "http://www.w3.org/2000/svg",
744
+ children: /*#__PURE__*/_jsx("path", {
745
+ d: "M19 9L12 16L5 9",
746
+ stroke: "white",
747
+ "stroke-width": "2",
748
+ "stroke-linecap": "round",
749
+ "stroke-linejoin": "round"
750
+ })
751
+ });
752
+ };
753
+ //# sourceMappingURL=CryptoPayment.js.map