@ensofinance/checkout-widget 0.1.6 → 0.1.8
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/dist/checkout-widget.es.js +25523 -24215
- package/dist/checkout-widget.es.js.map +1 -1
- package/dist/checkout-widget.umd.js +64 -59
- package/dist/checkout-widget.umd.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/package.json +1 -1
- package/src/assets/providers/alchemypay.svg +21 -0
- package/src/assets/providers/banxa.svg +21 -0
- package/src/assets/providers/binanceconnect.svg +14 -0
- package/src/assets/providers/kryptonim.svg +6 -0
- package/src/assets/providers/mercuryo.svg +21 -0
- package/src/assets/providers/moonpay.svg +14 -0
- package/src/assets/providers/stripe.svg +16 -0
- package/src/assets/providers/swapped.svg +1 -0
- package/src/assets/providers/topper.svg +14 -0
- package/src/assets/providers/transak.svg +21 -0
- package/src/assets/providers/unlimit.svg +21 -0
- package/src/components/AmountInput.tsx +41 -25
- package/src/components/ChakraProvider.tsx +36 -13
- package/src/components/Checkout.tsx +7 -1
- package/src/components/CurrencySwapDisplay.tsx +59 -22
- package/src/components/DepositProcessing.tsx +1 -1
- package/src/components/ExchangeConfirmSecurity.tsx +1 -1
- package/src/components/QuoteParameters.tsx +1 -1
- package/src/components/TransactionDetailRow.tsx +2 -2
- package/src/components/cards/ExchangeCard.tsx +1 -1
- package/src/components/cards/OptionCard.tsx +2 -1
- package/src/components/cards/WalletCard.tsx +1 -1
- package/src/components/modal.tsx +3 -3
- package/src/components/steps/CardBuyFlow/CardBuyFlow.tsx +412 -0
- package/src/components/steps/CardBuyFlow/ChooseAmountStep.tsx +352 -0
- package/src/components/steps/CardBuyFlow/OpenWidgetStep.tsx +193 -0
- package/src/components/steps/ExchangeFlow.tsx +254 -1416
- package/src/components/steps/FlowSelector.tsx +117 -60
- package/src/components/steps/SmartAccountFlow.tsx +372 -0
- package/src/components/steps/WalletFlow/WalletAmountStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletConfirmStep.tsx +92 -51
- package/src/components/steps/WalletFlow/WalletFlow.tsx +17 -16
- package/src/components/steps/WalletFlow/WalletQuoteStep.tsx +2 -2
- package/src/components/steps/WalletFlow/WalletTokenStep.tsx +6 -4
- package/src/components/steps/shared/ChooseAmountStep.tsx +325 -0
- package/src/components/steps/shared/SignUserOpStep.tsx +117 -0
- package/src/components/steps/shared/TrackUserOpStep.tsx +625 -0
- package/src/components/steps/shared/exchangeIntegration.ts +19 -0
- package/src/components/steps/shared/types.ts +22 -0
- package/src/components/ui/index.tsx +23 -6
- package/src/components/ui/toaster.tsx +2 -1
- package/src/components/ui/transitions.tsx +16 -0
- package/src/types/index.ts +99 -0
- package/src/util/constants.tsx +27 -0
- package/src/util/enso-hooks.tsx +75 -61
- package/src/util/meld-hooks.tsx +533 -0
- package/src/assets/usdc.webp +0 -0
- package/src/assets/usdt.webp +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Center,
|
|
3
|
+
Spinner,
|
|
4
|
+
Box,
|
|
5
|
+
Icon,
|
|
6
|
+
Text,
|
|
7
|
+
Flex,
|
|
8
|
+
Skeleton,
|
|
9
|
+
Image,
|
|
10
|
+
} from "@chakra-ui/react";
|
|
11
|
+
import { ChevronRight, Check } from "lucide-react";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { BodyWrapper } from "../../ui/styled";
|
|
14
|
+
import { Button, Input } from "../../ui";
|
|
15
|
+
import { formatProviderName, getProviderIcon } from "@/util/meld-hooks";
|
|
16
|
+
import type { MeldQuote } from "@/types";
|
|
17
|
+
|
|
18
|
+
interface ChooseAmountStepProps {
|
|
19
|
+
fiatCurrency: "USD" | "EUR";
|
|
20
|
+
setFiatCurrency: (currency: "USD" | "EUR") => void;
|
|
21
|
+
fiatAmount: string;
|
|
22
|
+
setFiatAmount: (amount: string) => void;
|
|
23
|
+
quotes: MeldQuote[] | undefined;
|
|
24
|
+
quotesLoading: boolean;
|
|
25
|
+
quotesError: string | null;
|
|
26
|
+
selectedQuote: MeldQuote | null;
|
|
27
|
+
setSelectedQuote: (quote: MeldQuote | null) => void;
|
|
28
|
+
destinationSymbol: string;
|
|
29
|
+
onContinue: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ChooseAmountStep = ({
|
|
33
|
+
fiatCurrency,
|
|
34
|
+
setFiatCurrency,
|
|
35
|
+
fiatAmount,
|
|
36
|
+
setFiatAmount,
|
|
37
|
+
quotes,
|
|
38
|
+
quotesLoading,
|
|
39
|
+
quotesError,
|
|
40
|
+
selectedQuote,
|
|
41
|
+
setSelectedQuote,
|
|
42
|
+
destinationSymbol,
|
|
43
|
+
onContinue,
|
|
44
|
+
}: ChooseAmountStepProps) => {
|
|
45
|
+
const [showProviderSelect, setShowProviderSelect] = useState(false);
|
|
46
|
+
const fiatPrefix = fiatCurrency === "EUR" ? "€" : "$";
|
|
47
|
+
|
|
48
|
+
const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
49
|
+
const raw = e.target.value.replace(/[^0-9.]/g, "");
|
|
50
|
+
// Prevent multiple decimals
|
|
51
|
+
const parts = raw.split(".");
|
|
52
|
+
const sanitized =
|
|
53
|
+
parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : raw;
|
|
54
|
+
setFiatAmount(sanitized);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const displayValue = fiatAmount ? `${fiatPrefix}${fiatAmount}` : "";
|
|
58
|
+
|
|
59
|
+
// Check if selected quote is the best (first in list)
|
|
60
|
+
const isBestRate =
|
|
61
|
+
selectedQuote &&
|
|
62
|
+
quotes &&
|
|
63
|
+
quotes.length > 0 &&
|
|
64
|
+
quotes[0].serviceProvider === selectedQuote.serviceProvider;
|
|
65
|
+
|
|
66
|
+
// Initial loading: no quote yet and quotes are being fetched
|
|
67
|
+
const isInitialLoading =
|
|
68
|
+
quotesLoading && !selectedQuote && parseFloat(fiatAmount) > 0;
|
|
69
|
+
|
|
70
|
+
// Provider Selection View
|
|
71
|
+
if (showProviderSelect) {
|
|
72
|
+
return (
|
|
73
|
+
<BodyWrapper>
|
|
74
|
+
{quotesLoading ? (
|
|
75
|
+
<Flex direction="column" gap={3}>
|
|
76
|
+
<Skeleton height="56px" borderRadius="card" />
|
|
77
|
+
<Skeleton height="56px" borderRadius="card" />
|
|
78
|
+
<Skeleton height="56px" borderRadius="card" />
|
|
79
|
+
</Flex>
|
|
80
|
+
) : quotes && quotes.length > 0 ? (
|
|
81
|
+
<Flex direction="column" gap={2}>
|
|
82
|
+
{quotes.map((quote, idx) => {
|
|
83
|
+
const selected =
|
|
84
|
+
selectedQuote?.serviceProvider ===
|
|
85
|
+
quote.serviceProvider;
|
|
86
|
+
return (
|
|
87
|
+
<Flex
|
|
88
|
+
key={`${quote.serviceProvider}-${idx}`}
|
|
89
|
+
p={3}
|
|
90
|
+
h="56px"
|
|
91
|
+
borderRadius="card"
|
|
92
|
+
border="1px solid"
|
|
93
|
+
borderColor={
|
|
94
|
+
selected ? "primary" : "border"
|
|
95
|
+
}
|
|
96
|
+
bg={selected ? "bg.subtle" : "transparent"}
|
|
97
|
+
cursor="pointer"
|
|
98
|
+
onClick={() => {
|
|
99
|
+
setSelectedQuote(quote);
|
|
100
|
+
setShowProviderSelect(false);
|
|
101
|
+
}}
|
|
102
|
+
align="center"
|
|
103
|
+
gap={3}
|
|
104
|
+
>
|
|
105
|
+
{/* Provider Icon */}
|
|
106
|
+
<Center
|
|
107
|
+
boxSize="40px"
|
|
108
|
+
borderRadius="card"
|
|
109
|
+
bg="bg.subtle"
|
|
110
|
+
overflow="hidden"
|
|
111
|
+
flexShrink={0}
|
|
112
|
+
>
|
|
113
|
+
<Image
|
|
114
|
+
src={getProviderIcon(
|
|
115
|
+
quote.serviceProvider,
|
|
116
|
+
)}
|
|
117
|
+
alt={quote.serviceProvider}
|
|
118
|
+
boxSize="40px"
|
|
119
|
+
objectFit="contain"
|
|
120
|
+
onError={(e) => {
|
|
121
|
+
(
|
|
122
|
+
e.target as HTMLImageElement
|
|
123
|
+
).style.display = "none";
|
|
124
|
+
}}
|
|
125
|
+
/>
|
|
126
|
+
</Center>
|
|
127
|
+
{/* Provider Info */}
|
|
128
|
+
<Flex direction="column" flex={1}>
|
|
129
|
+
<Flex align="center" gap={2}>
|
|
130
|
+
<Text
|
|
131
|
+
fontSize="sm"
|
|
132
|
+
fontWeight="semibold"
|
|
133
|
+
color="fg"
|
|
134
|
+
>
|
|
135
|
+
{formatProviderName(
|
|
136
|
+
quote.serviceProvider,
|
|
137
|
+
)}
|
|
138
|
+
</Text>
|
|
139
|
+
{idx === 0 && (
|
|
140
|
+
<Text
|
|
141
|
+
fontSize="xs"
|
|
142
|
+
color="success"
|
|
143
|
+
fontWeight="medium"
|
|
144
|
+
>
|
|
145
|
+
Best price
|
|
146
|
+
</Text>
|
|
147
|
+
)}
|
|
148
|
+
{quote.lowKyc && (
|
|
149
|
+
<Text
|
|
150
|
+
fontSize="xs"
|
|
151
|
+
color="primary.muted"
|
|
152
|
+
fontWeight="medium"
|
|
153
|
+
>
|
|
154
|
+
Low KYC
|
|
155
|
+
</Text>
|
|
156
|
+
)}
|
|
157
|
+
</Flex>
|
|
158
|
+
<Text fontSize="xs" color="fg.subtle">
|
|
159
|
+
{quote.destinationAmount.toFixed(4)}{" "}
|
|
160
|
+
{destinationSymbol} • Fee:{" "}
|
|
161
|
+
{fiatPrefix}
|
|
162
|
+
{quote.totalFee.toFixed(2)}
|
|
163
|
+
</Text>
|
|
164
|
+
</Flex>
|
|
165
|
+
{/* Selected Check */}
|
|
166
|
+
<Icon
|
|
167
|
+
as={Check}
|
|
168
|
+
color={selected ? "primary" : "transparent"}
|
|
169
|
+
boxSize={5}
|
|
170
|
+
flexShrink={0}
|
|
171
|
+
/>
|
|
172
|
+
</Flex>
|
|
173
|
+
);
|
|
174
|
+
})}
|
|
175
|
+
</Flex>
|
|
176
|
+
) : (
|
|
177
|
+
<Text color="fg.muted" textAlign="center" py={4}>
|
|
178
|
+
No providers available
|
|
179
|
+
</Text>
|
|
180
|
+
)}
|
|
181
|
+
</BodyWrapper>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Main View
|
|
186
|
+
return (
|
|
187
|
+
<BodyWrapper>
|
|
188
|
+
{/* Currency Toggle */}
|
|
189
|
+
<Flex gap={2} width="100%">
|
|
190
|
+
<Button
|
|
191
|
+
visual={fiatCurrency === "USD" ? "solid" : "lightGray"}
|
|
192
|
+
onClick={() => setFiatCurrency("USD")}
|
|
193
|
+
flex={1}
|
|
194
|
+
size="sm"
|
|
195
|
+
>
|
|
196
|
+
USD
|
|
197
|
+
</Button>
|
|
198
|
+
<Button
|
|
199
|
+
visual={fiatCurrency === "EUR" ? "solid" : "lightGray"}
|
|
200
|
+
onClick={() => setFiatCurrency("EUR")}
|
|
201
|
+
flex={1}
|
|
202
|
+
size="sm"
|
|
203
|
+
>
|
|
204
|
+
EUR
|
|
205
|
+
</Button>
|
|
206
|
+
</Flex>
|
|
207
|
+
|
|
208
|
+
{/* Amount Input - styled like AmountInput */}
|
|
209
|
+
<Box
|
|
210
|
+
display="flex"
|
|
211
|
+
flexDirection="column"
|
|
212
|
+
alignItems="center"
|
|
213
|
+
padding="25.5px"
|
|
214
|
+
>
|
|
215
|
+
<Input
|
|
216
|
+
variant="text"
|
|
217
|
+
inputMode="decimal"
|
|
218
|
+
placeholder={`${fiatPrefix}100`}
|
|
219
|
+
value={displayValue}
|
|
220
|
+
onChange={handleAmountChange}
|
|
221
|
+
marginY="8px"
|
|
222
|
+
/>
|
|
223
|
+
{/* Crypto equivalent */}
|
|
224
|
+
<Text fontSize="md" color="fg.muted" mt={2}>
|
|
225
|
+
{quotesLoading ? (
|
|
226
|
+
<Skeleton height="16px" width="120px" />
|
|
227
|
+
) : selectedQuote ? (
|
|
228
|
+
`Estimated amount: ${selectedQuote.destinationAmount?.toString()} ${destinationSymbol}`
|
|
229
|
+
) : (
|
|
230
|
+
"Enter amount to see quote"
|
|
231
|
+
)}
|
|
232
|
+
</Text>
|
|
233
|
+
</Box>
|
|
234
|
+
|
|
235
|
+
{/* Error */}
|
|
236
|
+
{!!quotesError && (
|
|
237
|
+
<Text color="error" fontSize="sm" textAlign="center">
|
|
238
|
+
{quotesError}
|
|
239
|
+
</Text>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{/* Provider Row */}
|
|
243
|
+
{isInitialLoading ? (
|
|
244
|
+
<Flex
|
|
245
|
+
p={3}
|
|
246
|
+
borderRadius="card"
|
|
247
|
+
border="1px solid"
|
|
248
|
+
borderColor="border"
|
|
249
|
+
align="center"
|
|
250
|
+
justify="center"
|
|
251
|
+
gap={2}
|
|
252
|
+
>
|
|
253
|
+
<Spinner size="sm" color="fg.muted" />
|
|
254
|
+
<Text fontSize="sm" color="fg.muted">
|
|
255
|
+
Finding best rates...
|
|
256
|
+
</Text>
|
|
257
|
+
</Flex>
|
|
258
|
+
) : (
|
|
259
|
+
<Flex
|
|
260
|
+
p={3}
|
|
261
|
+
borderRadius="card"
|
|
262
|
+
border="1px solid"
|
|
263
|
+
borderColor="border"
|
|
264
|
+
cursor="pointer"
|
|
265
|
+
onClick={() => setShowProviderSelect(true)}
|
|
266
|
+
align="center"
|
|
267
|
+
justify="space-between"
|
|
268
|
+
gap={2}
|
|
269
|
+
>
|
|
270
|
+
<Text fontSize="sm" color="fg.muted">
|
|
271
|
+
Provider
|
|
272
|
+
</Text>
|
|
273
|
+
|
|
274
|
+
<Flex align="center" gap={2}>
|
|
275
|
+
{quotesLoading ? (
|
|
276
|
+
<Skeleton height="20px" width="100px" />
|
|
277
|
+
) : selectedQuote ? (
|
|
278
|
+
<>
|
|
279
|
+
<Center
|
|
280
|
+
boxSize="32px"
|
|
281
|
+
borderRadius="card"
|
|
282
|
+
bg="bg.subtle"
|
|
283
|
+
overflow="hidden"
|
|
284
|
+
flexShrink={0}
|
|
285
|
+
>
|
|
286
|
+
<Image
|
|
287
|
+
src={getProviderIcon(
|
|
288
|
+
selectedQuote.serviceProvider,
|
|
289
|
+
)}
|
|
290
|
+
alt={selectedQuote.serviceProvider}
|
|
291
|
+
boxSize="32px"
|
|
292
|
+
objectFit="contain"
|
|
293
|
+
onError={(e) => {
|
|
294
|
+
(
|
|
295
|
+
e.target as HTMLImageElement
|
|
296
|
+
).style.display = "none";
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
</Center>
|
|
300
|
+
<Flex direction="column" align="end">
|
|
301
|
+
<Text
|
|
302
|
+
fontSize="sm"
|
|
303
|
+
fontWeight="semibold"
|
|
304
|
+
color="fg"
|
|
305
|
+
>
|
|
306
|
+
{formatProviderName(
|
|
307
|
+
selectedQuote.serviceProvider,
|
|
308
|
+
)}
|
|
309
|
+
</Text>
|
|
310
|
+
{isBestRate && (
|
|
311
|
+
<Text
|
|
312
|
+
fontSize="xs"
|
|
313
|
+
color="green.600"
|
|
314
|
+
fontWeight="medium"
|
|
315
|
+
>
|
|
316
|
+
Best price
|
|
317
|
+
</Text>
|
|
318
|
+
)}
|
|
319
|
+
{selectedQuote.lowKyc && (
|
|
320
|
+
<Text
|
|
321
|
+
fontSize="xs"
|
|
322
|
+
color="blue.600"
|
|
323
|
+
fontWeight="medium"
|
|
324
|
+
>
|
|
325
|
+
Low KYC
|
|
326
|
+
</Text>
|
|
327
|
+
)}
|
|
328
|
+
</Flex>
|
|
329
|
+
</>
|
|
330
|
+
) : (
|
|
331
|
+
<Text fontSize="sm" color="fg.subtle">
|
|
332
|
+
Select provider
|
|
333
|
+
</Text>
|
|
334
|
+
)}
|
|
335
|
+
<Icon as={ChevronRight} color="fg.muted" boxSize={5} />
|
|
336
|
+
</Flex>
|
|
337
|
+
</Flex>
|
|
338
|
+
)}
|
|
339
|
+
|
|
340
|
+
{/* Continue Button */}
|
|
341
|
+
<Button
|
|
342
|
+
onClick={onContinue}
|
|
343
|
+
disabled={!selectedQuote || parseFloat(fiatAmount) < 10}
|
|
344
|
+
width="100%"
|
|
345
|
+
>
|
|
346
|
+
Continue
|
|
347
|
+
</Button>
|
|
348
|
+
</BodyWrapper>
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
export default ChooseAmountStep;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Spinner, Text, Flex, Image, Box, Center } from "@chakra-ui/react";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { BodyWrapper } from "../../ui/styled";
|
|
4
|
+
import { Button } from "../../ui";
|
|
5
|
+
import { formatProviderName, useMeldTransaction } from "@/util/meld-hooks";
|
|
6
|
+
import { CircleTimer } from "@/components/CircleTimer";
|
|
7
|
+
import type { MeldQuote, MeldTransactionStatus } from "@/types";
|
|
8
|
+
import FailIcon from "@/assets/fail.svg";
|
|
9
|
+
import { useSmartAccountAddress } from "@/util/enso-hooks";
|
|
10
|
+
|
|
11
|
+
interface OpenWidgetStepProps {
|
|
12
|
+
selectedQuote: MeldQuote;
|
|
13
|
+
isCreatingSession: boolean;
|
|
14
|
+
widgetUrl: string | null;
|
|
15
|
+
sessionId: string | null;
|
|
16
|
+
onCreateSession: () => void;
|
|
17
|
+
onOpenWidget: () => void;
|
|
18
|
+
isTransferDetected: boolean;
|
|
19
|
+
detectedAmount: string | null;
|
|
20
|
+
error?: string | null;
|
|
21
|
+
}
|
|
22
|
+
// Use after implementing Meld webhooks if needed
|
|
23
|
+
function getStatusMessage(status: MeldTransactionStatus | undefined): string {
|
|
24
|
+
switch (status) {
|
|
25
|
+
case "PENDING_CREATED":
|
|
26
|
+
case "PENDING":
|
|
27
|
+
return "Waiting for payment...";
|
|
28
|
+
case "PROCESSING":
|
|
29
|
+
return "Processing payment...";
|
|
30
|
+
case "AUTHORIZED":
|
|
31
|
+
return "Payment authorized...";
|
|
32
|
+
case "TWO_FA_REQUIRED":
|
|
33
|
+
return "Two-factor authentication required...";
|
|
34
|
+
case "TWO_FA_PROVIDED":
|
|
35
|
+
return "Verifying authentication...";
|
|
36
|
+
case "SETTLING":
|
|
37
|
+
return "Payment confirmed, crypto being sent...";
|
|
38
|
+
case "SETTLED":
|
|
39
|
+
return "Payment complete!";
|
|
40
|
+
case "DECLINED":
|
|
41
|
+
return "Payment was declined.";
|
|
42
|
+
case "CANCELLED":
|
|
43
|
+
return "Payment was cancelled.";
|
|
44
|
+
case "FAILED":
|
|
45
|
+
case "ERROR":
|
|
46
|
+
return "Payment failed.";
|
|
47
|
+
case "REFUNDED":
|
|
48
|
+
return "Payment was refunded.";
|
|
49
|
+
case "VOIDED":
|
|
50
|
+
return "Payment was voided.";
|
|
51
|
+
case "AUTHORIZATION_EXPIRED":
|
|
52
|
+
return "Authorization expired.";
|
|
53
|
+
default:
|
|
54
|
+
return "Waiting for payment...";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const OpenWidgetStep = ({
|
|
59
|
+
selectedQuote,
|
|
60
|
+
isCreatingSession,
|
|
61
|
+
widgetUrl,
|
|
62
|
+
sessionId,
|
|
63
|
+
onCreateSession,
|
|
64
|
+
onOpenWidget,
|
|
65
|
+
isTransferDetected,
|
|
66
|
+
detectedAmount,
|
|
67
|
+
error,
|
|
68
|
+
}: OpenWidgetStepProps) => {
|
|
69
|
+
const openedWidgetUrlRef = useRef<string | null>(null);
|
|
70
|
+
const [isTimerFinished, setIsTimerFinished] = useState(false);
|
|
71
|
+
const { smartAccountAddress } = useSmartAccountAddress();
|
|
72
|
+
|
|
73
|
+
// Track payment status via MELD for informational UI only.
|
|
74
|
+
const { data: transaction } = useMeldTransaction(sessionId);
|
|
75
|
+
const txStatus = transaction?.status;
|
|
76
|
+
|
|
77
|
+
// Auto-create session on mount
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!widgetUrl && !isCreatingSession && !error) {
|
|
80
|
+
onCreateSession();
|
|
81
|
+
}
|
|
82
|
+
}, [widgetUrl, isCreatingSession, error, onCreateSession]);
|
|
83
|
+
|
|
84
|
+
// Auto-open each newly created widget URL once.
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!widgetUrl) return;
|
|
87
|
+
if (openedWidgetUrlRef.current === widgetUrl) return;
|
|
88
|
+
openedWidgetUrlRef.current = widgetUrl;
|
|
89
|
+
onOpenWidget();
|
|
90
|
+
}, [widgetUrl, onOpenWidget]);
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!sessionId) {
|
|
94
|
+
setIsTimerFinished(false);
|
|
95
|
+
}
|
|
96
|
+
}, [sessionId]);
|
|
97
|
+
|
|
98
|
+
const fiatPrefix = selectedQuote.sourceCurrencyCode === "EUR" ? "€" : "$";
|
|
99
|
+
|
|
100
|
+
// Creating session
|
|
101
|
+
if (isCreatingSession && !widgetUrl) {
|
|
102
|
+
return (
|
|
103
|
+
<BodyWrapper>
|
|
104
|
+
<Center>
|
|
105
|
+
<Spinner size="lg" color="primary" />
|
|
106
|
+
</Center>
|
|
107
|
+
</BodyWrapper>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Error before session created
|
|
112
|
+
if (error && !widgetUrl) {
|
|
113
|
+
return (
|
|
114
|
+
<BodyWrapper>
|
|
115
|
+
<Flex direction="column" gap={4} align="center">
|
|
116
|
+
<Box
|
|
117
|
+
display="flex"
|
|
118
|
+
flexDirection="column"
|
|
119
|
+
alignItems="center"
|
|
120
|
+
>
|
|
121
|
+
<Image
|
|
122
|
+
src={FailIcon}
|
|
123
|
+
boxShadow="0px 0px 20px var(--chakra-colors-error)"
|
|
124
|
+
borderRadius="90%"
|
|
125
|
+
width="58px"
|
|
126
|
+
height="58px"
|
|
127
|
+
/>
|
|
128
|
+
</Box>
|
|
129
|
+
<Text color="error" textAlign="center">
|
|
130
|
+
{error}
|
|
131
|
+
</Text>
|
|
132
|
+
</Flex>
|
|
133
|
+
</BodyWrapper>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<BodyWrapper>
|
|
139
|
+
<Flex direction="column" gap={4} align="center">
|
|
140
|
+
<CircleTimer
|
|
141
|
+
start={!!sessionId}
|
|
142
|
+
duration={300}
|
|
143
|
+
onFinish={() => setIsTimerFinished(true)}
|
|
144
|
+
/>
|
|
145
|
+
<Text textAlign="center" color="fg" fontWeight="semibold">
|
|
146
|
+
Complete your purchase of{" "}
|
|
147
|
+
<Text as="span" fontWeight="bold">
|
|
148
|
+
{fiatPrefix}
|
|
149
|
+
{selectedQuote.sourceAmount}
|
|
150
|
+
</Text>{" "}
|
|
151
|
+
via {formatProviderName(selectedQuote.serviceProvider)}
|
|
152
|
+
</Text>
|
|
153
|
+
{/*<Text fontSize="sm" color="fg.muted" textAlign="center">*/}
|
|
154
|
+
{/* {getStatusMessage(txStatus)}*/}
|
|
155
|
+
{/*</Text>*/}
|
|
156
|
+
<Text fontSize="sm" color="fg.muted" textAlign="center" mb={-2}>
|
|
157
|
+
Tracking incoming USDC transfer to your smart account:{" "}
|
|
158
|
+
</Text>{" "}
|
|
159
|
+
<Text fontSize="sm" fontWeight={500}>
|
|
160
|
+
{smartAccountAddress}
|
|
161
|
+
</Text>
|
|
162
|
+
{isTransferDetected && detectedAmount && (
|
|
163
|
+
<Text fontSize="sm" color="success" textAlign="center">
|
|
164
|
+
Detected {detectedAmount} USDC. Redirecting to Smart
|
|
165
|
+
Account quote...
|
|
166
|
+
</Text>
|
|
167
|
+
)}
|
|
168
|
+
{isTimerFinished && !isTransferDetected && (
|
|
169
|
+
<Text fontSize="sm" color="fg.muted" textAlign="center">
|
|
170
|
+
Believe transfer was not detected? Withdrawn funds can
|
|
171
|
+
be used for transacting any time later using Smart
|
|
172
|
+
Wallet as source.
|
|
173
|
+
</Text>
|
|
174
|
+
)}
|
|
175
|
+
{error && widgetUrl && (
|
|
176
|
+
<Text color="error" textAlign="center" fontSize="sm">
|
|
177
|
+
{error}
|
|
178
|
+
</Text>
|
|
179
|
+
)}
|
|
180
|
+
<Flex direction="column" gap={2} width="100%">
|
|
181
|
+
<Button
|
|
182
|
+
onClick={onOpenWidget}
|
|
183
|
+
disabled={!widgetUrl || isCreatingSession}
|
|
184
|
+
>
|
|
185
|
+
{widgetUrl ? "Open Provider" : "Preparing Provider..."}
|
|
186
|
+
</Button>
|
|
187
|
+
</Flex>
|
|
188
|
+
</Flex>
|
|
189
|
+
</BodyWrapper>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export default OpenWidgetStep;
|