herald-exchange-onramp_offramp-widget 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.
Files changed (37) hide show
  1. package/.babelrc +3 -0
  2. package/Readme.md +166 -0
  3. package/dist/index.css +1 -0
  4. package/dist/index.d.ts +24 -0
  5. package/dist/index.js +47 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +47 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +59 -0
  10. package/rollup.config.js +90 -0
  11. package/src/assets/css/style.module.css +1435 -0
  12. package/src/assets/icons-one.png +0 -0
  13. package/src/assets/react.svg +1 -0
  14. package/src/components/ButtonStepper.tsx +144 -0
  15. package/src/components/BuyField.tsx +632 -0
  16. package/src/components/CommonCenterLoader.tsx +119 -0
  17. package/src/components/CustomeSelect.tsx +180 -0
  18. package/src/components/DotLoader.tsx +8 -0
  19. package/src/components/NewBuyField.tsx +687 -0
  20. package/src/components/SellAdminCryptoAccount.tsx +601 -0
  21. package/src/components/SellField.tsx +712 -0
  22. package/src/components/WidgetBankDetails.tsx +612 -0
  23. package/src/components/WidgetComponent.tsx +49 -0
  24. package/src/components/WidgetContent.tsx +71 -0
  25. package/src/components/WidgetSuccesDetails.tsx +113 -0
  26. package/src/components/api.ts +59 -0
  27. package/src/components/chains.ts +319 -0
  28. package/src/components/images.d.ts +5 -0
  29. package/src/components/loader.tsx +14 -0
  30. package/src/components/style.module.css.d.ts +4 -0
  31. package/src/components/toast.tsx +51 -0
  32. package/src/components/types.ts +237 -0
  33. package/src/components/utils.ts +17 -0
  34. package/src/hooks/toastProvider.tsx +64 -0
  35. package/src/hooks/useSocketExchange.tsx +48 -0
  36. package/src/index.ts +3 -0
  37. package/tsconfig.json +118 -0
@@ -0,0 +1,687 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import CustomSelect from "./CustomeSelect";
3
+ import { useToast } from "../hooks/toastProvider";
4
+ import Loader, { Spinner } from "./loader";
5
+ import DotLoader from "./DotLoader";
6
+ import { getBaseUrl, isValidDecimal } from "./utils";
7
+ import { handleApiCall } from "./api";
8
+ import WAValidator from "multicoin-address-validator";
9
+ import type {
10
+ BuyParamsType,
11
+ comissionAPIResponse,
12
+ CurrenciesAPIResponse,
13
+ CustomOption,
14
+ rulesType,
15
+ } from "./types";
16
+ import styles from "../assets/css/style.module.css";
17
+
18
+ type NewBuyFieldProps = {
19
+ apiKey: string;
20
+ redirectUrl: string;
21
+ parameters: BuyParamsType;
22
+ clientReferenceID: string;
23
+ mode: string;
24
+ };
25
+
26
+ const NewBuyField = ({
27
+ apiKey,
28
+ redirectUrl,
29
+ parameters,
30
+ clientReferenceID,
31
+ mode,
32
+ }: NewBuyFieldProps) => {
33
+ const { addToast } = useToast();
34
+
35
+ const [loading, setLoading] = useState(false);
36
+ const [fiatOptions, setFiatOptions] = useState<CustomOption[] | []>([]);
37
+ const [cryptoOptions, setCryptoOptions] = useState<CustomOption[] | []>([]);
38
+ const [selectedCrypto, setSelectedCrypto] = useState<CustomOption | null>(null);
39
+ const [selectedFiat, setSelectedFiat] = useState<CustomOption | null>(null);
40
+ const [exchangeRateForThisUser, setExchangeRateForThisUser] = useState<number>(0);
41
+ const [commissionForThisUser, setCommissionForThisUser] = useState<number>(0);
42
+ // const [actualCommission, setActualCommission] = useState<number>(0);
43
+ const [inputRules, setInputRules] = useState<rulesType | null>(null);
44
+ const [currencies, setCurrencies] = useState<CurrenciesAPIResponse["data"] | null>(null);
45
+ // const [commissionApiResponse, setComissionApiResponse] = useState <comissionAPIResponse["data"] | null>(null);
46
+ const [invalidAmount, setInvalidAmount] = useState({
47
+ min: false,
48
+ max: false,
49
+ });
50
+ // const [addCommission, setAddCommission] = useState(null);
51
+ const [userReceivableCryptoValue, setUserReceivableCryptoValue] = useState(0);
52
+ const [exchangeLoading, setExchangeLoading] = useState(true);
53
+ const [buyStep, setBuyStep] = useState(1);
54
+ const [redirectURL, setRedirectURL] = useState();
55
+ const [walletAddress, setWalletAddress] = useState("");
56
+ const [walletAddressError, setWalletAddressError] = useState("");
57
+ // const [exchangeRateApiResponse, setExchangeRateApiResponse] = useState(null);
58
+ // You Pay / Fiat
59
+ const [fiatDetails, setfiatDetails] = useState({
60
+ from_amount:
61
+ parameters?.from_amount && parseFloat(parameters?.from_amount) > 0
62
+ ? parameters.from_amount
63
+ : "0",
64
+ selectedCrypto: parameters.token_type,
65
+ selectedFiat: "",
66
+ minAmount: "0.0000001",
67
+ maxAmount: "1000.00000",
68
+ });
69
+
70
+ useEffect(() => {
71
+ fetchCurrencies();
72
+ fetchCommissions();
73
+ // eslint-disable-next-line
74
+ }, []);
75
+
76
+ const fetchCurrencies = () => {
77
+ const baseUrl = getBaseUrl(mode);
78
+ console.log({ mode, baseUrl });
79
+
80
+ handleApiCall({
81
+ url: `${baseUrl}/api/lookup/currencies`,
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ "X-API-KEY": apiKey,
85
+ "User-Id": clientReferenceID,
86
+ },
87
+ onSuccess: (result: CurrenciesAPIResponse) => {
88
+ if (result?.success) {
89
+ setCurrencies(result?.data);
90
+ }
91
+ },
92
+ onError: () => {
93
+ addToast("Error fetching currency", "error");
94
+ },
95
+ });
96
+ };
97
+
98
+ const fetchCommissions = () => {
99
+ const baseUrl = getBaseUrl(mode);
100
+ handleApiCall({
101
+ url: `${baseUrl}/api/lookup/commissions`,
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ "X-API-KEY": apiKey,
105
+ "User-Id": clientReferenceID,
106
+ },
107
+ onSuccess: (result: comissionAPIResponse) => {
108
+ if (result?.success) {
109
+ const rules = result?.data?.rules?.find((item) => item?.service === "onramp") || null;
110
+ setInputRules(rules);
111
+ if (rules) {
112
+ setfiatDetails({
113
+ ...fiatDetails,
114
+ minAmount: rules?.schema?.min,
115
+ maxAmount: rules?.schema?.max,
116
+ });
117
+ }
118
+ }
119
+ },
120
+ onError: () => {
121
+ addToast("Error fetching comission", "error");
122
+ },
123
+ });
124
+ };
125
+
126
+ useEffect(() => {
127
+ if (currencies) {
128
+ const fiatCurrencyOptions = currencies?.currencies
129
+ ?.filter((item) => item?.supported_currency?.type === "FIAT" && item?.service === "onramp")
130
+ ?.map((item) => ({
131
+ label: item?.supported_currency?.code,
132
+ value: item?.unique_id,
133
+ icon: item?.supported_currency?.icon,
134
+ }));
135
+
136
+ setFiatOptions(fiatCurrencyOptions);
137
+
138
+ const selected_forex = fiatDetails?.selectedFiat
139
+ ? currencies?.currencies.find(
140
+ (currency) => currency?.supported_currency?.code === fiatDetails?.selectedFiat
141
+ ) ||
142
+ currencies?.currencies?.find(
143
+ (currency) => currency?.supported_currency?.code === fiatCurrencyOptions?.[0]?.label
144
+ )
145
+ : currencies?.currencies?.find(
146
+ (currency) => currency?.supported_currency?.code === fiatCurrencyOptions?.[0]?.label
147
+ );
148
+
149
+ const selectedFiat = fiatDetails.selectedFiat
150
+ ? fiatCurrencyOptions.find((fiat) => fiat.label === fiatDetails.selectedFiat) ||
151
+ fiatCurrencyOptions?.[0]
152
+ : fiatCurrencyOptions?.[0];
153
+
154
+ setSelectedFiat(selectedFiat);
155
+
156
+ // setCategoriesOptions(categories);
157
+
158
+ const cryptoOptions =
159
+ currencies?.currencies
160
+ ?.filter(
161
+ (item) => item?.supported_currency?.type === "CRYPTO" && item?.service === "onramp"
162
+ )
163
+ ?.map((item) => ({
164
+ label: item?.supported_currency?.code,
165
+ value: item?.unique_id,
166
+ icon: item?.supported_currency?.icon,
167
+ })) || [];
168
+
169
+ setCryptoOptions(cryptoOptions);
170
+
171
+ const selected_crypto = parameters?.token_type
172
+ ? currencies?.currencies?.find(
173
+ (currency) => currency?.supported_currency?.code === parameters?.token_type
174
+ ) ||
175
+ currencies?.currencies?.find(
176
+ (currency) => currency?.supported_currency?.code === cryptoOptions?.[0]?.label
177
+ )
178
+ : currencies?.currencies?.find(
179
+ (currency) => currency?.supported_currency?.code === cryptoOptions?.[0]?.label
180
+ );
181
+
182
+ setSelectedCrypto(
183
+ parameters?.token_type
184
+ ? cryptoOptions.find((currency) => currency.label === parameters?.token_type) ||
185
+ cryptoOptions?.[0]
186
+ : cryptoOptions?.[0] || []
187
+ );
188
+
189
+ setfiatDetails((prev) => ({
190
+ ...prev,
191
+ selectedCrypto: selected_crypto?.supported_currency?.code || "",
192
+ selectedFiat: selected_forex?.supported_currency?.code || "",
193
+ }));
194
+ }
195
+ // eslint-disable-next-line
196
+ }, [currencies]);
197
+
198
+ const handleFiatCurrencyChange = (selectedOption: CustomOption | null) => {
199
+ const fiat = currencies?.currencies?.find((fiat) => fiat.unique_id === selectedOption?.value);
200
+ setfiatDetails({
201
+ ...fiatDetails,
202
+ selectedFiat: fiat?.supported_currency?.code || "",
203
+ });
204
+ setSelectedFiat(selectedOption);
205
+ };
206
+
207
+ const handleFiatCurrencyValueChange = (value: string) => {
208
+ if (value.includes(".")) {
209
+ if (inputRules && !isValidDecimal(value, inputRules?.schema?.decimal)) {
210
+ addToast(`You can input upto ${inputRules?.schema?.decimal} decimal places.`, "error");
211
+ return;
212
+ } else {
213
+ if (value === "") {
214
+ setfiatDetails({
215
+ ...fiatDetails,
216
+ from_amount: "",
217
+ });
218
+ } else {
219
+ if (!isNaN(Number(value))) {
220
+ setfiatDetails({
221
+ ...fiatDetails,
222
+ from_amount: value,
223
+ });
224
+ }
225
+ }
226
+ }
227
+ } else {
228
+ if (value === "") {
229
+ setfiatDetails({
230
+ ...fiatDetails,
231
+ from_amount: "",
232
+ });
233
+ } else {
234
+ if (!isNaN(Number(value))) {
235
+ setfiatDetails({
236
+ ...fiatDetails,
237
+ from_amount: value,
238
+ });
239
+ }
240
+ }
241
+ }
242
+ };
243
+
244
+ const onCryptoChange = (selectedOption: CustomOption) => {
245
+ const crypto = currencies?.currencies?.find(
246
+ (crypto) => crypto?.unique_id === selectedOption.value
247
+ );
248
+ setfiatDetails({
249
+ ...fiatDetails,
250
+ selectedCrypto: crypto?.supported_currency?.code || "",
251
+ });
252
+ setSelectedCrypto(selectedOption);
253
+ };
254
+
255
+ useEffect(() => {
256
+ const handler = setTimeout(() => {
257
+ if (selectedFiat && selectedCrypto) {
258
+ fetchExchangeRate();
259
+ }
260
+ }, 300);
261
+
262
+ return () => clearTimeout(handler);
263
+ // eslint-disable-next-line
264
+ }, [selectedFiat, selectedCrypto, fiatDetails]);
265
+
266
+ const fetchExchangeRate = async () => {
267
+ const baseUrl = getBaseUrl(mode);
268
+
269
+ setExchangeLoading(true);
270
+ setCommissionForThisUser(0);
271
+ setExchangeRateForThisUser(0);
272
+ setUserReceivableCryptoValue(0);
273
+ if (
274
+ typeof fiatDetails.from_amount === "string"
275
+ ? parseFloat(fiatDetails?.from_amount) > 0
276
+ : fiatDetails.from_amount > 0
277
+ ) {
278
+ const payload = {
279
+ from: fiatDetails?.selectedFiat || selectedFiat?.label,
280
+ to: fiatDetails?.selectedCrypto || selectedCrypto?.label,
281
+ amount: fiatDetails?.from_amount,
282
+ };
283
+
284
+ try {
285
+ await fetch(
286
+ `${baseUrl}/api/onramp/fee?from=${payload.from}&to=${payload.to}&amount=${payload.amount}`,
287
+ {
288
+ method: "GET",
289
+ headers: {
290
+ "Content-Type": "application/json",
291
+ "X-API-KEY": apiKey,
292
+ "User-Id": clientReferenceID,
293
+ },
294
+ }
295
+ )
296
+ .then((response) => response.json())
297
+ .then((data) => {
298
+ if (Object.keys(data.data).length > 0) {
299
+ const values = data.data;
300
+ setCommissionForThisUser(
301
+ parseFloat(values.markupFee) +
302
+ parseFloat(values.networkFee) +
303
+ parseFloat(values.processingFee)
304
+ );
305
+ setExchangeRateForThisUser(values.exchangeRate);
306
+ setUserReceivableCryptoValue(values.amountOut);
307
+ } else {
308
+ addToast(data?.message, "error");
309
+ }
310
+ })
311
+ .catch((error) => {
312
+ addToast(error, "error");
313
+ console.error("Error Fetching Exchange Rate:", error);
314
+ });
315
+ } catch (error: any) {
316
+ addToast(error, "error");
317
+ console.error("Error Fetching Exchange Rate:", error);
318
+ } finally {
319
+ setExchangeLoading(false);
320
+ }
321
+ } else {
322
+ addToast("Please enter valid amount", "error");
323
+ setExchangeLoading(false);
324
+ }
325
+ };
326
+
327
+ useEffect(() => {
328
+ setInvalidAmount({
329
+ min:
330
+ parseFloat(fiatDetails.from_amount) < parseFloat(fiatDetails.minAmount) ||
331
+ fiatDetails.from_amount === "" ||
332
+ parseFloat(fiatDetails.from_amount) <= 0,
333
+ max: parseFloat(fiatDetails.from_amount) > parseFloat(fiatDetails.maxAmount),
334
+ });
335
+ }, [exchangeRateForThisUser, fiatDetails]);
336
+
337
+ const onComplete = async () => {
338
+ const baseUrl = getBaseUrl(mode);
339
+ if (parseFloat(fiatDetails.from_amount) > 0) {
340
+ setLoading(true);
341
+ const payload = {
342
+ from: fiatDetails?.selectedFiat || selectedFiat?.label,
343
+ to: fiatDetails?.selectedCrypto || selectedCrypto?.label,
344
+ value: fiatDetails?.from_amount,
345
+ wallet_address: walletAddress,
346
+ initial_exchange_rate: exchangeRateForThisUser,
347
+ redirect_url: redirectUrl,
348
+ };
349
+
350
+ try {
351
+ await fetch(`${baseUrl}/api/onramp/transactions`, {
352
+ method: "POST",
353
+ headers: {
354
+ "Content-Type": "application/json",
355
+ "X-API-KEY": apiKey,
356
+ "User-Id": clientReferenceID,
357
+ },
358
+ body: JSON.stringify(payload),
359
+ })
360
+ .then((response) => response.json())
361
+ .then((data) => {
362
+ if (data?.data?.redirect_url) {
363
+ setRedirectURL(data.data.redirect_url);
364
+ setBuyStep(2);
365
+ } else if (!data.success) {
366
+ addToast(data?.message, "error");
367
+ }
368
+ })
369
+ .catch((error) => {
370
+ addToast(error, "error");
371
+ console.error("Error in Buy Tokens:", error);
372
+ });
373
+ } catch (error: any) {
374
+ addToast(error, "error");
375
+ console.error("Error in Buy Tokens:", error);
376
+ } finally {
377
+ // setExchangeLoading(false);
378
+ setLoading(false);
379
+ }
380
+ } else {
381
+ addToast("Please enter valid amount", "error");
382
+ }
383
+ };
384
+
385
+ return (
386
+ <>
387
+ {buyStep === 1 ? (
388
+ <>
389
+ <div className={styles.field_card}>
390
+ <div className={styles.field_box}>
391
+ <div className={styles.field_label}>You Pay</div>
392
+ <div className={styles.field_form}>
393
+ <input
394
+ type="text"
395
+ value={fiatDetails?.from_amount}
396
+ onChange={(e) => {
397
+ const newValue = e.target.value;
398
+ handleFiatCurrencyValueChange(newValue);
399
+ }}
400
+ // disabled={exchangeLoading}
401
+ className={styles.input_control}
402
+ />
403
+ </div>
404
+ </div>
405
+
406
+ <div className={styles.field_select_card}>
407
+ <div className={styles.field_select}>
408
+ {fiatOptions?.length > 0 ? (
409
+ <CustomSelect
410
+ backgroundColor="#f3e2c3"
411
+ placeholder={"Select an option"}
412
+ image={true}
413
+ singleicons={true}
414
+ options={fiatOptions}
415
+ isSearchable={false}
416
+ value={selectedFiat}
417
+ onChange={(selectedOption) => handleFiatCurrencyChange(selectedOption)}
418
+ isDisabled={exchangeLoading}
419
+ />
420
+ ) : (
421
+ <div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
422
+ <Loader />
423
+ </div>
424
+ )}
425
+ </div>
426
+ </div>
427
+ </div>
428
+ {invalidAmount?.min && (
429
+ <p className={styles.error_msg}>
430
+ Invalid, Min : {parseFloat(fiatDetails?.minAmount)?.toFixed(8)}
431
+ </p>
432
+ )}
433
+ {invalidAmount?.max && (
434
+ <p className={styles.error_msg}>
435
+ Invalid, Max : {parseFloat(fiatDetails?.maxAmount)?.toString()}
436
+ </p>
437
+ )}
438
+ <div className={styles.info_box}>
439
+ <div className={`${styles.ramp_space} ${styles.label_card}`}>
440
+ <div className={styles.label_icons}>
441
+ <svg
442
+ xmlns="http://www.w3.org/2000/svg"
443
+ xmlSpace="preserve"
444
+ width="12"
445
+ height="12"
446
+ viewBox="0 0 300 300"
447
+ >
448
+ <path
449
+ d="M240.3 171.5H59.7c-11.9 0-21.5-9.6-21.5-21.5s9.6-21.5 21.5-21.5h180.6c11.9 0 21.5 9.6 21.5 21.5s-9.6 21.5-21.5 21.5"
450
+ data-original="#000000"
451
+ ></path>
452
+ <circle cx="150" cy="65.5" r="30.5" data-original="#000000"></circle>
453
+ <circle cx="150.5" cy="234.5" r="30.5" data-original="#000000"></circle>
454
+ </svg>
455
+ </div>
456
+ <div className={styles.label_value}>
457
+ <span style={{ marginRight: 10 }}>Rate :</span>
458
+ {exchangeLoading ? (
459
+ <DotLoader />
460
+ ) : (
461
+ `1
462
+ ${selectedFiat?.label || "N/A"}
463
+ = ${exchangeRateForThisUser > 0 ? (1 / exchangeRateForThisUser).toFixed(8) : 0} ${
464
+ selectedCrypto?.label
465
+ }`
466
+ )}
467
+ </div>
468
+ </div>
469
+ {/* <div className={`${styles.ramp_space_top} ${styles.label_titles}`}>
470
+ <div className={styles.label_dots}></div>
471
+ <div className={styles.ramp_details}>
472
+ <div className={styles.ramp_head}> See Fees Calculation</div>
473
+ </div>
474
+ </div> */}
475
+ <div className={`${styles.ramp_space} ${styles.label_card}`}>
476
+ <div className={styles.label_icons}>
477
+ <svg
478
+ xmlns="http://www.w3.org/2000/svg"
479
+ xmlSpace="preserve"
480
+ width="10"
481
+ height="10"
482
+ viewBox="0 0 121.805 121.804"
483
+ >
484
+ <path
485
+ d="M7.308 68.211h107.188a7.31 7.31 0 0 0 7.309-7.31 7.31 7.31 0 0 0-7.309-7.309H7.308a7.31 7.31 0 0 0 0 14.619"
486
+ data-original="#000000"
487
+ ></path>
488
+ </svg>
489
+ </div>
490
+ <div className={styles.label_value}>
491
+ <span style={{ marginRight: 10 }}>Total Fees :</span>
492
+ {exchangeLoading ? (
493
+ <DotLoader />
494
+ ) : (
495
+ `${commissionForThisUser || 0} ${selectedFiat?.label}`
496
+ )}
497
+ </div>
498
+ </div>
499
+ <div className={`${styles.ramp_space_top} ${styles.label_titles}`}>
500
+ <div className={styles.label_dots}></div>
501
+ <div className={styles.ramp_details}>
502
+ <div className={styles.ramp_head}>Payment Method</div>
503
+ <div className={styles.payment_box}>
504
+ <div className={`${styles.payment_check_card} ${styles.selected}`}>
505
+ <input
506
+ type="radio"
507
+ name="radioDefault"
508
+ id="radioDefault1"
509
+ value="0.10234"
510
+ className={styles.input_control}
511
+ />
512
+ <div>Card Payment</div>
513
+ <svg
514
+ xmlns="http://www.w3.org/2000/svg"
515
+ width="20"
516
+ height="20"
517
+ viewBox="0 0 408.576 408.576"
518
+ >
519
+ <g>
520
+ <path
521
+ d="M204.288 0C91.648 0 0 91.648 0 204.288s91.648 204.288 204.288 204.288 204.288-91.648 204.288-204.288S316.928 0 204.288 0zm114.176 150.528-130.56 129.536c-7.68 7.68-19.968 8.192-28.16.512L90.624 217.6c-8.192-7.68-8.704-20.48-1.536-28.672 7.68-8.192 20.48-8.704 28.672-1.024l54.784 50.176L289.28 121.344c8.192-8.192 20.992-8.192 29.184 0s8.192 20.992 0 29.184z"
522
+ fill="#F9C201"
523
+ ></path>
524
+ </g>
525
+ </svg>
526
+ </div>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+ <div className={styles.field_card}>
532
+ <div className={styles.field_box}>
533
+ <div className={styles.field_label}>You Receive (estimate)</div>
534
+ <div className={styles.field_form}>
535
+ {exchangeLoading ? (
536
+ <DotLoader size={20} />
537
+ ) : (
538
+ <input
539
+ type="text"
540
+ value={
541
+ invalidAmount?.min || invalidAmount?.max ? 0 : userReceivableCryptoValue || 0
542
+ }
543
+ className={styles.input_control}
544
+ disabled
545
+ />
546
+ )}
547
+ </div>
548
+ </div>
549
+ <div className={styles.field_select_card}>
550
+ <div className={styles.field_select}>
551
+ {cryptoOptions?.length > 0 ? (
552
+ <CustomSelect
553
+ backgroundColor="#f3e2c3"
554
+ placeholder="Select"
555
+ borderColor="var(--no-color)"
556
+ image={true}
557
+ singleicons={true}
558
+ options={cryptoOptions}
559
+ isSearchable={false}
560
+ value={selectedCrypto}
561
+ onChange={(selectedOption) => {
562
+ if (selectedOption) {
563
+ setExchangeRateForThisUser(0);
564
+ setWalletAddress("");
565
+ setWalletAddressError("Required");
566
+ onCryptoChange(selectedOption);
567
+ }
568
+ }}
569
+ isDisabled={exchangeLoading}
570
+ />
571
+ ) : (
572
+ <div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
573
+ <Loader />
574
+ </div>
575
+ )}
576
+ </div>
577
+ </div>
578
+ </div>
579
+
580
+ <div style={{ marginTop: "20px" }} className={styles.ramp_bank_box}>
581
+ <div className={styles.new_bank_label}>
582
+ Wallet Address <span className={styles.required}>*</span>
583
+ </div>
584
+ <div className={styles.new_bank_fields}>
585
+ <input
586
+ type="text"
587
+ value={walletAddress}
588
+ className={styles.input_control}
589
+ placeholder="Enter Wallet Address"
590
+ onChange={(e) => {
591
+ const value = e.target.value.trim();
592
+ setWalletAddress(value);
593
+ if (e.target.value !== "" && selectedCrypto) {
594
+ setWalletAddressError("");
595
+
596
+ const valid = ["USDT", "USDC"].includes(selectedCrypto.label)
597
+ ? WAValidator.validate(value, "ETH") ||
598
+ WAValidator.validate(value, "TRX") ||
599
+ WAValidator.validate(value, "SOL")
600
+ : WAValidator.validate(value, selectedCrypto.label);
601
+ if (!valid) {
602
+ setWalletAddressError("Invalid wallet address");
603
+ } else {
604
+ setWalletAddressError("");
605
+ }
606
+ } else {
607
+ setWalletAddressError("Required");
608
+ }
609
+ }}
610
+ name="bank_name"
611
+ />
612
+ </div>
613
+ {walletAddressError && <span className={styles.input_error}>{walletAddressError}</span>}
614
+ </div>
615
+
616
+ <div className={styles.info_card}>
617
+ <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32">
618
+ <g id="Layer_20" data-name="Layer 20">
619
+ <path fill="#fbd63b" d="M28 28H4L16 6z"></path>
620
+ <path fill="#f8be31" d="m16 6 12 22H16z"></path>
621
+ <path fill="#333" d="m16 21-2-7h4z"></path>
622
+ <circle cx="16" cy="25" r="1" fill="#333"></circle>
623
+ <path d="M16 14h2l-2 7zM16 24a1 1 0 0 1 0 2z"></path>
624
+ </g>
625
+ </svg>
626
+ <div className={styles.caution_info}>
627
+ Cryptocurrencies are volatile, and prices can change rapidly, make sure to research
628
+ and understand the market risks.
629
+ </div>
630
+ </div>
631
+
632
+ <div className={styles.ramp_action}>
633
+ <button
634
+ disabled={
635
+ exchangeRateForThisUser <= 0 ||
636
+ exchangeLoading ||
637
+ !walletAddress ||
638
+ loading ||
639
+ walletAddressError?.length > 0
640
+ }
641
+ className={`${styles.action_btn} ${styles.primary} `}
642
+ onClick={onComplete}
643
+ >
644
+ Continue {loading && <Spinner />}
645
+ </button>
646
+ </div>
647
+ </>
648
+ ) : (
649
+ <>
650
+ <div className={styles.bank_head} onClick={() => setBuyStep(1)}>
651
+ <div>
652
+ <svg
653
+ xmlns="http://www.w3.org/2000/svg"
654
+ xmlSpace="preserve"
655
+ width="18"
656
+ height="18"
657
+ viewBox="0 0 240.823 240.823"
658
+ >
659
+ <path
660
+ d="M57.633 129.007 165.93 237.268c4.752 4.74 12.451 4.74 17.215 0 4.752-4.74 4.752-12.439 0-17.179l-99.707-99.671 99.695-99.671c4.752-4.74 4.752-12.439 0-17.191-4.752-4.74-12.463-4.74-17.215 0L57.621 111.816c-4.679 4.691-4.679 12.511.012 17.191"
661
+ data-original="#000000"
662
+ ></path>
663
+ </svg>
664
+ </div>
665
+ <div className={styles.bank_titles}>Back</div>
666
+ </div>
667
+ <iframe
668
+ title="Buy Crypto"
669
+ id="chat-widget"
670
+ src={redirectURL}
671
+ style={{
672
+ width: "100%",
673
+ minHeight: "calc(100vh - 400px)",
674
+ // minHeight: "calc(100vh - 23%)",
675
+ // maxHeight: "calc(100vh - 10%)",
676
+ border: "none",
677
+ zIndex: "9999",
678
+ overflow: "auto",
679
+ }}
680
+ ></iframe>
681
+ </>
682
+ )}
683
+ </>
684
+ );
685
+ };
686
+
687
+ export default NewBuyField;