@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,533 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { useCallback, useState, useEffect, useMemo } from "react";
|
|
3
|
+
import { useReadContract } from "wagmi";
|
|
4
|
+
import { erc20Abi, formatUnits, isAddress, parseUnits } from "viem";
|
|
5
|
+
import type {
|
|
6
|
+
MeldQuote,
|
|
7
|
+
MeldQuotesResponse,
|
|
8
|
+
MeldSessionRequest,
|
|
9
|
+
MeldSessionResponse,
|
|
10
|
+
MeldSupportedCrypto,
|
|
11
|
+
MeldSupportedCryptoResponse,
|
|
12
|
+
MeldTransaction,
|
|
13
|
+
MeldTransactionStatus,
|
|
14
|
+
} from "@/types";
|
|
15
|
+
import { CHECKOUT_BFF_URL, USDC_ADDRESS_BY_CHAIN_ID } from "@/util/constants";
|
|
16
|
+
import transakIcon from "@/assets/providers/transak.svg";
|
|
17
|
+
import moonpayIcon from "@/assets/providers/moonpay.svg";
|
|
18
|
+
import banxaIcon from "@/assets/providers/banxa.svg";
|
|
19
|
+
import mercuryoIcon from "@/assets/providers/mercuryo.svg";
|
|
20
|
+
import kryptonimIcon from "@/assets/providers/kryptonim.svg";
|
|
21
|
+
import alchemypayIcon from "@/assets/providers/alchemypay.svg";
|
|
22
|
+
import unlimitIcon from "@/assets/providers/unlimit.svg";
|
|
23
|
+
import topperIcon from "@/assets/providers/topper.svg";
|
|
24
|
+
import stripeIcon from "@/assets/providers/stripe.svg";
|
|
25
|
+
import binanceconnectIcon from "@/assets/providers/binanceconnect.svg";
|
|
26
|
+
|
|
27
|
+
const MELD_BFF_URL = `${CHECKOUT_BFF_URL}/meld`;
|
|
28
|
+
const USDC_DECIMALS = 6;
|
|
29
|
+
|
|
30
|
+
function getMeldQuoteScore(quote: MeldQuote): number {
|
|
31
|
+
if (typeof quote.rampIntelligence?.rampScore === "number") {
|
|
32
|
+
return quote.rampIntelligence.rampScore;
|
|
33
|
+
}
|
|
34
|
+
if (typeof quote.customerScore === "number") {
|
|
35
|
+
return quote.customerScore;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Fetch MELD-supported destination currencies for a given country.
|
|
42
|
+
*/
|
|
43
|
+
export function useMeldSupportedCrypto(params?: {
|
|
44
|
+
countryCode?: string | null;
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
}) {
|
|
47
|
+
const { countryCode, enabled = true } = params ?? {};
|
|
48
|
+
|
|
49
|
+
return useQuery({
|
|
50
|
+
queryKey: ["meld-supported-crypto", countryCode],
|
|
51
|
+
queryFn: async (): Promise<MeldSupportedCrypto[]> => {
|
|
52
|
+
const searchParams = new URLSearchParams({
|
|
53
|
+
countryCode,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const res = await fetch(
|
|
57
|
+
`${MELD_BFF_URL}/supported-crypto?${searchParams.toString()}`,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const error = await res.json().catch(() => ({
|
|
62
|
+
error: "Failed to fetch supported crypto",
|
|
63
|
+
}));
|
|
64
|
+
throw new Error(
|
|
65
|
+
error.error ||
|
|
66
|
+
error.message ||
|
|
67
|
+
"Failed to fetch supported crypto",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const data:
|
|
72
|
+
| MeldSupportedCryptoResponse
|
|
73
|
+
| { currencies?: MeldSupportedCrypto[] }
|
|
74
|
+
| MeldSupportedCrypto[] = await res.json();
|
|
75
|
+
|
|
76
|
+
const rawCurrencies = Array.isArray(data)
|
|
77
|
+
? data
|
|
78
|
+
: Array.isArray(data.currencies)
|
|
79
|
+
? data.currencies
|
|
80
|
+
: [];
|
|
81
|
+
|
|
82
|
+
return rawCurrencies.filter((currency) => !!currency.currencyCode);
|
|
83
|
+
},
|
|
84
|
+
enabled,
|
|
85
|
+
staleTime: 5 * 60 * 1000,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve MELD destination currency code for symbol+chain from supported metadata.
|
|
91
|
+
*/
|
|
92
|
+
export function getMeldDestinationCurrencyCode(params: {
|
|
93
|
+
symbol: string;
|
|
94
|
+
chainId: number;
|
|
95
|
+
supportedCryptos: MeldSupportedCrypto[];
|
|
96
|
+
}): string | null {
|
|
97
|
+
const { symbol, chainId, supportedCryptos } = params;
|
|
98
|
+
|
|
99
|
+
const symbolUpper = symbol.toUpperCase();
|
|
100
|
+
const targetContract = USDC_ADDRESS_BY_CHAIN_ID[chainId]?.toLowerCase();
|
|
101
|
+
if (!targetContract) return null;
|
|
102
|
+
|
|
103
|
+
const symbolMatches = supportedCryptos.filter((currency) =>
|
|
104
|
+
currency.currencyCode.toUpperCase().startsWith(`${symbolUpper}_`),
|
|
105
|
+
);
|
|
106
|
+
if (symbolMatches.length === 0) return null;
|
|
107
|
+
|
|
108
|
+
const byContract = symbolMatches.find(
|
|
109
|
+
(currency) =>
|
|
110
|
+
currency.contractAddress?.toLowerCase() === targetContract,
|
|
111
|
+
);
|
|
112
|
+
if (byContract) {
|
|
113
|
+
return byContract.currencyCode;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const byChainId = symbolMatches.find(
|
|
117
|
+
(currency) => currency.chainId === chainId,
|
|
118
|
+
);
|
|
119
|
+
if (byChainId) {
|
|
120
|
+
return byChainId.currencyCode;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return symbolMatches.length === 1 ? symbolMatches[0].currencyCode : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Card-buy (fixed USDC) support check.
|
|
128
|
+
*/
|
|
129
|
+
export function isMeldCardBuySupportedChain(
|
|
130
|
+
chainId: number,
|
|
131
|
+
supportedCryptos: MeldSupportedCrypto[],
|
|
132
|
+
): boolean {
|
|
133
|
+
return (
|
|
134
|
+
!!USDC_ADDRESS_BY_CHAIN_ID[chainId] &&
|
|
135
|
+
!!getMeldDestinationCurrencyCode({
|
|
136
|
+
symbol: "USDC",
|
|
137
|
+
chainId,
|
|
138
|
+
supportedCryptos,
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Hook to fetch quotes from MELD
|
|
145
|
+
*/
|
|
146
|
+
export function useMeldQuotes(params: {
|
|
147
|
+
sourceCurrency: string;
|
|
148
|
+
destinationCurrency: string; // Should be in format SYMBOL_CHAINCODE
|
|
149
|
+
amount: number;
|
|
150
|
+
countryCode: string;
|
|
151
|
+
walletAddress: string;
|
|
152
|
+
enabled?: boolean;
|
|
153
|
+
}) {
|
|
154
|
+
const {
|
|
155
|
+
sourceCurrency,
|
|
156
|
+
destinationCurrency,
|
|
157
|
+
amount,
|
|
158
|
+
countryCode,
|
|
159
|
+
walletAddress,
|
|
160
|
+
enabled = true,
|
|
161
|
+
} = params;
|
|
162
|
+
|
|
163
|
+
return useQuery({
|
|
164
|
+
queryKey: [
|
|
165
|
+
"meld-quotes",
|
|
166
|
+
sourceCurrency,
|
|
167
|
+
destinationCurrency,
|
|
168
|
+
amount,
|
|
169
|
+
countryCode,
|
|
170
|
+
walletAddress,
|
|
171
|
+
],
|
|
172
|
+
queryFn: async (): Promise<MeldQuote[]> => {
|
|
173
|
+
const searchParams = new URLSearchParams({
|
|
174
|
+
sourceCurrencyCode: sourceCurrency,
|
|
175
|
+
destinationCurrencyCode: destinationCurrency,
|
|
176
|
+
sourceAmount: String(amount),
|
|
177
|
+
countryCode,
|
|
178
|
+
walletAddress,
|
|
179
|
+
paymentMethodType: "CREDIT_DEBIT_CARD",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const res = await fetch(
|
|
183
|
+
`${MELD_BFF_URL}/quotes?${searchParams.toString()}`,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (!res.ok) {
|
|
187
|
+
const error = await res
|
|
188
|
+
.json()
|
|
189
|
+
.catch(() => ({ error: "Failed to fetch quotes" }));
|
|
190
|
+
throw new Error(
|
|
191
|
+
error.error || error.message || "Failed to fetch quotes",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const data: MeldQuotesResponse = await res.json();
|
|
196
|
+
|
|
197
|
+
if (data.error) {
|
|
198
|
+
throw new Error(data.error);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Prefer modern MELD ramp score, fallback to legacy customerScore.
|
|
202
|
+
return (data.quotes || []).sort((a, b) => {
|
|
203
|
+
const scoreA = getMeldQuoteScore(a);
|
|
204
|
+
const scoreB = getMeldQuoteScore(b);
|
|
205
|
+
if (scoreB !== scoreA) return scoreB - scoreA;
|
|
206
|
+
return b.destinationAmount - a.destinationAmount;
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
enabled:
|
|
210
|
+
enabled &&
|
|
211
|
+
amount > 0 &&
|
|
212
|
+
!!destinationCurrency &&
|
|
213
|
+
!!countryCode &&
|
|
214
|
+
!!walletAddress,
|
|
215
|
+
staleTime: 30000, // 30 seconds
|
|
216
|
+
refetchInterval: 60000, // Refresh every minute
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Hook to create a MELD session
|
|
222
|
+
*/
|
|
223
|
+
export function useCreateMeldSession() {
|
|
224
|
+
const queryClient = useQueryClient();
|
|
225
|
+
|
|
226
|
+
return useMutation({
|
|
227
|
+
mutationFn: async (
|
|
228
|
+
params: MeldSessionRequest,
|
|
229
|
+
): Promise<MeldSessionResponse> => {
|
|
230
|
+
const res = await fetch(`${MELD_BFF_URL}/session`, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: { "Content-Type": "application/json" },
|
|
233
|
+
body: JSON.stringify(params),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
if (!res.ok) {
|
|
237
|
+
const error = await res
|
|
238
|
+
.json()
|
|
239
|
+
.catch(() => ({ error: "Failed to create session" }));
|
|
240
|
+
throw new Error(
|
|
241
|
+
error.error || error.message || "Failed to create session",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return res.json();
|
|
246
|
+
},
|
|
247
|
+
onSuccess: (data) => {
|
|
248
|
+
// Store session for tracking
|
|
249
|
+
if (data.sessionId) {
|
|
250
|
+
queryClient.setQueryData(
|
|
251
|
+
["meld-session", data.sessionId],
|
|
252
|
+
data,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const TERMINAL_MELD_STATUSES = new Set<MeldTransactionStatus>([
|
|
260
|
+
"SETTLED",
|
|
261
|
+
"REFUNDED",
|
|
262
|
+
"DECLINED",
|
|
263
|
+
"CANCELLED",
|
|
264
|
+
"FAILED",
|
|
265
|
+
"ERROR",
|
|
266
|
+
"VOIDED",
|
|
267
|
+
"AUTHORIZATION_EXPIRED",
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Hook to track a MELD transaction
|
|
272
|
+
*/
|
|
273
|
+
export function useMeldTransaction(sessionId: string | null) {
|
|
274
|
+
return useQuery({
|
|
275
|
+
queryKey: ["meld-transaction", sessionId],
|
|
276
|
+
queryFn: async (): Promise<MeldTransaction | null> => {
|
|
277
|
+
if (!sessionId) return null;
|
|
278
|
+
|
|
279
|
+
const res = await fetch(`${MELD_BFF_URL}/transaction/${sessionId}`);
|
|
280
|
+
|
|
281
|
+
if (!res.ok) {
|
|
282
|
+
if (res.status === 404) {
|
|
283
|
+
return null; // Transaction not found yet
|
|
284
|
+
}
|
|
285
|
+
const error = await res
|
|
286
|
+
.json()
|
|
287
|
+
.catch(() => ({ error: "Failed to fetch transaction" }));
|
|
288
|
+
throw new Error(
|
|
289
|
+
error.error ||
|
|
290
|
+
error.message ||
|
|
291
|
+
"Failed to fetch transaction",
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return res.json();
|
|
296
|
+
},
|
|
297
|
+
enabled: !!sessionId,
|
|
298
|
+
refetchInterval: (query) => {
|
|
299
|
+
const data = query.state.data;
|
|
300
|
+
if (data?.status && TERMINAL_MELD_STATUSES.has(data.status)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
return 3000; // Poll every 3 seconds
|
|
304
|
+
},
|
|
305
|
+
staleTime: 0, // Always refetch
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Terminal status check
|
|
311
|
+
*/
|
|
312
|
+
export function isTerminalStatus(status: MeldTransactionStatus): boolean {
|
|
313
|
+
return TERMINAL_MELD_STATUSES.has(status);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Success status check
|
|
318
|
+
*/
|
|
319
|
+
export function isSuccessStatus(status: MeldTransactionStatus): boolean {
|
|
320
|
+
return status === "SETTLED";
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Check if MELD payment has been initiated (settling or settled)
|
|
325
|
+
*/
|
|
326
|
+
export function isMeldPaymentInitiated(status: MeldTransactionStatus): boolean {
|
|
327
|
+
return status === "SETTLING" || status === "SETTLED";
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Hook for detecting user's country code via BFF /geo endpoint.
|
|
332
|
+
*/
|
|
333
|
+
export function useCountryCode() {
|
|
334
|
+
const { data, isLoading } = useQuery({
|
|
335
|
+
queryKey: ["geo-country"],
|
|
336
|
+
queryFn: async (): Promise<string | null> => {
|
|
337
|
+
try {
|
|
338
|
+
const res = await fetch(`${CHECKOUT_BFF_URL}/geo`, {
|
|
339
|
+
signal: AbortSignal.timeout(3000),
|
|
340
|
+
});
|
|
341
|
+
if (!res.ok) throw new Error("Failed to fetch country code");
|
|
342
|
+
const geo = (await res.json()) as { country?: string };
|
|
343
|
+
return geo.country || "PL";
|
|
344
|
+
} catch {
|
|
345
|
+
return "US";
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
retry: 0,
|
|
349
|
+
staleTime: 5 * 60 * 1000,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
countryCode: data ?? null,
|
|
354
|
+
isLoading,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function useIncomingUsdcTransferDetector(params: {
|
|
359
|
+
chainId?: number;
|
|
360
|
+
walletAddress?: string;
|
|
361
|
+
enabled?: boolean;
|
|
362
|
+
thresholdUsdc?: string;
|
|
363
|
+
}) {
|
|
364
|
+
const {
|
|
365
|
+
chainId,
|
|
366
|
+
walletAddress,
|
|
367
|
+
enabled = true,
|
|
368
|
+
thresholdUsdc = "0.01",
|
|
369
|
+
} = params;
|
|
370
|
+
|
|
371
|
+
const usdcAddress = chainId ? USDC_ADDRESS_BY_CHAIN_ID[chainId] : undefined;
|
|
372
|
+
const shouldTrack =
|
|
373
|
+
!!enabled &&
|
|
374
|
+
!!chainId &&
|
|
375
|
+
!!usdcAddress &&
|
|
376
|
+
!!walletAddress &&
|
|
377
|
+
isAddress(walletAddress);
|
|
378
|
+
const threshold = useMemo(
|
|
379
|
+
() => parseUnits(thresholdUsdc, USDC_DECIMALS),
|
|
380
|
+
[thresholdUsdc],
|
|
381
|
+
);
|
|
382
|
+
const [baselineBalance, setBaselineBalance] = useState<bigint | null>(null);
|
|
383
|
+
const [detectedAmount, setDetectedAmount] = useState<string | null>(null);
|
|
384
|
+
|
|
385
|
+
const { data, refetch, isFetching, isLoading } = useReadContract({
|
|
386
|
+
chainId,
|
|
387
|
+
address: usdcAddress as `0x${string}` | undefined,
|
|
388
|
+
abi: erc20Abi,
|
|
389
|
+
functionName: "balanceOf",
|
|
390
|
+
args: walletAddress ? [walletAddress as `0x${string}`] : undefined,
|
|
391
|
+
query: {
|
|
392
|
+
enabled: shouldTrack,
|
|
393
|
+
refetchInterval: shouldTrack ? 4000 : false,
|
|
394
|
+
staleTime: 0,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
if (!shouldTrack) {
|
|
400
|
+
setBaselineBalance(null);
|
|
401
|
+
setDetectedAmount(null);
|
|
402
|
+
}
|
|
403
|
+
}, [shouldTrack]);
|
|
404
|
+
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
if (!shouldTrack || typeof data !== "bigint" || detectedAmount) return;
|
|
407
|
+
|
|
408
|
+
if (baselineBalance === null) {
|
|
409
|
+
setBaselineBalance(data);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (data <= baselineBalance) return;
|
|
414
|
+
|
|
415
|
+
const delta = data - baselineBalance;
|
|
416
|
+
if (delta >= threshold) {
|
|
417
|
+
setDetectedAmount(formatUnits(delta, USDC_DECIMALS));
|
|
418
|
+
}
|
|
419
|
+
}, [
|
|
420
|
+
shouldTrack,
|
|
421
|
+
data,
|
|
422
|
+
baselineBalance,
|
|
423
|
+
detectedAmount,
|
|
424
|
+
threshold,
|
|
425
|
+
isFetching,
|
|
426
|
+
]);
|
|
427
|
+
|
|
428
|
+
console.log(data, baselineBalance, detectedAmount);
|
|
429
|
+
|
|
430
|
+
const reset = useCallback(() => {
|
|
431
|
+
setBaselineBalance(null);
|
|
432
|
+
setDetectedAmount(null);
|
|
433
|
+
}, []);
|
|
434
|
+
|
|
435
|
+
return {
|
|
436
|
+
detectedAmount,
|
|
437
|
+
isDetected: detectedAmount !== null,
|
|
438
|
+
isTracking: shouldTrack,
|
|
439
|
+
isRefreshing: isFetching || isLoading,
|
|
440
|
+
currentBalance: typeof data === "bigint" ? data : null,
|
|
441
|
+
baselineBalance,
|
|
442
|
+
reset,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Hook to manage the full MELD card buy flow state
|
|
448
|
+
*/
|
|
449
|
+
export function useMeldCardBuyFlow() {
|
|
450
|
+
const [sessionId, setSessionId] = useState<string | null>(null);
|
|
451
|
+
const [widgetUrl, setWidgetUrl] = useState<string | null>(null);
|
|
452
|
+
const [widgetOpen, setWidgetOpen] = useState(false);
|
|
453
|
+
|
|
454
|
+
const createSession = useCreateMeldSession();
|
|
455
|
+
|
|
456
|
+
const startSession = useCallback(
|
|
457
|
+
async (params: MeldSessionRequest) => {
|
|
458
|
+
const result = await createSession.mutateAsync(params);
|
|
459
|
+
setSessionId(result.sessionId);
|
|
460
|
+
setWidgetUrl(result.widgetUrl);
|
|
461
|
+
return result;
|
|
462
|
+
},
|
|
463
|
+
[createSession],
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const openWidget = useCallback(() => {
|
|
467
|
+
if (widgetUrl) {
|
|
468
|
+
setWidgetOpen(true);
|
|
469
|
+
// Open in new window/tab
|
|
470
|
+
window.open(widgetUrl, "_blank", "width=500,height=700");
|
|
471
|
+
}
|
|
472
|
+
}, [widgetUrl]);
|
|
473
|
+
|
|
474
|
+
const reset = useCallback(() => {
|
|
475
|
+
setSessionId(null);
|
|
476
|
+
setWidgetUrl(null);
|
|
477
|
+
setWidgetOpen(false);
|
|
478
|
+
}, []);
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
// Session management
|
|
482
|
+
sessionId,
|
|
483
|
+
widgetUrl,
|
|
484
|
+
widgetOpen,
|
|
485
|
+
startSession,
|
|
486
|
+
openWidget,
|
|
487
|
+
reset,
|
|
488
|
+
|
|
489
|
+
// Session creation state
|
|
490
|
+
isCreatingSession: createSession.isPending,
|
|
491
|
+
sessionError: createSession.error,
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Format MELD provider name for display
|
|
497
|
+
*/
|
|
498
|
+
export function formatProviderName(provider: string): string {
|
|
499
|
+
const names: Record<string, string> = {
|
|
500
|
+
TRANSAK: "Transak",
|
|
501
|
+
MOONPAY: "MoonPay",
|
|
502
|
+
BANXA: "Banxa",
|
|
503
|
+
RAMP: "Ramp",
|
|
504
|
+
SARDINE: "Sardine",
|
|
505
|
+
STRIPE: "Stripe",
|
|
506
|
+
MERCURYO: "Mercuryo",
|
|
507
|
+
ALCHEMY_PAY: "Alchemy Pay",
|
|
508
|
+
KRYPTONIM: "Kryptonim",
|
|
509
|
+
UNLIMIT: "Unlimit",
|
|
510
|
+
TOPPER: "Topper",
|
|
511
|
+
BINANCE_CONNECT: "Binance Connect",
|
|
512
|
+
};
|
|
513
|
+
return names[provider] || provider;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Get provider icon URL
|
|
518
|
+
*/
|
|
519
|
+
export function getProviderIcon(provider: string): string {
|
|
520
|
+
const icons: Record<string, string> = {
|
|
521
|
+
TRANSAK: transakIcon,
|
|
522
|
+
MOONPAY: moonpayIcon,
|
|
523
|
+
BANXA: banxaIcon,
|
|
524
|
+
MERCURYO: mercuryoIcon,
|
|
525
|
+
KRYPTONIM: kryptonimIcon,
|
|
526
|
+
ALCHEMY_PAY: alchemypayIcon,
|
|
527
|
+
UNLIMIT: unlimitIcon,
|
|
528
|
+
TOPPER: topperIcon,
|
|
529
|
+
STRIPE: stripeIcon,
|
|
530
|
+
BINANCE_CONNECT: binanceconnectIcon,
|
|
531
|
+
};
|
|
532
|
+
return icons[provider] || "";
|
|
533
|
+
}
|
package/src/assets/usdc.webp
DELETED
|
Binary file
|
package/src/assets/usdt.webp
DELETED
|
Binary file
|