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,712 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import CustomSelect from "./CustomeSelect";
3
+ import WidgetSuccesDetails from "./WidgetSuccesDetails";
4
+ import WidgetBankDetails from "./WidgetBankDetails";
5
+ import SellAdminCryptoAccount from "./SellAdminCryptoAccount";
6
+ import { useToast } from "../hooks/toastProvider";
7
+ import Loader from "./loader";
8
+ import { useExchangeSocket } from "../hooks/useSocketExchange";
9
+ import DotLoader from "./DotLoader";
10
+ import { handleApiCall } from "./api";
11
+ import { getBaseUrl, isValidDecimal } from "./utils";
12
+ import styles from "../assets/css/style.module.css";
13
+ import type {
14
+ comissionAPIResponse,
15
+ ConfigurationAPIResponse,
16
+ CurrenciesAPIResponse,
17
+ CustomOption,
18
+ OfframpTransactionAPIResponse,
19
+ rulesType,
20
+ SellParamsType,
21
+ } from "./types";
22
+
23
+ type SellFieldProps = {
24
+ apiKey: string;
25
+ parameters: SellParamsType;
26
+ clientReferenceID: string;
27
+ mode: string;
28
+ };
29
+
30
+ export type tokenSellDataType = {
31
+ from_amount: string;
32
+ selectedCrypto: string;
33
+ selectedFiat: string;
34
+ to_currency_conversion_value: number;
35
+ exchange_rate: number;
36
+ minAmount: string;
37
+ maxAmount: string;
38
+ network_type: string;
39
+ selectedBeneficiary: string;
40
+ transaction_hash?: string;
41
+ response?: OfframpTransactionAPIResponse["data"];
42
+ };
43
+
44
+ // Sell Crypto and receive Fiat
45
+ const SellField = ({ apiKey, parameters, clientReferenceID, mode }: SellFieldProps) => {
46
+ const { addToast } = useToast(); // to show toast
47
+ const exchangeSocket: any = useExchangeSocket({}); // socket connevction to fetch exchange rate
48
+
49
+ const [step, setStep] = useState(1); // used to render components
50
+
51
+ const [currenciesLoading, setCurrenciesLoading] = useState(false);
52
+ const [currencies, setCurrencies] = useState<CurrenciesAPIResponse["data"] | null>(null);
53
+ const [tokenSellData, setTokenSellData] = useState<tokenSellDataType>({
54
+ from_amount:
55
+ parameters?.from_amount && parseFloat(parameters?.from_amount) > 0
56
+ ? parameters.from_amount
57
+ : "0",
58
+ selectedCrypto: parameters?.from_currency || "",
59
+ selectedFiat: parameters?.to_currency || "",
60
+ to_currency_conversion_value: 0,
61
+ exchange_rate: 0,
62
+ minAmount: "0.0000001",
63
+ maxAmount: "100000.0000",
64
+ network_type: "",
65
+ selectedBeneficiary: "",
66
+ });
67
+ // From
68
+ const [selectedCrypto, setSelectedCrypto] = useState<CustomOption | null>(null);
69
+ // TO
70
+ const [selectedFiat, setSelectedFiat] = useState<CustomOption | null>(null);
71
+
72
+ // Options
73
+ const [cryptoCurrencyOptions, setCryptoCurrencyOptions] = useState<CustomOption[] | []>([]); // crypto currency options
74
+ const [fiatCurrencyOptions, setFiatCurrencyOptions] = useState<CustomOption[] | []>([]); // fiat currency options
75
+ const [networkOptions, setNetworkOptions] = useState<CustomOption[] | []>([]); // network options
76
+
77
+ // Comissions
78
+ const [commissionApiResponse, setComissionApiResponse] = useState<
79
+ comissionAPIResponse["data"] | null
80
+ >(null);
81
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
82
+ const [commissionForThisUser, setCommissionForThisUser] = useState(0);
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ const [actualCommission, setActualCommission] = useState(0); // original commission
85
+ // Exchange rate
86
+ const [exchangeRate, setExchangeRate] = useState(0); // actual exchange rate
87
+ const [exchangeRateForThisUser, setExchangeRateForThisUser] = useState(0); // exchange rate shown to user
88
+ const [userReceivableFiatValue, setUserReceivableFiatValue] = useState(0); //fiat value shown to user
89
+ const [isRangeComissionEnabled, setIsRangeComissionEnabled] = useState<boolean>(false);
90
+ // Fot loaders and to disable fields
91
+ const [isFetchingExchangeRate, setIsFetchingExchangeRate] = useState(true);
92
+ // Validation
93
+ const [inputRules, setInputRules] = useState<rulesType | null>(null);
94
+ const [invalidAmount, setInvalidAmount] = useState({
95
+ min: false,
96
+ max: false,
97
+ });
98
+
99
+ const [commissionData, setCommissionData] = useState<any>({
100
+ admin_commission: 0,
101
+ merchant_commission: 0,
102
+ });
103
+
104
+ useEffect(() => {
105
+ fetchCurrencies();
106
+ // fetchCommissionRanges();
107
+ fetchCommissions();
108
+ fetchConfiguration();
109
+ }, []);
110
+
111
+ const fetchConfiguration = () => {
112
+ const baseUrl = getBaseUrl(mode);
113
+ handleApiCall({
114
+ url: `${baseUrl}/api/lookup/configurations`,
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ "X-API-KEY": apiKey,
118
+ "User-Id": clientReferenceID,
119
+ },
120
+ onSuccess: (result: ConfigurationAPIResponse) => {
121
+ if (result?.success) {
122
+ setIsRangeComissionEnabled(result?.data?.merchant_configuration?.range_calculation === 1);
123
+ }
124
+ },
125
+ onError: () => {
126
+ addToast("Error fetching configuration", "error");
127
+ },
128
+ });
129
+ };
130
+
131
+ const fetchCommissions = () => {
132
+ const baseUrl = getBaseUrl(mode);
133
+
134
+ handleApiCall({
135
+ url: `${baseUrl}/api/lookup/commissions`,
136
+ headers: {
137
+ "Content-Type": "application/json",
138
+ "X-API-KEY": apiKey,
139
+ "User-Id": clientReferenceID,
140
+ },
141
+ onSuccess: (result: comissionAPIResponse) => {
142
+ if (result?.success) {
143
+ setComissionApiResponse(result?.data);
144
+ }
145
+ },
146
+ onError: () => {
147
+ addToast("Error fetching comission", "error");
148
+ },
149
+ });
150
+ };
151
+
152
+ const fetchCurrencies = () => {
153
+ const baseUrl = getBaseUrl(mode);
154
+ setCurrenciesLoading(true);
155
+ handleApiCall({
156
+ url: `${baseUrl}/api/lookup/currencies`,
157
+ headers: {
158
+ "Content-Type": "application/json",
159
+ "X-API-KEY": apiKey,
160
+ "User-Id": clientReferenceID,
161
+ },
162
+ onSuccess: (result: CurrenciesAPIResponse) => {
163
+ if (result?.success) {
164
+ setCurrencies(result?.data);
165
+ }
166
+ setCurrenciesLoading(false);
167
+ },
168
+
169
+ onError: () => {
170
+ addToast("Error fetching currency", "error");
171
+ setCurrenciesLoading(false);
172
+ },
173
+ });
174
+ };
175
+
176
+ useEffect(() => {
177
+ if (currencies) {
178
+ const fiatCurrencyOptions =
179
+ currencies?.currencies
180
+ ?.filter(
181
+ (item) => item?.supported_currency?.type === "FIAT" && item?.service === "offramp"
182
+ )
183
+ ?.map((item) => ({
184
+ label: item?.supported_currency?.code,
185
+ value: item?.unique_id,
186
+ icon: item?.supported_currency?.icon,
187
+ })) || [];
188
+
189
+ setFiatCurrencyOptions(fiatCurrencyOptions);
190
+
191
+ const selected_forex = tokenSellData?.selectedFiat
192
+ ? currencies?.currencies?.find(
193
+ (currency) => currency?.supported_currency?.code === tokenSellData?.selectedFiat
194
+ ) ||
195
+ currencies?.currencies?.find(
196
+ (currency) => currency?.supported_currency?.code === fiatCurrencyOptions?.[0]?.label
197
+ )
198
+ : currencies?.currencies?.find(
199
+ (currency) => currency?.supported_currency?.code === fiatCurrencyOptions?.[0]?.label
200
+ );
201
+
202
+ const selectedFiat = tokenSellData.selectedFiat
203
+ ? fiatCurrencyOptions.find((fiat) => fiat.label === tokenSellData.selectedFiat) ||
204
+ fiatCurrencyOptions?.[0]
205
+ : fiatCurrencyOptions?.[0];
206
+
207
+ setSelectedFiat(selectedFiat);
208
+
209
+ const cryptoOptions =
210
+ currencies?.currencies
211
+ ?.filter(
212
+ (item) => item?.supported_currency?.type === "CRYPTO" && item?.service === "offramp"
213
+ )
214
+ ?.map((item) => ({
215
+ label: item?.supported_currency?.code,
216
+ value: item?.unique_id,
217
+ icon: item?.supported_currency?.icon,
218
+ })) || [];
219
+
220
+ setCryptoCurrencyOptions(cryptoOptions);
221
+
222
+ const networkOptions =
223
+ currencies?.currencies
224
+ ?.filter(
225
+ (item) =>
226
+ item?.supported_currency?.type === "CRYPTO" &&
227
+ item?.service === "offramp" &&
228
+ !["USDT", "USDC"].includes(item?.supported_currency?.code)
229
+ )
230
+ ?.map((item) => ({
231
+ label: item?.supported_currency?.code,
232
+ value: item?.supported_currency?.code,
233
+ icon: item?.supported_currency?.icon,
234
+ })) || [];
235
+
236
+ setNetworkOptions(networkOptions);
237
+
238
+ const selected_crypto = parameters?.from_currency
239
+ ? currencies?.currencies?.find(
240
+ (currency) => currency?.supported_currency?.code === parameters?.from_currency
241
+ ) ||
242
+ currencies?.currencies?.find(
243
+ (currency) => currency?.supported_currency?.code === cryptoOptions?.[0]?.label
244
+ )
245
+ : currencies?.currencies?.find(
246
+ (currency) => currency?.supported_currency?.code === cryptoOptions?.[0]?.label
247
+ );
248
+
249
+ setSelectedCrypto(
250
+ parameters?.from_currency
251
+ ? cryptoOptions.find((currency) => currency.label === parameters?.from_currency) ||
252
+ cryptoOptions?.[0]
253
+ : cryptoOptions?.[0] || []
254
+ );
255
+
256
+ setTokenSellData((prev) => ({
257
+ ...prev,
258
+ selectedCrypto: selected_crypto?.supported_currency?.code || "",
259
+ selectedFiat: selected_forex?.supported_currency?.code || "",
260
+ }));
261
+ }
262
+ }, [currencies]);
263
+
264
+ useEffect(() => {
265
+ if (commissionApiResponse && currencies) {
266
+ const rules = commissionApiResponse?.rules?.find((item) => item?.service === "offramp");
267
+ if (rules) {
268
+ setInputRules(rules);
269
+ }
270
+ const commission = commissionApiResponse?.commission?.find(
271
+ (item) => item?.service == "offramp"
272
+ );
273
+ setActualCommission(commission?.commission ? parseFloat(commission?.commission) : 0);
274
+
275
+ setCommissionData({
276
+ admin_commission: commission?.admin_commission
277
+ ? parseFloat(commission?.admin_commission)
278
+ : 0,
279
+ merchant_commission: commission?.commission ? parseFloat(commission?.commission) : 0,
280
+ });
281
+
282
+ if (rules) {
283
+ setTokenSellData((prev) => ({
284
+ ...prev,
285
+ minAmount:
286
+ parseFloat(rules?.schema?.min) <= 0
287
+ ? "0.0000001"
288
+ : parseFloat(rules?.schema?.min)?.toFixed(8),
289
+ maxAmount:
290
+ parseFloat(rules?.schema?.max) <= 0
291
+ ? "100000"
292
+ : parseFloat(rules?.schema?.max)?.toFixed(8),
293
+ // buy_commission: commission,
294
+ }));
295
+ }
296
+ }
297
+ }, [commissionApiResponse, currencies]);
298
+
299
+ useEffect(() => {
300
+ if (exchangeSocket && selectedFiat && selectedCrypto) {
301
+ console.log("fetching exchange rate");
302
+
303
+ setExchangeRateForThisUser(0);
304
+ fetchExchangeRate();
305
+ }
306
+ }, [selectedCrypto, selectedFiat]);
307
+
308
+ useEffect(() => {
309
+ if (exchangeSocket) {
310
+ exchangeSocket.on("getSDKExchangeRate", (data: string) => {
311
+ console.log("ex rate :", data);
312
+ setExchangeRate(parseFloat(data));
313
+ setIsFetchingExchangeRate(false);
314
+ });
315
+ }
316
+ }, [exchangeSocket]);
317
+
318
+ const fetchExchangeRate = () => {
319
+ setExchangeRate(0);
320
+ setIsFetchingExchangeRate(true);
321
+ exchangeSocket.emit("fetchSDKExchangeRate", {
322
+ pair: `${selectedFiat?.label}-${selectedCrypto?.label}`,
323
+ user_id: "",
324
+ type: "sell",
325
+ apiKey: apiKey,
326
+ userId: clientReferenceID,
327
+ });
328
+ };
329
+
330
+ const handleCryptoCurrencyChange = (selectedOption: CustomOption | null) => {
331
+ const fiat = currencies?.currencies?.find((fiat) => fiat.unique_id === selectedOption?.value);
332
+ setTokenSellData({
333
+ ...tokenSellData,
334
+ selectedCrypto: fiat?.supported_currency?.code || "",
335
+ });
336
+ setSelectedCrypto(selectedOption);
337
+ };
338
+
339
+ const handleCryptoCurrencyValueChange = (value: string) => {
340
+ if (value.includes(".")) {
341
+ if (inputRules && !isValidDecimal(value, inputRules?.schema?.decimal)) {
342
+ addToast(`You can input upto ${inputRules?.schema?.decimal} decimal places.`, "error");
343
+ return;
344
+ } else {
345
+ if (value === "") {
346
+ setTokenSellData({
347
+ ...tokenSellData,
348
+ from_amount: "",
349
+ });
350
+ } else {
351
+ if (!isNaN(Number(value))) {
352
+ setTokenSellData({
353
+ ...tokenSellData,
354
+ from_amount: value,
355
+ });
356
+ }
357
+ }
358
+ }
359
+ } else {
360
+ if (value === "") {
361
+ setTokenSellData({
362
+ ...tokenSellData,
363
+ from_amount: "",
364
+ });
365
+ } else {
366
+ if (!isNaN(Number(value))) {
367
+ setTokenSellData({
368
+ ...tokenSellData,
369
+ from_amount: value,
370
+ });
371
+ }
372
+ }
373
+ }
374
+ };
375
+
376
+ const onComplete = () => {
377
+ if (parseFloat(tokenSellData.from_amount) > 0) {
378
+ setTokenSellData({
379
+ ...tokenSellData,
380
+ to_currency_conversion_value: userReceivableFiatValue,
381
+ exchange_rate: exchangeRateForThisUser,
382
+ });
383
+
384
+ setStep(2);
385
+ } else {
386
+ addToast("Please enter valid amount", "error");
387
+ }
388
+ };
389
+
390
+ useEffect(() => {
391
+ setInvalidAmount({
392
+ min:
393
+ parseFloat(tokenSellData.from_amount) < parseFloat(tokenSellData.minAmount) ||
394
+ tokenSellData.from_amount === "" ||
395
+ parseFloat(tokenSellData.from_amount) <= 0,
396
+ max: parseFloat(tokenSellData.from_amount) > parseFloat(tokenSellData.maxAmount),
397
+ });
398
+ }, [exchangeRateForThisUser, tokenSellData]);
399
+
400
+ useEffect(() => {
401
+ if (tokenSellData) {
402
+ console.log("calculating");
403
+
404
+ const actualExchangeRate = exchangeRate;
405
+
406
+ const range = isRangeComissionEnabled
407
+ ? commissionApiResponse?.commission_ranges?.find(
408
+ (item) =>
409
+ parseFloat(item.from_amount) <=
410
+ parseFloat(tokenSellData.from_amount) * actualExchangeRate &&
411
+ parseFloat(item.to_amount) >=
412
+ parseFloat(tokenSellData.from_amount) * actualExchangeRate &&
413
+ item.currency === selectedFiat?.label &&
414
+ item.type === "c-f"
415
+ )
416
+ : null;
417
+
418
+ const from_commission = range ? parseFloat(range?.from_commission) : 0;
419
+ const to_commission = range ? parseFloat(range?.to_commission) : 0;
420
+ const token_from_amount = tokenSellData.from_amount
421
+ ? parseFloat(tokenSellData.from_amount)
422
+ : 0;
423
+ const range_from_amount = range ? parseFloat(range.from_amount) : 0;
424
+ const range_to_amount = range ? parseFloat(range.to_amount) : 0;
425
+
426
+ const additonalCommissionRate = range
427
+ ? parseFloat(range.from_commission) -
428
+ ((from_commission - to_commission) / (range_to_amount - range_from_amount)) *
429
+ (token_from_amount * actualExchangeRate - range_from_amount)
430
+ : 0;
431
+
432
+ const ceffective: number = totalEffectiveCommission(
433
+ commissionData?.admin_commission + additonalCommissionRate,
434
+ commissionData?.merchant_commission
435
+ );
436
+ // let commissionEffective:any = ((ceffective/100) * actualExchangeRate)
437
+ // console.log("Effective Commission", ceffective, commissionEffective);
438
+ const userCommission = ceffective || 0;
439
+
440
+ const newCommission = (userCommission / 100) * actualExchangeRate;
441
+ setCommissionForThisUser(newCommission || 0);
442
+
443
+ const newExchangeRate = parseFloat((actualExchangeRate - newCommission).toFixed(6));
444
+
445
+ setExchangeRateForThisUser(newExchangeRate || 0);
446
+
447
+ setUserReceivableFiatValue(token_from_amount * newExchangeRate);
448
+ }
449
+ }, [exchangeRate, tokenSellData, commissionApiResponse]);
450
+
451
+ function totalEffectiveCommission(c1: number, c2: number) {
452
+ const factor = (1 - c1 / 100) * (1 - c2 / 100);
453
+ return 100 * (1 - factor);
454
+ }
455
+
456
+ useEffect(() => {
457
+ if (networkOptions?.length > 0) {
458
+ if (["USDT", "USDC"].includes(tokenSellData.selectedCrypto) && !tokenSellData.network_type) {
459
+ setTokenSellData((prev) => ({ ...prev, network_type: networkOptions[0].value }));
460
+ }
461
+ }
462
+ }, [tokenSellData, networkOptions]);
463
+
464
+ const onFiatChange = (selectedOption: CustomOption | null) => {
465
+ const fiat = currencies?.currencies?.find(
466
+ (crypto) => crypto?.unique_id === selectedOption?.value
467
+ );
468
+ setTokenSellData({
469
+ ...tokenSellData,
470
+ selectedFiat: fiat?.supported_currency?.code || "",
471
+ });
472
+ setSelectedFiat(selectedOption);
473
+ };
474
+
475
+ return (
476
+ <>
477
+ {step === 1 ? (
478
+ <>
479
+ {" "}
480
+ <div className={styles.field_card}>
481
+ <div className={styles.field_box}>
482
+ <div className={styles.field_label}>You Pay</div>
483
+ <div className={styles.field_form}>
484
+ <input
485
+ type="text"
486
+ className={styles.input_control}
487
+ value={tokenSellData.from_amount}
488
+ onChange={(e) => {
489
+ const newValue = e.target.value;
490
+ handleCryptoCurrencyValueChange(newValue);
491
+ }}
492
+ />
493
+ </div>
494
+ </div>
495
+ <div className={styles.field_select_card}>
496
+ <div className={styles.field_select}>
497
+ {cryptoCurrencyOptions?.length > 0 ? (
498
+ <CustomSelect
499
+ backgroundColor="#f3e2c3"
500
+ placeholder="Select"
501
+ image={true}
502
+ singleicons={true}
503
+ value={selectedCrypto}
504
+ isSearchable={false}
505
+ onChange={(selectedOption) => {
506
+ // setExchangeRate(0);
507
+ handleCryptoCurrencyChange(selectedOption);
508
+ }}
509
+ isDisabled={currenciesLoading}
510
+ options={cryptoCurrencyOptions}
511
+ />
512
+ ) : (
513
+ <div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
514
+ <Loader />
515
+ </div>
516
+ )}
517
+ </div>
518
+ {networkOptions?.length > 0 &&
519
+ ["USDT", "USDC"].includes(tokenSellData.selectedCrypto) && (
520
+ <div className={styles.selected__two}>
521
+ <CustomSelect
522
+ backgroundColor="#fff"
523
+ placeholder="Select"
524
+ options={networkOptions}
525
+ image={true}
526
+ singleicons={true}
527
+ isSearchable={false}
528
+ onChange={(select) => {
529
+ setTokenSellData({
530
+ ...tokenSellData,
531
+ network_type: select?.value || "",
532
+ });
533
+ }}
534
+ value={networkOptions.find(
535
+ (item) => item.value == tokenSellData.network_type
536
+ )}
537
+ isDisabled={currenciesLoading}
538
+ />
539
+ </div>
540
+ )}
541
+ </div>
542
+ </div>
543
+ {invalidAmount?.min && (
544
+ <p>Invalid, Min : {parseFloat(tokenSellData?.minAmount)?.toFixed(8)}</p>
545
+ )}
546
+ {invalidAmount?.max && (
547
+ <p>Invalid, Max : {parseFloat(tokenSellData?.maxAmount)?.toString()}</p>
548
+ )}
549
+ <div className={styles.info_box}>
550
+ <div className={`${styles.ramp_space} ${styles.label_card}`}>
551
+ <div className={styles.label_icons}>
552
+ <svg
553
+ xmlns="http://www.w3.org/2000/svg"
554
+ xmlSpace="preserve"
555
+ width="12"
556
+ height="12"
557
+ viewBox="0 0 300 300"
558
+ >
559
+ <path
560
+ 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"
561
+ data-original="#000000"
562
+ ></path>
563
+ <circle cx="150" cy="65.5" r="30.5" data-original="#000000"></circle>
564
+ <circle cx="150.5" cy="234.5" r="30.5" data-original="#000000"></circle>
565
+ </svg>
566
+ </div>
567
+ <div className={styles.label_value}>
568
+ <span style={{ marginRight: 10 }}>Rate :</span>
569
+ {isFetchingExchangeRate ? (
570
+ <DotLoader />
571
+ ) : (
572
+ `1 ${tokenSellData.selectedCrypto} = ${
573
+ exchangeRateForThisUser?.toFixed(4) || 0
574
+ } ${tokenSellData.selectedFiat}`
575
+ )}
576
+ </div>
577
+ </div>
578
+
579
+ <div className={`${styles.ramp_space_top} ${styles.label_titles}`}>
580
+ <div className={styles.label_dots}></div>
581
+ <div className={styles.ramp_details}>
582
+ <div className={styles.ramp_head}>Withdrawal Method</div>
583
+ <div className={styles.payment_box}>
584
+ <div className={`${styles.payment_check_card} ${styles.selected}`}>
585
+ <input
586
+ type="radio"
587
+ name="radioDefault"
588
+ id="radioDefault1"
589
+ value="0.10234"
590
+ className={styles.input_control}
591
+ />
592
+ <div>Bank Transfer</div>
593
+ <svg
594
+ xmlns="http://www.w3.org/2000/svg"
595
+ width="20"
596
+ height="20"
597
+ viewBox="0 0 408.576 408.576"
598
+ >
599
+ <g>
600
+ <path
601
+ 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"
602
+ fill="#F9C201"
603
+ ></path>
604
+ </g>
605
+ </svg>
606
+ </div>
607
+ </div>
608
+ </div>
609
+ </div>
610
+ </div>
611
+ <div className={styles.field_card}>
612
+ <div className={`${styles.field_box_res} ${styles.field_box}`}>
613
+ <div className={styles.field_label}>You Receive ( estimate )</div>
614
+ <div className={styles.field_form}>
615
+ {isFetchingExchangeRate ? (
616
+ <DotLoader size={20} />
617
+ ) : (
618
+ <input
619
+ type="text"
620
+ className={styles.input_control}
621
+ value={
622
+ invalidAmount?.min || invalidAmount?.max
623
+ ? 0
624
+ : Number(userReceivableFiatValue) && Number(userReceivableFiatValue) > 0
625
+ ? userReceivableFiatValue?.toFixed(4)
626
+ : 0
627
+ }
628
+ disabled
629
+ />
630
+ )}
631
+ </div>
632
+ </div>
633
+ <div className={styles.field_select_card}>
634
+ <div className={styles.field_select}>
635
+ {fiatCurrencyOptions?.length > 0 ? (
636
+ <CustomSelect
637
+ backgroundColor="#f3e2c3"
638
+ placeholder="Select"
639
+ borderColor="var(--no-color)"
640
+ image={true}
641
+ singleicons={true}
642
+ options={fiatCurrencyOptions}
643
+ value={selectedFiat}
644
+ isSearchable={false}
645
+ onChange={(selectedOption) => {
646
+ // setExchangeRate(0);
647
+ onFiatChange(selectedOption);
648
+ }}
649
+ isDisabled={currenciesLoading || isFetchingExchangeRate}
650
+ />
651
+ ) : (
652
+ <div style={{ display: "flex", justifyContent: "center", alignItems: "center" }}>
653
+ <Loader />
654
+ </div>
655
+ )}
656
+ </div>
657
+ </div>
658
+ </div>
659
+ <div style={{ marginTop: 20 }} className={styles.info_card}>
660
+ <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32">
661
+ <g id="Layer_20" data-name="Layer 20">
662
+ <path fill="#fbd63b" d="M28 28H4L16 6z"></path>
663
+ <path fill="#f8be31" d="m16 6 12 22H16z"></path>
664
+ <path fill="#333" d="m16 21-2-7h4z"></path>
665
+ <circle cx="16" cy="25" r="1" fill="#333"></circle>
666
+ <path d="M16 14h2l-2 7zM16 24a1 1 0 0 1 0 2z"></path>
667
+ </g>
668
+ </svg>
669
+ <div className={styles.caution_info}>
670
+ Cryptocurrencies are volatile, and prices can change rapidly, make sure to research
671
+ and understand the market risks.
672
+ </div>
673
+ </div>
674
+ <div className={styles.ramp_action}>
675
+ <button
676
+ className={`${styles.action_btn} ${styles.primary} `}
677
+ onClick={onComplete}
678
+ disabled={
679
+ currenciesLoading || !(exchangeRate > 0) || invalidAmount.min || invalidAmount.max
680
+ }
681
+ >
682
+ Sell Now
683
+ </button>
684
+ </div>
685
+ </>
686
+ ) : step === 2 ? (
687
+ <WidgetBankDetails
688
+ defaultTab={"select"}
689
+ setStep={setStep}
690
+ data={tokenSellData}
691
+ apiKey={apiKey}
692
+ clientReferenceID={clientReferenceID}
693
+ setTokenSellData={setTokenSellData}
694
+ mode={mode}
695
+ />
696
+ ) : step === 3 ? (
697
+ <SellAdminCryptoAccount
698
+ tokenSellData={tokenSellData}
699
+ setTokenSellData={setTokenSellData}
700
+ apiKey={apiKey}
701
+ clientReferenceID={clientReferenceID}
702
+ setStep={setStep}
703
+ mode={mode}
704
+ />
705
+ ) : step === 4 ? (
706
+ <WidgetSuccesDetails tokenSellData={tokenSellData} />
707
+ ) : null}
708
+ </>
709
+ );
710
+ };
711
+
712
+ export default SellField;