@pollar/react 0.10.1-rc.0 → 0.10.1-rc.2

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
@@ -1035,7 +1035,7 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.10.1-rc.0"
1038
+ "0.10.1-rc.2"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
@@ -2459,20 +2459,45 @@ function RouteDisplay({ quote, onSelect }) {
2459
2459
  );
2460
2460
  }
2461
2461
  var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
2462
- var COUNTRY_CURRENCIES = {
2463
- MX: "MXN",
2464
- BR: "BRL",
2465
- CO: "COP",
2466
- CL: "CLP",
2467
- PE: "PEN",
2468
- AR: "ARS"
2469
- };
2462
+ function flagEmoji(code) {
2463
+ const cc = code.trim().toUpperCase();
2464
+ if (!/^[A-Z]{2}$/.test(cc)) return "";
2465
+ const base = 127462;
2466
+ return String.fromCodePoint(base + (cc.charCodeAt(0) - 65), base + (cc.charCodeAt(1) - 65));
2467
+ }
2470
2468
  var STATUS_LABEL = {
2471
2469
  pending: "Pending",
2472
2470
  processing: "Processing",
2473
2471
  completed: "Completed",
2474
2472
  failed: "Failed"
2475
2473
  };
2474
+ var INSTRUCTION_LABELS = {
2475
+ br_code: "Pix code",
2476
+ account_holder_name: "Account holder",
2477
+ bank_name: "Bank",
2478
+ bank_address: "Bank address",
2479
+ bank_account_number: "Account number",
2480
+ bank_routing_number: "Routing number",
2481
+ iban: "IBAN",
2482
+ bic: "BIC",
2483
+ clabe: "CLABE",
2484
+ amount: "Amount",
2485
+ currency: "Currency",
2486
+ payment_rails: "Rails"
2487
+ };
2488
+ function flattenInstructions(instr) {
2489
+ const out = [];
2490
+ for (const [k, v] of Object.entries(instr)) {
2491
+ let value = "";
2492
+ if (typeof v === "string" || typeof v === "number") value = String(v);
2493
+ else if (Array.isArray(v))
2494
+ value = v.filter((x) => typeof x === "string" || typeof x === "number").join(", ");
2495
+ else continue;
2496
+ if (!value) continue;
2497
+ out.push({ key: k, label: INSTRUCTION_LABELS[k] ?? k, value });
2498
+ }
2499
+ return out;
2500
+ }
2476
2501
  function RampWidgetTemplate({
2477
2502
  theme,
2478
2503
  accentColor,
@@ -2481,24 +2506,33 @@ function RampWidgetTemplate({
2481
2506
  amount,
2482
2507
  currency,
2483
2508
  country,
2509
+ email,
2510
+ fullName,
2511
+ countries,
2512
+ countriesLoading,
2513
+ refreshing,
2484
2514
  quotes,
2485
2515
  isLoading,
2486
2516
  provider,
2487
2517
  txStatus,
2488
2518
  kycUrl,
2489
2519
  stellarTxHash,
2520
+ depositInstructions,
2490
2521
  canComplete,
2491
2522
  completing,
2492
2523
  errorMsg,
2493
2524
  onDirectionChange,
2494
2525
  onAmountChange,
2495
- onCurrencyChange,
2526
+ onEmailChange,
2527
+ onFullNameChange,
2496
2528
  onCountryChange,
2497
2529
  onFindRoute,
2498
2530
  onSelectQuote,
2531
+ onContactContinue,
2499
2532
  onOpenKyc,
2500
2533
  onCompleteWithdraw,
2501
2534
  onRetry,
2535
+ onRefresh,
2502
2536
  onClose
2503
2537
  }) {
2504
2538
  const isDark = theme === "dark";
@@ -2526,6 +2560,7 @@ function RampWidgetTemplate({
2526
2560
  input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
2527
2561
  loading_quote: "Finding best route",
2528
2562
  select_route: "Select provider",
2563
+ contact: "Your details",
2529
2564
  status: direction === "onramp" ? "Complete your deposit" : "Complete your withdrawal",
2530
2565
  error: "Something went wrong"
2531
2566
  };
@@ -2533,13 +2568,40 @@ function RampWidgetTemplate({
2533
2568
  input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
2534
2569
  loading_quote: "Comparing providers in real time\u2026",
2535
2570
  select_route: "All prices include fees",
2571
+ contact: `${provider || "This provider"} needs your name and email to verify you`,
2536
2572
  status: `Finish the flow at ${provider || "the provider"} to continue`,
2537
2573
  error: "Please try again"
2538
2574
  };
2539
2575
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2540
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header", children: [
2541
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-ramp-title", children: stepTitle[step] }),
2542
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
2576
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
2577
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header-text", children: [
2578
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: stepTitle[step] }),
2579
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
2580
+ ] }),
2581
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header-actions", children: [
2582
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onRefresh, disabled: refreshing, "aria-label": "Refresh", children: /* @__PURE__ */ jsxRuntime.jsx(
2583
+ "svg",
2584
+ {
2585
+ className: refreshing ? "pollar-modal-refresh-icon spinning" : "pollar-modal-refresh-icon",
2586
+ width: "16",
2587
+ height: "16",
2588
+ viewBox: "0 0 16 16",
2589
+ fill: "none",
2590
+ "aria-hidden": true,
2591
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2592
+ "path",
2593
+ {
2594
+ d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
2595
+ stroke: "currentColor",
2596
+ strokeWidth: "1.6",
2597
+ strokeLinecap: "round",
2598
+ strokeLinejoin: "round"
2599
+ }
2600
+ )
2601
+ }
2602
+ ) }),
2603
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2604
+ ] })
2543
2605
  ] }),
2544
2606
  step === "input" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2545
2607
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-tabs", children: [
@@ -2564,62 +2626,44 @@ function RampWidgetTemplate({
2564
2626
  }
2565
2627
  )
2566
2628
  ] }),
2567
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-input-row", children: [
2568
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2569
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Amount" }),
2570
- /* @__PURE__ */ jsxRuntime.jsx(
2571
- "input",
2572
- {
2573
- type: "number",
2574
- className: "pollar-ramp-input",
2575
- placeholder: "0.00",
2576
- value: amount,
2577
- min: "0",
2578
- onChange: (e) => onAmountChange(e.target.value)
2579
- }
2580
- )
2581
- ] }),
2582
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", style: { maxWidth: 90 }, children: [
2583
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Currency" }),
2584
- /* @__PURE__ */ jsxRuntime.jsx(
2585
- "input",
2586
- {
2587
- type: "text",
2588
- className: "pollar-ramp-input",
2589
- placeholder: "MXN",
2590
- value: currency,
2591
- maxLength: 5,
2592
- onChange: (e) => onCurrencyChange(e.target.value.toUpperCase())
2593
- }
2594
- )
2595
- ] })
2596
- ] }),
2597
2629
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2598
2630
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Country" }),
2599
- /* @__PURE__ */ jsxRuntime.jsxs(
2600
- "select",
2631
+ countriesLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-input pollar-ramp-input-loading", children: "Loading countries\u2026" }) : countries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsxRuntime.jsx("select", { className: "pollar-ramp-input", value: country, onChange: (e) => onCountryChange(e.target.value), children: countries.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.code, children: [
2632
+ flagEmoji(c.code),
2633
+ " ",
2634
+ c.code,
2635
+ c.currency ? ` \u2014 ${c.currency}` : ""
2636
+ ] }, c.code)) })
2637
+ ] }),
2638
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2639
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "pollar-ramp-label", children: [
2640
+ "Amount",
2641
+ currency ? ` (${currency})` : ""
2642
+ ] }),
2643
+ /* @__PURE__ */ jsxRuntime.jsx(
2644
+ "input",
2601
2645
  {
2646
+ type: "number",
2602
2647
  className: "pollar-ramp-input",
2603
- value: country,
2604
- onChange: (e) => {
2605
- const c = e.target.value;
2606
- onCountryChange(c);
2607
- if (COUNTRY_CURRENCIES[c]) onCurrencyChange(COUNTRY_CURRENCIES[c]);
2608
- },
2609
- children: [
2610
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "MX", children: "\u{1F1F2}\u{1F1FD} Mexico" }),
2611
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "BR", children: "\u{1F1E7}\u{1F1F7} Brazil" }),
2612
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CO", children: "\u{1F1E8}\u{1F1F4} Colombia" }),
2613
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CL", children: "\u{1F1E8}\u{1F1F1} Chile" }),
2614
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "PE", children: "\u{1F1F5}\u{1F1EA} Peru" }),
2615
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "AR", children: "\u{1F1E6}\u{1F1F7} Argentina" })
2616
- ]
2648
+ placeholder: "0.00",
2649
+ value: amount,
2650
+ min: "0",
2651
+ onChange: (e) => onAmountChange(e.target.value)
2617
2652
  }
2618
2653
  )
2619
2654
  ] }),
2620
2655
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2621
2656
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
2622
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", disabled: !amount || isLoading, onClick: onFindRoute, children: "Find best route" })
2657
+ /* @__PURE__ */ jsxRuntime.jsx(
2658
+ "button",
2659
+ {
2660
+ type: "button",
2661
+ className: "pollar-btn-primary",
2662
+ disabled: !amount || isLoading || countriesLoading || countries.length === 0,
2663
+ onClick: onFindRoute,
2664
+ children: "Find best route"
2665
+ }
2666
+ )
2623
2667
  ] })
2624
2668
  ] }),
2625
2669
  step === "loading_quote" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-loading", children: LOADING_STEPS.map((text, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-loading-row", children: [
@@ -2630,6 +2674,48 @@ function RampWidgetTemplate({
2630
2674
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx(RouteDisplay, { quote: q, onSelect: onSelectQuote }, i)) }),
2631
2675
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
2632
2676
  ] }),
2677
+ step === "contact" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2679
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Full name" }),
2680
+ /* @__PURE__ */ jsxRuntime.jsx(
2681
+ "input",
2682
+ {
2683
+ type: "text",
2684
+ className: "pollar-ramp-input",
2685
+ placeholder: "Jane Doe",
2686
+ value: fullName,
2687
+ onChange: (e) => onFullNameChange(e.target.value)
2688
+ }
2689
+ )
2690
+ ] }),
2691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2692
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Email" }),
2693
+ /* @__PURE__ */ jsxRuntime.jsx(
2694
+ "input",
2695
+ {
2696
+ type: "email",
2697
+ className: "pollar-ramp-input",
2698
+ placeholder: "jane@example.com",
2699
+ value: email,
2700
+ autoComplete: "email",
2701
+ onChange: (e) => onEmailChange(e.target.value)
2702
+ }
2703
+ )
2704
+ ] }),
2705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2706
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetry, children: "Back" }),
2707
+ /* @__PURE__ */ jsxRuntime.jsx(
2708
+ "button",
2709
+ {
2710
+ type: "button",
2711
+ className: "pollar-btn-primary",
2712
+ disabled: !fullName.trim() || !email.trim() || isLoading,
2713
+ onClick: onContactContinue,
2714
+ children: isLoading ? "Starting\u2026" : "Continue"
2715
+ }
2716
+ )
2717
+ ] })
2718
+ ] }),
2633
2719
  step === "status" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment", children: [
2634
2720
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2635
2721
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Provider" }),
@@ -2637,13 +2723,7 @@ function RampWidgetTemplate({
2637
2723
  ] }),
2638
2724
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2639
2725
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Status" }),
2640
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx(
2641
- "code",
2642
- {
2643
- style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 },
2644
- children: txStatus ? STATUS_LABEL[txStatus] : "Processing"
2645
- }
2646
- ) })
2726
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx("code", { style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 }, children: txStatus ? STATUS_LABEL[txStatus] : "Processing" }) })
2647
2727
  ] }),
2648
2728
  stellarTxHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2649
2729
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Stellar tx" }),
@@ -2653,6 +2733,10 @@ function RampWidgetTemplate({
2653
2733
  stellarTxHash.slice(-8)
2654
2734
  ] }) })
2655
2735
  ] }),
2736
+ depositInstructions && txStatus !== "completed" && flattenInstructions(depositInstructions).map(({ key, label, value }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2737
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: label }),
2738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx("code", { style: { wordBreak: "break-all" }, children: value }) })
2739
+ ] }, key)),
2656
2740
  kycUrl && txStatus !== "completed" && /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenKyc, children: [
2657
2741
  "Continue at ",
2658
2742
  provider
@@ -2679,15 +2763,22 @@ function RampWidget({ onClose }) {
2679
2763
  const [step, setStep] = react.useState("input");
2680
2764
  const [direction, setDirection] = react.useState("onramp");
2681
2765
  const [amount, setAmount] = react.useState("");
2682
- const [currency, setCurrency] = react.useState("ARS");
2683
- const [country, setCountry] = react.useState("AR");
2766
+ const [currency, setCurrency] = react.useState("");
2767
+ const [country, setCountry] = react.useState("");
2768
+ const [email, setEmail] = react.useState("");
2769
+ const [fullName, setFullName] = react.useState("");
2770
+ const [countries, setCountries] = react.useState([]);
2771
+ const [countriesLoading, setCountriesLoading] = react.useState(true);
2772
+ const [refreshing, setRefreshing] = react.useState(false);
2684
2773
  const [quotes, setQuotes] = react.useState([]);
2774
+ const [selectedQuote, setSelectedQuote] = react.useState(null);
2685
2775
  const [isLoading, setIsLoading] = react.useState(false);
2686
2776
  const [txId, setTxId] = react.useState(null);
2687
2777
  const [provider, setProvider] = react.useState("");
2688
2778
  const [kycUrl, setKycUrl] = react.useState(null);
2689
2779
  const [txStatus, setTxStatus] = react.useState(null);
2690
2780
  const [stellarTxHash, setStellarTxHash] = react.useState(null);
2781
+ const [depositInstructions, setDepositInstructions] = react.useState(null);
2691
2782
  const [completing, setCompleting] = react.useState(false);
2692
2783
  const [errorMsg, setErrorMsg] = react.useState(null);
2693
2784
  const directionRef = react.useRef(direction);
@@ -2703,6 +2794,8 @@ function RampWidget({ onClose }) {
2703
2794
  setTxStatus(tx.status);
2704
2795
  if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
2705
2796
  if (tx.kycUrl) setKycUrl(tx.kycUrl);
2797
+ const di = tx.depositInstructions;
2798
+ if (di) setDepositInstructions(di);
2706
2799
  if (TERMINAL.includes(tx.status)) clearInterval(id);
2707
2800
  } catch {
2708
2801
  }
@@ -2712,14 +2805,62 @@ function RampWidget({ onClose }) {
2712
2805
  clearInterval(id);
2713
2806
  };
2714
2807
  }, [step, txId, txStatus, client]);
2808
+ async function loadCountries(resetSelection) {
2809
+ setCountriesLoading(true);
2810
+ try {
2811
+ const { countries: list } = await client.getRampCountries();
2812
+ setCountries(list);
2813
+ const first = list[0];
2814
+ const stillValid = list.some((c) => c.code === country);
2815
+ if (first && (resetSelection || !stillValid)) {
2816
+ setCountry(first.code);
2817
+ if (first.currency) setCurrency(first.currency);
2818
+ }
2819
+ } catch {
2820
+ setCountries([]);
2821
+ } finally {
2822
+ setCountriesLoading(false);
2823
+ }
2824
+ }
2825
+ react.useEffect(() => {
2826
+ loadCountries(true);
2827
+ }, []);
2828
+ function handleCountryChange(code) {
2829
+ setCountry(code);
2830
+ const match = countries.find((c) => c.code === code);
2831
+ if (match?.currency) setCurrency(match.currency);
2832
+ }
2833
+ async function handleRefresh() {
2834
+ if (refreshing) return;
2835
+ setRefreshing(true);
2836
+ try {
2837
+ await loadCountries(false);
2838
+ if (step === "select_route") {
2839
+ const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
2840
+ setQuotes(result.quotes ?? []);
2841
+ } else if (step === "status" && txId) {
2842
+ const tx = await client.getRampTransaction(txId);
2843
+ setTxStatus(tx.status);
2844
+ if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
2845
+ if (tx.kycUrl) setKycUrl(tx.kycUrl);
2846
+ const di = tx.depositInstructions;
2847
+ if (di) setDepositInstructions(di);
2848
+ }
2849
+ } catch {
2850
+ } finally {
2851
+ setRefreshing(false);
2852
+ }
2853
+ }
2715
2854
  function resetToInput() {
2716
2855
  setStep("input");
2717
2856
  setQuotes([]);
2857
+ setSelectedQuote(null);
2718
2858
  setTxId(null);
2719
2859
  setProvider("");
2720
2860
  setKycUrl(null);
2721
2861
  setTxStatus(null);
2722
2862
  setStellarTxHash(null);
2863
+ setDepositInstructions(null);
2723
2864
  setErrorMsg(null);
2724
2865
  }
2725
2866
  async function resumeWithSignature(id, ps) {
@@ -2745,6 +2886,7 @@ function RampWidget({ onClose }) {
2745
2886
  setKycUrl(result.kycUrl ?? null);
2746
2887
  setTxStatus(result.status);
2747
2888
  setStellarTxHash(result.stellarTxHash ?? null);
2889
+ setDepositInstructions(result.depositInstructions ?? null);
2748
2890
  setStep("status");
2749
2891
  }
2750
2892
  async function handleFindRoute() {
@@ -2768,12 +2910,33 @@ function RampWidget({ onClose }) {
2768
2910
  setIsLoading(false);
2769
2911
  }
2770
2912
  }
2771
- async function handleSelectQuote(quote) {
2913
+ function handleSelectQuote(quote) {
2914
+ setSelectedQuote(quote);
2915
+ setErrorMsg(null);
2916
+ const requiresContact = quote.requiresContact ?? false;
2917
+ if (requiresContact && (!email.trim() || !fullName.trim())) {
2918
+ setStep("contact");
2919
+ return;
2920
+ }
2921
+ void startRamp(quote);
2922
+ }
2923
+ function handleContactContinue() {
2924
+ if (selectedQuote) void startRamp(selectedQuote);
2925
+ }
2926
+ async function startRamp(quote) {
2772
2927
  setIsLoading(true);
2773
2928
  setErrorMsg(null);
2774
2929
  try {
2775
- const base = { quoteId: quote.quoteId, amount: Number(amount), currency, country };
2776
- const result = direction === "onramp" ? await client.createOnRamp({ ...base, ...walletAddress ? { walletAddress } : {} }) : await client.createOffRamp({ ...base, ...walletAddress ? { walletAddress } : {} });
2930
+ const base = {
2931
+ quoteId: quote.quoteId,
2932
+ amount: Number(amount),
2933
+ currency,
2934
+ country,
2935
+ ...email.trim() ? { email: email.trim() } : {},
2936
+ ...fullName.trim() ? { fullName: fullName.trim() } : {},
2937
+ ...walletAddress ? { walletAddress } : {}
2938
+ };
2939
+ const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
2777
2940
  await applyResult(result);
2778
2941
  } catch (e) {
2779
2942
  setErrorMsg(e instanceof Error ? e.message : "Failed to start the ramp.");
@@ -2817,24 +2980,33 @@ function RampWidget({ onClose }) {
2817
2980
  amount,
2818
2981
  currency,
2819
2982
  country,
2983
+ email,
2984
+ fullName,
2985
+ countries,
2986
+ countriesLoading,
2987
+ refreshing,
2820
2988
  quotes,
2821
2989
  isLoading,
2822
2990
  provider,
2823
2991
  txStatus,
2824
2992
  kycUrl,
2825
2993
  stellarTxHash,
2994
+ depositInstructions,
2826
2995
  canComplete,
2827
2996
  completing,
2828
2997
  errorMsg,
2829
2998
  onDirectionChange: setDirection,
2830
2999
  onAmountChange: setAmount,
2831
- onCurrencyChange: setCurrency,
2832
- onCountryChange: setCountry,
3000
+ onEmailChange: setEmail,
3001
+ onFullNameChange: setFullName,
3002
+ onCountryChange: handleCountryChange,
2833
3003
  onFindRoute: handleFindRoute,
2834
3004
  onSelectQuote: handleSelectQuote,
3005
+ onContactContinue: handleContactContinue,
2835
3006
  onOpenKyc: handleOpenKyc,
2836
3007
  onCompleteWithdraw: handleCompleteWithdraw,
2837
3008
  onRetry: resetToInput,
3009
+ onRefresh: handleRefresh,
2838
3010
  onClose
2839
3011
  }
2840
3012
  ) });
@@ -3544,6 +3716,7 @@ function SwapModalTemplate({
3544
3716
  onRefresh,
3545
3717
  onSelectSell,
3546
3718
  onSelectBuy,
3719
+ onAddCustomToken,
3547
3720
  onAmountChange,
3548
3721
  onProviderChange,
3549
3722
  onSwap,
@@ -3553,6 +3726,21 @@ function SwapModalTemplate({
3553
3726
  onDone
3554
3727
  }) {
3555
3728
  const isDark = theme === "dark";
3729
+ const [customOpen, setCustomOpen] = react.useState(false);
3730
+ const [customCode, setCustomCode] = react.useState("");
3731
+ const [customIssuer, setCustomIssuer] = react.useState("");
3732
+ const [customError, setCustomError] = react.useState("");
3733
+ const submitCustomToken = () => {
3734
+ const err = onAddCustomToken(customCode, customIssuer);
3735
+ if (err) {
3736
+ setCustomError(err);
3737
+ return;
3738
+ }
3739
+ setCustomCode("");
3740
+ setCustomIssuer("");
3741
+ setCustomError("");
3742
+ setCustomOpen(false);
3743
+ };
3556
3744
  const cssVars = {
3557
3745
  "--pollar-accent": accentColor,
3558
3746
  "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
@@ -3671,7 +3859,45 @@ function SwapModalTemplate({
3671
3859
  ] }, assetOptionKey(o)))
3672
3860
  ]
3673
3861
  }
3674
- )
3862
+ ),
3863
+ !isLoadingData && (customOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-custom", children: [
3864
+ /* @__PURE__ */ jsxRuntime.jsx(
3865
+ "input",
3866
+ {
3867
+ className: "pollar-input",
3868
+ type: "text",
3869
+ placeholder: "Code (e.g. USDC)",
3870
+ value: customCode,
3871
+ onChange: (e) => setCustomCode(e.target.value)
3872
+ }
3873
+ ),
3874
+ /* @__PURE__ */ jsxRuntime.jsx(
3875
+ "input",
3876
+ {
3877
+ className: "pollar-input",
3878
+ type: "text",
3879
+ placeholder: "Issuer (G...)",
3880
+ value: customIssuer,
3881
+ onChange: (e) => setCustomIssuer(e.target.value)
3882
+ }
3883
+ ),
3884
+ customError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: customError }),
3885
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-custom-actions", children: [
3886
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
3887
+ /* @__PURE__ */ jsxRuntime.jsx(
3888
+ "button",
3889
+ {
3890
+ type: "button",
3891
+ className: "pollar-swap-custom-cancel",
3892
+ onClick: () => {
3893
+ setCustomOpen(false);
3894
+ setCustomError("");
3895
+ },
3896
+ children: "Cancel"
3897
+ }
3898
+ )
3899
+ ] })
3900
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setCustomOpen(true), children: "+ Add a token by code / issuer" }))
3675
3901
  ] }),
3676
3902
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3677
3903
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "Route" }),
@@ -3779,6 +4005,7 @@ function SwapModal({ onClose }) {
3779
4005
  const [provider, setProvider] = react.useState("auto");
3780
4006
  const [venues, setVenues] = react.useState(null);
3781
4007
  const [catalogTokens, setCatalogTokens] = react.useState([]);
4008
+ const [customTokens, setCustomTokens] = react.useState([]);
3782
4009
  const [quotes, setQuotes] = react.useState([]);
3783
4010
  const [quoteLoading, setQuoteLoading] = react.useState(false);
3784
4011
  const [quoteError, setQuoteError] = react.useState("");
@@ -3836,7 +4063,21 @@ function SwapModal({ onClose }) {
3836
4063
  issuer: tk.issuer,
3837
4064
  enabledInApp: false
3838
4065
  })).filter((o) => !enabledKeys.has(optKey(o)));
3839
- const buyOptions = [...enabledBuy, ...catalogBuy].filter((o) => optKey(o) !== buyKeyOfSell);
4066
+ const knownKeys = /* @__PURE__ */ new Set([...enabledKeys, ...catalogBuy.map(optKey)]);
4067
+ const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o)));
4068
+ const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter(
4069
+ (o) => optKey(o) !== buyKeyOfSell
4070
+ );
4071
+ function addCustomToken(code, issuer) {
4072
+ const c = code.trim().toUpperCase();
4073
+ const i = issuer.trim();
4074
+ if (c.length < 1 || c.length > 12) return "Code must be 1-12 characters";
4075
+ if (i.length !== 56 || !i.startsWith("G")) return "Issuer must be a Stellar address (G..., 56 chars)";
4076
+ const opt = { ref: catalogRef(c, i), code: c, issuer: i, enabledInApp: false };
4077
+ setCustomTokens((prev) => prev.some((o) => optKey(o) === optKey(opt)) ? prev : [...prev, opt]);
4078
+ setSelectedBuy(opt);
4079
+ return null;
4080
+ }
3840
4081
  const buyNeedsTrustline = (() => {
3841
4082
  if (!selectedBuy || smartUnsupported) return false;
3842
4083
  if (selectedBuy.ref.type === "native") return false;
@@ -3976,6 +4217,7 @@ function SwapModal({ onClose }) {
3976
4217
  onRefresh: handleRefresh,
3977
4218
  onSelectSell: setSelectedSell,
3978
4219
  onSelectBuy: setSelectedBuy,
4220
+ onAddCustomToken: addCustomToken,
3979
4221
  onAmountChange: setAmount,
3980
4222
  onProviderChange: setProvider,
3981
4223
  onSwap: () => void handleSwap(),
@@ -4413,6 +4655,10 @@ function TxHistoryModalTemplate({
4413
4655
  /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: record.status }),
4414
4656
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-hist-item-meta", children: [
4415
4657
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: record.operation }),
4658
+ typeof record.details?.sponsored === "boolean" && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4659
+ "\xB7 ",
4660
+ record.details.sponsored ? "Sponsored" : "Self-paid"
4661
+ ] }),
4416
4662
  record.feeXlm && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4417
4663
  "\xB7 ",
4418
4664
  record.feeXlm,