@oasisprotocol/privana-sdk 0.5.6 → 0.5.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/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  "use client";
2
- import { usePrivanaContext, createHostedAuthPendingStorageKey, clearHostedAuthPendingTransaction, HostedAuthRequiredError, HostedAuthError, createPkceVerifier, createPkceChallenge, createHostedAuthState, persistHostedAuthPendingTransaction, parseHostedAuthCallback, readHostedAuthPendingTransaction, HostedAuthStateMismatchError, buildHostedAuthSession, useSafePrivanaContext, usePrivateReadRequest, formatTokenAmount, submitPendingLock, PostDepositLockError, useDepositVerification, useEnsureCorrectChain, getTransactionReceipt, getBlockNumber, waitForTransactionReceipt, canUseBrowserStorage, clampLockAmount, requireDepositLockOwner, getWalletClient, createSignedLockRequest, requireServiceAddress, signWithdrawMessage, signLockMessage, signTransferMessage, signTransferLockedMessage, signModifyLockMessage, cn, stripHostedAuthCallbackParams, AccountingApiError, getBrowserStorageItem, removeBrowserStorageItem, isSignedLockUsable, setBrowserStorageItem, canUseSharedBrowserStorage, DEFAULT_LOCK_DURATION_SECONDS, parseTokenAmount, Skeleton, shortenAddress, formatCountdown, FiatOnRampForm, getExplorerAddressUrl, getExplorerLabel, formatTimeRemaining, getSharedBrowserStorageItem, removeSharedBrowserStorageItem, setSharedBrowserStorageItem } from './chunk-DLGIPF4N.js';
3
- export { AccountingApiError, Button, DEFAULT_LOCK_DURATION_SECONDS, DEFAULT_ONRAMP_LOCK_BUFFER, HOSTED_AUTH_CLOCK_SKEW_MS, HostedAuthError, HostedAuthRequiredError, HostedAuthStateMismatchError, HttpClient, LOCK_TYPES, MODIFY_LOCK_TYPES, NETWORK_CONFIG, NetworkError, PostDepositLockError, PrivanaClient, PrivanaProvider, SUPPORTED_CHAINS, SiweAuthProvider, Skeleton, TRANSFER_LOCKED_TYPES, TRANSFER_TYPES, ValidationError, WITHDRAW_FROM_LOCK_TYPES, WITHDRAW_TYPES, applyLockBuffer, applyRefreshResponse, buildHostedAuthSession, buildSiweStatement, buttonVariants, clampLockAmount, clearHostedAuthPendingTransaction, clearPendingLock, createDomain, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createLockExpiry, createPkceChallenge, createPkceVerifier, createSignedLockRequest, getAccountingContract, getApiUrl, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, isHostedAuthRefreshActive, isHostedAuthSessionActive, isSignedLockUsable, loadPendingLock, normalizeAddress, normalizeHex, parseHostedAuthCallback, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, requireDepositLockOwner, requireServiceAddress, savePendingLock, signLockMessage, signModifyLockMessage, signTransferLockedMessage, signTransferMessage, signWithdrawFromLockMessage, signWithdrawMessage, stripHostedAuthCallbackParams, submitPendingLock, syncHostedAuthSessionToClient, useDepositVerification, usePrivanaContext, useSafeAccount, useSafePrivanaContext, useSiweAuth } from './chunk-DLGIPF4N.js';
2
+ import { usePrivanaContext, createHostedAuthPendingStorageKey, clearHostedAuthPendingTransaction, HostedAuthRequiredError, HostedAuthError, createPkceVerifier, createPkceChallenge, createHostedAuthState, persistHostedAuthPendingTransaction, parseHostedAuthCallback, readHostedAuthPendingTransaction, HostedAuthStateMismatchError, buildHostedAuthSession, useSafePrivanaContext, usePrivateReadRequest, formatTokenAmount, submitPendingLock, PostDepositLockError, useDepositVerification, useEnsureCorrectChain, getTransactionReceipt, getBlockNumber, waitForTransactionReceipt, canUseBrowserStorage, clampLockAmount, requireDepositLockOwner, getWalletClient, createSignedLockRequest, requireServiceAddress, signWithdrawMessage, signLockMessage, signTransferMessage, signTransferLockedMessage, signModifyLockMessage, cn, stripHostedAuthCallbackParams, AccountingApiError, getBrowserStorageItem, removeBrowserStorageItem, isSignedLockUsable, setBrowserStorageItem, resolveProductOnRamp, matchesProductOnRampScope, createProductOnRampOutcomeCallbacks, canUseSharedBrowserStorage, DEFAULT_LOCK_DURATION_SECONDS, isMoonPayProductOnRamp, parseTokenAmount, createProductOnRampFlowSnapshot, Skeleton, shortenAddress, formatCountdown, getExplorerAddressUrl, getExplorerLabel, formatTimeRemaining, getSharedBrowserStorageItem, removeSharedBrowserStorageItem, setSharedBrowserStorageItem, matchesOnRampTransaction, useTransakOnRamp, getTransakMinimumTargetBaseUnits, matchesFrozenOnRampToken, Button, canRetryOnRampVerification, FiatOnRampForm } from './chunk-ONXJGUOH.js';
3
+ export { AccountingApiError, Button, DEFAULT_LOCK_DURATION_SECONDS, DEFAULT_ONRAMP_LOCK_BUFFER, HOSTED_AUTH_CLOCK_SKEW_MS, HostedAuthError, HostedAuthRequiredError, HostedAuthStateMismatchError, HttpClient, LOCK_TYPES, MODIFY_LOCK_TYPES, NETWORK_CONFIG, NetworkError, PostDepositLockError, PrivanaClient, PrivanaProvider, SUPPORTED_CHAINS, SiweAuthProvider, Skeleton, TRANSFER_LOCKED_TYPES, TRANSFER_TYPES, ValidationError, WITHDRAW_FROM_LOCK_TYPES, WITHDRAW_TYPES, applyLockBuffer, applyRefreshResponse, buildHostedAuthSession, buildSiweStatement, buttonVariants, clampLockAmount, clearHostedAuthPendingTransaction, clearPendingLock, createDomain, createHostedAuthPendingStorageKey, createHostedAuthState, createHostedAuthStorageKey, createLockExpiry, createPkceChallenge, createPkceVerifier, createSignedLockRequest, getAccountingContract, getApiUrl, getChainById, getChainId, getExplorerAddressUrl, getExplorerLabel, isHostedAuthRefreshActive, isHostedAuthSessionActive, isSignedLockUsable, loadPendingLock, normalizeAddress, normalizeHex, parseHostedAuthCallback, persistHostedAuthPendingTransaction, readHostedAuthPendingTransaction, readStoredHostedAuthSession, requireDepositLockOwner, requireServiceAddress, savePendingLock, signLockMessage, signModifyLockMessage, signTransferLockedMessage, signTransferMessage, signWithdrawFromLockMessage, signWithdrawMessage, stripHostedAuthCallbackParams, submitPendingLock, syncHostedAuthSessionToClient, useDepositVerification, usePrivanaContext, useSafeAccount, useSafePrivanaContext, useSiweAuth } from './chunk-ONXJGUOH.js';
4
4
  import { useState, useRef, useMemo, useCallback, useContext, useEffect, useId } from 'react';
5
5
  import { QueryClientContext, useQuery, useQueryClient, useMutation, keepPreviousData } from '@tanstack/react-query';
6
6
  import { useAccount, useWalletClient, useConfig, useWriteContract, useSendTransaction, useBalance as useBalance$1, useReadContract } from 'wagmi';
7
- import { zeroAddress, erc20Abi, formatUnits } from 'viem';
7
+ import { zeroAddress, erc20Abi, formatUnits, parseUnits } from 'viem';
8
8
  import { MoonPayProvider, MoonPayContext } from '@moonpay/moonpay-react';
9
9
  import * as DialogPrimitive from '@radix-ui/react-dialog';
10
- import { XIcon } from 'lucide-react';
10
+ import { XIcon, CircleCheckIcon, Loader2 } from 'lucide-react';
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
12
  import { toast } from 'sonner';
13
13
  import { QRCodeSVG } from 'qrcode.react';
@@ -2315,7 +2315,7 @@ function BaseIcon({ className, size = 24 }) {
2315
2315
  }
2316
2316
  );
2317
2317
  }
2318
- function WETHIcon({ className, size = 24 }) {
2318
+ function EthereumIcon({ className, size = 24 }) {
2319
2319
  return /* @__PURE__ */ jsxs(
2320
2320
  "svg",
2321
2321
  {
@@ -2366,18 +2366,19 @@ function WETHIcon({ className, size = 24 }) {
2366
2366
  }
2367
2367
  function getTokenIcon(symbol, size) {
2368
2368
  const iconSize = size ?? 24;
2369
- switch (symbol.toUpperCase()) {
2369
+ switch ((symbol ?? "").toUpperCase()) {
2370
2370
  case "USDC":
2371
2371
  return /* @__PURE__ */ jsx(USDCIcon, { size: iconSize });
2372
+ case "ETH":
2372
2373
  case "WETH":
2373
- return /* @__PURE__ */ jsx(WETHIcon, { size: iconSize });
2374
+ return /* @__PURE__ */ jsx(EthereumIcon, { size: iconSize });
2374
2375
  default:
2375
2376
  return /* @__PURE__ */ jsx(
2376
2377
  "div",
2377
2378
  {
2378
2379
  className: "bg-muted flex items-center justify-center rounded-full text-xs font-bold",
2379
2380
  style: { width: iconSize, height: iconSize },
2380
- children: symbol.slice(0, 2)
2381
+ children: (symbol ?? "?").slice(0, 2)
2381
2382
  }
2382
2383
  );
2383
2384
  }
@@ -2385,8 +2386,12 @@ function getTokenIcon(symbol, size) {
2385
2386
  function getChainIcon(chainId, size) {
2386
2387
  const iconSize = size ?? 24;
2387
2388
  switch (chainId) {
2389
+ case 8453:
2388
2390
  case 84532:
2389
2391
  return /* @__PURE__ */ jsx(BaseIcon, { size: iconSize });
2392
+ case 1:
2393
+ case 11155111:
2394
+ return /* @__PURE__ */ jsx(EthereumIcon, { size: iconSize });
2390
2395
  default:
2391
2396
  return /* @__PURE__ */ jsx(
2392
2397
  "div",
@@ -2399,10 +2404,11 @@ function getChainIcon(chainId, size) {
2399
2404
  }
2400
2405
  }
2401
2406
  function TokenSelectorView({ selectedTokenId, onSelect }) {
2402
- const { enabledTokens } = usePrivanaContext();
2407
+ const { enabledTokens, getChainById: getChainById2 } = usePrivanaContext();
2403
2408
  return /* @__PURE__ */ jsx("div", { className: "bg-muted flex flex-col rounded-[10px] p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex max-h-72 flex-col gap-1 overflow-y-auto", children: [
2404
2409
  enabledTokens.map((token) => {
2405
2410
  const isSelected = selectedTokenId === token.id;
2411
+ const chainName = getChainById2(token.chainId)?.name;
2406
2412
  return /* @__PURE__ */ jsxs(
2407
2413
  "button",
2408
2414
  {
@@ -2416,7 +2422,10 @@ function TokenSelectorView({ selectedTokenId, onSelect }) {
2416
2422
  /* @__PURE__ */ jsx("div", { className: "h-6 w-6 shrink-0 overflow-hidden rounded-full", children: getTokenIcon(token.symbol, 24) }),
2417
2423
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2418
2424
  /* @__PURE__ */ jsx("div", { className: "text-foreground text-sm font-medium", children: token.symbol }),
2419
- /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-[11px]", children: token.name })
2425
+ /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground text-[11px]", children: [
2426
+ "on ",
2427
+ chainName ?? "\u2014"
2428
+ ] })
2420
2429
  ] }),
2421
2430
  isSelected && /* @__PURE__ */ jsx("div", { className: "bg-primary flex h-4 w-4 shrink-0 items-center justify-center rounded-full", children: /* @__PURE__ */ jsx(
2422
2431
  "svg",
@@ -2446,15 +2455,356 @@ function TokenSelectorView({ selectedTokenId, onSelect }) {
2446
2455
  enabledTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground px-3 py-6 text-center text-sm", children: "No tokens available" })
2447
2456
  ] }) });
2448
2457
  }
2458
+ function assertTransakCheckoutPreconditions({
2459
+ activeIntentId,
2460
+ canOpen,
2461
+ canRecreateSession
2462
+ }) {
2463
+ if (activeIntentId && !canRecreateSession) {
2464
+ throw new Error("Continue signed-intent recovery instead of reopening this checkout");
2465
+ }
2466
+ if (!canOpen) {
2467
+ throw new Error("Card checkout is unavailable; close this purchase and start again");
2468
+ }
2469
+ }
2470
+ function TransakCardWidgetView({
2471
+ token,
2472
+ providerAssetCode,
2473
+ amount,
2474
+ allowance,
2475
+ lockServiceAddress,
2476
+ onCredited,
2477
+ onLockSubmitted,
2478
+ onLockFailed,
2479
+ onLeave,
2480
+ onUnsafeToCloseChange,
2481
+ onActiveFlowChange,
2482
+ useOnRamp = useTransakOnRamp
2483
+ }) {
2484
+ const [actionError, setActionError] = useState(null);
2485
+ const [rowError, setRowError] = useState(null);
2486
+ const [lockSettled, setLockSettled] = useState(false);
2487
+ const [lockError, setLockError] = useState(null);
2488
+ const [ownedTerminal, setOwnedTerminal] = useState(false);
2489
+ const ownedIntentIdRef = useRef(null);
2490
+ const postDepositLock = useMemo(
2491
+ () => allowance ? {
2492
+ serviceAddress: lockServiceAddress,
2493
+ maxAmount: BigInt(allowance.value),
2494
+ lockDuration: allowance.lockDuration
2495
+ } : void 0,
2496
+ [allowance, lockServiceAddress]
2497
+ );
2498
+ const ownsRecord = useCallback(
2499
+ (record) => Boolean(
2500
+ ownedIntentIdRef.current && matchesOnRampTransaction(record, ownedIntentIdRef.current)
2501
+ ),
2502
+ []
2503
+ );
2504
+ const {
2505
+ status,
2506
+ activeIntentId,
2507
+ pending,
2508
+ activeVerificationId,
2509
+ error,
2510
+ finalityProgress,
2511
+ depositAddress,
2512
+ minDepositBaseUnits,
2513
+ selectedToken,
2514
+ isLaunching,
2515
+ isWidgetOpen,
2516
+ canRecreateSession,
2517
+ widget,
2518
+ launch,
2519
+ recreateSession,
2520
+ closeWidget,
2521
+ finishPendingVerification,
2522
+ refreshPending
2523
+ } = useOnRamp({
2524
+ tokenId: token.id,
2525
+ postDepositLock,
2526
+ iframeTitle: "Card checkout",
2527
+ iframeClassName: "h-[680px] w-full border-0",
2528
+ onCredited: (_depositTxHash, record) => {
2529
+ if (!ownsRecord(record)) return;
2530
+ onCredited?.();
2531
+ if (!postDepositLock) {
2532
+ setOwnedTerminal(true);
2533
+ onActiveFlowChange?.(false);
2534
+ }
2535
+ },
2536
+ onLockSubmitted: (_response, record) => {
2537
+ if (!ownsRecord(record)) return;
2538
+ setLockError(null);
2539
+ setLockSettled(true);
2540
+ setOwnedTerminal(true);
2541
+ onActiveFlowChange?.(false);
2542
+ onLockSubmitted?.();
2543
+ },
2544
+ onLockFailed: (nextError, record) => {
2545
+ if (!ownsRecord(record)) return;
2546
+ setLockError(nextError);
2547
+ setOwnedTerminal(true);
2548
+ onActiveFlowChange?.(false);
2549
+ onLockFailed?.(nextError);
2550
+ }
2551
+ });
2552
+ const ownedIntentId = ownedIntentIdRef.current ?? activeIntentId;
2553
+ useEffect(() => {
2554
+ if (activeIntentId && !ownedIntentIdRef.current) {
2555
+ ownedIntentIdRef.current = activeIntentId;
2556
+ }
2557
+ }, [activeIntentId]);
2558
+ let amountBaseUnits;
2559
+ try {
2560
+ amountBaseUnits = parseUnits(amount, token.decimals);
2561
+ } catch {
2562
+ }
2563
+ const minimumTargetBaseUnits = minDepositBaseUnits === void 0 ? void 0 : getTransakMinimumTargetBaseUnits(minDepositBaseUnits);
2564
+ const belowMinimum = amountBaseUnits !== void 0 && minimumTargetBaseUnits !== void 0 && amountBaseUnits < minimumTargetBaseUnits;
2565
+ const minimumUnknown = minimumTargetBaseUnits === void 0;
2566
+ const tokenMismatch = !matchesFrozenOnRampToken(token, selectedToken);
2567
+ const isSettling = status === "awaiting-delivery" || status === "verifying";
2568
+ const lockPending = !!allowance && status === "credited" && !lockSettled && !lockError;
2569
+ const ownedPending = ownedIntentId ? pending.filter((record) => matchesOnRampTransaction(record, ownedIntentId)) : [];
2570
+ const ownedFlowActive = !ownedTerminal && (Boolean(activeIntentId && activeIntentId === ownedIntentId) || ownedPending.length > 0 || lockPending);
2571
+ const canOpen = Boolean(selectedToken && depositAddress && amountBaseUnits && amountBaseUnits > 0n) && !tokenMismatch && !minimumUnknown && !belowMinimum && !isLaunching && !isWidgetOpen && !isSettling && status !== "credited" && (!activeIntentId || canRecreateSession);
2572
+ useEffect(() => {
2573
+ onActiveFlowChange?.(ownedFlowActive);
2574
+ }, [onActiveFlowChange, ownedFlowActive]);
2575
+ const unsafeToClose = isLaunching || lockPending;
2576
+ useEffect(() => {
2577
+ onUnsafeToCloseChange?.(unsafeToClose);
2578
+ return () => onUnsafeToCloseChange?.(false);
2579
+ }, [onUnsafeToCloseChange, unsafeToClose]);
2580
+ const runCheckout = useCallback(async () => {
2581
+ setActionError(null);
2582
+ try {
2583
+ assertTransakCheckoutPreconditions({ activeIntentId, canOpen, canRecreateSession });
2584
+ setLockError(null);
2585
+ setLockSettled(false);
2586
+ setOwnedTerminal(false);
2587
+ if (!activeIntentId) ownedIntentIdRef.current = null;
2588
+ if (activeIntentId) await recreateSession();
2589
+ else await launch({ providerAssetCode, quoteCurrencyAmount: amount });
2590
+ } catch (nextError) {
2591
+ setActionError(
2592
+ nextError instanceof Error ? nextError : new Error("Failed to open card checkout")
2593
+ );
2594
+ }
2595
+ }, [
2596
+ activeIntentId,
2597
+ amount,
2598
+ canOpen,
2599
+ canRecreateSession,
2600
+ launch,
2601
+ providerAssetCode,
2602
+ recreateSession
2603
+ ]);
2604
+ const handleClose = useCallback(async () => {
2605
+ setActionError(null);
2606
+ try {
2607
+ await closeWidget();
2608
+ } catch (nextError) {
2609
+ setActionError(
2610
+ nextError instanceof Error ? nextError : new Error("Failed to close card checkout")
2611
+ );
2612
+ }
2613
+ }, [closeWidget]);
2614
+ const handleLeave = useCallback(() => {
2615
+ setActionError(null);
2616
+ onActiveFlowChange?.(false);
2617
+ onLeave?.();
2618
+ }, [onActiveFlowChange, onLeave]);
2619
+ return /* @__PURE__ */ jsxs("div", { className: "bg-muted flex flex-col gap-4 rounded-[10px] p-5", children: [
2620
+ /* @__PURE__ */ jsxs("div", { children: [
2621
+ /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: "Complete your purchase" }),
2622
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground mt-1 text-sm", children: [
2623
+ "Buy ",
2624
+ token.symbol,
2625
+ " and deposit it directly into your Privana balance."
2626
+ ] })
2627
+ ] }),
2628
+ /* @__PURE__ */ jsxs("div", { className: "border-border bg-card rounded-lg border p-3 text-sm", children: [
2629
+ /* @__PURE__ */ jsxs("p", { className: "text-foreground font-medium", children: [
2630
+ "Target receipt: at least ",
2631
+ amount,
2632
+ " ",
2633
+ token.symbol
2634
+ ] }),
2635
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1", children: "In Transak, adjust the fiat amount until \u201CYou receive\u201D is at least this target." }),
2636
+ allowance && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1", children: "The pre-signed lock is 98% of the target, capped by the service allowance." })
2637
+ ] }),
2638
+ widget ? /* @__PURE__ */ jsxs("div", { className: "border-border bg-card overflow-hidden rounded-xl border", children: [
2639
+ /* @__PURE__ */ jsxs("div", { className: "border-border flex items-center justify-between border-b px-4 py-2 text-sm", children: [
2640
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Card checkout" }),
2641
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void handleClose(), children: "Close" })
2642
+ ] }),
2643
+ widget
2644
+ ] }) : status === "credited" && !lockPending && !lockError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-8 text-center", children: [
2645
+ /* @__PURE__ */ jsx(CircleCheckIcon, { className: "text-primary size-8", "aria-hidden": true }),
2646
+ /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium", children: "Purchase credited" }),
2647
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-sm", children: [
2648
+ "Your ",
2649
+ token.symbol,
2650
+ " deposit is now available in your balance."
2651
+ ] })
2652
+ ] }) : !activeIntentId || canRecreateSession ? /* @__PURE__ */ jsxs(Button, { type: "button", disabled: !canOpen, onClick: () => void runCheckout(), children: [
2653
+ isLaunching && /* @__PURE__ */ jsx(Loader2, { className: "animate-spin", "aria-hidden": true }),
2654
+ isLaunching ? "Opening checkout\u2026" : activeIntentId ? "Reopen card checkout" : allowance ? "Sign policy and open checkout" : "Open card checkout"
2655
+ ] }) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "This signed purchase is in recovery. Do not open another checkout for it." }),
2656
+ !depositAddress && !error && !actionError && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Preparing your secure deposit address\u2026" }),
2657
+ tokenMismatch && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", role: "alert", children: "The token configuration changed. Close this purchase and start again." }),
2658
+ !tokenMismatch && depositAddress && minimumUnknown && !error && !actionError && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", role: "alert", children: "The minimum purchase amount is unavailable. Close this purchase and try again." }),
2659
+ belowMinimum && minimumTargetBaseUnits !== void 0 && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", role: "alert", children: [
2660
+ "Minimum target is ",
2661
+ formatUnits(minimumTargetBaseUnits, token.decimals),
2662
+ " ",
2663
+ token.symbol,
2664
+ ", including a 5% delivery buffer."
2665
+ ] }),
2666
+ isSettling && pending.length === 0 && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [
2667
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
2668
+ "Verifying your purchase\u2026"
2669
+ ] }),
2670
+ lockPending && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [
2671
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": true }),
2672
+ "Purchase credited. Locking your funds\u2026"
2673
+ ] }),
2674
+ pending.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
2675
+ /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium", children: "Validating purchases" }),
2676
+ pending.map((record) => {
2677
+ const isActivelyVerifying = record.transaction_id === activeVerificationId;
2678
+ const progress = parseFinalityProgress(finalityProgress[record.transaction_id]);
2679
+ const showRetry = canRetryOnRampVerification(record, activeVerificationId);
2680
+ return /* @__PURE__ */ jsxs(
2681
+ "div",
2682
+ {
2683
+ className: "border-border flex flex-col gap-2 rounded-md border p-2",
2684
+ children: [
2685
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground flex items-center gap-1 text-xs", children: [
2686
+ /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin", "aria-hidden": true }),
2687
+ record.on_chain_tx_hash ? progress ?? (isActivelyVerifying ? "Verifying\u2026" : "Ready to verify") : "Waiting for provider delivery\u2026"
2688
+ ] }),
2689
+ rowError?.id === record.transaction_id && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs", children: rowError.message }),
2690
+ showRetry && /* @__PURE__ */ jsx(
2691
+ Button,
2692
+ {
2693
+ type: "button",
2694
+ variant: "outline",
2695
+ size: "sm",
2696
+ onClick: async () => {
2697
+ setRowError(null);
2698
+ try {
2699
+ await finishPendingVerification(record);
2700
+ } catch (nextError) {
2701
+ setRowError({
2702
+ id: record.transaction_id,
2703
+ message: nextError instanceof Error ? nextError.message : "Verification failed"
2704
+ });
2705
+ }
2706
+ },
2707
+ children: "Retry verification"
2708
+ }
2709
+ ),
2710
+ !record.on_chain_tx_hash && /* @__PURE__ */ jsx(
2711
+ Button,
2712
+ {
2713
+ type: "button",
2714
+ variant: "outline",
2715
+ size: "sm",
2716
+ onClick: () => void refreshPending(),
2717
+ children: "Refresh delivery"
2718
+ }
2719
+ )
2720
+ ]
2721
+ },
2722
+ record.transaction_id
2723
+ );
2724
+ })
2725
+ ] }),
2726
+ onLeave && ownedFlowActive && !unsafeToClose && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2727
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: handleLeave, children: "Leave checkout" }),
2728
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: "Privana will keep this signed purchase available for exact recovery." })
2729
+ ] }),
2730
+ (actionError ?? error) && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", role: "alert", children: (actionError ?? error)?.message }),
2731
+ lockError && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", role: "alert", children: [
2732
+ "Purchase credited, but locking the funds failed: ",
2733
+ lockError.message
2734
+ ] })
2735
+ ] });
2736
+ }
2737
+ function parseFinalityProgress(message) {
2738
+ if (!message) return null;
2739
+ const match = message.match(/(\d+\/\d+)\s+confirmations/i);
2740
+ return match ? `${match[1]} confirmations` : null;
2741
+ }
2449
2742
  function CreditCardWidgetView({
2450
2743
  token,
2744
+ onRamp,
2451
2745
  amount,
2452
2746
  allowance,
2747
+ lockServiceAddress,
2453
2748
  onCredited,
2454
2749
  onLockSubmitted,
2455
- onLockFailed
2750
+ onLockFailed,
2751
+ onLeave,
2752
+ onUnsafeToCloseChange,
2753
+ onActiveFlowChange
2456
2754
  }) {
2457
- const moonpayCurrencyCode = token?.moonpayCurrencyCode;
2755
+ if (onRamp.unavailableReason || !token || !onRamp.provider || !onRamp.providerAssetCode) {
2756
+ return /* @__PURE__ */ jsxs("div", { className: "bg-muted flex flex-col gap-2 rounded-[10px] p-5", children: [
2757
+ /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: "Card purchases unavailable" }),
2758
+ /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: onRamp.unavailableReason ?? "The card on-ramp is not configured for this token." })
2759
+ ] });
2760
+ }
2761
+ if (onRamp.provider === "transak") {
2762
+ return /* @__PURE__ */ jsx(
2763
+ TransakCardWidgetView,
2764
+ {
2765
+ token,
2766
+ providerAssetCode: onRamp.providerAssetCode,
2767
+ amount,
2768
+ allowance,
2769
+ lockServiceAddress,
2770
+ onCredited,
2771
+ onLockSubmitted,
2772
+ onLockFailed,
2773
+ onLeave,
2774
+ onUnsafeToCloseChange,
2775
+ onActiveFlowChange
2776
+ }
2777
+ );
2778
+ }
2779
+ return /* @__PURE__ */ jsx(
2780
+ MoonPayCardWidgetView,
2781
+ {
2782
+ token,
2783
+ amount,
2784
+ allowance,
2785
+ lockServiceAddress,
2786
+ onCredited,
2787
+ onLockSubmitted,
2788
+ onLockFailed,
2789
+ onLeaveFlow: onLeave,
2790
+ onUnsafeToCloseChange,
2791
+ onActiveFlowChange
2792
+ }
2793
+ );
2794
+ }
2795
+ function MoonPayCardWidgetView({
2796
+ token,
2797
+ amount,
2798
+ allowance,
2799
+ lockServiceAddress,
2800
+ onCredited,
2801
+ onLockSubmitted,
2802
+ onLockFailed,
2803
+ onLeaveFlow,
2804
+ onUnsafeToCloseChange,
2805
+ onActiveFlowChange
2806
+ }) {
2807
+ const moonpayCurrencyCode = token.moonpayCurrencyCode;
2458
2808
  const containerRef = useRef(null);
2459
2809
  const [widgetTheme, setWidgetTheme] = useState();
2460
2810
  useEffect(() => {
@@ -2462,26 +2812,32 @@ function CreditCardWidgetView({
2462
2812
  }, []);
2463
2813
  return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "bg-muted flex flex-col gap-6 rounded-[10px] p-5", children: [
2464
2814
  /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: "Complete your purchase" }),
2465
- token && moonpayCurrencyCode ? /* @__PURE__ */ jsx(
2815
+ moonpayCurrencyCode ? /* @__PURE__ */ jsx(
2466
2816
  FiatOnRampForm,
2467
2817
  {
2468
2818
  tokenId: token.id,
2819
+ frozenToken: token,
2469
2820
  currencyCode: moonpayCurrencyCode,
2470
2821
  quoteCurrencyAmount: amount || void 0,
2471
2822
  lockAmount: true,
2472
2823
  variant: "embedded",
2473
2824
  autoStart: true,
2474
2825
  theme: widgetTheme,
2826
+ onlyRouteActiveIntentCallbacks: true,
2827
+ onUnsafeToCloseChange,
2828
+ onActiveFlowChange,
2475
2829
  postDepositLock: allowance ? {
2830
+ serviceAddress: lockServiceAddress,
2476
2831
  maxAmount: BigInt(allowance.value),
2477
2832
  lockDuration: allowance.lockDuration
2478
2833
  } : void 0,
2479
2834
  onCredited,
2480
2835
  onLockSubmitted,
2481
- onLockFailed
2836
+ onLockFailed,
2837
+ onLeaveFlow
2482
2838
  }
2483
2839
  ) : /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-sm", children: [
2484
- token?.symbol ?? "This token",
2840
+ token.symbol,
2485
2841
  " isn\u2019t available for card purchases yet."
2486
2842
  ] })
2487
2843
  ] });
@@ -2790,7 +3146,7 @@ function CopyIcon() {
2790
3146
  }
2791
3147
  );
2792
3148
  }
2793
- function CircleCheckIcon() {
3149
+ function CircleCheckIcon2() {
2794
3150
  return /* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
2795
3151
  /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "9", stroke: "currentColor", strokeWidth: "1.5" }),
2796
3152
  /* @__PURE__ */ jsx(
@@ -2909,7 +3265,7 @@ function PolicyTermRow({
2909
3265
  "mt-0.5 shrink-0",
2910
3266
  kind === "permission" ? "text-emerald-500" : "text-orange-500"
2911
3267
  ),
2912
- children: kind === "permission" ? /* @__PURE__ */ jsx(CircleCheckIcon, {}) : /* @__PURE__ */ jsx(CircleXIcon, {})
3268
+ children: kind === "permission" ? /* @__PURE__ */ jsx(CircleCheckIcon2, {}) : /* @__PURE__ */ jsx(CircleXIcon, {})
2913
3269
  }
2914
3270
  ),
2915
3271
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
@@ -2969,6 +3325,7 @@ function AllowancePolicySection({
2969
3325
  function DepositView({
2970
3326
  source,
2971
3327
  selectedToken,
3328
+ onRamp,
2972
3329
  amount,
2973
3330
  allowance,
2974
3331
  externalMinimum,
@@ -2987,6 +3344,8 @@ function DepositView({
2987
3344
  const isConnectedSource = source === "connected";
2988
3345
  const isExternal = source === "external";
2989
3346
  const isCreditCard = source === "credit-card";
3347
+ const isMoonPayCard = isCreditCard && isMoonPayProductOnRamp(onRamp);
3348
+ const isTransakCard = isCreditCard && onRamp.provider === "transak";
2990
3349
  const isNative = selectedToken?.contract === zeroAddress;
2991
3350
  const { data: nativeBalanceData } = useBalance$1({
2992
3351
  address,
@@ -3010,15 +3369,15 @@ function DepositView({
3010
3369
  } = useMoonpayLimits({
3011
3370
  currencyCode: selectedToken?.moonpayCurrencyCode,
3012
3371
  apiBaseUrl: networkConfig.moonpayApiUrl,
3013
- enabled: isCreditCard
3372
+ enabled: isMoonPayCard
3014
3373
  });
3015
3374
  const hasValidAmount = !!amount && parseFloat(amount) > 0;
3016
- const maxAmountDecimals = isCreditCard ? 2 : selectedToken?.decimals;
3375
+ const maxAmountDecimals = isMoonPayCard ? 2 : selectedToken?.decimals;
3017
3376
  const tooManyDecimals = hasValidAmount && maxAmountDecimals != null && amount.includes(".") && amount.split(".")[1].length > maxAmountDecimals;
3018
3377
  const exceedsBalance = isConnectedSource && hasValidAmount && !tooManyDecimals && !!selectedToken && walletBalance != null && parseTokenAmount(amount, selectedToken.decimals) > walletBalance;
3019
- const belowMoonpayMin = isCreditCard && hasValidAmount && moonpayMinBuy != null && parseFloat(amount) < moonpayMinBuy;
3020
- const moonpayLimitsUnready = isCreditCard && !!selectedToken?.moonpayCurrencyCode && moonpayMinBuy == null;
3021
- const creditCardUnavailable = isCreditCard && !!selectedToken && !selectedToken.moonpayCurrencyCode;
3378
+ const belowMoonpayMin = isMoonPayCard && hasValidAmount && moonpayMinBuy != null && parseFloat(amount) < moonpayMinBuy;
3379
+ const moonpayLimitsUnready = isMoonPayCard && !!onRamp.providerAssetCode && moonpayMinBuy == null;
3380
+ const creditCardUnavailable = isCreditCard && !!onRamp.unavailableReason;
3022
3381
  const externalTokenUnavailable = isExternal && !!selectedToken && isNative;
3023
3382
  const externalMinimumRequired = isExternal && !!allowance && !!selectedToken;
3024
3383
  const externalMinimumLoading = externalMinimumRequired && externalMinimum === void 0;
@@ -3033,8 +3392,8 @@ function DepositView({
3033
3392
  };
3034
3393
  return /* @__PURE__ */ jsxs("div", { className: "bg-muted flex flex-col gap-6 rounded-[10px] p-5", children: [
3035
3394
  isCreditCard ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3036
- /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: "Buy with credit card and deposit" }),
3037
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Enter your deposit amount and proceed to sign a policy." })
3395
+ /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: "Buy with card and deposit" }),
3396
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: isTransakCard ? "Enter your target deposit amount and continue." : "Enter your deposit amount and proceed to sign a policy." })
3038
3397
  ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3039
3398
  /* @__PURE__ */ jsxs("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: [
3040
3399
  "Deposit from ",
@@ -3049,7 +3408,7 @@ function DepositView({
3049
3408
  {
3050
3409
  type: "button",
3051
3410
  onClick: onSelectToken,
3052
- disabled: isSubmitting,
3411
+ disabled: isSubmitting || isCreditCard && onRamp.tokenSelectionLocked,
3053
3412
  className: "border-border bg-input flex w-full cursor-pointer items-center gap-3 rounded-lg border p-3 text-left disabled:cursor-not-allowed disabled:opacity-50",
3054
3413
  children: [
3055
3414
  selectedToken ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -3062,7 +3421,7 @@ function DepositView({
3062
3421
  ] })
3063
3422
  ] })
3064
3423
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground flex-1 text-sm", children: "Select token" }),
3065
- /* @__PURE__ */ jsx("div", { className: "text-muted-foreground flex h-5 w-5 items-center justify-center", children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) })
3424
+ (!isCreditCard || !onRamp.tokenSelectionLocked) && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground flex h-5 w-5 items-center justify-center", children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) })
3066
3425
  ]
3067
3426
  }
3068
3427
  )
@@ -3132,12 +3491,9 @@ function DepositView({
3132
3491
  ] }),
3133
3492
  externalMinimumLoading && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Checking minimum deposit\u2026" }),
3134
3493
  externalMinimumUnavailable && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: "Couldn\u2019t load the minimum deposit. Please try again." }),
3135
- isCreditCard && moonpayLimitsError && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: "Couldn\u2019t load purchase limits. Please try again." }),
3136
- isCreditCard && moonpayLimitsLoading && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Checking purchase limits\u2026" }),
3137
- creditCardUnavailable && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", children: [
3138
- selectedToken?.symbol ?? "This token",
3139
- " isn\u2019t available for card purchases yet."
3140
- ] }),
3494
+ isMoonPayCard && moonpayLimitsError && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: "Couldn\u2019t load purchase limits. Please try again." }),
3495
+ isMoonPayCard && moonpayLimitsLoading && /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: "Checking purchase limits\u2026" }),
3496
+ creditCardUnavailable && /* @__PURE__ */ jsx("p", { className: "text-destructive text-sm", children: onRamp.unavailableReason }),
3141
3497
  externalTokenUnavailable && /* @__PURE__ */ jsxs("p", { className: "text-destructive text-sm", children: [
3142
3498
  selectedToken?.symbol ?? "This token",
3143
3499
  " can\u2019t be used for external deposits. Choose an ERC20 token."
@@ -3164,7 +3520,7 @@ function DepositView({
3164
3520
  if (selectedToken) onSubmit({ source, tokenId: selectedToken.id, amount });
3165
3521
  },
3166
3522
  className: "bg-primary text-primary-foreground hover:bg-primary/90 flex h-10 w-full cursor-pointer items-center justify-center rounded-[10px] px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50",
3167
- children: needsConnect ? "Connect Wallet" : allowance ? "Sign Policy and Deposit" : "Deposit"
3523
+ children: needsConnect ? "Connect Wallet" : isTransakCard ? "Continue" : allowance ? "Sign Policy and Deposit" : "Deposit"
3168
3524
  }
3169
3525
  )
3170
3526
  ] });
@@ -3476,9 +3832,11 @@ function ExternalDepositView({
3476
3832
  ] })
3477
3833
  ] });
3478
3834
  }
3479
- function MoonPayGate({ enabled, children }) {
3480
- const { networkConfig } = usePrivanaContext();
3481
- const apiKey = networkConfig.moonpayApiKey;
3835
+ function MoonPayGate({
3836
+ enabled,
3837
+ apiKey,
3838
+ children
3839
+ }) {
3482
3840
  if (!enabled || !apiKey) return /* @__PURE__ */ jsx(Fragment, { children });
3483
3841
  return /* @__PURE__ */ jsx(MoonPayProvider, { apiKey, children });
3484
3842
  }
@@ -3565,15 +3923,52 @@ function DepositModalContent({
3565
3923
  onCloseBlockedChange,
3566
3924
  onExit
3567
3925
  }) {
3568
- const { serviceName, enabledTokens, defaultToken, hostedAuthConfig, getChainById: getChainById2, getTokenById } = usePrivanaContext();
3926
+ const {
3927
+ serviceName,
3928
+ enabledTokens,
3929
+ defaultToken,
3930
+ hostedAuthConfig,
3931
+ serviceAddress,
3932
+ getChainById: getChainById2,
3933
+ getTokenById,
3934
+ networkConfig,
3935
+ onRamp
3936
+ } = usePrivanaContext();
3569
3937
  const { address } = useAccount();
3938
+ const { privateReadQueryScope } = usePrivateReadRequest();
3939
+ const [privateReadApiUrl, accountingChainId, beneficiaryAddress] = privateReadQueryScope;
3570
3940
  const appName = serviceName ?? "Privana";
3571
3941
  const [activeTab, setActiveTab] = useState(defaultTab);
3572
3942
  const [view, setView] = useState("method");
3573
3943
  const [source, setSource] = useState("connected");
3574
3944
  const [selectedTokenId, setSelectedTokenId] = useState(defaultToken?.id ?? "");
3575
3945
  const [amount, setAmount] = useState("");
3576
- const selectedToken = enabledTokens.find((t) => t.id === selectedTokenId) ?? defaultToken;
3946
+ const [cardFlow, setCardFlow] = useState(null);
3947
+ const [cardFlowActive, setCardFlowActive] = useState(false);
3948
+ const [cardUnsafeToClose, setCardUnsafeToClose] = useState(false);
3949
+ const nextCardFlowId = useRef(0);
3950
+ const stateSelectedToken = enabledTokens.find((t) => t.id === selectedTokenId) ?? defaultToken;
3951
+ const cardOnRamp = useMemo(
3952
+ () => resolveProductOnRamp({
3953
+ config: onRamp,
3954
+ enabledTokens,
3955
+ legacyToken: stateSelectedToken,
3956
+ moonpayApiKey: networkConfig.moonpayApiKey
3957
+ }),
3958
+ [enabledTokens, networkConfig.moonpayApiKey, onRamp, stateSelectedToken]
3959
+ );
3960
+ const cardOnRampScope = useMemo(
3961
+ () => ({
3962
+ apiUrl: privateReadApiUrl,
3963
+ accountingChainId,
3964
+ accountingContract: networkConfig.accountingContract,
3965
+ beneficiaryAddress
3966
+ }),
3967
+ [accountingChainId, beneficiaryAddress, networkConfig.accountingContract, privateReadApiUrl]
3968
+ );
3969
+ const mountedCardFlow = cardFlow && matchesProductOnRampScope(cardFlow.scope, cardOnRampScope) ? cardFlow : null;
3970
+ const displayedCardOnRamp = view === "credit-card-widget" && mountedCardFlow ? mountedCardFlow.selection : cardOnRamp;
3971
+ const selectedToken = source === "credit-card" ? displayedCardOnRamp.token : stateSelectedToken;
3577
3972
  const externalDepositAddress = useDepositAddress({
3578
3973
  enabled: source === "external" && (view === "deposit" || view === "external-deposit")
3579
3974
  });
@@ -3587,6 +3982,9 @@ function DepositModalContent({
3587
3982
  setView("method");
3588
3983
  setAmount("");
3589
3984
  setSelectedTokenId("");
3985
+ setCardFlow(null);
3986
+ setCardFlowActive(false);
3987
+ setCardUnsafeToClose(false);
3590
3988
  }
3591
3989
  }, [address, hostedAuthConfig]);
3592
3990
  const openDeposit = (next) => {
@@ -3609,9 +4007,33 @@ function DepositModalContent({
3609
4007
  }
3610
4008
  };
3611
4009
  const finishCardPurchase = () => {
4010
+ setCardFlowActive(false);
4011
+ setCardUnsafeToClose(false);
3612
4012
  setAmount("");
3613
4013
  onDepositSuccess?.();
3614
4014
  };
4015
+ const leaveCardPurchase = useCallback(() => {
4016
+ setCardFlow(null);
4017
+ setCardFlowActive(false);
4018
+ setCardUnsafeToClose(false);
4019
+ setAmount("");
4020
+ setView("deposit");
4021
+ }, []);
4022
+ useEffect(() => {
4023
+ if (cardFlow && !matchesProductOnRampScope(cardFlow.scope, cardOnRampScope)) {
4024
+ leaveCardPurchase();
4025
+ }
4026
+ }, [cardFlow, cardOnRampScope, leaveCardPurchase]);
4027
+ const cardOutcomeCallbacks = mountedCardFlow ? createProductOnRampOutcomeCallbacks({
4028
+ requiresLock: mountedCardFlow.requiresLock,
4029
+ onComplete: finishCardPurchase,
4030
+ onLockFailed: (err) => {
4031
+ setCardFlowActive(false);
4032
+ setCardUnsafeToClose(false);
4033
+ setLockFailure(err);
4034
+ onLockFailed?.(err);
4035
+ }
4036
+ }) : null;
3615
4037
  const {
3616
4038
  txHash,
3617
4039
  isGettingAddress,
@@ -3681,7 +4103,7 @@ function DepositModalContent({
3681
4103
  setView("deposit");
3682
4104
  }
3683
4105
  }, [allowance, externalLock.session, view]);
3684
- const isUnsafeToClose = (isGettingAddress || isSendingTransaction) && !cancelled || externalLock.isSigning || externalLock.isSubmittingLock;
4106
+ const isUnsafeToClose = (isGettingAddress || isSendingTransaction) && !cancelled || externalLock.isSigning || externalLock.isSubmittingLock || cardUnsafeToClose;
3685
4107
  useEffect(() => {
3686
4108
  onCloseBlockedChange?.(isUnsafeToClose);
3687
4109
  }, [isUnsafeToClose, onCloseBlockedChange]);
@@ -3724,6 +4146,28 @@ function DepositModalContent({
3724
4146
  return;
3725
4147
  }
3726
4148
  if (args.source === "credit-card") {
4149
+ if (cardFlow && cardFlowActive) {
4150
+ setView("credit-card-widget");
4151
+ return;
4152
+ }
4153
+ try {
4154
+ setCardFlow(
4155
+ createProductOnRampFlowSnapshot({
4156
+ id: ++nextCardFlowId.current,
4157
+ selection: cardOnRamp,
4158
+ amount: args.amount,
4159
+ allowance,
4160
+ lockServiceAddress: serviceAddress,
4161
+ moonpayApiKey: networkConfig.moonpayApiKey,
4162
+ scope: cardOnRampScope
4163
+ })
4164
+ );
4165
+ } catch (error) {
4166
+ toast.error(error instanceof Error ? error.message : "Card purchase is unavailable");
4167
+ return;
4168
+ }
4169
+ setCardFlowActive(false);
4170
+ setCardUnsafeToClose(false);
3727
4171
  setView("credit-card-widget");
3728
4172
  return;
3729
4173
  }
@@ -3801,6 +4245,12 @@ function DepositModalContent({
3801
4245
  setCancelled(false);
3802
4246
  resetDeposit();
3803
4247
  if (view === "external-deposit") setView("deposit");
4248
+ if (view === "credit-card-widget") {
4249
+ setCardFlow(null);
4250
+ setCardFlowActive(false);
4251
+ setCardUnsafeToClose(false);
4252
+ setView("deposit");
4253
+ }
3804
4254
  };
3805
4255
  const handleExternalLockRetry = () => {
3806
4256
  externalLock.retryAfterCredit().catch((err) => {
@@ -3824,12 +4274,20 @@ function DepositModalContent({
3824
4274
  retryVerification().catch(() => {
3825
4275
  });
3826
4276
  };
3827
- const back = view === "external-deposit" ? { to: "deposit", label: "Deposit from External Wallet" } : view === "credit-card-widget" ? { to: "deposit", label: "Buy with credit card and deposit" } : view === "select-token" ? { to: "deposit", label: "Deposit" } : { to: "method", label: "Deposit Method" };
4277
+ const back = view === "external-deposit" ? { to: "deposit", label: "Deposit from External Wallet" } : view === "credit-card-widget" ? { to: "deposit", label: "Buy with card and deposit" } : view === "select-token" ? { to: "deposit", label: "Deposit" } : { to: "method", label: "Deposit Method" };
3828
4278
  const goBack = () => {
3829
4279
  if (view === "external-deposit" && externalLock.session) {
3830
4280
  toast.error("Cancel the active deposit from the address screen before going back");
3831
4281
  return;
3832
4282
  }
4283
+ if (view === "credit-card-widget" && (cardUnsafeToClose || cardFlowActive)) {
4284
+ toast.error("Finish or recover the active card purchase before going back");
4285
+ return;
4286
+ }
4287
+ if (view === "credit-card-widget") {
4288
+ setCardFlow(null);
4289
+ setCardUnsafeToClose(false);
4290
+ }
3833
4291
  if (back.to === "method") {
3834
4292
  setAmount("");
3835
4293
  setSelectedTokenId("");
@@ -3915,8 +4373,8 @@ function DepositModalContent({
3915
4373
  ] }),
3916
4374
  activeView === "method" && /* @__PURE__ */ jsxs("div", { className: "bg-muted flex flex-col gap-6 rounded-[10px] p-5", children: [
3917
4375
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3918
- /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: activeTab === "crypto" ? "Choose the deposit method" : "Buy with credit card and deposit" }),
3919
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: activeTab === "crypto" ? "Choose the deposit method." : "Use credit card to buy crypto and deposit" })
4376
+ /* @__PURE__ */ jsx("h2", { className: "text-foreground text-[28px] leading-8 font-medium", children: activeTab === "crypto" ? "Choose the deposit method" : "Buy with card and deposit" }),
4377
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm", children: activeTab === "crypto" ? "Choose the deposit method." : "Buy crypto by card and deposit it into your account." })
3920
4378
  ] }),
3921
4379
  /* @__PURE__ */ jsx(MethodTabs, { activeTab, onTabChange: setActiveTab }),
3922
4380
  activeTab === "crypto" ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
@@ -3945,8 +4403,8 @@ function DepositModalContent({
3945
4403
  ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx(
3946
4404
  MethodOption,
3947
4405
  {
3948
- title: "Moonpay",
3949
- description: "on selected address",
4406
+ title: "Buy with card",
4407
+ description: "Purchase and deposit directly into your account.",
3950
4408
  onClick: () => {
3951
4409
  onSelectCreditCard?.();
3952
4410
  openDeposit("credit-card");
@@ -3954,21 +4412,29 @@ function DepositModalContent({
3954
4412
  }
3955
4413
  ) })
3956
4414
  ] }),
3957
- activeView === "deposit" && /* @__PURE__ */ jsx(MoonPayGate, { enabled: source === "credit-card", children: /* @__PURE__ */ jsx(
3958
- DepositView,
4415
+ activeView === "deposit" && /* @__PURE__ */ jsx(
4416
+ MoonPayGate,
3959
4417
  {
3960
- source,
3961
- selectedToken,
3962
- amount,
3963
- allowance,
3964
- externalMinimum,
3965
- onAmountChange: setAmount,
3966
- onSelectToken: () => setView("select-token"),
3967
- onConnectWallet,
3968
- onSubmit: handleSubmit,
3969
- isSubmitting: isPending || externalLock.isSigning || externalLock.isSubmittingLock
4418
+ enabled: source === "credit-card" && cardOnRamp.provider === "moonpay" && !cardOnRamp.unavailableReason,
4419
+ apiKey: networkConfig.moonpayApiKey,
4420
+ children: /* @__PURE__ */ jsx(
4421
+ DepositView,
4422
+ {
4423
+ source,
4424
+ selectedToken,
4425
+ onRamp: cardOnRamp,
4426
+ amount,
4427
+ allowance,
4428
+ externalMinimum,
4429
+ onAmountChange: setAmount,
4430
+ onSelectToken: () => setView("select-token"),
4431
+ onConnectWallet,
4432
+ onSubmit: handleSubmit,
4433
+ isSubmitting: isPending || externalLock.isSigning || externalLock.isSubmittingLock
4434
+ }
4435
+ )
3970
4436
  }
3971
- ) }),
4437
+ ),
3972
4438
  activeView === "select-token" && /* @__PURE__ */ jsx(
3973
4439
  TokenSelectorView,
3974
4440
  {
@@ -3990,20 +4456,30 @@ function DepositModalContent({
3990
4456
  lock: externalLock.session ? externalLock : void 0
3991
4457
  }
3992
4458
  ),
3993
- activeView === "credit-card-widget" && /* @__PURE__ */ jsx(MoonPayGate, { enabled: true, children: /* @__PURE__ */ jsx(
3994
- CreditCardWidgetView,
4459
+ activeView === "credit-card-widget" && mountedCardFlow && /* @__PURE__ */ jsx(
4460
+ MoonPayGate,
3995
4461
  {
3996
- token: selectedToken,
3997
- amount,
3998
- allowance,
3999
- onCredited: allowance ? void 0 : finishCardPurchase,
4000
- onLockSubmitted: finishCardPurchase,
4001
- onLockFailed: (err) => {
4002
- setLockFailure(err);
4003
- onLockFailed?.(err);
4004
- }
4462
+ enabled: mountedCardFlow.selection.provider === "moonpay",
4463
+ apiKey: mountedCardFlow.moonpayApiKey,
4464
+ children: /* @__PURE__ */ jsx(
4465
+ CreditCardWidgetView,
4466
+ {
4467
+ token: mountedCardFlow.selection.token,
4468
+ onRamp: mountedCardFlow.selection,
4469
+ amount: mountedCardFlow.amount,
4470
+ allowance: mountedCardFlow.allowance,
4471
+ lockServiceAddress: mountedCardFlow.lockServiceAddress,
4472
+ onUnsafeToCloseChange: setCardUnsafeToClose,
4473
+ onActiveFlowChange: setCardFlowActive,
4474
+ onCredited: cardOutcomeCallbacks?.onCredited,
4475
+ onLockSubmitted: cardOutcomeCallbacks?.onLockSubmitted,
4476
+ onLockFailed: cardOutcomeCallbacks?.onLockFailed,
4477
+ onLeave: leaveCardPurchase
4478
+ },
4479
+ mountedCardFlow.id
4480
+ )
4005
4481
  }
4006
- ) })
4482
+ )
4007
4483
  ] });
4008
4484
  }
4009
4485
  function DepositModal({ open, onClose, ...handlers }) {