@ensofinance/checkout-widget 0.1.8 → 0.1.9

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 (61) hide show
  1. package/dist/checkout-widget.es.js +24133 -25422
  2. package/dist/checkout-widget.umd.js +59 -65
  3. package/dist/index.d.ts +1 -3
  4. package/package.json +2 -2
  5. package/src/assets/usdc.webp +0 -0
  6. package/src/assets/usdt.webp +0 -0
  7. package/src/components/AmountInput.tsx +25 -41
  8. package/src/components/ChakraProvider.tsx +13 -36
  9. package/src/components/Checkout.tsx +0 -3
  10. package/src/components/CurrencySwapDisplay.tsx +22 -59
  11. package/src/components/DepositProcessing.tsx +1 -1
  12. package/src/components/ExchangeConfirmSecurity.tsx +1 -1
  13. package/src/components/QuoteParameters.tsx +1 -1
  14. package/src/components/TransactionDetailRow.tsx +2 -2
  15. package/src/components/cards/ExchangeCard.tsx +1 -1
  16. package/src/components/cards/OptionCard.tsx +1 -2
  17. package/src/components/cards/WalletCard.tsx +1 -1
  18. package/src/components/modal.tsx +3 -3
  19. package/src/components/steps/ExchangeFlow.tsx +1404 -231
  20. package/src/components/steps/FlowSelector.tsx +60 -117
  21. package/src/components/steps/WalletFlow/WalletAmountStep.tsx +2 -2
  22. package/src/components/steps/WalletFlow/WalletConfirmStep.tsx +51 -92
  23. package/src/components/steps/WalletFlow/WalletFlow.tsx +16 -17
  24. package/src/components/steps/WalletFlow/WalletQuoteStep.tsx +2 -2
  25. package/src/components/steps/WalletFlow/WalletTokenStep.tsx +4 -6
  26. package/src/components/ui/index.tsx +6 -23
  27. package/src/components/ui/toaster.tsx +1 -2
  28. package/src/types/index.ts +0 -97
  29. package/src/util/constants.tsx +0 -27
  30. package/src/util/enso-hooks.tsx +61 -75
  31. package/dist/checkout-widget.es.js.map +0 -1
  32. package/dist/checkout-widget.umd.js.map +0 -1
  33. package/src/assets/providers/alchemypay.svg +0 -21
  34. package/src/assets/providers/banxa.svg +0 -21
  35. package/src/assets/providers/binanceconnect.svg +0 -14
  36. package/src/assets/providers/kryptonim.svg +0 -6
  37. package/src/assets/providers/mercuryo.svg +0 -21
  38. package/src/assets/providers/moonpay.svg +0 -14
  39. package/src/assets/providers/stripe.svg +0 -16
  40. package/src/assets/providers/swapped.svg +0 -1
  41. package/src/assets/providers/topper.svg +0 -14
  42. package/src/assets/providers/transak.svg +0 -21
  43. package/src/assets/providers/unlimit.svg +0 -21
  44. package/src/components/steps/CardBuyFlow/CardBuyFlow.tsx +0 -412
  45. package/src/components/steps/CardBuyFlow/ChooseAmountStep.tsx +0 -352
  46. package/src/components/steps/CardBuyFlow/OpenWidgetStep.tsx +0 -193
  47. package/src/components/steps/SmartAccountFlow.tsx +0 -372
  48. package/src/components/steps/shared/ChooseAmountStep.tsx +0 -325
  49. package/src/components/steps/shared/SignUserOpStep.tsx +0 -117
  50. package/src/components/steps/shared/TrackUserOpStep.tsx +0 -625
  51. package/src/components/steps/shared/exchangeIntegration.ts +0 -19
  52. package/src/components/steps/shared/types.ts +0 -22
  53. package/src/components/ui/transitions.tsx +0 -16
  54. package/src/enso-api/model/bridgeTransactionResponse.ts +0 -37
  55. package/src/enso-api/model/bridgeTransactionResponseStatus.ts +0 -25
  56. package/src/enso-api/model/ensoEvent.ts +0 -30
  57. package/src/enso-api/model/ensoMetadata.ts +0 -23
  58. package/src/enso-api/model/layerZeroControllerCheckBridgeTransactionParams.ts +0 -21
  59. package/src/enso-api/model/layerZeroMessageStatus.ts +0 -39
  60. package/src/enso-api/model/refundDetails.ts +0 -21
  61. package/src/util/meld-hooks.tsx +0 -533
@@ -1,412 +0,0 @@
1
- import { Center, Box, Icon, Text } from "@chakra-ui/react";
2
- import { ChevronLeft, X, XCircle } from "lucide-react";
3
- import {
4
- useContext,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- useCallback,
10
- } from "react";
11
- import { useAccount } from "wagmi";
12
- import { BodyWrapper, HeaderTitle, HeaderWrapper } from "../../ui/styled";
13
- import { IconButton } from "../../ui";
14
- import { CheckoutContext } from "../../Checkout";
15
- import Modal from "../../modal";
16
- import { useAppStore } from "@/store";
17
- import { denormalizeValue } from "@/util";
18
- import { getUsdcAddress } from "@/util/constants";
19
- import { useSmartAccountAddress } from "@/util/enso-hooks";
20
- import {
21
- useMeldQuotes,
22
- useMeldCardBuyFlow,
23
- useCountryCode,
24
- getMeldDestinationCurrencyCode,
25
- isMeldCardBuySupportedChain,
26
- useMeldSupportedCrypto,
27
- useIncomingUsdcTransferDetector,
28
- } from "@/util/meld-hooks";
29
- import type { MeldQuote } from "@/types";
30
-
31
- import ChooseAmountStep from "./ChooseAmountStep";
32
- import OpenWidgetStep from "./OpenWidgetStep";
33
- import { AnimatedStep } from "../../ui/transitions";
34
-
35
- interface CardBuyFlowProps {
36
- setFlow: (flow: string) => void;
37
- initialStep?: CardBuyStep;
38
- onCardBuyDepositDetected: () => void;
39
- }
40
-
41
- // Card buy flow steps
42
- export enum CardBuyStep {
43
- ChooseAmount = 0,
44
- OpenWidget = 1,
45
- }
46
-
47
- export const STEP_TITLES: Record<CardBuyStep, string> = {
48
- [CardBuyStep.ChooseAmount]: "Buy with Card",
49
- [CardBuyStep.OpenWidget]: "Complete Purchase",
50
- };
51
-
52
- const cardPreviousStep: Partial<Record<CardBuyStep, CardBuyStep>> = {
53
- [CardBuyStep.OpenWidget]: CardBuyStep.ChooseAmount,
54
- };
55
-
56
- const CardBuyFlow = ({
57
- setFlow,
58
- initialStep = CardBuyStep.ChooseAmount,
59
- onCardBuyDepositDetected,
60
- }: CardBuyFlowProps) => {
61
- const { handleClose } = useContext(CheckoutContext);
62
- const { address } = useAccount();
63
-
64
- // Store
65
- const chainIdOut = useAppStore((s) => s.chainIdOut);
66
- const setAmountIn = useAppStore((s) => s.setAmountIn);
67
- const setTokenIn = useAppStore((s) => s.setTokenIn);
68
- const setChainIdIn = useAppStore((s) => s.setChainIdIn);
69
- const setSelectedIntegration = useAppStore((s) => s.setSelectedIntegration);
70
- const handoffTriggeredRef = useRef(false);
71
-
72
- // Set integration on mount so useRouteData picks "checkout" strategy,
73
- // and clean up on unmount (same pattern as ExchangeFlow).
74
- useEffect(() => {
75
- setSelectedIntegration({ id: "meld", type: "meld", name: "Card Buy" });
76
- return () => setSelectedIntegration(null);
77
- }, [setSelectedIntegration]);
78
-
79
- // Local state
80
- const [currentStep, setCurrentStep] = useState<CardBuyStep>(initialStep);
81
- const [fiatCurrency, setFiatCurrency] = useState<"USD" | "EUR">("USD");
82
- const [fiatAmount, setFiatAmount] = useState<string>("100");
83
- const [selectedQuote, setSelectedQuote] = useState<MeldQuote | null>(null);
84
- const [error, setError] = useState<string | null>(null);
85
-
86
- // Smart account address - use recipient override if provided
87
- const { smartAccountAddress } = useSmartAccountAddress(chainIdOut);
88
-
89
- // Country code detection
90
- const { countryCode, isLoading: countryCodeLoading } = useCountryCode();
91
- const { data: supportedCryptos = [], isLoading: supportedCryptosLoading } =
92
- useMeldSupportedCrypto({
93
- countryCode,
94
- enabled: !!chainIdOut,
95
- });
96
-
97
- const destinationSymbol = "USDC";
98
-
99
- const destinationCryptoCode = useMemo(() => {
100
- if (!chainIdOut) return null;
101
- return getMeldDestinationCurrencyCode({
102
- symbol: destinationSymbol,
103
- chainId: chainIdOut,
104
- supportedCryptos,
105
- });
106
- }, [destinationSymbol, chainIdOut, supportedCryptos]);
107
-
108
- const isCardBuySupported =
109
- !!chainIdOut &&
110
- isMeldCardBuySupportedChain(chainIdOut, supportedCryptos);
111
-
112
- // Fetch quotes
113
- const {
114
- data: quotes,
115
- isLoading: quotesLoading,
116
- error: quotesError,
117
- } = useMeldQuotes({
118
- sourceCurrency: fiatCurrency,
119
- destinationCurrency: destinationCryptoCode || "",
120
- amount: parseFloat(fiatAmount) || 0,
121
- countryCode: countryCode || "",
122
- walletAddress: smartAccountAddress || "",
123
- enabled:
124
- !!countryCode &&
125
- !!destinationCryptoCode &&
126
- parseFloat(fiatAmount) > 0 &&
127
- !!smartAccountAddress,
128
- });
129
-
130
- // Select best quote automatically
131
- useEffect(() => {
132
- if (!quotes || quotes.length === 0) {
133
- if (selectedQuote) setSelectedQuote(null);
134
- return;
135
- }
136
-
137
- // Default to best quote
138
- if (!selectedQuote) {
139
- setSelectedQuote(quotes[0]);
140
- return;
141
- }
142
-
143
- // Keep the same provider selected if still available, but refresh the
144
- // quote object (rates/fees can update on refetch).
145
- const matching = quotes.find(
146
- (q) => q.serviceProvider === selectedQuote.serviceProvider,
147
- );
148
- if (!matching) {
149
- setSelectedQuote(quotes[0]);
150
- return;
151
- }
152
-
153
- const didChange =
154
- matching.destinationAmount !== selectedQuote.destinationAmount ||
155
- matching.totalFee !== selectedQuote.totalFee ||
156
- matching.exchangeRate !== selectedQuote.exchangeRate;
157
- if (didChange) setSelectedQuote(matching);
158
- }, [quotes, selectedQuote]);
159
-
160
- // MELD flow state
161
- const meldFlow = useMeldCardBuyFlow();
162
-
163
- const shouldTrackIncomingTransfer =
164
- currentStep === CardBuyStep.OpenWidget &&
165
- !!chainIdOut &&
166
- !!smartAccountAddress;
167
- const {
168
- detectedAmount,
169
- isDetected,
170
- reset: resetIncomingDetector,
171
- } = useIncomingUsdcTransferDetector({
172
- chainId: chainIdOut,
173
- walletAddress: smartAccountAddress,
174
- enabled: shouldTrackIncomingTransfer,
175
- thresholdUsdc: "0.01",
176
- });
177
-
178
- const resetToChooseAmount = useCallback(
179
- (clearQuote = false) => {
180
- meldFlow.reset();
181
- resetIncomingDetector();
182
- handoffTriggeredRef.current = false;
183
- setError(null);
184
- if (clearQuote) {
185
- setSelectedQuote(null);
186
- }
187
- setCurrentStep(CardBuyStep.ChooseAmount);
188
- },
189
- [meldFlow, resetIncomingDetector],
190
- );
191
-
192
- // Navigation
193
- const goBack = useCallback(() => {
194
- if (currentStep === CardBuyStep.ChooseAmount) {
195
- setFlow("");
196
- return;
197
- }
198
- const previousStep = cardPreviousStep[currentStep];
199
- if (previousStep !== undefined) {
200
- resetToChooseAmount(false);
201
- return;
202
- }
203
- setFlow("");
204
- }, [currentStep, setFlow, resetToChooseAmount]);
205
-
206
- const goToOpenWidget = useCallback(() => {
207
- if (!selectedQuote) return;
208
- setError(null);
209
- handoffTriggeredRef.current = false;
210
- resetIncomingDetector();
211
- setCurrentStep(CardBuyStep.OpenWidget);
212
- }, [selectedQuote, resetIncomingDetector]);
213
-
214
- // Start MELD session
215
- const handleStartSession = useCallback(async () => {
216
- if (
217
- !selectedQuote ||
218
- !smartAccountAddress ||
219
- !destinationCryptoCode ||
220
- !countryCode
221
- )
222
- return;
223
-
224
- try {
225
- await meldFlow.startSession({
226
- countryCode: countryCode,
227
- sourceCurrencyCode: fiatCurrency,
228
- destinationCurrencyCode: destinationCryptoCode,
229
- sourceAmount: selectedQuote.sourceAmount,
230
- walletAddress: smartAccountAddress,
231
- serviceProvider: selectedQuote.serviceProvider,
232
- paymentMethodType: "CREDIT_DEBIT_CARD",
233
- externalCustomerId: address,
234
- externalSessionId: `enso-${Date.now()}`,
235
- });
236
- } catch (err: any) {
237
- console.error(err);
238
- setError(err.message || "Failed to create session");
239
- }
240
- }, [
241
- selectedQuote,
242
- smartAccountAddress,
243
- destinationCryptoCode,
244
- countryCode,
245
- fiatCurrency,
246
- address,
247
- meldFlow,
248
- ]);
249
-
250
- const handleOpenWidget = useCallback(() => {
251
- meldFlow.openWidget();
252
- }, [meldFlow]);
253
-
254
- // Funds arrival is the source of truth for handing off into Smart Account flow.
255
- useEffect(() => {
256
- if (currentStep !== CardBuyStep.OpenWidget) return;
257
- if (!isDetected || !detectedAmount || !chainIdOut) return;
258
- if (handoffTriggeredRef.current) return;
259
-
260
- try {
261
- handoffTriggeredRef.current = true;
262
- setError(null);
263
- resetIncomingDetector();
264
-
265
- // Store the detected amount and token before switching flow
266
- setAmountIn(denormalizeValue(detectedAmount, 6));
267
- setTokenIn(getUsdcAddress(chainIdOut));
268
- setChainIdIn(chainIdOut);
269
-
270
- onCardBuyDepositDetected();
271
- } catch (err: any) {
272
- handoffTriggeredRef.current = false;
273
- setError(
274
- err.message || "Failed to redirect to Smart Account quote",
275
- );
276
- }
277
- }, [
278
- currentStep,
279
- isDetected,
280
- detectedAmount,
281
- chainIdOut,
282
- setAmountIn,
283
- setTokenIn,
284
- setChainIdIn,
285
- onCardBuyDepositDetected,
286
- resetIncomingDetector,
287
- ]);
288
-
289
- // Check if chain is supported
290
- if (!chainIdOut || (!supportedCryptosLoading && !isCardBuySupported)) {
291
- return (
292
- <>
293
- <Modal.Header>
294
- <HeaderWrapper>
295
- <IconButton onClick={() => setFlow("")} width="40px">
296
- <Icon
297
- as={ChevronLeft}
298
- color="fg.muted"
299
- width="16px"
300
- height="16px"
301
- />
302
- </IconButton>
303
- <Box>
304
- <HeaderTitle>Buy with Card</HeaderTitle>
305
- </Box>
306
- {handleClose && (
307
- <IconButton onClick={handleClose} width="40px">
308
- <Icon
309
- as={X}
310
- color="fg.muted"
311
- width="16px"
312
- height="16px"
313
- />
314
- </IconButton>
315
- )}
316
- </HeaderWrapper>
317
- </Modal.Header>
318
- <Modal.Body>
319
- <BodyWrapper>
320
- <Center py={8} flexDirection="column" gap={4}>
321
- <Icon as={XCircle} color="error" boxSize={12} />
322
- <Text color="fg.muted" textAlign="center">
323
- Card purchases are not supported for this chain.
324
- </Text>
325
- </Center>
326
- </BodyWrapper>
327
- </Modal.Body>
328
- </>
329
- );
330
- }
331
-
332
- // Render step content
333
- const renderStepContent = () => {
334
- switch (currentStep) {
335
- case CardBuyStep.ChooseAmount:
336
- return (
337
- <ChooseAmountStep
338
- fiatCurrency={fiatCurrency}
339
- setFiatCurrency={setFiatCurrency}
340
- fiatAmount={fiatAmount}
341
- setFiatAmount={setFiatAmount}
342
- quotes={quotes}
343
- quotesLoading={quotesLoading || countryCodeLoading}
344
- quotesError={
345
- quotesError
346
- ? quotesError.message || String(quotesError)
347
- : null
348
- }
349
- selectedQuote={selectedQuote}
350
- setSelectedQuote={setSelectedQuote}
351
- destinationSymbol={destinationSymbol}
352
- onContinue={goToOpenWidget}
353
- />
354
- );
355
-
356
- case CardBuyStep.OpenWidget:
357
- return (
358
- <OpenWidgetStep
359
- selectedQuote={selectedQuote!}
360
- isCreatingSession={meldFlow.isCreatingSession}
361
- widgetUrl={meldFlow.widgetUrl}
362
- sessionId={meldFlow.sessionId}
363
- onCreateSession={handleStartSession}
364
- onOpenWidget={handleOpenWidget}
365
- isTransferDetected={isDetected}
366
- detectedAmount={detectedAmount}
367
- error={error || meldFlow.sessionError?.message}
368
- />
369
- );
370
-
371
- default:
372
- return null;
373
- }
374
- };
375
-
376
- return (
377
- <>
378
- <Modal.Header>
379
- <HeaderWrapper>
380
- <IconButton onClick={goBack} width="40px">
381
- <Icon
382
- as={ChevronLeft}
383
- color="fg.muted"
384
- width="16px"
385
- height="16px"
386
- />
387
- </IconButton>
388
- <Box>
389
- <HeaderTitle>{STEP_TITLES[currentStep]}</HeaderTitle>
390
- </Box>
391
- {handleClose && (
392
- <IconButton onClick={handleClose} width="40px">
393
- <Icon
394
- as={X}
395
- color="fg.muted"
396
- width="16px"
397
- height="16px"
398
- />
399
- </IconButton>
400
- )}
401
- </HeaderWrapper>
402
- </Modal.Header>
403
- <Modal.Body>
404
- <AnimatedStep key={currentStep}>
405
- {renderStepContent()}
406
- </AnimatedStep>
407
- </Modal.Body>
408
- </>
409
- );
410
- };
411
-
412
- export default CardBuyFlow;