@pollar/react 0.11.3 → 0.11.4-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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { AUTH_ERROR_CODES, WalletType, isPollarClient, PollarClient, isInteractiveAuthAdapter, toBaseUnits } from '@pollar/core';
2
+ import { AUTH_ERROR_CODES, WalletType, isPollarClient, PollarClient, isInteractiveAuthAdapter, isPollarApiError, toBaseUnits } from '@pollar/core';
3
3
  import { forwardRef, createContext, useState, useCallback, useEffect, useMemo, useRef, useContext, Component } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
@@ -1025,19 +1025,77 @@ var ModalErrorBoundary = class extends Component {
1025
1025
  return this.props.children;
1026
1026
  }
1027
1027
  };
1028
- var PollarModalFooter = () => {
1028
+ var PollarModalFooter = ({ shield = false }) => {
1029
1029
  return /* @__PURE__ */ jsxs("div", { className: "pollar-footer", children: [
1030
1030
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
1031
1031
  /* @__PURE__ */ jsxs("div", { className: "pollar-footer-brand", children: [
1032
- /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR, alt: "Pollar", className: "pollar-footer-logo" }),
1032
+ shield ? /* @__PURE__ */ jsxs("svg", { className: "pollar-footer-shield", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
1033
+ /* @__PURE__ */ jsx("path", { d: "M12 3.2 19 6v5.3c0 4.4-2.9 7.8-7 9.5-4.1-1.7-7-5.1-7-9.5V6l7-2.8Z", fill: "var(--pollar-accent)" }),
1034
+ /* @__PURE__ */ jsx("path", { d: "m8.5 12 2.2 2.2 4.8-5", stroke: "white", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })
1035
+ ] }) : /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR, alt: "Pollar", className: "pollar-footer-logo" }),
1033
1036
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1034
1037
  /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1035
1038
  "v",
1036
- "0.11.3"
1039
+ "0.11.4-rc.2"
1037
1040
  ] })
1038
1041
  ] })
1039
1042
  ] });
1040
1043
  };
1044
+ function RefreshIcon({ spinning = false, className = "" }) {
1045
+ return /* @__PURE__ */ jsxs(
1046
+ "svg",
1047
+ {
1048
+ className: `${spinning ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon"}${className ? ` ${className}` : ""}`,
1049
+ width: "17",
1050
+ height: "17",
1051
+ viewBox: "0 0 17 17",
1052
+ fill: "none",
1053
+ "aria-hidden": true,
1054
+ children: [
1055
+ /* @__PURE__ */ jsx(
1056
+ "path",
1057
+ {
1058
+ d: "M2.125 8.5C2.125 6.80924 2.79665 5.18774 3.99219 3.99219C5.18774 2.79665 6.80924 2.125 8.5 2.125C10.2822 2.1317 11.9928 2.82712 13.2742 4.06583L14.875 5.66667",
1059
+ stroke: "currentColor",
1060
+ strokeWidth: "2",
1061
+ strokeLinecap: "round",
1062
+ strokeLinejoin: "round"
1063
+ }
1064
+ ),
1065
+ /* @__PURE__ */ jsx(
1066
+ "path",
1067
+ {
1068
+ d: "M14.8747 2.125V5.66667H11.333",
1069
+ stroke: "currentColor",
1070
+ strokeWidth: "2",
1071
+ strokeLinecap: "round",
1072
+ strokeLinejoin: "round"
1073
+ }
1074
+ ),
1075
+ /* @__PURE__ */ jsx(
1076
+ "path",
1077
+ {
1078
+ d: "M14.875 8.5C14.875 10.1908 14.2033 11.8123 13.0078 13.0078C11.8123 14.2033 10.1908 14.875 8.5 14.875C6.7178 14.8683 5.00719 14.1729 3.72583 12.9342L2.125 11.3333",
1079
+ stroke: "currentColor",
1080
+ strokeWidth: "2",
1081
+ strokeLinecap: "round",
1082
+ strokeLinejoin: "round"
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsx(
1086
+ "path",
1087
+ {
1088
+ d: "M2.125 14.875V11.3333H5.66667",
1089
+ stroke: "currentColor",
1090
+ strokeWidth: "2",
1091
+ strokeLinecap: "round",
1092
+ strokeLinejoin: "round"
1093
+ }
1094
+ )
1095
+ ]
1096
+ }
1097
+ );
1098
+ }
1041
1099
  function CopyButton({ value, label = "Copy", className }) {
1042
1100
  const [copied, setCopied] = useState(false);
1043
1101
  const timerRef = useRef(null);
@@ -1825,9 +1883,10 @@ var STATUS_CONFIG = {
1825
1883
  none: { label: "Not started", color: "#6b7280", dot: false },
1826
1884
  pending: { label: "Pending review", color: "#f59e0b", dot: true },
1827
1885
  approved: { label: "Verified", color: "#10b981", dot: false },
1828
- rejected: { label: "Rejected", color: "#ef4444", dot: false }
1886
+ rejected: { label: "Rejected", color: "#ef4444", dot: false },
1887
+ expired: { label: "Expired", color: "#6b7280", dot: false }
1829
1888
  };
1830
- function KycStatus({ status, className }) {
1889
+ function KycStatus({ status, label, className }) {
1831
1890
  const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.none;
1832
1891
  return /* @__PURE__ */ jsxs(
1833
1892
  "span",
@@ -1836,11 +1895,42 @@ function KycStatus({ status, className }) {
1836
1895
  style: { "--pollar-kyc-color": config.color },
1837
1896
  children: [
1838
1897
  config.dot && /* @__PURE__ */ jsx("span", { className: "pollar-kyc-badge-dot" }),
1839
- config.label
1898
+ label ?? config.label
1840
1899
  ]
1841
1900
  }
1842
1901
  );
1843
1902
  }
1903
+
1904
+ // src/components/kyc-modal/kyc-messages.ts
1905
+ function kycErrorMessage(error, phase) {
1906
+ const code = error?.code;
1907
+ switch (code) {
1908
+ case "SDK_KYC_NOT_CONFIGURED":
1909
+ return "Identity verification is not available for this app yet.";
1910
+ case "SDK_KYC_PROVIDER_ERROR":
1911
+ return "The verification service is not responding right now. Please try again in a moment.";
1912
+ case "SDK_KYC_SESSION_EXPIRED":
1913
+ return "Your previous verification session expired. Select the provider to start a new one.";
1914
+ case "SDK_KYC_PROVIDER_NOT_FOUND":
1915
+ case "SDK_KYC_PROVIDER_NOT_ENABLED":
1916
+ return "This verification option is no longer available. Refresh to see the current options.";
1917
+ case "SDK_RAMPS_PROVIDER_NOT_CONFIGURED":
1918
+ return "This route is not set up for identity verification yet.";
1919
+ case "VALIDATION_ERROR":
1920
+ return "This verification option is not available for your country.";
1921
+ default:
1922
+ return phase === "load" ? "Could not load verification options. Please refresh to try again." : "Could not start verification. Please try again.";
1923
+ }
1924
+ }
1925
+ function kycReviewMessage(reviewReason) {
1926
+ if (reviewReason === "DUPLICATE_DOCUMENT") {
1927
+ return "This document is already linked to another account, so our team is reviewing it. You can check again later.";
1928
+ }
1929
+ return "Your verification is still being reviewed. You can check again shortly.";
1930
+ }
1931
+ function kycProcessingMessage() {
1932
+ return "Your verification was approved. We are finishing setting it up; check again in a minute.";
1933
+ }
1844
1934
  function KycModalTemplate({
1845
1935
  theme,
1846
1936
  accentColor,
@@ -1850,151 +1940,274 @@ function KycModalTemplate({
1850
1940
  selectedProvider,
1851
1941
  session,
1852
1942
  kycStatus,
1943
+ reviewReason,
1944
+ processing = false,
1853
1945
  isLoading,
1946
+ error,
1854
1947
  onSelectProvider,
1855
1948
  onDoneVerifying,
1949
+ onStartAgain,
1856
1950
  onRefresh,
1857
1951
  onClose
1858
1952
  }) {
1859
1953
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
1860
- return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-kyc-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
1861
- /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
1862
- /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-header-text", children: [
1863
- /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Identity verification" }),
1864
- /* @__PURE__ */ jsxs("p", { className: "pollar-kyc-subtitle", children: [
1865
- step === "select_provider" && "Choose your verification provider",
1866
- step === "verifying" && `Verifying with ${selectedProvider?.name}`,
1867
- step === "polling" && "Waiting for verification result",
1868
- step === "done" && "Verification complete"
1869
- ] })
1870
- ] }),
1871
- /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
1872
- step === "select_provider" && /* @__PURE__ */ jsx(
1873
- "button",
1874
- {
1875
- type: "button",
1876
- className: "pollar-modal-close",
1877
- onClick: onRefresh,
1878
- disabled: isLoading,
1879
- "aria-label": "Refresh",
1880
- title: "Refresh providers",
1881
- children: /* @__PURE__ */ jsx(
1882
- "svg",
1954
+ return /* @__PURE__ */ jsxs(
1955
+ "div",
1956
+ {
1957
+ className: `pollar-modal-card pollar-kyc-modal ${step === "verifying" ? "pollar-kyc-modal--verifying" : ""}`,
1958
+ role: "dialog",
1959
+ "aria-modal": "true",
1960
+ "aria-label": "Identity verification",
1961
+ style: cssVars,
1962
+ onClick: (e) => e.stopPropagation(),
1963
+ children: [
1964
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
1965
+ /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-header-text", children: [
1966
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Identity verification" }),
1967
+ /* @__PURE__ */ jsxs("p", { className: "pollar-kyc-subtitle", children: [
1968
+ step === "select_provider" && "Choose your verification provider",
1969
+ step === "verifying" && `Complete the steps below with ${selectedProvider?.name}`,
1970
+ step === "polling" && "Waiting for verification result",
1971
+ step === "done" && (kycStatus === "approved" ? "You\u2019re ready to continue" : "Your verification status")
1972
+ ] })
1973
+ ] }),
1974
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
1975
+ step === "select_provider" && /* @__PURE__ */ jsx(
1976
+ "button",
1883
1977
  {
1884
- className: isLoading ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon",
1885
- width: "16",
1886
- height: "16",
1887
- viewBox: "0 0 16 16",
1888
- fill: "none",
1889
- "aria-hidden": true,
1978
+ type: "button",
1979
+ className: "pollar-modal-close",
1980
+ onClick: onRefresh,
1981
+ disabled: isLoading,
1982
+ "aria-label": "Refresh",
1983
+ title: "Refresh providers",
1890
1984
  children: /* @__PURE__ */ jsx(
1891
- "path",
1985
+ "svg",
1892
1986
  {
1893
- d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
1894
- stroke: "currentColor",
1895
- strokeWidth: "1.6",
1896
- strokeLinecap: "round",
1897
- strokeLinejoin: "round"
1987
+ className: isLoading ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon",
1988
+ width: "16",
1989
+ height: "16",
1990
+ viewBox: "0 0 16 16",
1991
+ fill: "none",
1992
+ "aria-hidden": true,
1993
+ children: /* @__PURE__ */ jsx(
1994
+ "path",
1995
+ {
1996
+ d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
1997
+ stroke: "currentColor",
1998
+ strokeWidth: "1.6",
1999
+ strokeLinecap: "round",
2000
+ strokeLinejoin: "round"
2001
+ }
2002
+ )
1898
2003
  }
1899
2004
  )
1900
2005
  }
1901
- )
1902
- }
1903
- ),
1904
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
1905
- ] })
1906
- ] }),
1907
- step === "select_provider" && (isLoading && providers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", children: [
1908
- /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
1909
- /* @__PURE__ */ jsx("span", { children: "Loading providers\u2026" })
1910
- ] }) : /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-providers", children: [
1911
- providers.length === 0 && /* @__PURE__ */ jsx("p", { style: { color: "var(--pollar-muted)", textAlign: "center" }, children: "No providers available for your country." }),
1912
- providers.map((p) => /* @__PURE__ */ jsxs(
1913
- "button",
1914
- {
1915
- type: "button",
1916
- className: "pollar-kyc-provider-btn",
1917
- disabled: isLoading,
1918
- onClick: () => onSelectProvider(p),
1919
- children: [
1920
- /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-name", children: p.name }),
1921
- /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-flow", children: p.flow })
1922
- ]
1923
- },
1924
- p.id
1925
- ))
1926
- ] })),
1927
- step === "verifying" && selectedProvider && /* @__PURE__ */ jsxs(Fragment, { children: [
1928
- /* @__PURE__ */ jsx("div", { className: "pollar-kyc-iframe-wrap", children: session?.kycUrl ? /* @__PURE__ */ jsx("iframe", { className: "pollar-kyc-iframe", src: session.kycUrl, title: "KYC verification", allow: "camera; microphone" }) : /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-iframe-mock", children: [
1929
- /* @__PURE__ */ jsx("span", { children: "\u{1F512}" }),
1930
- /* @__PURE__ */ jsx("span", { children: selectedProvider.flow === "form" ? "The identity verification form will appear here." : "Identity verification will open here." })
1931
- ] }) }),
1932
- /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
1933
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
1934
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onDoneVerifying, children: "I've completed verification" })
1935
- ] })
1936
- ] }),
1937
- step === "polling" && /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-polling", children: [
1938
- /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
1939
- /* @__PURE__ */ jsx("p", { className: "pollar-kyc-polling-text", children: "Checking verification status\u2026" })
1940
- ] }),
1941
- step === "done" && /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-result", children: [
1942
- /* @__PURE__ */ jsx("span", { className: "pollar-kyc-result-icon", children: kycStatus === "approved" ? "\u2705" : "\u274C" }),
1943
- /* @__PURE__ */ jsx(KycStatus, { status: kycStatus }),
1944
- /* @__PURE__ */ jsx("p", { className: "pollar-kyc-result-text", children: kycStatus === "approved" ? "Your identity has been verified successfully." : "Verification was not approved. Please try again." }),
1945
- /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Close" }) })
1946
- ] })
1947
- ] });
2006
+ ),
2007
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2008
+ ] })
2009
+ ] }),
2010
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-kyc-error", role: "alert", children: error }),
2011
+ step === "select_provider" && (isLoading && providers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", children: [
2012
+ /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
2013
+ /* @__PURE__ */ jsx("span", { children: "Loading providers\u2026" })
2014
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-providers", children: [
2015
+ providers.length === 0 && /* @__PURE__ */ jsx("p", { style: { color: "var(--pollar-muted)", textAlign: "center" }, children: "No providers available for your country." }),
2016
+ providers.map((p) => /* @__PURE__ */ jsxs(
2017
+ "button",
2018
+ {
2019
+ type: "button",
2020
+ className: "pollar-kyc-provider-btn",
2021
+ disabled: isLoading,
2022
+ onClick: () => onSelectProvider(p),
2023
+ children: [
2024
+ /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-name", children: p.name }),
2025
+ /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-flow", children: isLoading && selectedProvider?.id === p.id ? "Opening\u2026" : p.flow === "redirect" ? "Continue in a new tab" : "Continue here" })
2026
+ ]
2027
+ },
2028
+ p.id
2029
+ ))
2030
+ ] })),
2031
+ step === "verifying" && selectedProvider && /* @__PURE__ */ jsxs(Fragment, { children: [
2032
+ /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-session-bar", children: [
2033
+ /* @__PURE__ */ jsxs("span", { className: "pollar-kyc-session-label", children: [
2034
+ /* @__PURE__ */ jsx("span", { className: "pollar-kyc-session-dot" }),
2035
+ "Verification in progress"
2036
+ ] }),
2037
+ session?.kycUrl && /* @__PURE__ */ jsx("a", { href: session.kycUrl, target: "_blank", rel: "noopener noreferrer", children: "Open in new tab \u2197" })
2038
+ ] }),
2039
+ /* @__PURE__ */ jsx("div", { className: "pollar-kyc-iframe-wrap", children: session?.kycUrl && selectedProvider.flow === "redirect" ? /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-iframe-mock", children: [
2040
+ /* @__PURE__ */ jsx("p", { children: "Complete verification in a separate tab, then return here to check your status." }),
2041
+ /* @__PURE__ */ jsx("a", { className: "pollar-btn-primary", href: session.kycUrl, target: "_blank", rel: "noopener noreferrer", children: "Open verification" })
2042
+ ] }) : session?.kycUrl ? /* @__PURE__ */ jsx("iframe", { className: "pollar-kyc-iframe", src: session.kycUrl, title: "KYC verification", allow: "camera; microphone" }) : /* @__PURE__ */ jsx("div", { className: "pollar-kyc-iframe-mock", children: /* @__PURE__ */ jsx("span", { children: selectedProvider.flow === "form" ? "The identity verification form will appear here." : "Identity verification will open here." }) }) }),
2043
+ /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-footer", children: [
2044
+ /* @__PURE__ */ jsx("p", { children: "Finished the steps? Check your status to continue." }),
2045
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
2046
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Close" }),
2047
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onDoneVerifying, children: "Check status" })
2048
+ ] })
2049
+ ] })
2050
+ ] }),
2051
+ step === "polling" && /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-polling", children: [
2052
+ /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
2053
+ /* @__PURE__ */ jsx("p", { className: "pollar-kyc-polling-text", children: "Checking verification status\u2026" })
2054
+ ] }),
2055
+ step === "done" && /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-result", children: [
2056
+ /* @__PURE__ */ jsx("span", { className: `pollar-kyc-result-icon pollar-kyc-result-icon--${kycStatus}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx(
2057
+ "svg",
2058
+ {
2059
+ width: "28",
2060
+ height: "28",
2061
+ viewBox: "0 0 24 24",
2062
+ fill: "none",
2063
+ stroke: "currentColor",
2064
+ strokeWidth: "1.5",
2065
+ strokeLinecap: "round",
2066
+ strokeLinejoin: "round",
2067
+ children: kycStatus === "approved" ? /* @__PURE__ */ jsx("path", { d: "m5 12 4 4L19 6" }) : kycStatus === "rejected" ? /* @__PURE__ */ jsx("path", { d: "m7 7 10 10M17 7 7 17" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2068
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "8" }),
2069
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
2070
+ ] })
2071
+ }
2072
+ ) }),
2073
+ /* @__PURE__ */ jsx(KycStatus, { status: kycStatus, ...processing ? { label: "Approved" } : {} }),
2074
+ /* @__PURE__ */ jsx("p", { className: "pollar-kyc-result-text", children: kycStatus === "approved" ? "Your identity has been verified successfully." : kycStatus === "rejected" ? "Your verification was not approved. Contact support for the next steps." : kycStatus === "expired" ? "Your verification expired. Start again to verify your identity." : processing ? kycProcessingMessage() : kycReviewMessage(reviewReason) }),
2075
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
2076
+ kycStatus === "expired" && onStartAgain && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onStartAgain, disabled: isLoading, children: "Start again" }),
2077
+ kycStatus !== "approved" && kycStatus !== "rejected" && kycStatus !== "expired" && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onDoneVerifying, children: "Check again" }),
2078
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Close" })
2079
+ ] })
2080
+ ] })
2081
+ ]
2082
+ }
2083
+ );
1948
2084
  }
1949
- function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
2085
+ function newIdempotencyKey() {
2086
+ const c = globalThis.crypto;
2087
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
2088
+ return `kyc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
2089
+ }
2090
+ function KycModal({ onClose, country = "MX", level = "basic", onApproved, corridorId, providerId }) {
1950
2091
  const { getClient, styles } = usePollar();
1951
2092
  const [step, setStep] = useState("select_provider");
1952
2093
  const [providers, setProviders] = useState([]);
1953
2094
  const [selectedProvider, setSelectedProvider] = useState(null);
1954
2095
  const [session, setSession] = useState(null);
1955
2096
  const [kycStatus, setKycStatus] = useState("none");
2097
+ const [reviewReason, setReviewReason] = useState(null);
2098
+ const [processing, setProcessing] = useState(false);
1956
2099
  const [isLoading, setIsLoading] = useState(false);
2100
+ const [error, setError] = useState(null);
2101
+ const idempotencyKeys = useRef({});
2102
+ const autoOpened = useRef(false);
1957
2103
  const client = getClient();
1958
2104
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
1959
- const loadProviders = useCallback(() => {
1960
- setIsLoading(true);
1961
- return getClient().getKycProviders(country).then((result) => setProviders(result.providers)).catch(() => setProviders([])).finally(() => setIsLoading(false));
1962
- }, [getClient, country]);
1963
- useEffect(() => {
1964
- void loadProviders();
1965
- }, [loadProviders]);
1966
- async function handleSelectProvider(provider) {
2105
+ function finish(status, reason = null, isProcessing = false) {
2106
+ setKycStatus(status);
2107
+ setReviewReason(reason);
2108
+ setProcessing(isProcessing);
2109
+ setStep("done");
2110
+ if (status === "approved") onApproved?.();
2111
+ }
2112
+ function finishWith(read) {
2113
+ if (read.status === "pending" && read.decisionStatus === "approved") finish("pending", null, true);
2114
+ else if (read.decisionStatus === "manual_review") finish("pending", read.reviewReason ?? null);
2115
+ else finish(read.decisionStatus === "expired" ? "expired" : read.status);
2116
+ }
2117
+ function readStatus(providerId2) {
2118
+ return corridorId ? client.getKycStatus(void 0, corridorId) : client.getKycStatus(providerId2);
2119
+ }
2120
+ const starting = useRef(false);
2121
+ async function handleSelectProvider(provider, listed = true) {
2122
+ var _a, _b;
2123
+ if (starting.current) return;
2124
+ starting.current = true;
1967
2125
  setSelectedProvider(provider);
2126
+ setError(null);
1968
2127
  setIsLoading(true);
2128
+ const key = (_a = idempotencyKeys.current)[_b = provider.id] ?? (_a[_b] = newIdempotencyKey());
1969
2129
  try {
1970
- const result = await client.resolveKyc(provider.id, level);
2130
+ const result = await client.resolveKyc(
2131
+ provider.id,
2132
+ provider.levels?.[0] ?? level,
2133
+ listed ? country : void 0,
2134
+ corridorId,
2135
+ key
2136
+ );
1971
2137
  if (result.alreadyApproved) {
1972
- setKycStatus("approved");
1973
- setStep("done");
1974
- onApproved?.();
2138
+ finish("approved");
1975
2139
  return;
1976
2140
  }
1977
2141
  setSession(result);
1978
2142
  setStep("verifying");
1979
- } catch {
2143
+ } catch (e) {
2144
+ const code = isPollarApiError(e) ? e.code : void 0;
2145
+ if (code === "SDK_KYC_ALREADY_APPROVED") {
2146
+ finish("approved");
2147
+ return;
2148
+ }
2149
+ if (code === "SDK_KYC_UNDER_REVIEW") {
2150
+ await readStatus(provider.id).then(finishWith, () => finish("pending"));
2151
+ return;
2152
+ }
2153
+ if (code === "SDK_KYC_SESSION_EXPIRED") delete idempotencyKeys.current[provider.id];
2154
+ setError(kycErrorMessage(e, "start"));
1980
2155
  setStep("select_provider");
1981
2156
  } finally {
2157
+ starting.current = false;
1982
2158
  setIsLoading(false);
1983
2159
  }
1984
2160
  }
2161
+ const loadProviders = useCallback(() => {
2162
+ setIsLoading(true);
2163
+ setError(null);
2164
+ return getClient().getKycProviders(country, corridorId).then((result) => {
2165
+ setProviders(result.providers);
2166
+ return result.providers;
2167
+ }).catch((e) => {
2168
+ setProviders([]);
2169
+ setError(kycErrorMessage(e, "load"));
2170
+ return [];
2171
+ }).finally(() => setIsLoading(false));
2172
+ }, [getClient, country, corridorId]);
2173
+ useEffect(() => {
2174
+ void loadProviders().then((list) => {
2175
+ if (!providerId || autoOpened.current) return;
2176
+ autoOpened.current = true;
2177
+ const target = list.find((p) => p.id === providerId);
2178
+ if (target) void handleSelectProvider(target);
2179
+ else
2180
+ void handleSelectProvider(
2181
+ { id: providerId, name: "Identity verification", flow: "iframe", levels: [level] },
2182
+ false
2183
+ );
2184
+ });
2185
+ }, [loadProviders, providerId]);
1985
2186
  async function handleDoneVerifying() {
1986
2187
  if (!selectedProvider) return;
2188
+ setError(null);
1987
2189
  setStep("polling");
1988
2190
  try {
1989
- const finalStatus = await client.pollKycStatus(selectedProvider.id, { intervalMs: 3e3, timeoutMs: 12e4 });
1990
- setKycStatus(finalStatus);
1991
- setStep("done");
1992
- if (finalStatus === "approved") onApproved?.();
2191
+ const read = await client.pollKycDecision(selectedProvider.id, {
2192
+ intervalMs: 3e3,
2193
+ timeoutMs: 12e4,
2194
+ ...corridorId ? { corridorId } : {}
2195
+ });
2196
+ finishWith(read);
1993
2197
  } catch {
1994
- setKycStatus("rejected");
1995
- setStep("done");
2198
+ setError("We could not confirm your result yet. Check again shortly; this does not mean your verification was rejected.");
2199
+ setStep("verifying");
1996
2200
  }
1997
2201
  }
2202
+ function handleStartAgain() {
2203
+ if (selectedProvider) delete idempotencyKeys.current[selectedProvider.id];
2204
+ setSession(null);
2205
+ setKycStatus("none");
2206
+ setReviewReason(null);
2207
+ setProcessing(false);
2208
+ if (selectedProvider) void handleSelectProvider(selectedProvider);
2209
+ else setStep("select_provider");
2210
+ }
1998
2211
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
1999
2212
  KycModalTemplate,
2000
2213
  {
@@ -2006,91 +2219,477 @@ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
2006
2219
  selectedProvider,
2007
2220
  session,
2008
2221
  kycStatus,
2222
+ reviewReason,
2223
+ processing,
2009
2224
  isLoading,
2225
+ error,
2010
2226
  onSelectProvider: handleSelectProvider,
2011
2227
  onDoneVerifying: handleDoneVerifying,
2228
+ onStartAgain: handleStartAgain,
2012
2229
  onRefresh: () => void loadProviders(),
2013
2230
  onClose
2014
2231
  }
2015
2232
  ) });
2016
2233
  }
2017
- function EmailCodeInput({ email, onSubmit }) {
2018
- const [digits, setDigits] = useState(["", "", "", "", "", ""]);
2019
- const inputRefs = useRef([]);
2020
- function submit(next) {
2021
- if (next.every(Boolean)) onSubmit(next.join(""));
2234
+
2235
+ // src/components/requirement-form-modal/form-fields.ts
2236
+ function formLanguage() {
2237
+ const language = typeof navigator !== "undefined" ? navigator.language : "";
2238
+ return language.toLowerCase().startsWith("es") ? "es" : "en";
2239
+ }
2240
+ function localized(text, language) {
2241
+ return (language === "es" ? text?.es || text?.en : text?.en || text?.es) ?? "";
2242
+ }
2243
+ function initialValues(fields, answers) {
2244
+ const values = {};
2245
+ for (const field of fields) {
2246
+ const answer = answers[field.key];
2247
+ if (field.type === "checkbox") values[field.key] = answer === true;
2248
+ else if (field.type === "multiselect") values[field.key] = Array.isArray(answer) ? answer : [];
2249
+ else values[field.key] = answer === void 0 || answer === null ? "" : String(answer);
2022
2250
  }
2023
- function handleChange(index, value) {
2024
- const cleaned = value.replace(/\D/g, "").slice(-1);
2025
- const next = digits.map((d, i) => i === index ? cleaned : d);
2026
- setDigits(next);
2027
- if (cleaned && index < 5) inputRefs.current[index + 1]?.focus();
2028
- submit(next);
2251
+ return values;
2252
+ }
2253
+ var DATE = /^\d{4}-\d{2}-\d{2}$/;
2254
+ function localFieldErrors(fields, values) {
2255
+ const errors = {};
2256
+ for (const field of fields) {
2257
+ const value = values[field.key];
2258
+ if (typeof value !== "string" || !value.trim()) continue;
2259
+ if (field.type === "number" && !Number.isFinite(Number(value))) errors[field.key] = "invalid_type";
2260
+ if (field.type === "date" && (!DATE.test(value.trim()) || Number.isNaN(Date.parse(`${value.trim()}T00:00:00Z`)))) {
2261
+ errors[field.key] = "invalid_format";
2262
+ }
2029
2263
  }
2030
- function handleKeyDown(index, e) {
2031
- if (e.key === "Backspace" && !digits[index] && index > 0) {
2032
- inputRefs.current[index - 1]?.focus();
2264
+ return errors;
2265
+ }
2266
+ function answersOf(fields, values) {
2267
+ const answers = {};
2268
+ for (const field of fields) {
2269
+ const value = values[field.key];
2270
+ if (field.type === "checkbox") {
2271
+ answers[field.key] = value === true;
2272
+ } else if (field.type === "multiselect") {
2273
+ if (Array.isArray(value) && value.length) answers[field.key] = value;
2274
+ } else if (typeof value === "string" && value.trim()) {
2275
+ answers[field.key] = field.type === "number" ? Number(value) : value.trim();
2033
2276
  }
2034
2277
  }
2035
- function handlePaste(e) {
2036
- e.preventDefault();
2037
- const text = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, 6);
2038
- const next = Array.from({ length: 6 }, (_, i) => text[i] ?? "");
2039
- setDigits(next);
2040
- inputRefs.current[Math.min(text.length - 1, 5)]?.focus();
2041
- submit(next);
2278
+ return answers;
2279
+ }
2280
+ var MESSAGES = {
2281
+ en: {
2282
+ required: "This field is required.",
2283
+ invalid_type: "This value is not valid.",
2284
+ invalid_option: "Choose one of the options.",
2285
+ too_long: "This is too long.",
2286
+ too_small: "This value is too small.",
2287
+ too_large: "This value is too large.",
2288
+ pattern: "This does not have the expected format.",
2289
+ invalid_format: "This does not have the expected format."
2290
+ },
2291
+ es: {
2292
+ required: "Este campo es obligatorio.",
2293
+ invalid_type: "Este valor no es v\xE1lido.",
2294
+ invalid_option: "Elige una de las opciones.",
2295
+ too_long: "Es demasiado largo.",
2296
+ too_small: "Este valor es demasiado bajo.",
2297
+ too_large: "Este valor es demasiado alto.",
2298
+ pattern: "No tiene el formato esperado.",
2299
+ invalid_format: "No tiene el formato esperado."
2042
2300
  }
2043
- return /* @__PURE__ */ jsxs("div", { className: "pollar-code-section", children: [
2044
- /* @__PURE__ */ jsxs("p", { className: "pollar-code-label", children: [
2045
- "Enter the 6-digit code sent to ",
2046
- email ? /* @__PURE__ */ jsx("strong", { children: email }) : "your email"
2047
- ] }),
2048
- /* @__PURE__ */ jsx("div", { className: "pollar-code-inputs", children: digits.map((digit, i) => /* @__PURE__ */ jsx(
2049
- "input",
2050
- {
2051
- ref: (el) => {
2052
- inputRefs.current[i] = el;
2053
- },
2054
- type: "text",
2055
- inputMode: "numeric",
2056
- maxLength: 2,
2057
- value: digit,
2058
- className: "pollar-code-input",
2059
- onChange: (e) => handleChange(i, e.target.value),
2060
- onKeyDown: (e) => handleKeyDown(i, e),
2061
- onPaste: handlePaste
2062
- },
2063
- i
2064
- )) })
2065
- ] });
2301
+ };
2302
+ function fieldErrorMessage(code, language) {
2303
+ return MESSAGES[language][code] ?? MESSAGES[language].invalid_type;
2066
2304
  }
2067
- var GithubButton = ({ disabled, onClick }) => {
2068
- return /* @__PURE__ */ jsxs("button", { className: "pollar-github-button", disabled, onClick, children: [
2069
- /* @__PURE__ */ jsx("svg", { className: "pollar-github-button-icon", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx(
2070
- "path",
2071
- {
2072
- fillRule: "evenodd",
2073
- d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z",
2074
- clipRule: "evenodd"
2075
- }
2076
- ) }),
2077
- /* @__PURE__ */ jsx("span", { className: "pollar-github-button-contents", children: "GitHub" })
2078
- ] });
2305
+ function fieldErrorsOf(error) {
2306
+ if (!error || typeof error !== "object") return null;
2307
+ const { code, body } = error;
2308
+ if (code !== "KYC_FORM_INVALID_ANSWERS" || !Array.isArray(body?.errors)) return null;
2309
+ const errors = {};
2310
+ for (const item of body.errors) {
2311
+ if (typeof item.key === "string" && typeof item.code === "string") errors[item.key] = item.code;
2312
+ }
2313
+ return Object.keys(errors).length ? errors : null;
2314
+ }
2315
+ var COPY = {
2316
+ en: {
2317
+ title: "A few more details",
2318
+ loading: "Loading the form\u2026",
2319
+ submit: "Continue",
2320
+ submitting: "Saving\u2026",
2321
+ close: "Close",
2322
+ loadError: "Could not load the form. Please try again.",
2323
+ submitError: "Could not save your answers. Please try again.",
2324
+ step: "Step {n} of {total}"
2325
+ },
2326
+ es: {
2327
+ title: "Unos datos m\xE1s",
2328
+ loading: "Cargando el formulario\u2026",
2329
+ submit: "Continuar",
2330
+ submitting: "Guardando\u2026",
2331
+ close: "Cerrar",
2332
+ loadError: "No se pudo cargar el formulario. Intenta de nuevo.",
2333
+ submitError: "No se pudieron guardar tus respuestas. Intenta de nuevo.",
2334
+ step: "Paso {n} de {total}"
2335
+ }
2079
2336
  };
2080
- var GoogleButton = ({ disabled, onClick }) => {
2081
- return /* @__PURE__ */ jsxs("button", { className: "pollar-gsi-material-button", disabled, onClick, children: [
2082
- /* @__PURE__ */ jsx("div", { className: "pollar-gsi-material-button-state" }),
2083
- /* @__PURE__ */ jsxs("div", { className: "pollar-gsi-material-button-content-wrapper", children: [
2084
- /* @__PURE__ */ jsx("div", { className: "pollar-gsi-material-button-icon", children: /* @__PURE__ */ jsxs("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 48 48", style: { display: "block" }, children: [
2337
+ function FieldInput({
2338
+ field,
2339
+ value,
2340
+ language,
2341
+ disabled,
2342
+ onChange
2343
+ }) {
2344
+ const id = `pollar-form-${field.key}`;
2345
+ const text = typeof value === "string" ? value : "";
2346
+ switch (field.type) {
2347
+ case "select":
2348
+ return /* @__PURE__ */ jsxs("select", { id, className: "pollar-input", value: text, disabled, onChange: (e) => onChange(e.target.value), children: [
2349
+ /* @__PURE__ */ jsx("option", { value: "" }),
2350
+ (field.options ?? []).map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: localized(option.label, language) }, option.value))
2351
+ ] });
2352
+ case "multiselect": {
2353
+ const selected = Array.isArray(value) ? value : [];
2354
+ return /* @__PURE__ */ jsx("div", { className: "pollar-form-choices", children: (field.options ?? []).map((option) => /* @__PURE__ */ jsxs("label", { className: "pollar-form-choice", children: [
2085
2355
  /* @__PURE__ */ jsx(
2086
- "path",
2356
+ "input",
2087
2357
  {
2088
- fill: "#EA4335",
2089
- d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
2358
+ type: "checkbox",
2359
+ checked: selected.includes(option.value),
2360
+ disabled,
2361
+ onChange: (e) => onChange(e.target.checked ? [...selected, option.value] : selected.filter((item) => item !== option.value))
2090
2362
  }
2091
2363
  ),
2092
- /* @__PURE__ */ jsx(
2093
- "path",
2364
+ localized(option.label, language)
2365
+ ] }, option.value)) });
2366
+ }
2367
+ case "textarea":
2368
+ return /* @__PURE__ */ jsx(
2369
+ "textarea",
2370
+ {
2371
+ id,
2372
+ className: "pollar-input pollar-form-textarea",
2373
+ value: text,
2374
+ disabled,
2375
+ maxLength: field.validation?.maxLength,
2376
+ onChange: (e) => onChange(e.target.value)
2377
+ }
2378
+ );
2379
+ default:
2380
+ return /* @__PURE__ */ jsx(
2381
+ "input",
2382
+ {
2383
+ id,
2384
+ className: "pollar-input",
2385
+ type: field.type === "phone" ? "tel" : field.type === "text" ? "text" : field.type,
2386
+ inputMode: field.type === "number" ? "decimal" : void 0,
2387
+ value: text,
2388
+ disabled,
2389
+ min: field.validation?.min,
2390
+ max: field.validation?.max,
2391
+ maxLength: field.validation?.maxLength,
2392
+ onChange: (e) => onChange(e.target.value)
2393
+ }
2394
+ );
2395
+ }
2396
+ }
2397
+ function RequirementFormModal({ formId, progress, onClose, onSubmitted }) {
2398
+ const { getClient, styles } = usePollar();
2399
+ const client = getClient();
2400
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
2401
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2402
+ const language = useMemo(formLanguage, []);
2403
+ const copy = COPY[language];
2404
+ const [form, setForm] = useState(null);
2405
+ const [values, setValues] = useState({});
2406
+ const [fieldErrors, setFieldErrors] = useState({});
2407
+ const [error, setError] = useState(null);
2408
+ const [submitting, setSubmitting] = useState(false);
2409
+ const mounted = useRef(true);
2410
+ useEffect(() => {
2411
+ mounted.current = true;
2412
+ return () => {
2413
+ mounted.current = false;
2414
+ };
2415
+ }, []);
2416
+ useEffect(() => {
2417
+ client.getRequirementForm(formId).then((loaded) => {
2418
+ if (!mounted.current) return;
2419
+ setForm(loaded);
2420
+ setValues(initialValues(loaded.fields, loaded.answers));
2421
+ }).catch(() => mounted.current && setError(copy.loadError));
2422
+ }, [formId]);
2423
+ async function submit() {
2424
+ if (!form) return;
2425
+ setError(null);
2426
+ const local = localFieldErrors(form.fields, values);
2427
+ if (Object.keys(local).length) return setFieldErrors(local);
2428
+ setSubmitting(true);
2429
+ setFieldErrors({});
2430
+ try {
2431
+ await client.submitRequirementForm(form.formId, answersOf(form.fields, values));
2432
+ if (mounted.current) onSubmitted();
2433
+ } catch (e) {
2434
+ if (!mounted.current) return;
2435
+ const byField = fieldErrorsOf(e);
2436
+ if (byField) setFieldErrors(byField);
2437
+ else setError(copy.submitError);
2438
+ } finally {
2439
+ if (mounted.current) setSubmitting(false);
2440
+ }
2441
+ }
2442
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
2443
+ "div",
2444
+ {
2445
+ className: "pollar-modal-card pollar-form-modal",
2446
+ role: "dialog",
2447
+ "aria-modal": "true",
2448
+ "aria-label": form?.name ?? copy.title,
2449
+ style: cssVars,
2450
+ onClick: (e) => e.stopPropagation(),
2451
+ children: [
2452
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
2453
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-header-text", children: [
2454
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: form?.name ?? copy.title }),
2455
+ progress && /* @__PURE__ */ jsx("p", { className: "pollar-form-subtitle", children: copy.step.replace("{n}", String(progress.position)).replace("{total}", String(progress.total)) })
2456
+ ] }),
2457
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": copy.close, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2458
+ ] }),
2459
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "alert", children: error }),
2460
+ !form && !error && /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", children: [
2461
+ /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
2462
+ /* @__PURE__ */ jsx("span", { children: copy.loading })
2463
+ ] }),
2464
+ form && /* @__PURE__ */ jsxs(
2465
+ "form",
2466
+ {
2467
+ className: "pollar-form-body",
2468
+ onSubmit: (e) => {
2469
+ e.preventDefault();
2470
+ void submit();
2471
+ },
2472
+ children: [
2473
+ form.description && /* @__PURE__ */ jsx("p", { className: "pollar-form-description", children: form.description }),
2474
+ form.fields.map((field) => {
2475
+ const fieldError = fieldErrors[field.key];
2476
+ const help = localized(field.help, language);
2477
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
2478
+ field.type === "checkbox" ? /* @__PURE__ */ jsxs("label", { className: "pollar-form-choice", children: [
2479
+ /* @__PURE__ */ jsx(
2480
+ "input",
2481
+ {
2482
+ type: "checkbox",
2483
+ checked: values[field.key] === true,
2484
+ disabled: submitting,
2485
+ onChange: (e) => setValues((v) => ({ ...v, [field.key]: e.target.checked }))
2486
+ }
2487
+ ),
2488
+ localized(field.label, language),
2489
+ field.required && /* @__PURE__ */ jsx("span", { className: "pollar-form-required", children: " *" })
2490
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2491
+ /* @__PURE__ */ jsxs("label", { htmlFor: `pollar-form-${field.key}`, className: "pollar-form-label", children: [
2492
+ localized(field.label, language),
2493
+ field.required && /* @__PURE__ */ jsx("span", { className: "pollar-form-required", children: " *" })
2494
+ ] }),
2495
+ /* @__PURE__ */ jsx(
2496
+ FieldInput,
2497
+ {
2498
+ field,
2499
+ value: values[field.key] ?? "",
2500
+ language,
2501
+ disabled: submitting,
2502
+ onChange: (value) => setValues((v) => ({ ...v, [field.key]: value }))
2503
+ }
2504
+ )
2505
+ ] }),
2506
+ help && /* @__PURE__ */ jsx("span", { className: "pollar-form-help", children: help }),
2507
+ fieldError && /* @__PURE__ */ jsx("span", { className: "pollar-form-field-error", role: "alert", children: fieldErrorMessage(fieldError, language) })
2508
+ ] }, field.key);
2509
+ }),
2510
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
2511
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, disabled: submitting, children: copy.close }),
2512
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "pollar-btn-primary", disabled: submitting, children: submitting ? copy.submitting : copy.submit })
2513
+ ] })
2514
+ ]
2515
+ }
2516
+ )
2517
+ ]
2518
+ }
2519
+ ) });
2520
+ }
2521
+ function AppKycFlow({ onClose, country, level, onApproved }) {
2522
+ const { getClient } = usePollar();
2523
+ const client = getClient();
2524
+ const [state, setState] = useState({ kind: "loading" });
2525
+ const kycStepDone = useRef(false);
2526
+ const firstRead = useRef(true);
2527
+ const loading = useRef(false);
2528
+ const mounted = useRef(true);
2529
+ useEffect(() => {
2530
+ mounted.current = true;
2531
+ return () => {
2532
+ mounted.current = false;
2533
+ };
2534
+ }, []);
2535
+ async function approvedOption(steps) {
2536
+ for (const step2 of steps) {
2537
+ if (step2.type !== "KYC") continue;
2538
+ for (const optionId of step2.optionIds) {
2539
+ const status = await client.getKycStatus(optionId).catch(() => null);
2540
+ if (status?.status === "approved") return optionId;
2541
+ }
2542
+ }
2543
+ return null;
2544
+ }
2545
+ const load = useCallback(async () => {
2546
+ if (loading.current) return;
2547
+ loading.current = true;
2548
+ kycStepDone.current = false;
2549
+ try {
2550
+ const requirements = await client.getAppRequirements();
2551
+ if (!mounted.current) return;
2552
+ if (!requirements.total) return setState({ kind: "options" });
2553
+ const first = firstRead.current;
2554
+ firstRead.current = false;
2555
+ if (requirements.next) return setState({ kind: "step", step: requirements.next });
2556
+ onApproved?.();
2557
+ const providerId = first ? await approvedOption(requirements.steps) : null;
2558
+ if (!mounted.current) return;
2559
+ if (providerId) setState({ kind: "done", providerId });
2560
+ else onClose();
2561
+ } catch {
2562
+ if (mounted.current) setState({ kind: "options" });
2563
+ } finally {
2564
+ loading.current = false;
2565
+ }
2566
+ }, []);
2567
+ useEffect(() => {
2568
+ void load();
2569
+ }, [load]);
2570
+ if (state.kind === "loading") return null;
2571
+ if (state.kind === "options") {
2572
+ return /* @__PURE__ */ jsx(
2573
+ KycModal,
2574
+ {
2575
+ onClose,
2576
+ ...country !== void 0 && { country },
2577
+ ...level !== void 0 && { level },
2578
+ ...onApproved !== void 0 && { onApproved }
2579
+ }
2580
+ );
2581
+ }
2582
+ if (state.kind === "done") {
2583
+ return /* @__PURE__ */ jsx(KycModal, { onClose, providerId: state.providerId, ...country !== void 0 && { country } });
2584
+ }
2585
+ const { step } = state;
2586
+ const progress = { position: step.completed + 1, total: step.total };
2587
+ if (step.type === "FORM") {
2588
+ return /* @__PURE__ */ jsx(
2589
+ RequirementFormModal,
2590
+ {
2591
+ formId: step.optionId,
2592
+ progress,
2593
+ onClose,
2594
+ onSubmitted: () => void load()
2595
+ },
2596
+ step.optionId
2597
+ );
2598
+ }
2599
+ return /* @__PURE__ */ jsx(
2600
+ KycModal,
2601
+ {
2602
+ providerId: step.optionId,
2603
+ ...country !== void 0 && { country },
2604
+ ...level !== void 0 && { level },
2605
+ onApproved: () => {
2606
+ kycStepDone.current = true;
2607
+ },
2608
+ onClose: () => {
2609
+ if (kycStepDone.current) void load();
2610
+ else onClose();
2611
+ }
2612
+ },
2613
+ step.optionId
2614
+ );
2615
+ }
2616
+ function EmailCodeInput({ email, onSubmit }) {
2617
+ const [digits, setDigits] = useState(["", "", "", "", "", ""]);
2618
+ const inputRefs = useRef([]);
2619
+ function submit(next) {
2620
+ if (next.every(Boolean)) onSubmit(next.join(""));
2621
+ }
2622
+ function handleChange(index, value) {
2623
+ const cleaned = value.replace(/\D/g, "").slice(-1);
2624
+ const next = digits.map((d, i) => i === index ? cleaned : d);
2625
+ setDigits(next);
2626
+ if (cleaned && index < 5) inputRefs.current[index + 1]?.focus();
2627
+ submit(next);
2628
+ }
2629
+ function handleKeyDown(index, e) {
2630
+ if (e.key === "Backspace" && !digits[index] && index > 0) {
2631
+ inputRefs.current[index - 1]?.focus();
2632
+ }
2633
+ }
2634
+ function handlePaste(e) {
2635
+ e.preventDefault();
2636
+ const text = e.clipboardData.getData("text").replace(/\D/g, "").slice(0, 6);
2637
+ const next = Array.from({ length: 6 }, (_, i) => text[i] ?? "");
2638
+ setDigits(next);
2639
+ inputRefs.current[Math.min(text.length - 1, 5)]?.focus();
2640
+ submit(next);
2641
+ }
2642
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-code-section", children: [
2643
+ /* @__PURE__ */ jsxs("p", { className: "pollar-code-label", children: [
2644
+ "Enter the 6-digit code sent to ",
2645
+ email ? /* @__PURE__ */ jsx("strong", { children: email }) : "your email"
2646
+ ] }),
2647
+ /* @__PURE__ */ jsx("div", { className: "pollar-code-inputs", children: digits.map((digit, i) => /* @__PURE__ */ jsx(
2648
+ "input",
2649
+ {
2650
+ ref: (el) => {
2651
+ inputRefs.current[i] = el;
2652
+ },
2653
+ type: "text",
2654
+ inputMode: "numeric",
2655
+ maxLength: 2,
2656
+ value: digit,
2657
+ className: "pollar-code-input",
2658
+ onChange: (e) => handleChange(i, e.target.value),
2659
+ onKeyDown: (e) => handleKeyDown(i, e),
2660
+ onPaste: handlePaste
2661
+ },
2662
+ i
2663
+ )) })
2664
+ ] });
2665
+ }
2666
+ var GithubButton = ({ disabled, onClick }) => {
2667
+ return /* @__PURE__ */ jsxs("button", { className: "pollar-github-button", disabled, onClick, children: [
2668
+ /* @__PURE__ */ jsx("svg", { className: "pollar-github-button-icon", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ jsx(
2669
+ "path",
2670
+ {
2671
+ fillRule: "evenodd",
2672
+ d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z",
2673
+ clipRule: "evenodd"
2674
+ }
2675
+ ) }),
2676
+ /* @__PURE__ */ jsx("span", { className: "pollar-github-button-contents", children: "GitHub" })
2677
+ ] });
2678
+ };
2679
+ var GoogleButton = ({ disabled, onClick }) => {
2680
+ return /* @__PURE__ */ jsxs("button", { className: "pollar-gsi-material-button", disabled, onClick, children: [
2681
+ /* @__PURE__ */ jsx("div", { className: "pollar-gsi-material-button-state" }),
2682
+ /* @__PURE__ */ jsxs("div", { className: "pollar-gsi-material-button-content-wrapper", children: [
2683
+ /* @__PURE__ */ jsx("div", { className: "pollar-gsi-material-button-icon", children: /* @__PURE__ */ jsxs("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 48 48", style: { display: "block" }, children: [
2684
+ /* @__PURE__ */ jsx(
2685
+ "path",
2686
+ {
2687
+ fill: "#EA4335",
2688
+ d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
2689
+ }
2690
+ ),
2691
+ /* @__PURE__ */ jsx(
2692
+ "path",
2094
2693
  {
2095
2694
  fill: "#4285F4",
2096
2695
  d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
@@ -2730,6 +3329,53 @@ function LoginModal({ onClose }) {
2730
3329
  }
2731
3330
  ) });
2732
3331
  }
3332
+
3333
+ // src/components/ramp-widget/ramp-kyc.ts
3334
+ var nonEmpty = (value) => typeof value === "string" && !!value.trim();
3335
+ function requiredRampKyc(error) {
3336
+ if (!error || typeof error !== "object") return null;
3337
+ const { code, body } = error;
3338
+ if (code !== "SDK_RAMPS_KYC_REQUIRED" || !body || typeof body !== "object") return null;
3339
+ const { rampProviderId, corridorId, requirementType, optionId, kycProviderId } = body;
3340
+ if (!nonEmpty(rampProviderId) || !nonEmpty(corridorId)) return null;
3341
+ if ((requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") && nonEmpty(optionId)) {
3342
+ return { rampProviderId, corridorId, type: requirementType, optionId };
3343
+ }
3344
+ const kyc = nonEmpty(optionId) && requirementType === "KYC" ? optionId : kycProviderId;
3345
+ return nonEmpty(kyc) ? { rampProviderId, corridorId, type: "KYC", optionId: kyc } : null;
3346
+ }
3347
+ function pendingFromQuote(requirement) {
3348
+ return {
3349
+ rampProviderId: requirement.rampProviderId,
3350
+ corridorId: requirement.corridorId,
3351
+ type: requirement.type,
3352
+ optionId: requirement.optionId,
3353
+ progress: { position: requirement.completed + 1, total: requirement.total }
3354
+ };
3355
+ }
3356
+ function lockedRouteCopy(requirement) {
3357
+ if (requirement.type === "FORM") return { message: "Answer a few questions to see this route", action: "Continue" };
3358
+ if (requirement.type === "PROVIDER_REGISTRATION") {
3359
+ return { message: "Register with the provider to see this route", action: "Continue" };
3360
+ }
3361
+ if (requirement.type === "REGISTRY_CHECK") {
3362
+ return requirement.status === "pending" ? { message: "Your details are under review", action: null } : { message: "Confirm your details to see this route", action: "Confirm" };
3363
+ }
3364
+ switch (requirement.status) {
3365
+ case "expired":
3366
+ return { message: "Your verification expired", action: "Verify again" };
3367
+ case "pending":
3368
+ if (!requirement.reviewReason) return { message: "Verification in progress", action: "Continue" };
3369
+ return {
3370
+ message: requirement.reviewReason === "DUPLICATE_DOCUMENT" ? kycReviewMessage(requirement.reviewReason) : "Your verification is under review",
3371
+ action: null
3372
+ };
3373
+ case "rejected":
3374
+ return { message: "Verification was not approved", action: null };
3375
+ default:
3376
+ return { message: "Verify your identity to see this route", action: "Verify" };
3377
+ }
3378
+ }
2733
3379
  var RAIL_LABELS = {
2734
3380
  SPEI: "SPEI (Mexico)",
2735
3381
  PIX: "PIX (Brazil)",
@@ -2770,6 +3416,34 @@ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
2770
3416
  }
2771
3417
  );
2772
3418
  }
3419
+ function LockedRouteDisplay({ requirement, disabled = false, onVerify }) {
3420
+ const { message, action } = lockedRouteCopy(requirement);
3421
+ return /* @__PURE__ */ jsxs(
3422
+ "div",
3423
+ {
3424
+ className: "pollar-ramp-route-card",
3425
+ "data-locked": true,
3426
+ "data-kyc-status": requirement.status,
3427
+ "data-disabled": disabled || void 0,
3428
+ children: [
3429
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
3430
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: requirement.provider }),
3431
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: message })
3432
+ ] }),
3433
+ action && /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: /* @__PURE__ */ jsx(
3434
+ "button",
3435
+ {
3436
+ type: "button",
3437
+ className: "pollar-btn-secondary pollar-ramp-route-verify",
3438
+ disabled,
3439
+ onClick: () => onVerify(requirement),
3440
+ children: action
3441
+ }
3442
+ ) })
3443
+ ]
3444
+ }
3445
+ );
3446
+ }
2773
3447
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2774
3448
  function isFieldValid(spec, raw) {
2775
3449
  const value = (raw ?? "").trim();
@@ -2777,6 +3451,9 @@ function isFieldValid(spec, raw) {
2777
3451
  if (spec.type === "email") return EMAIL_RE.test(value);
2778
3452
  return true;
2779
3453
  }
3454
+ function scannableImageSrc(image) {
3455
+ return image.encoding === "base64" ? `data:${image.mediaType};base64,${image.data}` : `data:${image.mediaType};charset=utf-8,${encodeURIComponent(image.data)}`;
3456
+ }
2780
3457
  function listOf(items) {
2781
3458
  const last = items[items.length - 1];
2782
3459
  if (last === void 0) return "a few details";
@@ -2827,12 +3504,14 @@ function RampWidgetTemplate({
2827
3504
  countriesLoading,
2828
3505
  refreshing,
2829
3506
  quotes,
3507
+ kycRequired,
2830
3508
  isLoading,
2831
3509
  provider,
2832
3510
  txStatus,
2833
3511
  kycUrl,
2834
3512
  tosUrl,
2835
3513
  kycBlocking,
3514
+ onboardingStatus,
2836
3515
  kycJustApproved,
2837
3516
  stellarTxHash,
2838
3517
  explorerUrl,
@@ -2840,12 +3519,14 @@ function RampWidgetTemplate({
2840
3519
  canComplete,
2841
3520
  completing,
2842
3521
  errorMsg,
3522
+ noticeMsg,
2843
3523
  onDirectionChange,
2844
3524
  onAmountChange,
2845
3525
  onFieldChange,
2846
3526
  onCountryChange,
2847
3527
  onFindRoute,
2848
3528
  onSelectQuote,
3529
+ onVerifyRoute,
2849
3530
  onContactContinue,
2850
3531
  onOpenKyc,
2851
3532
  onOpenTos,
@@ -3002,16 +3683,28 @@ function RampWidgetTemplate({
3002
3683
  ] }, i))
3003
3684
  ] }),
3004
3685
  step === "select_route" && /* @__PURE__ */ jsxs(Fragment, { children: [
3005
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsx(
3006
- RouteDisplay,
3007
- {
3008
- quote: q,
3009
- busy: startingQuoteId != null && q.quoteId === startingQuoteId,
3010
- disabled: startingQuoteId != null && q.quoteId !== startingQuoteId,
3011
- onSelect: onSelectQuote
3012
- },
3013
- i
3014
- )) }),
3686
+ noticeMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: noticeMsg }),
3687
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-list", children: [
3688
+ quotes.map((q, i) => /* @__PURE__ */ jsx(
3689
+ RouteDisplay,
3690
+ {
3691
+ quote: q,
3692
+ busy: startingQuoteId != null && q.quoteId === startingQuoteId,
3693
+ disabled: startingQuoteId != null && q.quoteId !== startingQuoteId,
3694
+ onSelect: onSelectQuote
3695
+ },
3696
+ i
3697
+ )),
3698
+ kycRequired.map((r) => /* @__PURE__ */ jsx(
3699
+ LockedRouteDisplay,
3700
+ {
3701
+ requirement: r,
3702
+ disabled: startingQuoteId != null,
3703
+ onVerify: onVerifyRoute
3704
+ },
3705
+ `${r.rampProviderId}:${r.corridorId}`
3706
+ ))
3707
+ ] }),
3015
3708
  errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
3016
3709
  /* @__PURE__ */ jsx("button", { type: "button", className: errorMsg ? "pollar-btn-primary" : "pollar-btn-secondary", onClick: onBack, children: "Back" })
3017
3710
  ] }),
@@ -3131,7 +3824,7 @@ function RampWidgetTemplate({
3131
3824
  ) : /* @__PURE__ */ jsx(
3132
3825
  "img",
3133
3826
  {
3134
- src: `data:${depositInstructions.scannable.image.mediaType};base64,${depositInstructions.scannable.image.data}`,
3827
+ src: scannableImageSrc(depositInstructions.scannable.image),
3135
3828
  alt: "Payment QR",
3136
3829
  style: { width: "100%", maxWidth: 220, height: "auto", display: "block", margin: "0 auto" }
3137
3830
  }
@@ -3165,15 +3858,24 @@ function RampWidgetTemplate({
3165
3858
  "Continue at ",
3166
3859
  provider
3167
3860
  ] }),
3168
- kycBlocking && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3861
+ kycBlocking && onboardingStatus === "awaiting_provider" && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3862
+ provider,
3863
+ " is still setting up your account for this payment method. Your verification is complete and there is nothing left for you to do \u2014 nothing has been sent, and this will update on its own once ",
3864
+ provider,
3865
+ " is ready."
3866
+ ] }),
3867
+ kycBlocking && onboardingStatus !== "awaiting_provider" && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3169
3868
  provider,
3170
3869
  " needs to verify your identity before this payout. Nothing has been sent yet \u2014 complete verification with ",
3171
3870
  provider,
3172
3871
  ", and this will update on its own."
3173
3872
  ] }),
3174
- kycJustApproved && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3175
- provider,
3176
- " approved your verification. Request a new quote to continue \u2014 the previous one was consumed."
3873
+ kycJustApproved && /* @__PURE__ */ jsxs(Fragment, { children: [
3874
+ /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
3875
+ provider,
3876
+ " approved your verification. Request a new quote to continue \u2014 the previous one was consumed."
3877
+ ] }),
3878
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onRetry, children: "Request a new quote" })
3177
3879
  ] }),
3178
3880
  canComplete && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxs(Fragment, { children: [
3179
3881
  /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
@@ -3191,6 +3893,383 @@ function RampWidgetTemplate({
3191
3893
  ] })
3192
3894
  ] });
3193
3895
  }
3896
+
3897
+ // src/components/registry-check-modal/registry-copy.ts
3898
+ var REGISTRY_COPY = {
3899
+ en: {
3900
+ title: "Confirm your details",
3901
+ intro: "We check these details with SEGIP, Bolivia's civil registry. They come from your verified document: you can only adjust how your surnames are split and your ID complement.",
3902
+ givenNames: "Names",
3903
+ surname1: "First surname",
3904
+ surname2: "Second surname",
3905
+ birthdate: "Date of birth",
3906
+ documentNumber: "ID number",
3907
+ complement: "Complement (if your ID has one)",
3908
+ submit: "Confirm",
3909
+ submitting: "Checking\u2026",
3910
+ close: "Close",
3911
+ approved: "Your details were confirmed.",
3912
+ review: "Your details are being reviewed. You can continue once they are confirmed.",
3913
+ notApplicable: "This check is only for Bolivian ID cards (CI). Your verified document is not one, so this route is not available.",
3914
+ identityRequired: "Verify your identity first.",
3915
+ nameMismatch: "Both surnames together must read as on your document: {full}.",
3916
+ rejected: "The registry did not confirm: {fields}. Check how they are written and try again.",
3917
+ loadError: "Could not load your details. Please try again.",
3918
+ submitError: "Could not check your details. Please try again.",
3919
+ step: "Step {n} of {total}"
3920
+ },
3921
+ es: {
3922
+ title: "Confirma tus datos",
3923
+ intro: "Validamos estos datos con el SEGIP, el registro civil de Bolivia. Vienen de tu documento verificado: solo puedes ajustar c\xF3mo se separan tus apellidos y el complemento de tu CI.",
3924
+ givenNames: "Nombres",
3925
+ surname1: "Primer apellido",
3926
+ surname2: "Segundo apellido",
3927
+ birthdate: "Fecha de nacimiento",
3928
+ documentNumber: "N\xFAmero de CI",
3929
+ complement: "Complemento (si tu CI lo tiene)",
3930
+ submit: "Confirmar",
3931
+ submitting: "Validando\u2026",
3932
+ close: "Cerrar",
3933
+ approved: "Tus datos fueron confirmados.",
3934
+ review: "Tus datos est\xE1n en revisi\xF3n. Podr\xE1s continuar cuando se confirmen.",
3935
+ notApplicable: "Esta validaci\xF3n es solo para c\xE9dulas bolivianas (CI). Tu documento verificado no lo es, as\xED que esta ruta no est\xE1 disponible.",
3936
+ identityRequired: "Primero verifica tu identidad.",
3937
+ nameMismatch: "Los dos apellidos juntos deben leerse como en tu documento: {full}.",
3938
+ rejected: "El registro no confirm\xF3: {fields}. Revisa c\xF3mo est\xE1n escritos e intenta de nuevo.",
3939
+ loadError: "No se pudieron cargar tus datos. Intenta de nuevo.",
3940
+ submitError: "No se pudieron validar tus datos. Intenta de nuevo.",
3941
+ step: "Paso {n} de {total}"
3942
+ }
3943
+ };
3944
+ var REGISTRATION_COPY = {
3945
+ en: {
3946
+ title: "Register with the payment provider",
3947
+ intro: "To use this route, the provider registers you as its customer. These details will be sent to it:",
3948
+ consent: "I agree to share these details with the provider.",
3949
+ submit: "Register",
3950
+ submitting: "Registering\u2026",
3951
+ close: "Close",
3952
+ notReady: "Complete the previous steps first.",
3953
+ missing: "Some details are missing: {fields}.",
3954
+ loadError: "Could not load the registration. Please try again.",
3955
+ submitError: "Could not register you. Please try again.",
3956
+ step: "Step {n} of {total}"
3957
+ },
3958
+ es: {
3959
+ title: "Registro con el proveedor de pagos",
3960
+ intro: "Para usar esta ruta, el proveedor te registra como su cliente. Se le enviar\xE1n estos datos:",
3961
+ consent: "Acepto compartir estos datos con el proveedor.",
3962
+ submit: "Registrarme",
3963
+ submitting: "Registrando\u2026",
3964
+ close: "Cerrar",
3965
+ notReady: "Primero completa los pasos anteriores.",
3966
+ missing: "Faltan algunos datos: {fields}.",
3967
+ loadError: "No se pudo cargar el registro. Intenta de nuevo.",
3968
+ submitError: "No se pudo completar el registro. Intenta de nuevo.",
3969
+ step: "Paso {n} de {total}"
3970
+ }
3971
+ };
3972
+ var SHARED_FIELD_LABELS = {
3973
+ en: {
3974
+ name: "Names",
3975
+ lastname: "Surnames",
3976
+ document_type: "Document type",
3977
+ document_number: "Document number",
3978
+ country: "Country",
3979
+ state_of_residence: "Department of residence",
3980
+ economic_activity: "Economic activity",
3981
+ source_of_funds: "Source of funds",
3982
+ destination_of_funds: "Use of funds",
3983
+ income_level: "Income level",
3984
+ doc_provider_id: "SEGIP validation"
3985
+ },
3986
+ es: {
3987
+ name: "Nombres",
3988
+ lastname: "Apellidos",
3989
+ document_type: "Tipo de documento",
3990
+ document_number: "N\xFAmero de documento",
3991
+ country: "Pa\xEDs",
3992
+ state_of_residence: "Departamento de residencia",
3993
+ economic_activity: "Actividad econ\xF3mica",
3994
+ source_of_funds: "Origen de los fondos",
3995
+ destination_of_funds: "Destino de los fondos",
3996
+ income_level: "Nivel de ingresos",
3997
+ doc_provider_id: "Validaci\xF3n SEGIP"
3998
+ }
3999
+ };
4000
+ var errorCode = (error) => error && typeof error === "object" && typeof error.code === "string" ? error.code : null;
4001
+ function missingFieldsOf(error) {
4002
+ const body = error && typeof error === "object" ? error.body : void 0;
4003
+ return Array.isArray(body?.missing) ? body.missing.filter((item) => typeof item === "string") : [];
4004
+ }
4005
+ function RegistryCheckModal({ optionId, progress, onClose, onApproved }) {
4006
+ const { getClient, styles } = usePollar();
4007
+ const client = getClient();
4008
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4009
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
4010
+ const language = useMemo(formLanguage, []);
4011
+ const copy = REGISTRY_COPY[language];
4012
+ const [check, setCheck] = useState(null);
4013
+ const [surname1, setSurname1] = useState("");
4014
+ const [surname2, setSurname2] = useState("");
4015
+ const [complement, setComplement] = useState("");
4016
+ const [error, setError] = useState(null);
4017
+ const [review, setReview] = useState(false);
4018
+ const [submitting, setSubmitting] = useState(false);
4019
+ const mounted = useRef(true);
4020
+ useEffect(() => {
4021
+ mounted.current = true;
4022
+ return () => {
4023
+ mounted.current = false;
4024
+ };
4025
+ }, []);
4026
+ useEffect(() => {
4027
+ client.getRegistryCheck(optionId).then((loaded) => {
4028
+ if (!mounted.current) return;
4029
+ if (loaded.status === "approved") return onApproved();
4030
+ setCheck(loaded);
4031
+ if (loaded.status === "pending") setReview(true);
4032
+ if (loaded.status === "rejected") setError(rejectedMessage(loaded.rejectedFields));
4033
+ if (loaded.prefill.applies) {
4034
+ setSurname1(loaded.prefill.surname1);
4035
+ setSurname2(loaded.prefill.surname2 ?? "");
4036
+ setComplement(loaded.prefill.complementNumber ?? "");
4037
+ }
4038
+ }).catch((e) => {
4039
+ if (!mounted.current) return;
4040
+ setError(errorCode(e) === "KYC_REGISTRY_IDENTITY_REQUIRED" ? copy.identityRequired : copy.loadError);
4041
+ });
4042
+ }, [optionId]);
4043
+ const prefill = check?.prefill.applies ? check.prefill : null;
4044
+ function rejectedMessage(fields) {
4045
+ const labels = { surname1: copy.surname1, surname2: copy.surname2, complementNumber: copy.complement };
4046
+ const named = (fields ?? []).map((field) => labels[field] ?? field);
4047
+ return copy.rejected.replace("{fields}", named.length ? named.join(", ") : copy.surname2);
4048
+ }
4049
+ async function submit() {
4050
+ if (!prefill) return;
4051
+ setSubmitting(true);
4052
+ setError(null);
4053
+ try {
4054
+ const result = await client.submitRegistryCheck(optionId, {
4055
+ surname1: surname1.trim(),
4056
+ ...surname2.trim() ? { surname2: surname2.trim() } : {},
4057
+ ...complement.trim() ? { complementNumber: complement.trim() } : {}
4058
+ });
4059
+ if (!mounted.current) return;
4060
+ if (result.status === "approved") onApproved();
4061
+ else if (result.status === "rejected") setError(rejectedMessage(result.rejectedFields));
4062
+ else setReview(true);
4063
+ } catch (e) {
4064
+ if (!mounted.current) return;
4065
+ const code = errorCode(e);
4066
+ if (code === "SDK_KYC_ALREADY_APPROVED") return onApproved();
4067
+ if (code === "KYC_REGISTRY_NAME_MISMATCH") {
4068
+ setError(copy.nameMismatch.replace("{full}", [prefill.surname1, prefill.surname2].filter(Boolean).join(" ")));
4069
+ } else if (code === "SDK_KYC_UNDER_REVIEW") setReview(true);
4070
+ else setError(copy.submitError);
4071
+ } finally {
4072
+ if (mounted.current) setSubmitting(false);
4073
+ }
4074
+ }
4075
+ const readOnly = (label, value) => /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4076
+ /* @__PURE__ */ jsx("span", { className: "pollar-form-label", children: label }),
4077
+ /* @__PURE__ */ jsx("input", { className: "pollar-input", value, readOnly: true, disabled: true })
4078
+ ] });
4079
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
4080
+ "div",
4081
+ {
4082
+ className: "pollar-modal-card pollar-form-modal",
4083
+ role: "dialog",
4084
+ "aria-modal": "true",
4085
+ "aria-label": copy.title,
4086
+ style: cssVars,
4087
+ onClick: (e) => e.stopPropagation(),
4088
+ children: [
4089
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4090
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-header-text", children: [
4091
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: copy.title }),
4092
+ progress && /* @__PURE__ */ jsx("p", { className: "pollar-form-subtitle", children: copy.step.replace("{n}", String(progress.position)).replace("{total}", String(progress.total)) })
4093
+ ] }),
4094
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": copy.close, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
4095
+ ] }),
4096
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "alert", children: error }),
4097
+ !check && !error && /* @__PURE__ */ jsx("div", { className: "pollar-loading-block", children: /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }) }),
4098
+ check && (review || !prefill) && /* @__PURE__ */ jsxs("div", { className: "pollar-form-body", children: [
4099
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "status", children: review ? copy.review : copy.notApplicable }),
4100
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: copy.close }) })
4101
+ ] }),
4102
+ prefill && !review && /* @__PURE__ */ jsxs(
4103
+ "form",
4104
+ {
4105
+ className: "pollar-form-body",
4106
+ onSubmit: (e) => {
4107
+ e.preventDefault();
4108
+ void submit();
4109
+ },
4110
+ children: [
4111
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-description", children: copy.intro }),
4112
+ readOnly(copy.givenNames, prefill.givenNames),
4113
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4114
+ /* @__PURE__ */ jsxs("label", { htmlFor: "pollar-registry-surname1", className: "pollar-form-label", children: [
4115
+ copy.surname1,
4116
+ /* @__PURE__ */ jsx("span", { className: "pollar-form-required", children: " *" })
4117
+ ] }),
4118
+ /* @__PURE__ */ jsx(
4119
+ "input",
4120
+ {
4121
+ id: "pollar-registry-surname1",
4122
+ className: "pollar-input",
4123
+ value: surname1,
4124
+ required: true,
4125
+ disabled: submitting,
4126
+ onChange: (e) => setSurname1(e.target.value)
4127
+ }
4128
+ )
4129
+ ] }),
4130
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4131
+ /* @__PURE__ */ jsx("label", { htmlFor: "pollar-registry-surname2", className: "pollar-form-label", children: copy.surname2 }),
4132
+ /* @__PURE__ */ jsx(
4133
+ "input",
4134
+ {
4135
+ id: "pollar-registry-surname2",
4136
+ className: "pollar-input",
4137
+ value: surname2,
4138
+ disabled: submitting,
4139
+ onChange: (e) => setSurname2(e.target.value)
4140
+ }
4141
+ )
4142
+ ] }),
4143
+ readOnly(copy.birthdate, prefill.birthdate),
4144
+ readOnly(copy.documentNumber, prefill.documentNumber),
4145
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4146
+ /* @__PURE__ */ jsx("label", { htmlFor: "pollar-registry-complement", className: "pollar-form-label", children: copy.complement }),
4147
+ /* @__PURE__ */ jsx(
4148
+ "input",
4149
+ {
4150
+ id: "pollar-registry-complement",
4151
+ className: "pollar-input",
4152
+ value: complement,
4153
+ maxLength: 3,
4154
+ disabled: submitting,
4155
+ onChange: (e) => setComplement(e.target.value.toUpperCase().replace(/[^0-9A-Z]/g, ""))
4156
+ }
4157
+ )
4158
+ ] }),
4159
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4160
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, disabled: submitting, children: copy.close }),
4161
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "pollar-btn-primary", disabled: submitting || !surname1.trim(), children: submitting ? copy.submitting : copy.submit })
4162
+ ] })
4163
+ ]
4164
+ }
4165
+ )
4166
+ ]
4167
+ }
4168
+ ) });
4169
+ }
4170
+ function ProviderRegistrationModal({ corridorId, progress, onClose, onRegistered }) {
4171
+ const { getClient, styles } = usePollar();
4172
+ const client = getClient();
4173
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4174
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
4175
+ const language = useMemo(formLanguage, []);
4176
+ const copy = REGISTRATION_COPY[language];
4177
+ const labels = SHARED_FIELD_LABELS[language];
4178
+ const [registration, setRegistration] = useState(null);
4179
+ const [consent, setConsent] = useState(false);
4180
+ const [error, setError] = useState(null);
4181
+ const [submitting, setSubmitting] = useState(false);
4182
+ const mounted = useRef(true);
4183
+ useEffect(() => {
4184
+ mounted.current = true;
4185
+ return () => {
4186
+ mounted.current = false;
4187
+ };
4188
+ }, []);
4189
+ useEffect(() => {
4190
+ client.getProviderRegistration(corridorId).then((loaded) => {
4191
+ if (!mounted.current) return;
4192
+ if (loaded.status === "registered") onRegistered();
4193
+ else setRegistration(loaded);
4194
+ }).catch(() => mounted.current && setError(copy.loadError));
4195
+ }, [corridorId]);
4196
+ async function submit() {
4197
+ setSubmitting(true);
4198
+ setError(null);
4199
+ try {
4200
+ await client.submitProviderRegistration(corridorId);
4201
+ if (mounted.current) onRegistered();
4202
+ } catch (e) {
4203
+ if (!mounted.current) return;
4204
+ const code = errorCode(e);
4205
+ if (code === "KYC_REGISTRATION_MISSING_DATA") {
4206
+ setError(
4207
+ copy.missing.replace(
4208
+ "{fields}",
4209
+ missingFieldsOf(e).map((key) => labels[key] ?? key).join(", ")
4210
+ )
4211
+ );
4212
+ } else if (code === "KYC_REGISTRATION_STEPS_PENDING") setError(copy.notReady);
4213
+ else setError(copy.submitError);
4214
+ } finally {
4215
+ if (mounted.current) setSubmitting(false);
4216
+ }
4217
+ }
4218
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
4219
+ "div",
4220
+ {
4221
+ className: "pollar-modal-card pollar-form-modal",
4222
+ role: "dialog",
4223
+ "aria-modal": "true",
4224
+ "aria-label": copy.title,
4225
+ style: cssVars,
4226
+ onClick: (e) => e.stopPropagation(),
4227
+ children: [
4228
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4229
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-header-text", children: [
4230
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: copy.title }),
4231
+ progress && /* @__PURE__ */ jsx("p", { className: "pollar-form-subtitle", children: copy.step.replace("{n}", String(progress.position)).replace("{total}", String(progress.total)) })
4232
+ ] }),
4233
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": copy.close, children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
4234
+ ] }),
4235
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "alert", children: error }),
4236
+ !registration && !error && /* @__PURE__ */ jsx("div", { className: "pollar-loading-block", children: /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }) }),
4237
+ registration && /* @__PURE__ */ jsxs(
4238
+ "form",
4239
+ {
4240
+ className: "pollar-form-body",
4241
+ onSubmit: (e) => {
4242
+ e.preventDefault();
4243
+ void submit();
4244
+ },
4245
+ children: [
4246
+ registration.ready ? /* @__PURE__ */ jsxs(Fragment, { children: [
4247
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-description", children: copy.intro }),
4248
+ /* @__PURE__ */ jsx("ul", { className: "pollar-form-description", children: registration.fields.map((key) => /* @__PURE__ */ jsx("li", { children: labels[key] ?? key }, key)) }),
4249
+ /* @__PURE__ */ jsxs("label", { className: "pollar-form-choice", children: [
4250
+ /* @__PURE__ */ jsx(
4251
+ "input",
4252
+ {
4253
+ type: "checkbox",
4254
+ checked: consent,
4255
+ disabled: submitting,
4256
+ onChange: (e) => setConsent(e.target.checked)
4257
+ }
4258
+ ),
4259
+ copy.consent
4260
+ ] })
4261
+ ] }) : /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "status", children: copy.notReady }),
4262
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4263
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, disabled: submitting, children: copy.close }),
4264
+ registration.ready && /* @__PURE__ */ jsx("button", { type: "submit", className: "pollar-btn-primary", disabled: submitting || !consent, children: submitting ? copy.submitting : copy.submit })
4265
+ ] })
4266
+ ]
4267
+ }
4268
+ )
4269
+ ]
4270
+ }
4271
+ ) });
4272
+ }
3194
4273
  var TERMINAL = ["completed", "failed"];
3195
4274
  function requiredFieldsOf(quote) {
3196
4275
  return quote.requiredFields ?? [];
@@ -3223,7 +4302,12 @@ var RAMP_ERROR_MESSAGES = {
3223
4302
  SDK_RAMPS_WALLET_UNSUPPORTED: "This wallet type cannot be used for this ramp.",
3224
4303
  SDK_RAMPS_PROVIDER_NOT_CONFIGURED: "This ramp provider is not configured for this app yet.",
3225
4304
  SDK_RAMPS_ANCHOR_ERROR: "The provider rejected the request. Please try again in a moment.",
3226
- SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment."
4305
+ SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment.",
4306
+ SDK_RAMPS_ETHERFUSE_ERROR: "The provider rejected the request. Please try again in a moment.",
4307
+ SDK_RAMPS_INSUFFICIENT_BALANCE: "This wallet does not hold enough to cover that amount. Try a smaller one.",
4308
+ // Nothing moved on-chain, so the balance is untouched. Almost always no XLM
4309
+ // for the network fee on a wallet the app does not sponsor.
4310
+ SDK_RAMPS_ONCHAIN_SUBMIT_FAILED: "The network rejected the transaction, so nothing was sent. The wallet may need XLM for the fee."
3227
4311
  };
3228
4312
  function rampErrorMessage(e, fallback) {
3229
4313
  const body = e?.body;
@@ -3253,14 +4337,25 @@ function RampWidget({ onClose }) {
3253
4337
  const [countriesLoading, setCountriesLoading] = useState(true);
3254
4338
  const [refreshing, setRefreshing] = useState(false);
3255
4339
  const [quotes, setQuotes] = useState([]);
4340
+ const [requirementsRequired, setRequirementsRequired] = useState([]);
3256
4341
  const [selectedQuote, setSelectedQuote] = useState(null);
3257
4342
  const [isLoading, setIsLoading] = useState(false);
4343
+ const [pendingKyc, setPendingKyc] = useState(null);
4344
+ const [noticeMsg, setNoticeMsg] = useState(null);
4345
+ const kycAttempt = useRef(null);
4346
+ useEffect(
4347
+ () => () => {
4348
+ kycAttempt.current = null;
4349
+ },
4350
+ []
4351
+ );
3258
4352
  const [txId, setTxId] = useState(null);
3259
4353
  const [provider, setProvider] = useState("");
3260
4354
  const [kycUrl, setKycUrl] = useState(null);
3261
4355
  const [tosUrl, setTosUrl] = useState(null);
3262
4356
  const [kycPending, setKycPending] = useState(false);
3263
4357
  const [kycApproved, setKycApproved] = useState(false);
4358
+ const [onboardingStatus, setOnboardingStatus] = useState(void 0);
3264
4359
  const [txStatus, setTxStatus] = useState(null);
3265
4360
  const [stellarTxHash, setStellarTxHash] = useState(null);
3266
4361
  const [depositInstructions, setDepositInstructions] = useState(null);
@@ -3291,6 +4386,7 @@ function RampWidget({ onClose }) {
3291
4386
  }, [step, txId, txStatus, client]);
3292
4387
  useEffect(() => {
3293
4388
  if (step !== "status" || !kycPending || kycApproved) return;
4389
+ if (onboardingStatus === "awaiting_provider") return;
3294
4390
  let active = true;
3295
4391
  const check = async () => {
3296
4392
  try {
@@ -3305,7 +4401,7 @@ function RampWidget({ onClose }) {
3305
4401
  active = false;
3306
4402
  clearInterval(id);
3307
4403
  };
3308
- }, [step, kycPending, kycApproved, client]);
4404
+ }, [step, kycPending, kycApproved, onboardingStatus, client]);
3309
4405
  async function loadCountries(resetSelection) {
3310
4406
  setCountriesLoading(true);
3311
4407
  try {
@@ -3339,6 +4435,7 @@ function RampWidget({ onClose }) {
3339
4435
  if (step === "select_route") {
3340
4436
  const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
3341
4437
  setQuotes(result.quotes ?? []);
4438
+ setRequirementsRequired(result.requirementsRequired ?? []);
3342
4439
  } else if (step === "status" && txId) {
3343
4440
  const tx = await client.getRampTransaction(txId);
3344
4441
  setTxStatus(tx.status);
@@ -3354,6 +4451,7 @@ function RampWidget({ onClose }) {
3354
4451
  function resetToInput({ keepMessage = false } = {}) {
3355
4452
  setStep("input");
3356
4453
  setQuotes([]);
4454
+ setRequirementsRequired([]);
3357
4455
  setSelectedQuote(null);
3358
4456
  setFieldValues({});
3359
4457
  setTxId(null);
@@ -3363,6 +4461,9 @@ function RampWidget({ onClose }) {
3363
4461
  setTxStatus(null);
3364
4462
  setStellarTxHash(null);
3365
4463
  setDepositInstructions(null);
4464
+ setKycPending(false);
4465
+ setKycApproved(false);
4466
+ setNoticeMsg(null);
3366
4467
  if (!keepMessage) setErrorMsg(null);
3367
4468
  }
3368
4469
  async function resumeWithSignature(id, ps) {
@@ -3387,6 +4488,7 @@ function RampWidget({ onClose }) {
3387
4488
  }
3388
4489
  setKycUrl(result.kycUrl ?? null);
3389
4490
  setTosUrl(result.tosUrl ?? null);
4491
+ setOnboardingStatus(result.onboardingStatus);
3390
4492
  setKycPending(result.kycRequired === true);
3391
4493
  if (result.kycRequired) setKycApproved(false);
3392
4494
  setTxStatus(result.status);
@@ -3394,19 +4496,28 @@ function RampWidget({ onClose }) {
3394
4496
  setDepositInstructions(result.depositInstructions ?? null);
3395
4497
  setStep("status");
3396
4498
  }
4499
+ async function loadQuotes() {
4500
+ const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
4501
+ const list = result.quotes ?? [];
4502
+ const locked = result.requirementsRequired ?? [];
4503
+ if (list.length === 0 && locked.length === 0) {
4504
+ const down = (result.unavailable ?? []).map((u) => u.provider);
4505
+ setErrorMsg(
4506
+ down.length > 0 ? `${down.join(", ")} ${down.length === 1 ? "is" : "are"} temporarily unavailable. Please try again later.` : `No ramp providers available for ${country} yet.`
4507
+ );
4508
+ setStep("error");
4509
+ return false;
4510
+ }
4511
+ setQuotes(list);
4512
+ setRequirementsRequired(locked);
4513
+ return true;
4514
+ }
3397
4515
  async function handleFindRoute() {
3398
4516
  setStep("loading_quote");
3399
4517
  setIsLoading(true);
3400
4518
  setErrorMsg(null);
3401
4519
  try {
3402
- const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
3403
- const list = result.quotes ?? [];
3404
- if (list.length === 0) {
3405
- setErrorMsg(`No ramp providers available for ${country} yet.`);
3406
- setStep("error");
3407
- return;
3408
- }
3409
- setQuotes(list);
4520
+ if (!await loadQuotes()) return;
3410
4521
  setStep("select_route");
3411
4522
  } catch (e) {
3412
4523
  setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
@@ -3418,6 +4529,7 @@ function RampWidget({ onClose }) {
3418
4529
  function handleSelectQuote(quote) {
3419
4530
  setSelectedQuote(quote);
3420
4531
  setErrorMsg(null);
4532
+ setNoticeMsg(null);
3421
4533
  const broken = brokenLimitOf(Number(amount), quote);
3422
4534
  if (broken) {
3423
4535
  setErrorMsg(limitMessage(broken.limit, broken.value, currency));
@@ -3453,12 +4565,47 @@ function RampWidget({ onClose }) {
3453
4565
  const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
3454
4566
  await applyResult(result);
3455
4567
  } catch (e) {
4568
+ const requirement = requiredRampKyc(e);
4569
+ if (requirement) {
4570
+ setStep(requiredFieldsOf(quote).length ? "contact" : "select_route");
4571
+ setErrorMsg(
4572
+ requirement.type === "KYC" ? "Identity verification is required before continuing." : "A few more details are required before continuing."
4573
+ );
4574
+ kycAttempt.current = requirement;
4575
+ setPendingKyc(requirement);
4576
+ return;
4577
+ }
3456
4578
  setErrorMsg(rampErrorMessage(e, "Failed to start the ramp."));
3457
4579
  setStep("error");
3458
4580
  } finally {
3459
4581
  setIsLoading(false);
3460
4582
  }
3461
4583
  }
4584
+ async function requoteAfterKyc(completed = "KYC") {
4585
+ setStep("loading_quote");
4586
+ setIsLoading(true);
4587
+ setErrorMsg(null);
4588
+ setSelectedQuote(null);
4589
+ try {
4590
+ if (!await loadQuotes()) return;
4591
+ setNoticeMsg(
4592
+ completed === "KYC" ? "Your identity is verified. Prices may have changed, so choose a route to continue." : "Thanks, your details are saved. Prices may have changed, so choose a route to continue."
4593
+ );
4594
+ setStep("select_route");
4595
+ } catch (e) {
4596
+ setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
4597
+ setStep("error");
4598
+ } finally {
4599
+ setIsLoading(false);
4600
+ }
4601
+ }
4602
+ function handleVerifyRoute(requirement) {
4603
+ const attempt = pendingFromQuote(requirement);
4604
+ setErrorMsg(null);
4605
+ setNoticeMsg(null);
4606
+ kycAttempt.current = attempt;
4607
+ setPendingKyc(attempt);
4608
+ }
3462
4609
  function handleOpenKyc() {
3463
4610
  if (kycUrl) window.open(kycUrl, "_blank", "noopener,noreferrer");
3464
4611
  }
@@ -3487,9 +4634,61 @@ function RampWidget({ onClose }) {
3487
4634
  }
3488
4635
  }
3489
4636
  const kycBlocking = kycPending && !kycApproved;
3490
- const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycBlocking;
4637
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycPending;
3491
4638
  const flowSteps = flowStepsOf(quotes, selectedQuote);
3492
4639
  const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
4640
+ if (pendingKyc?.type === "FORM") {
4641
+ return /* @__PURE__ */ jsx(
4642
+ RequirementFormModal,
4643
+ {
4644
+ formId: pendingKyc.optionId,
4645
+ ...pendingKyc.progress ? { progress: pendingKyc.progress } : {},
4646
+ onClose: () => {
4647
+ kycAttempt.current = null;
4648
+ setPendingKyc(null);
4649
+ },
4650
+ onSubmitted: () => {
4651
+ if (kycAttempt.current !== pendingKyc) return;
4652
+ kycAttempt.current = null;
4653
+ setPendingKyc(null);
4654
+ void requoteAfterKyc("FORM");
4655
+ }
4656
+ }
4657
+ );
4658
+ }
4659
+ if (pendingKyc?.type === "REGISTRY_CHECK" || pendingKyc?.type === "PROVIDER_REGISTRATION") {
4660
+ const close = () => {
4661
+ kycAttempt.current = null;
4662
+ setPendingKyc(null);
4663
+ };
4664
+ const done = () => {
4665
+ if (kycAttempt.current !== pendingKyc) return;
4666
+ close();
4667
+ void requoteAfterKyc(pendingKyc.type);
4668
+ };
4669
+ const progress = pendingKyc.progress ? { progress: pendingKyc.progress } : {};
4670
+ return pendingKyc.type === "REGISTRY_CHECK" ? /* @__PURE__ */ jsx(RegistryCheckModal, { optionId: pendingKyc.optionId, ...progress, onClose: close, onApproved: done }) : /* @__PURE__ */ jsx(ProviderRegistrationModal, { corridorId: pendingKyc.corridorId, ...progress, onClose: close, onRegistered: done });
4671
+ }
4672
+ if (pendingKyc) {
4673
+ return /* @__PURE__ */ jsx(
4674
+ KycModal,
4675
+ {
4676
+ country,
4677
+ corridorId: pendingKyc.corridorId,
4678
+ providerId: pendingKyc.optionId,
4679
+ onClose: () => {
4680
+ kycAttempt.current = null;
4681
+ setPendingKyc(null);
4682
+ },
4683
+ onApproved: () => {
4684
+ if (kycAttempt.current !== pendingKyc) return;
4685
+ kycAttempt.current = null;
4686
+ setPendingKyc(null);
4687
+ void requoteAfterKyc();
4688
+ }
4689
+ }
4690
+ );
4691
+ }
3493
4692
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
3494
4693
  RampWidgetTemplate,
3495
4694
  {
@@ -3510,12 +4709,14 @@ function RampWidget({ onClose }) {
3510
4709
  countriesLoading,
3511
4710
  refreshing,
3512
4711
  quotes,
4712
+ kycRequired: requirementsRequired,
3513
4713
  isLoading,
3514
4714
  provider,
3515
4715
  txStatus,
3516
4716
  kycUrl,
3517
4717
  tosUrl,
3518
4718
  kycBlocking,
4719
+ onboardingStatus: onboardingStatus ?? null,
3519
4720
  kycJustApproved: kycPending && kycApproved,
3520
4721
  stellarTxHash,
3521
4722
  explorerUrl: stellarTxHash ? `https://stellar.expert/explorer/${network === "mainnet" ? "public" : "testnet"}/tx/${stellarTxHash}` : null,
@@ -3523,6 +4724,7 @@ function RampWidget({ onClose }) {
3523
4724
  canComplete,
3524
4725
  completing,
3525
4726
  errorMsg,
4727
+ noticeMsg,
3526
4728
  onDirectionChange: setDirection,
3527
4729
  onAmountChange: (next) => {
3528
4730
  setAmount(next);
@@ -3532,6 +4734,7 @@ function RampWidget({ onClose }) {
3532
4734
  onCountryChange: handleCountryChange,
3533
4735
  onFindRoute: handleFindRoute,
3534
4736
  onSelectQuote: handleSelectQuote,
4737
+ onVerifyRoute: handleVerifyRoute,
3535
4738
  onContactContinue: handleContactContinue,
3536
4739
  onOpenKyc: handleOpenKyc,
3537
4740
  onOpenTos: handleOpenTos,
@@ -3544,6 +4747,18 @@ function RampWidget({ onClose }) {
3544
4747
  ) });
3545
4748
  }
3546
4749
 
4750
+ // src/lib/wallet-provisioning.ts
4751
+ function walletNotReadyReason(wallet, chain) {
4752
+ if (chain !== "STELLAR") return null;
4753
+ if (wallet?.provisioning === "CREATING") {
4754
+ return "Your wallet is still being prepared on the network. This usually takes a few seconds.";
4755
+ }
4756
+ if (wallet?.provisioning === "FAILED") {
4757
+ return "Your wallet could not be created on the network. Sign in again to retry.";
4758
+ }
4759
+ return null;
4760
+ }
4761
+
3547
4762
  // src/lib/qr-code/index.tsx
3548
4763
  var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
3549
4764
  var import_QRCode = __toESM(require_QRCode());
@@ -3586,6 +4801,31 @@ var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#0000
3586
4801
  }
3587
4802
  );
3588
4803
  });
4804
+ function PollarLogo(props) {
4805
+ return /* @__PURE__ */ jsxs("svg", { width: "45", height: "48", viewBox: "0 0 45 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
4806
+ /* @__PURE__ */ jsx(
4807
+ "path",
4808
+ {
4809
+ d: "M27.2476 11.7526L23.3926 10.0529L24.6679 9.14844L26.8168 9.19868L27.7248 10.3873L27.2476 11.7526Z",
4810
+ fill: "#005DB4"
4811
+ }
4812
+ ),
4813
+ /* @__PURE__ */ jsx(
4814
+ "path",
4815
+ {
4816
+ d: "M27.2307 33.2204L35.6034 28.6687L35.5931 25.9484L35.5707 20.0746L37.96 20.0989C37.96 20.0989 35.8728 18.3558 34.958 17.571C34.0431 16.7861 29.1016 12.8772 29.1016 12.8772L29.7315 11.2883L29.723 9.3148L27.3303 7.45046L23.7464 7.40887C23.7464 7.40887 20.5179 8.96827 20.4115 9.12248C20.2227 9.12248 15.4579 9.17619 14.5568 9.17619C12.3907 10.5103 8.21642 13.1527 8.21642 13.1527L8.22672 15.1002L2.54028 18.4858L5.60406 18.8704L5.99368 19.9568L4.93466 21.7276C4.93466 21.7276 3.61131 22.4796 3.43624 22.5264C3.76248 23.1143 4.12596 23.6803 4.52444 24.2209C4.72869 24.4773 5.01362 24.5259 5.26421 24.5969C5.66242 24.7078 6.09152 24.6991 6.50517 24.7563C6.71629 24.784 6.92226 24.8585 7.13509 24.8863C7.34792 24.914 10.6966 25.4061 11.1566 25.4424C11.5767 25.519 11.9895 25.6315 12.3907 25.7786C13.1476 26.1719 13.8685 26.6449 14.622 27.0226C15.4236 27.4298 16.1668 27.9184 16.9426 28.3689C17.5519 28.7258 18.7397 29.5281 18.7397 29.5281L19.4366 30.3372C19.4366 30.3372 17.7819 30.4135 17.219 30.5261C16.9117 30.5885 16.6302 30.63 16.4534 31.0286C16.2286 31.5345 6.69054 47.5703 6.43136 48C6.36442 47.9134 6.38845 28.6687 6.38845 28.6687L8.60776 27.2513L3.81385 26.0662L0.53552 20.818L0 18.3662L6.45368 14.414L6.36957 0H28.8304C28.8304 0 30.5468 0.223514 31.0618 0.377721C31.5767 0.531928 31.98 0.609898 33.7205 1.8938C35.3373 3.30592 39.7502 7.82125 42.3214 10.4098C42.6028 10.694 43.4422 11.7735 43.6087 12.47C43.7752 13.1665 44 14.2442 44 15.0586C43.9708 19.1148 44.0172 23.2853 43.9571 27.3397C43.9382 28.615 43.516 29.8226 42.8449 30.9506C42.5531 31.4427 41.3276 32.8201 40.3595 33.8233C39.9109 34.2848 39.3577 34.6291 38.7478 34.8266L27.2307 38.5847V33.2204Z",
4817
+ fill: "#005DB4"
4818
+ }
4819
+ ),
4820
+ /* @__PURE__ */ jsx(
4821
+ "path",
4822
+ {
4823
+ d: "M14.2192 17.0897C13.6705 17.1694 11.7754 15.6937 11.7754 15.6937L13.4741 14.3999H16.5112L18.0217 15.7182C18.0217 15.7182 16.6853 15.708 16.2399 15.708C15.6669 16.3927 14.7679 17.0181 14.2152 17.0999",
4824
+ fill: "#005DB4"
4825
+ }
4826
+ )
4827
+ ] });
4828
+ }
3589
4829
  var CHAIN_NAME = {
3590
4830
  STELLAR: "Stellar",
3591
4831
  POLYGON: "Polygon",
@@ -3600,10 +4840,10 @@ function ReceiveModalTemplate({
3600
4840
  selectedChain,
3601
4841
  onSelectChain,
3602
4842
  copied,
4843
+ notReadyReason,
3603
4844
  onCopy,
3604
4845
  onClose
3605
4846
  }) {
3606
- const isDark = theme === "dark";
3607
4847
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3608
4848
  const chainName = selectedChain ? CHAIN_NAME[selectedChain] ?? selectedChain : "wallet";
3609
4849
  return /* @__PURE__ */ jsxs(
@@ -3618,18 +4858,32 @@ function ReceiveModalTemplate({
3618
4858
  /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Receive" }),
3619
4859
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) })
3620
4860
  ] }),
3621
- /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
4861
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
3622
4862
  walletAddress ? /* @__PURE__ */ jsxs(Fragment, { children: [
3623
- /* @__PURE__ */ jsx("div", { className: "pollar-receive-qr", children: /* @__PURE__ */ jsx(QRCode, { value: walletAddress, size: 180, fgColor: isDark ? "#ffffff" : "#111827", bgColor: "transparent" }) }),
3624
- /* @__PURE__ */ jsxs("p", { className: "pollar-receive-instructions", children: [
3625
- "Share your ",
3626
- chainName,
3627
- " address to receive any asset. Only send ",
3628
- chainName,
3629
- " assets to this address. Funds sent from another network are lost."
4863
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr", children: [
4864
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-network-badge", children: [
4865
+ /* @__PURE__ */ jsx("span", { className: "pollar-receive-network-dot", "aria-hidden": true }),
4866
+ chainName,
4867
+ " network"
4868
+ ] }),
4869
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr-frame", children: [
4870
+ /* @__PURE__ */ jsx(
4871
+ QRCode,
4872
+ {
4873
+ value: walletAddress,
4874
+ size: 220,
4875
+ level: "H",
4876
+ fgColor: "#111827",
4877
+ bgColor: "#ffffff",
4878
+ title: `${chainName} wallet QR code`
4879
+ }
4880
+ ),
4881
+ /* @__PURE__ */ jsx("span", { className: "pollar-receive-qr-mark", "aria-hidden": true, children: /* @__PURE__ */ jsx(PollarLogo, { width: "22", height: "24" }) })
4882
+ ] })
3630
4883
  ] }),
3631
- /* @__PURE__ */ jsxs("div", { className: "pollar-receive-address-row", children: [
3632
- /* @__PURE__ */ jsx("span", { className: "pollar-receive-address", children: walletAddress }),
4884
+ notReadyReason && /* @__PURE__ */ jsx("div", { className: "pollar-receive-notice", children: notReadyReason }),
4885
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-address-actions", children: [
4886
+ /* @__PURE__ */ jsx("div", { className: "pollar-receive-address-row", children: /* @__PURE__ */ jsx("span", { className: "pollar-receive-address", children: walletAddress }) }),
3633
4887
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-receive-copy-btn", onClick: onCopy, "aria-label": "Copy address", children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
3634
4888
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
3635
4889
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
@@ -3644,7 +4898,7 @@ function ReceiveModalTemplate({
3644
4898
  }
3645
4899
  )
3646
4900
  ] }),
3647
- "Copied!"
4901
+ "Copied"
3648
4902
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3649
4903
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
3650
4904
  /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
@@ -3658,17 +4912,28 @@ function ReceiveModalTemplate({
3658
4912
  }
3659
4913
  )
3660
4914
  ] }),
3661
- "Copy address"
4915
+ "Copy"
3662
4916
  ] }) })
4917
+ ] }),
4918
+ /* @__PURE__ */ jsxs("p", { className: "pollar-receive-warning", children: [
4919
+ /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
4920
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.25" }),
4921
+ /* @__PURE__ */ jsx("path", { d: "M7 6.25V9.25M7 4.5V4.75", stroke: "currentColor", strokeWidth: "1.25", strokeLinecap: "round" })
4922
+ ] }),
4923
+ /* @__PURE__ */ jsxs("span", { children: [
4924
+ "Only send ",
4925
+ chainName,
4926
+ " assets to this address"
4927
+ ] })
3663
4928
  ] })
3664
4929
  ] }) : /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No wallet connected." }),
3665
- /* @__PURE__ */ jsx(PollarModalFooter, {})
4930
+ /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
3666
4931
  ]
3667
4932
  }
3668
4933
  );
3669
4934
  }
3670
4935
  function ReceiveModal({ onClose }) {
3671
- const { wallets, styles } = usePollar();
4936
+ const { wallet, wallets, styles } = usePollar();
3672
4937
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3673
4938
  const [copied, setCopied] = useState(false);
3674
4939
  const copyTimerRef = useRef(null);
@@ -3678,6 +4943,7 @@ function ReceiveModal({ onClose }) {
3678
4943
  if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
3679
4944
  }, [chains, selectedChain]);
3680
4945
  const walletAddress = addressForChain(wallets, selectedChain);
4946
+ const notReadyReason = walletNotReadyReason(wallet, selectedChain);
3681
4947
  useEffect(
3682
4948
  () => () => {
3683
4949
  if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
@@ -3708,53 +4974,13 @@ function ReceiveModal({ onClose }) {
3708
4974
  setCopied(false);
3709
4975
  setSelectedChain(chain);
3710
4976
  },
4977
+ notReadyReason,
3711
4978
  copied,
3712
4979
  onCopy: handleCopy,
3713
4980
  onClose
3714
4981
  }
3715
4982
  ) });
3716
4983
  }
3717
- function formatAmount2(value) {
3718
- const n = parseFloat(value);
3719
- return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
3720
- }
3721
- var optionLabel = (o) => o.available !== void 0 ? `${o.code} \u2014 ${formatAmount2(o.available)} available` : o.code;
3722
- function AssetSelect({
3723
- label,
3724
- value,
3725
- options,
3726
- onChange,
3727
- loading = false,
3728
- loadingLabel,
3729
- disabled = false,
3730
- appGroupLabel = "App assets",
3731
- otherGroupLabel = "Other assets",
3732
- children
3733
- }) {
3734
- const appAssets = options.filter((o) => o.enabledInApp);
3735
- const otherAssets = options.filter((o) => !o.enabledInApp);
3736
- return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
3737
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: label }),
3738
- loading ? loadingLabel ? /* @__PURE__ */ jsxs("div", { className: "pollar-input pollar-select-loading", children: [
3739
- /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3740
- /* @__PURE__ */ jsx("span", { children: loadingLabel })
3741
- ] }) : /* @__PURE__ */ jsx("div", { className: "pollar-send-skeleton" }) : /* @__PURE__ */ jsxs(
3742
- "select",
3743
- {
3744
- className: "pollar-input pollar-send-select",
3745
- value,
3746
- disabled,
3747
- onChange: (e) => onChange(e.target.value),
3748
- children: [
3749
- value === "" && /* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true }),
3750
- appAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: appGroupLabel, children: appAssets.map((o) => /* @__PURE__ */ jsx("option", { value: o.key, children: optionLabel(o) }, o.key)) }),
3751
- otherAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: otherGroupLabel, children: otherAssets.map((o) => /* @__PURE__ */ jsx("option", { value: o.key, children: optionLabel(o) }, o.key)) })
3752
- ]
3753
- }
3754
- ),
3755
- !loading && children
3756
- ] });
3757
- }
3758
4984
  var STATUS_MESSAGES = {
3759
4985
  idle: "",
3760
4986
  building: "Building transaction\u2026",
@@ -3960,6 +5186,97 @@ function formatBalance(balance) {
3960
5186
  function assetKey3(record) {
3961
5187
  return `${record.code}:${record.issuer ?? "native"}`;
3962
5188
  }
5189
+ function chainLabel(chain) {
5190
+ if (chain === "STELLAR") return "Stellar";
5191
+ if (chain === "SOLANA") return "Solana";
5192
+ if (chain === "POLYGON") return "Polygon";
5193
+ return "network";
5194
+ }
5195
+ function AssetIcon({ code }) {
5196
+ return /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
5197
+ }
5198
+ function SendAssetSelector({ assets, selectedAsset, loading, onSelectAsset }) {
5199
+ const [open, setOpen] = useState(false);
5200
+ const triggerRef = useRef(null);
5201
+ useEffect(() => {
5202
+ if (!open) return;
5203
+ const closeOnOutsideClick = (event) => {
5204
+ if (!(event.target instanceof Node) || !triggerRef.current?.parentElement?.contains(event.target)) setOpen(false);
5205
+ };
5206
+ document.addEventListener("mousedown", closeOnOutsideClick);
5207
+ return () => document.removeEventListener("mousedown", closeOnOutsideClick);
5208
+ }, [open]);
5209
+ function handleTriggerKeyDown(event) {
5210
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
5211
+ event.preventDefault();
5212
+ setOpen(true);
5213
+ }
5214
+ }
5215
+ if (loading) {
5216
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
5217
+ /* @__PURE__ */ jsx("span", { className: "pollar-send-label", children: "Asset" }),
5218
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-asset-selector pollar-send-asset-selector-loading", "aria-label": "Loading assets", children: [
5219
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
5220
+ /* @__PURE__ */ jsx("span", { children: "Loading assets\u2026" })
5221
+ ] })
5222
+ ] });
5223
+ }
5224
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
5225
+ /* @__PURE__ */ jsx("label", { className: "pollar-send-label", id: "pollar-send-asset-label", children: "Asset" }),
5226
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-asset-picker", children: [
5227
+ /* @__PURE__ */ jsxs(
5228
+ "button",
5229
+ {
5230
+ ref: triggerRef,
5231
+ type: "button",
5232
+ className: "pollar-send-asset-selector",
5233
+ "aria-labelledby": "pollar-send-asset-label",
5234
+ "aria-haspopup": "listbox",
5235
+ "aria-expanded": open,
5236
+ disabled: !selectedAsset,
5237
+ onClick: () => setOpen((value) => !value),
5238
+ onKeyDown: handleTriggerKeyDown,
5239
+ children: [
5240
+ selectedAsset ? /* @__PURE__ */ jsxs(Fragment, { children: [
5241
+ /* @__PURE__ */ jsx(AssetIcon, { code: selectedAsset.code }),
5242
+ /* @__PURE__ */ jsxs("span", { className: "pollar-send-asset-copy", children: [
5243
+ /* @__PURE__ */ jsx("strong", { children: selectedAsset.code }),
5244
+ /* @__PURE__ */ jsx("small", { children: selectedAsset.available === null ? "Balance unavailable" : `${formatBalance(selectedAsset.available)} available` })
5245
+ ] })
5246
+ ] }) : /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-copy", children: /* @__PURE__ */ jsx("strong", { children: "No assets available" }) }),
5247
+ /* @__PURE__ */ jsx("svg", { className: "pollar-send-chevron", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
5248
+ ]
5249
+ }
5250
+ ),
5251
+ open && assets.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-send-asset-menu", role: "listbox", "aria-label": "Assets", children: assets.map((asset) => {
5252
+ const key = assetKey3(asset);
5253
+ const selected = selectedAsset ? key === assetKey3(selectedAsset) : false;
5254
+ return /* @__PURE__ */ jsxs(
5255
+ "button",
5256
+ {
5257
+ type: "button",
5258
+ role: "option",
5259
+ "aria-selected": selected,
5260
+ className: `pollar-send-asset-option${selected ? " is-selected" : ""}`,
5261
+ onClick: () => {
5262
+ onSelectAsset(asset);
5263
+ setOpen(false);
5264
+ triggerRef.current?.focus();
5265
+ },
5266
+ children: [
5267
+ /* @__PURE__ */ jsx(AssetIcon, { code: asset.code }),
5268
+ /* @__PURE__ */ jsxs("span", { className: "pollar-send-asset-copy", children: [
5269
+ /* @__PURE__ */ jsx("strong", { children: asset.code }),
5270
+ /* @__PURE__ */ jsx("small", { children: asset.available === null ? "Balance unavailable" : `${formatBalance(asset.available)} available` })
5271
+ ] })
5272
+ ]
5273
+ },
5274
+ key
5275
+ );
5276
+ }) })
5277
+ ] })
5278
+ ] });
5279
+ }
3963
5280
  function SendModalTemplate({
3964
5281
  theme,
3965
5282
  accentColor,
@@ -3968,10 +5285,10 @@ function SendModalTemplate({
3968
5285
  txTitle,
3969
5286
  assets,
3970
5287
  selectedAsset,
3971
- chains,
3972
5288
  selectedChain,
3973
- walletAddress,
3974
5289
  canSendOnChain,
5290
+ notReadyReason,
5291
+ chains,
3975
5292
  onSelectChain,
3976
5293
  amount,
3977
5294
  destination,
@@ -3989,6 +5306,8 @@ function SendModalTemplate({
3989
5306
  onRefresh,
3990
5307
  onSelectAsset,
3991
5308
  onAmountChange,
5309
+ onMax,
5310
+ onPaste,
3992
5311
  onDestinationChange,
3993
5312
  onSubmit,
3994
5313
  onSignAndSend,
@@ -3998,8 +5317,9 @@ function SendModalTemplate({
3998
5317
  onDone
3999
5318
  }) {
4000
5319
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4001
- const selectedKey = selectedAsset ? assetKey3(selectedAsset) : "";
4002
- const canSubmit = canSendOnChain && !!selectedAsset && !!amount && !!destination.trim() && !isLoadingBalance;
5320
+ const parsedAmount = amount ? Number(amount) : 0;
5321
+ const availableAmount = selectedAsset?.available === null || selectedAsset?.available === void 0 ? 0 : Number(selectedAsset.available);
5322
+ const canSubmit = canSendOnChain && !notReadyReason && !!selectedAsset && Number.isFinite(parsedAmount) && parsedAmount > 0 && parsedAmount <= availableAmount && !!destination.trim() && !isLoadingBalance;
4003
5323
  const title = step === "form" ? "Send" : txTitle;
4004
5324
  return /* @__PURE__ */ jsxs(
4005
5325
  "div",
@@ -4012,7 +5332,14 @@ function SendModalTemplate({
4012
5332
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4013
5333
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-header-left", children: [
4014
5334
  showBack && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M10 3L5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
4015
- /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title })
5335
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-title-copy", children: [
5336
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title }),
5337
+ step === "form" && /* @__PURE__ */ jsxs("p", { className: "pollar-send-subtitle", children: [
5338
+ "Transfer assets to another ",
5339
+ chainLabel(selectedChain),
5340
+ " wallet"
5341
+ ] })
5342
+ ] })
4016
5343
  ] }),
4017
5344
  !isInProgress && /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
4018
5345
  step === "form" && /* @__PURE__ */ jsx(
@@ -4024,58 +5351,23 @@ function SendModalTemplate({
4024
5351
  disabled: isLoadingBalance,
4025
5352
  "aria-label": "Refresh",
4026
5353
  title: "Refresh balances",
4027
- children: /* @__PURE__ */ jsx(
4028
- "svg",
4029
- {
4030
- className: isLoadingBalance ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon",
4031
- width: "16",
4032
- height: "16",
4033
- viewBox: "0 0 16 16",
4034
- fill: "none",
4035
- "aria-hidden": true,
4036
- children: /* @__PURE__ */ jsx(
4037
- "path",
4038
- {
4039
- d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
4040
- stroke: "currentColor",
4041
- strokeWidth: "1.6",
4042
- strokeLinecap: "round",
4043
- strokeLinejoin: "round"
4044
- }
4045
- )
4046
- }
4047
- )
5354
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoadingBalance })
4048
5355
  }
4049
5356
  ),
4050
5357
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
4051
5358
  ] })
4052
5359
  ] }),
4053
5360
  step === "form" && /* @__PURE__ */ jsxs(Fragment, { children: [
4054
- /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoadingBalance }),
4055
- walletAddress && /* @__PURE__ */ jsxs("div", { className: "pollar-address-row", children: [
4056
- /* @__PURE__ */ jsx("span", { className: "pollar-address", children: cropAddress(walletAddress) }),
4057
- /* @__PURE__ */ jsx(CopyButton, { value: walletAddress, label: "Copy wallet address" })
4058
- ] }),
5361
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoadingBalance }),
4059
5362
  !canSendOnChain && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "Sending is not available on this network yet." }),
5363
+ canSendOnChain && notReadyReason && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: notReadyReason }),
4060
5364
  /* @__PURE__ */ jsx(
4061
- AssetSelect,
5365
+ SendAssetSelector,
4062
5366
  {
4063
- label: "Asset",
4064
- value: selectedKey,
5367
+ assets,
5368
+ selectedAsset,
4065
5369
  loading: isLoadingBalance,
4066
- loadingLabel: "Loading assets\u2026",
4067
- options: assets.map((a) => ({
4068
- key: assetKey3(a),
4069
- code: a.code,
4070
- // Unreadable (null) drops the "- X available" suffix instead of
4071
- // claiming a zero balance.
4072
- available: a.available ?? void 0,
4073
- enabledInApp: a.enabledInApp
4074
- })),
4075
- onChange: (key) => {
4076
- const found = assets.find((a) => assetKey3(a) === key);
4077
- if (found) onSelectAsset(found);
4078
- }
5370
+ onSelectAsset
4079
5371
  }
4080
5372
  ),
4081
5373
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
@@ -4088,30 +5380,50 @@ function SendModalTemplate({
4088
5380
  selectedAsset.code
4089
5381
  ] })
4090
5382
  ] }),
4091
- /* @__PURE__ */ jsx(
4092
- "input",
4093
- {
4094
- className: "pollar-input",
4095
- type: "text",
4096
- inputMode: "decimal",
4097
- placeholder: "0.00",
4098
- value: amount,
4099
- onChange: (e) => onAmountChange(e.target.value)
4100
- }
4101
- )
5383
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
5384
+ /* @__PURE__ */ jsx(
5385
+ "input",
5386
+ {
5387
+ className: "pollar-input pollar-send-amount-input",
5388
+ type: "text",
5389
+ inputMode: "decimal",
5390
+ placeholder: "0.00",
5391
+ value: amount,
5392
+ onChange: (e) => onAmountChange(e.target.value),
5393
+ "aria-label": "Amount"
5394
+ }
5395
+ ),
5396
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-suffix", children: [
5397
+ /* @__PURE__ */ jsx(
5398
+ "button",
5399
+ {
5400
+ type: "button",
5401
+ className: "pollar-send-inline-action",
5402
+ onClick: onMax,
5403
+ disabled: !selectedAsset?.available,
5404
+ children: "Max"
5405
+ }
5406
+ ),
5407
+ /* @__PURE__ */ jsx("span", { children: selectedAsset?.code ?? "" })
5408
+ ] })
5409
+ ] })
4102
5410
  ] }),
4103
5411
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
4104
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Destination wallet" }),
4105
- /* @__PURE__ */ jsx(
4106
- "input",
4107
- {
4108
- className: "pollar-input",
4109
- type: "text",
4110
- placeholder: "G\u2026",
4111
- value: destination,
4112
- onChange: (e) => onDestinationChange(e.target.value)
4113
- }
4114
- )
5412
+ /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Destination wallet" }),
5413
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
5414
+ /* @__PURE__ */ jsx(
5415
+ "input",
5416
+ {
5417
+ className: "pollar-input pollar-send-destination-input",
5418
+ type: "text",
5419
+ placeholder: "G\u2026",
5420
+ value: destination,
5421
+ onChange: (e) => onDestinationChange(e.target.value),
5422
+ "aria-label": "Destination wallet"
5423
+ }
5424
+ ),
5425
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-send-inline-action", onClick: onPaste, children: "Paste" })
5426
+ ] })
4115
5427
  ] }),
4116
5428
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
4117
5429
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: "Continue" }) })
@@ -4152,7 +5464,6 @@ function SendModal({ onClose }) {
4152
5464
  signAndSubmitTx,
4153
5465
  tx: transaction,
4154
5466
  wallet,
4155
- wallets,
4156
5467
  network,
4157
5468
  styles
4158
5469
  } = usePollar();
@@ -4171,8 +5482,8 @@ function SendModal({ onClose }) {
4171
5482
  useEffect(() => {
4172
5483
  if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
4173
5484
  }, [chains, selectedChain]);
4174
- const walletAddress = addressForChain(wallets, selectedChain);
4175
5485
  const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA";
5486
+ const notReadyReason = walletNotReadyReason(wallet, selectedChain);
4176
5487
  const isBaseUnitChain = selectedChain === "SOLANA";
4177
5488
  useEffect(() => {
4178
5489
  void refreshWalletBalance();
@@ -4219,6 +5530,10 @@ function SendModal({ onClose }) {
4219
5530
  setFormError("Sending is not available on this network yet.");
4220
5531
  return;
4221
5532
  }
5533
+ if (notReadyReason) {
5534
+ setFormError(notReadyReason);
5535
+ return;
5536
+ }
4222
5537
  if (!selectedAsset) {
4223
5538
  setFormError("Select an asset");
4224
5539
  return;
@@ -4269,6 +5584,29 @@ function SendModal({ onClose }) {
4269
5584
  void signAndSubmitTx(transaction.buildData.unsignedXdr);
4270
5585
  }
4271
5586
  }
5587
+ function handleMax() {
5588
+ if (selectedAsset?.available !== null && selectedAsset?.available !== void 0) {
5589
+ setAmount(selectedAsset.available);
5590
+ setFormError("");
5591
+ }
5592
+ }
5593
+ async function handlePaste() {
5594
+ if (!navigator.clipboard?.readText) {
5595
+ setFormError("Clipboard access is not available. Paste the address manually.");
5596
+ return;
5597
+ }
5598
+ try {
5599
+ const value = (await navigator.clipboard.readText()).trim();
5600
+ if (!value) {
5601
+ setFormError("The clipboard is empty.");
5602
+ return;
5603
+ }
5604
+ setDestination(value);
5605
+ setFormError("");
5606
+ } catch {
5607
+ setFormError("Could not read the clipboard. Paste the address manually.");
5608
+ }
5609
+ }
4272
5610
  function handleCopyHash() {
4273
5611
  if (!hash) return;
4274
5612
  navigator.clipboard.writeText(hash).then(() => {
@@ -4302,8 +5640,8 @@ function SendModal({ onClose }) {
4302
5640
  selectedAsset,
4303
5641
  chains,
4304
5642
  selectedChain,
4305
- walletAddress,
4306
5643
  canSendOnChain,
5644
+ notReadyReason,
4307
5645
  onSelectChain: setSelectedChain,
4308
5646
  amount,
4309
5647
  destination,
@@ -4321,6 +5659,8 @@ function SendModal({ onClose }) {
4321
5659
  onRefresh: () => void refreshWalletBalance(),
4322
5660
  onSelectAsset: setSelectedAsset,
4323
5661
  onAmountChange: setAmount,
5662
+ onMax: handleMax,
5663
+ onPaste: handlePaste,
4324
5664
  onDestinationChange: setDestination,
4325
5665
  onSubmit: () => void handleSubmit(),
4326
5666
  onSignAndSend: handleSignAndSend,
@@ -4335,15 +5675,77 @@ function assetOptionKey(o) {
4335
5675
  return `${o.code}:${o.issuer ?? "native"}`;
4336
5676
  }
4337
5677
  var PROVIDER_LABELS = {
4338
- auto: "Best price (auto)",
5678
+ auto: "Best price",
4339
5679
  aquarius: "Aquarius",
4340
5680
  soroswap: "Soroswap",
4341
5681
  sdex: "Stellar DEX"
4342
5682
  };
4343
- function formatAmount3(value) {
5683
+ function formatAmount2(value) {
5684
+ if (value === void 0) return "\u2014";
4344
5685
  const n = parseFloat(value);
4345
5686
  return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
4346
5687
  }
5688
+ function formatRate(numerator, denominator) {
5689
+ const parse = (value) => {
5690
+ const trimmed = value.trim();
5691
+ if (!/^\d+(\.\d+)?$/.test(trimmed)) return null;
5692
+ const [whole2 = "0", fraction2 = ""] = trimmed.split(".");
5693
+ return { digits: BigInt(`${whole2}${fraction2}`), scale: fraction2.length };
5694
+ };
5695
+ const top = parse(numerator);
5696
+ const bottom = parse(denominator);
5697
+ if (!top || !bottom || bottom.digits === 0n || top.digits === 0n) return null;
5698
+ const precision = 8;
5699
+ const scaledNumerator = top.digits * 10n ** BigInt(bottom.scale + precision);
5700
+ const scaledDenominator = bottom.digits * 10n ** BigInt(top.scale);
5701
+ const scaled = scaledNumerator / scaledDenominator;
5702
+ const whole = scaled / 10n ** BigInt(precision);
5703
+ const fraction = (scaled % 10n ** BigInt(precision)).toString().padStart(precision, "0").replace(/0+$/, "");
5704
+ return fraction ? `${whole}.${fraction}` : whole.toString();
5705
+ }
5706
+ function AssetIcon2({ code }) {
5707
+ return /* @__PURE__ */ jsx("span", { className: "pollar-swap-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
5708
+ }
5709
+ function AssetPill({
5710
+ label,
5711
+ value,
5712
+ options,
5713
+ disabled,
5714
+ onChange
5715
+ }) {
5716
+ const selected = options.find((option) => assetOptionKey(option) === value);
5717
+ const appAssets = options.filter((option) => option.enabledInApp);
5718
+ const otherAssets = options.filter((option) => !option.enabledInApp);
5719
+ return /* @__PURE__ */ jsxs("label", { className: "pollar-swap-asset-pill-wrap", children: [
5720
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
5721
+ /* @__PURE__ */ jsx(AssetIcon2, { code: selected?.code ?? "\u2014" }),
5722
+ /* @__PURE__ */ jsxs(
5723
+ "select",
5724
+ {
5725
+ className: "pollar-swap-asset-pill",
5726
+ "aria-label": label,
5727
+ value,
5728
+ disabled,
5729
+ onChange: (event) => onChange(event.target.value),
5730
+ children: [
5731
+ value === "" && /* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true }),
5732
+ appAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: "App assets", children: appAssets.map((option) => /* @__PURE__ */ jsx("option", { value: assetOptionKey(option), children: option.code }, assetOptionKey(option))) }),
5733
+ otherAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: "Other assets", children: otherAssets.map((option) => /* @__PURE__ */ jsx("option", { value: assetOptionKey(option), children: option.code }, assetOptionKey(option))) })
5734
+ ]
5735
+ }
5736
+ ),
5737
+ /* @__PURE__ */ jsx("svg", { className: "pollar-swap-chevron", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
5738
+ "path",
5739
+ {
5740
+ d: "m3.5 5.25 3.5 3.5 3.5-3.5",
5741
+ stroke: "currentColor",
5742
+ strokeWidth: "1.5",
5743
+ strokeLinecap: "round",
5744
+ strokeLinejoin: "round"
5745
+ }
5746
+ ) })
5747
+ ] });
5748
+ }
4347
5749
  function SwapModalTemplate({
4348
5750
  theme,
4349
5751
  accentColor,
@@ -4379,6 +5781,8 @@ function SwapModalTemplate({
4379
5781
  onSelectSell,
4380
5782
  onSelectBuy,
4381
5783
  onAddCustomToken,
5784
+ onReverse,
5785
+ onMax,
4382
5786
  onAmountChange,
4383
5787
  onProviderChange,
4384
5788
  onSwap,
@@ -4405,19 +5809,29 @@ function SwapModalTemplate({
4405
5809
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4406
5810
  const sellKey = selectedSell ? assetOptionKey(selectedSell) : "";
4407
5811
  const buyKey = selectedBuy ? assetOptionKey(selectedBuy) : "";
4408
- const canSwap = !smartUnsupported && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
5812
+ const sameAsset = !!selectedSell && !!selectedBuy && sellKey === buyKey;
5813
+ const canSwap = !smartUnsupported && !sameAsset && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
5814
+ const rate = quote && amount ? formatRate(quote.amountOut, amount) : null;
4409
5815
  const title = step === "form" ? "Swap" : txTitle;
5816
+ const selectSell = (key) => {
5817
+ const found = sellOptions.find((option) => assetOptionKey(option) === key);
5818
+ if (found) onSelectSell(found);
5819
+ };
5820
+ const selectBuy = (key) => {
5821
+ const found = buyOptions.find((option) => assetOptionKey(option) === key);
5822
+ if (found) onSelectBuy(found);
5823
+ };
4410
5824
  return /* @__PURE__ */ jsxs(
4411
5825
  "div",
4412
5826
  {
4413
- className: "pollar-modal-card pollar-send-modal",
5827
+ className: "pollar-modal-card pollar-swap-modal",
4414
5828
  "data-theme": theme,
4415
5829
  style: cssVars,
4416
- onClick: (e) => e.stopPropagation(),
5830
+ onClick: (event) => event.stopPropagation(),
4417
5831
  children: [
4418
5832
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4419
5833
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-header-left", children: [
4420
- showBack && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M10 3L5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
5834
+ showBack && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M10 3 5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
4421
5835
  /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title })
4422
5836
  ] }),
4423
5837
  !isInProgress && /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
@@ -4430,16 +5844,7 @@ function SwapModalTemplate({
4430
5844
  disabled: configLoading || isLoadingData,
4431
5845
  "aria-label": "Refresh",
4432
5846
  title: "Refresh balances and options",
4433
- children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
4434
- "path",
4435
- {
4436
- d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2.5v3h-3",
4437
- stroke: "currentColor",
4438
- strokeWidth: "1.6",
4439
- strokeLinecap: "round",
4440
- strokeLinejoin: "round"
4441
- }
4442
- ) })
5847
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: configLoading || isLoadingData })
4443
5848
  }
4444
5849
  ),
4445
5850
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
@@ -4448,174 +5853,205 @@ function SwapModalTemplate({
4448
5853
  step === "form" && !configLoading && swapUnavailable && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Swap is not available for this app." }),
4449
5854
  step === "form" && !swapUnavailable && /* @__PURE__ */ jsxs(Fragment, { children: [
4450
5855
  smartUnsupported && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Swaps are not yet available for smart (passkey) wallets." }),
4451
- /* @__PURE__ */ jsx(
4452
- AssetSelect,
4453
- {
4454
- label: "You pay",
4455
- value: sellKey,
4456
- loading: isLoadingData || configLoading,
4457
- loadingLabel: "Loading assets\u2026",
4458
- disabled: configLoading,
4459
- options: sellOptions.map((o) => ({
4460
- key: assetOptionKey(o),
4461
- code: o.code,
4462
- available: o.available,
4463
- enabledInApp: o.enabledInApp
4464
- })),
4465
- onChange: (key) => {
4466
- const found = sellOptions.find((o) => assetOptionKey(o) === key);
4467
- if (found) onSelectSell(found);
4468
- }
4469
- }
4470
- ),
4471
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
4472
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-label-row", children: [
4473
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Amount" }),
4474
- selectedSell?.available !== void 0 && /* @__PURE__ */ jsxs("span", { className: "pollar-send-hint", children: [
4475
- "Available: ",
4476
- formatAmount3(selectedSell.available),
4477
- " ",
4478
- selectedSell.code
4479
- ] })
4480
- ] }),
4481
- /* @__PURE__ */ jsx(
4482
- "input",
4483
- {
4484
- className: "pollar-input",
4485
- type: "text",
4486
- inputMode: "decimal",
4487
- placeholder: "0.00",
4488
- value: amount,
4489
- disabled: configLoading,
4490
- onChange: (e) => onAmountChange(e.target.value)
4491
- }
4492
- )
4493
- ] }),
4494
- /* @__PURE__ */ jsx(
4495
- AssetSelect,
4496
- {
4497
- label: "You receive",
4498
- value: buyKey,
4499
- loading: isLoadingData || configLoading,
4500
- loadingLabel: "Loading assets\u2026",
4501
- disabled: configLoading,
4502
- options: buyOptions.map((o) => ({
4503
- key: assetOptionKey(o),
4504
- code: o.code,
4505
- enabledInApp: o.enabledInApp
4506
- })),
4507
- onChange: (key) => {
4508
- const found = buyOptions.find((o) => assetOptionKey(o) === key);
4509
- if (found) onSelectBuy(found);
4510
- },
4511
- children: customOpen ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom", children: [
4512
- /* @__PURE__ */ jsx(
4513
- "input",
4514
- {
4515
- className: "pollar-input",
4516
- type: "text",
4517
- placeholder: "Code (e.g. USDC)",
4518
- value: customCode,
4519
- onChange: (e) => setCustomCode(e.target.value)
4520
- }
4521
- ),
5856
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panels", children: [
5857
+ /* @__PURE__ */ jsxs("section", { className: "pollar-swap-panel", "aria-labelledby": "pollar-swap-pay-label", children: [
5858
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-heading", children: [
5859
+ /* @__PURE__ */ jsx("span", { id: "pollar-swap-pay-label", className: "pollar-swap-panel-label", children: "You pay" }),
5860
+ /* @__PURE__ */ jsxs("span", { className: "pollar-swap-balance", children: [
5861
+ "Balance: ",
5862
+ formatAmount2(selectedSell?.available)
5863
+ ] })
5864
+ ] }),
5865
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-main", children: [
4522
5866
  /* @__PURE__ */ jsx(
4523
5867
  "input",
4524
5868
  {
4525
- className: "pollar-input",
5869
+ className: "pollar-swap-amount",
4526
5870
  type: "text",
4527
- placeholder: "Issuer (G...)",
4528
- value: customIssuer,
4529
- onChange: (e) => setCustomIssuer(e.target.value)
5871
+ inputMode: "decimal",
5872
+ "aria-label": "Amount to pay",
5873
+ placeholder: "0.00",
5874
+ value: amount,
5875
+ disabled: configLoading || isLoadingData,
5876
+ onChange: (event) => onAmountChange(event.target.value)
4530
5877
  }
4531
5878
  ),
4532
- customError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: customError }),
4533
- /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom-actions", children: [
4534
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
5879
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-selector", children: [
5880
+ /* @__PURE__ */ jsx(
5881
+ AssetPill,
5882
+ {
5883
+ label: "Asset to pay",
5884
+ value: sellKey,
5885
+ options: sellOptions,
5886
+ disabled: configLoading || isLoadingData,
5887
+ onChange: selectSell
5888
+ }
5889
+ ),
4535
5890
  /* @__PURE__ */ jsx(
4536
5891
  "button",
4537
5892
  {
4538
5893
  type: "button",
4539
- className: "pollar-swap-custom-cancel",
4540
- onClick: () => {
4541
- setCustomOpen(false);
4542
- setCustomError("");
4543
- },
4544
- children: "Cancel"
5894
+ className: "pollar-swap-max",
5895
+ onClick: onMax,
5896
+ disabled: !selectedSell?.available || configLoading || isLoadingData,
5897
+ children: "Max"
4545
5898
  }
4546
5899
  )
4547
5900
  ] })
4548
- ] }) : /* @__PURE__ */ jsx(
5901
+ ] })
5902
+ ] }),
5903
+ /* @__PURE__ */ jsx(
5904
+ "button",
5905
+ {
5906
+ type: "button",
5907
+ className: "pollar-swap-reverse",
5908
+ onClick: onReverse,
5909
+ disabled: !selectedSell || !selectedBuy,
5910
+ "aria-label": "Swap pay and receive assets",
5911
+ title: "Swap pay and receive assets",
5912
+ children: /* @__PURE__ */ jsx("svg", { width: "17", height: "17", viewBox: "0 0 17 17", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
5913
+ "path",
5914
+ {
5915
+ d: "M5 3v10M5 3 2.5 5.5M5 3l2.5 2.5M12 14V4M12 14l2.5-2.5M12 14 9.5 11.5",
5916
+ stroke: "currentColor",
5917
+ strokeWidth: "1.6",
5918
+ strokeLinecap: "round",
5919
+ strokeLinejoin: "round"
5920
+ }
5921
+ ) })
5922
+ }
5923
+ ),
5924
+ /* @__PURE__ */ jsxs("section", { className: "pollar-swap-panel", "aria-labelledby": "pollar-swap-receive-label", children: [
5925
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-heading", children: [
5926
+ /* @__PURE__ */ jsx("span", { id: "pollar-swap-receive-label", className: "pollar-swap-panel-label", children: "You receive" }),
5927
+ /* @__PURE__ */ jsxs("span", { className: "pollar-swap-balance", children: [
5928
+ "Balance: ",
5929
+ formatAmount2(selectedBuy?.available)
5930
+ ] })
5931
+ ] }),
5932
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-main", children: [
5933
+ /* @__PURE__ */ jsx("div", { className: `pollar-swap-output${quoteLoading ? " is-loading" : ""}`, "aria-live": "polite", children: quoteLoading ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : formatAmount2(quote?.amountOut) }),
5934
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-selector", children: [
5935
+ /* @__PURE__ */ jsx(
5936
+ AssetPill,
5937
+ {
5938
+ label: "Asset to receive",
5939
+ value: buyKey,
5940
+ options: buyOptions,
5941
+ disabled: configLoading || isLoadingData,
5942
+ onChange: selectBuy
5943
+ }
5944
+ ),
5945
+ /* @__PURE__ */ jsx("span", { className: "pollar-swap-panel-note", children: "Quoted amount" })
5946
+ ] })
5947
+ ] })
5948
+ ] })
5949
+ ] }),
5950
+ /* @__PURE__ */ jsx("div", { className: "pollar-swap-custom-area", children: customOpen ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom", children: [
5951
+ /* @__PURE__ */ jsx(
5952
+ "input",
5953
+ {
5954
+ className: "pollar-input",
5955
+ type: "text",
5956
+ placeholder: "Code (e.g. USDC)",
5957
+ value: customCode,
5958
+ onChange: (e) => setCustomCode(e.target.value)
5959
+ }
5960
+ ),
5961
+ /* @__PURE__ */ jsx(
5962
+ "input",
5963
+ {
5964
+ className: "pollar-input",
5965
+ type: "text",
5966
+ placeholder: "Issuer (G...)",
5967
+ value: customIssuer,
5968
+ onChange: (e) => setCustomIssuer(e.target.value)
5969
+ }
5970
+ ),
5971
+ customError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: customError }),
5972
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom-actions", children: [
5973
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
5974
+ /* @__PURE__ */ jsx(
4549
5975
  "button",
4550
5976
  {
4551
5977
  type: "button",
4552
- className: "pollar-swap-custom-toggle",
4553
- disabled: configLoading,
4554
- onClick: () => setCustomOpen(true),
4555
- children: "+ Add a token by code / issuer"
5978
+ className: "pollar-swap-custom-cancel",
5979
+ onClick: () => {
5980
+ setCustomOpen(false);
5981
+ setCustomError("");
5982
+ },
5983
+ children: "Cancel"
4556
5984
  }
4557
5985
  )
5986
+ ] })
5987
+ ] }) : /* @__PURE__ */ jsx(
5988
+ "button",
5989
+ {
5990
+ type: "button",
5991
+ className: "pollar-swap-custom-toggle",
5992
+ disabled: configLoading,
5993
+ onClick: () => setCustomOpen(true),
5994
+ children: "+ Add a token by code / issuer"
4558
5995
  }
4559
- ),
4560
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
4561
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Route" }),
4562
- configLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-input pollar-select-loading", children: [
5996
+ ) }),
5997
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-rate-row", children: [
5998
+ /* @__PURE__ */ jsx("div", { className: "pollar-swap-rate", "aria-live": "polite", children: rate && selectedSell && selectedBuy ? /* @__PURE__ */ jsxs(Fragment, { children: [
5999
+ "1 ",
6000
+ selectedSell.code,
6001
+ " \u2248 ",
6002
+ rate,
6003
+ " ",
6004
+ selectedBuy.code
6005
+ ] }) : quoteLoading ? "Updating rate\u2026" : "Exchange rate unavailable" }),
6006
+ configLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-route-pill", children: [
4563
6007
  /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
4564
- /* @__PURE__ */ jsx("span", { children: "Loading routes\u2026" })
4565
- ] }) : /* @__PURE__ */ jsx(
4566
- "select",
4567
- {
4568
- className: "pollar-input pollar-send-select",
4569
- value: provider,
4570
- disabled: providers.length === 0,
4571
- onChange: (e) => onProviderChange(e.target.value),
4572
- children: providers.length === 0 ? /* @__PURE__ */ jsx("option", { value: provider, children: PROVIDER_LABELS[provider] }) : providers.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: PROVIDER_LABELS[p] }, p))
4573
- }
4574
- )
4575
- ] }),
4576
- quoteLoading && /* @__PURE__ */ jsxs("div", { className: "pollar-send-hint pollar-inline-loading", children: [
4577
- /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
4578
- "Fetching best price\u2026"
6008
+ " Loading"
6009
+ ] }) : /* @__PURE__ */ jsxs("label", { className: "pollar-swap-route-wrap", children: [
6010
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Route" }),
6011
+ /* @__PURE__ */ jsx(
6012
+ "select",
6013
+ {
6014
+ className: "pollar-swap-route-pill",
6015
+ "aria-label": "Route",
6016
+ value: provider,
6017
+ disabled: providers.length === 0,
6018
+ onChange: (event) => onProviderChange(event.target.value),
6019
+ children: providers.length === 0 ? /* @__PURE__ */ jsx("option", { value: provider, children: PROVIDER_LABELS[provider] }) : providers.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: PROVIDER_LABELS[p] }, p))
6020
+ }
6021
+ )
6022
+ ] })
4579
6023
  ] }),
4580
6024
  !quoteLoading && quoteError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: quoteError }),
4581
- !quoteLoading && !quoteError && quote && selectedBuy && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote", children: [
4582
- /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
4583
- /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "You receive" }),
4584
- /* @__PURE__ */ jsxs("span", { children: [
4585
- "~ ",
4586
- formatAmount3(quote.amountOut),
4587
- " ",
4588
- selectedBuy.code
4589
- ] })
4590
- ] }),
6025
+ !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
6026
+ quote && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote", children: [
4591
6027
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
4592
- /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "Minimum received" }),
4593
- /* @__PURE__ */ jsxs("span", { children: [
4594
- formatAmount3(quote.minReceived),
6028
+ /* @__PURE__ */ jsx("span", { children: "Minimum received" }),
6029
+ /* @__PURE__ */ jsxs("strong", { children: [
6030
+ formatAmount2(quote.minReceived),
4595
6031
  " ",
4596
- selectedBuy.code
6032
+ selectedBuy?.code
4597
6033
  ] })
4598
6034
  ] }),
4599
6035
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
4600
- /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "Price impact" }),
4601
- /* @__PURE__ */ jsxs("span", { children: [
6036
+ /* @__PURE__ */ jsx("span", { children: "Price impact" }),
6037
+ /* @__PURE__ */ jsxs("strong", { children: [
4602
6038
  quote.priceImpactPct,
4603
6039
  "%"
4604
6040
  ] })
4605
6041
  ] }),
4606
6042
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
4607
- /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "Route" }),
4608
- /* @__PURE__ */ jsx("span", { children: PROVIDER_LABELS[quote.provider] })
6043
+ /* @__PURE__ */ jsx("span", { children: "Route" }),
6044
+ /* @__PURE__ */ jsx("strong", { children: PROVIDER_LABELS[quote.provider] })
4609
6045
  ] })
4610
6046
  ] }),
4611
- !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
4612
6047
  buyNeedsTrustline && selectedBuy && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-trustline-notice", children: [
4613
6048
  "To receive ",
4614
6049
  selectedBuy.code,
4615
6050
  " your wallet needs a trustline. Swapping will create it first (~0.5 XLM reserve, refundable if you later remove it)."
4616
6051
  ] }),
6052
+ sameAsset && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Choose two different assets to swap." }),
4617
6053
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
4618
- /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: buyNeedsTrustline ? "Create trustline & swap" : "Swap" }) })
6054
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: buyNeedsTrustline ? "Create trustline & swap" : "Swap" }) })
4619
6055
  ] }),
4620
6056
  step === "tx" && /* @__PURE__ */ jsx(
4621
6057
  TxStatusView,
@@ -4633,7 +6069,7 @@ function SwapModalTemplate({
4633
6069
  onDone
4634
6070
  }
4635
6071
  ),
4636
- /* @__PURE__ */ jsx(PollarModalFooter, {})
6072
+ /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
4637
6073
  ]
4638
6074
  }
4639
6075
  );
@@ -4714,6 +6150,7 @@ function SwapModal({ onClose }) {
4714
6150
  const balances = walletBalance.step === "loaded" ? walletBalance.data.balances : [];
4715
6151
  const assetRecords = enabledAssets.step === "loaded" ? enabledAssets.data.assets : [];
4716
6152
  const isLoadingData = walletBalance.step === "loading" || enabledAssets.step === "loading";
6153
+ const availableFor = (code, issuer) => balances.find((b) => b.code === code && (b.issuer ?? "") === (issuer ?? ""))?.available ?? void 0;
4717
6154
  const sellOptions = balances.filter(
4718
6155
  (b) => (b.chain === void 0 || b.chain === "STELLAR") && (b.type === "native" || (b.type === "credit_alphanum4" || b.type === "credit_alphanum12") && !b.trustlineRemoved)
4719
6156
  ).map((b) => ({
@@ -4731,6 +6168,7 @@ function SwapModal({ onClose }) {
4731
6168
  ref: toRef(a),
4732
6169
  code: a.code,
4733
6170
  issuer: a.issuer,
6171
+ available: availableFor(a.code, a.issuer),
4734
6172
  enabledInApp: a.enabledInApp
4735
6173
  }));
4736
6174
  const enabledKeys = new Set(enabledBuy.map(optKey));
@@ -4738,11 +6176,14 @@ function SwapModal({ onClose }) {
4738
6176
  ref: catalogRef(tk.code, tk.issuer),
4739
6177
  code: tk.code,
4740
6178
  issuer: tk.issuer,
6179
+ available: availableFor(tk.code, tk.issuer),
4741
6180
  enabledInApp: false
4742
6181
  })).filter((o) => !enabledKeys.has(optKey(o)));
4743
6182
  const knownKeys = /* @__PURE__ */ new Set([...enabledKeys, ...catalogBuy.map(optKey)]);
4744
- const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o)));
4745
- const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter((o) => optKey(o) !== buyKeyOfSell);
6183
+ const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o))).map((o) => ({ ...o, available: availableFor(o.code, o.issuer) }));
6184
+ const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter(
6185
+ (o) => optKey(o) !== buyKeyOfSell || optKey(o) === (selectedBuy ? optKey(selectedBuy) : "")
6186
+ );
4746
6187
  useEffect(() => {
4747
6188
  if (sellOptions.length === 0) return;
4748
6189
  if (!selectedSell || !sellOptions.some((o) => optKey(o) === optKey(selectedSell))) {
@@ -4853,6 +6294,15 @@ function SwapModal({ onClose }) {
4853
6294
  async function handleRetry() {
4854
6295
  if (transaction.step === "error" && quote) await swap(quote);
4855
6296
  }
6297
+ function handleMax() {
6298
+ if (selectedSell?.available !== void 0) setAmount(selectedSell.available);
6299
+ }
6300
+ function handleReverse() {
6301
+ if (!selectedSell || !selectedBuy) return;
6302
+ const previousSell = selectedSell;
6303
+ setSelectedSell(selectedBuy);
6304
+ setSelectedBuy(previousSell);
6305
+ }
4856
6306
  function handleCopyHash() {
4857
6307
  if (!hash) return;
4858
6308
  navigator.clipboard.writeText(hash).then(() => {
@@ -4905,6 +6355,8 @@ function SwapModal({ onClose }) {
4905
6355
  onRefresh: handleRefresh,
4906
6356
  onSelectSell: setSelectedSell,
4907
6357
  onSelectBuy: setSelectedBuy,
6358
+ onReverse: handleReverse,
6359
+ onMax: handleMax,
4908
6360
  onAddCustomToken: addCustomToken,
4909
6361
  onAmountChange: setAmount,
4910
6362
  onProviderChange: setProvider,
@@ -4929,7 +6381,7 @@ var IN_FLIGHT_STEPS2 = [
4929
6381
  "signing-submitting",
4930
6382
  "building-signing-submitting"
4931
6383
  ];
4932
- function formatAmount4(value) {
6384
+ function formatAmount3(value) {
4933
6385
  const n = parseFloat(value);
4934
6386
  return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
4935
6387
  }
@@ -5243,7 +6695,7 @@ function EarnModal({ onClose }) {
5243
6695
  tab === "deposit" && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
5244
6696
  /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "In your wallet" }),
5245
6697
  /* @__PURE__ */ jsxs("span", { children: [
5246
- walletAvailable != null ? formatAmount4(walletAvailable) : "\u2026",
6698
+ walletAvailable != null ? formatAmount3(walletAvailable) : "\u2026",
5247
6699
  " ",
5248
6700
  assetCode
5249
6701
  ] })
@@ -5251,7 +6703,7 @@ function EarnModal({ onClose }) {
5251
6703
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
5252
6704
  /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "Your position" }),
5253
6705
  /* @__PURE__ */ jsxs("span", { children: [
5254
- position ? formatAmount4(position.balance) : "\u2026",
6706
+ position ? formatAmount3(position.balance) : "\u2026",
5255
6707
  " ",
5256
6708
  assetCode
5257
6709
  ] })
@@ -5269,13 +6721,13 @@ function EarnModal({ onClose }) {
5269
6721
  /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: tab === "deposit" ? "Amount" : "Withdraw amount" }),
5270
6722
  tab === "deposit" && walletAvailable != null && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(walletAvailable), children: [
5271
6723
  "Max: ",
5272
- formatAmount4(walletAvailable),
6724
+ formatAmount3(walletAvailable),
5273
6725
  " ",
5274
6726
  amountUnitLabel
5275
6727
  ] }),
5276
6728
  tab === "withdraw" && position && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(position.withdrawable), children: [
5277
6729
  "Max: ",
5278
- formatAmount4(position.withdrawable),
6730
+ formatAmount3(position.withdrawable),
5279
6731
  " ",
5280
6732
  amountUnitLabel
5281
6733
  ] })
@@ -5325,10 +6777,15 @@ function EarnModal({ onClose }) {
5325
6777
  ) });
5326
6778
  }
5327
6779
  function describeDevice(s) {
5328
- if (s.deviceLabel) return s.deviceLabel;
6780
+ if (s.deviceLabel) return normalizeDeviceLabel(s.deviceLabel);
5329
6781
  if (!s.userAgent) return "Unknown device";
5330
6782
  return parseUserAgent(s.userAgent);
5331
6783
  }
6784
+ function normalizeDeviceLabel(label) {
6785
+ const parts = label.replace(/\s+/g, " ").split(/ (?:[—–-]|·) /);
6786
+ if (parts.length === 2 && parts[1] !== void 0) return `${parts[0]} \xB7 ${parts[1].toLowerCase()}`;
6787
+ return label;
6788
+ }
5332
6789
  function detectBrowser(ua) {
5333
6790
  if (/Edg\//.test(ua)) return "Edge";
5334
6791
  if (/OPR\//.test(ua)) return "Opera";
@@ -5348,7 +6805,7 @@ function detectOS(ua) {
5348
6805
  function parseUserAgent(ua) {
5349
6806
  const browser = detectBrowser(ua);
5350
6807
  const os = detectOS(ua);
5351
- if (browser && os) return `${os} \u2014 ${browser}`;
6808
+ if (browser && os) return `${os} \xB7 ${browser.toLowerCase()}`;
5352
6809
  if (os) return os;
5353
6810
  if (browser) return browser;
5354
6811
  return ua.slice(0, 48);
@@ -5372,6 +6829,37 @@ function shortIp(hash) {
5372
6829
  if (!hash) return "";
5373
6830
  return hash.slice(0, 8);
5374
6831
  }
6832
+ function deviceKind(session) {
6833
+ const value = `${session.deviceLabel ?? ""} ${session.userAgent ?? ""}`.toLowerCase();
6834
+ if (/ipad|tablet/.test(value)) return "tablet";
6835
+ if (/android|iphone|ipod|mobile|phone/.test(value)) return "phone";
6836
+ return "desktop";
6837
+ }
6838
+ function DeviceIcon({ kind, size = "small" }) {
6839
+ const dimensions = size === "large" ? 30 : 18;
6840
+ if (kind === "phone") {
6841
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6842
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "2.5", width: "10", height: "19", rx: "2.5", stroke: "currentColor", strokeWidth: "1.8" }),
6843
+ /* @__PURE__ */ jsx("path", { d: "M10.5 5h3M11 18.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
6844
+ ] });
6845
+ }
6846
+ if (kind === "tablet") {
6847
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6848
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "2.5", width: "15", height: "19", rx: "2", stroke: "currentColor", strokeWidth: "1.8" }),
6849
+ /* @__PURE__ */ jsx("path", { d: "M11 18.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
6850
+ ] });
6851
+ }
6852
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6853
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "18", height: "12", rx: "1.8", stroke: "currentColor", strokeWidth: "1.8" }),
6854
+ /* @__PURE__ */ jsx("path", { d: "M8 20.5h8M12 15.5v5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })
6855
+ ] });
6856
+ }
6857
+ function CheckIcon() {
6858
+ return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m2.5 6.2 2.1 2.1L9.6 3.6", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" }) });
6859
+ }
6860
+ function CloseIcon() {
6861
+ return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
6862
+ }
5375
6863
  function SessionsModalTemplate({
5376
6864
  theme,
5377
6865
  accentColor,
@@ -5379,6 +6867,7 @@ function SessionsModalTemplate({
5379
6867
  state,
5380
6868
  revokingFamilyId,
5381
6869
  signingOutEverywhere,
6870
+ revokeError,
5382
6871
  onRefresh,
5383
6872
  onRevoke,
5384
6873
  onLogoutEverywhere,
@@ -5387,7 +6876,9 @@ function SessionsModalTemplate({
5387
6876
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5388
6877
  const isLoading = state.step === "loading";
5389
6878
  const sessions = state.step === "loaded" ? state.sessions : [];
5390
- const otherCount = sessions.filter((s) => !s.current).length;
6879
+ const currentSession = sessions.find((s) => s.current);
6880
+ const otherSessions = sessions.filter((s) => !s.current);
6881
+ const otherCount = otherSessions.length;
5391
6882
  return /* @__PURE__ */ jsxs(
5392
6883
  "div",
5393
6884
  {
@@ -5408,30 +6899,10 @@ function SessionsModalTemplate({
5408
6899
  disabled: isLoading,
5409
6900
  "aria-label": "Refresh",
5410
6901
  title: "Refresh",
5411
- children: /* @__PURE__ */ jsx(
5412
- "svg",
5413
- {
5414
- className: isLoading ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon",
5415
- width: "16",
5416
- height: "16",
5417
- viewBox: "0 0 16 16",
5418
- fill: "none",
5419
- "aria-hidden": true,
5420
- children: /* @__PURE__ */ jsx(
5421
- "path",
5422
- {
5423
- d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
5424
- stroke: "currentColor",
5425
- strokeWidth: "1.6",
5426
- strokeLinecap: "round",
5427
- strokeLinejoin: "round"
5428
- }
5429
- )
5430
- }
5431
- )
6902
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoading })
5432
6903
  }
5433
6904
  ),
5434
- /* @__PURE__ */ jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
6905
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
5435
6906
  ] })
5436
6907
  ] }),
5437
6908
  /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-list", children: [
@@ -5441,40 +6912,54 @@ function SessionsModalTemplate({
5441
6912
  ] }),
5442
6913
  state.step === "error" && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: state.message }),
5443
6914
  state.step === "loaded" && sessions.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No active sessions." }),
5444
- sessions.map((s) => {
5445
- const isRevoking = revokingFamilyId === s.familyId;
5446
- return /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item", "data-current": s.current || void 0, children: [
5447
- /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-main", children: [
5448
- /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-device", children: describeDevice(s) }),
5449
- s.current && /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-badge", children: "This device" })
5450
- ] }),
5451
- /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-meta", children: [
5452
- /* @__PURE__ */ jsxs("span", { children: [
5453
- "Last used ",
5454
- formatRelative(s.lastUsedAt ?? s.createdAt)
5455
- ] }),
5456
- s.ipHash && /* @__PURE__ */ jsxs(Fragment, { children: [
5457
- /* @__PURE__ */ jsx("span", { children: "\xB7" }),
5458
- /* @__PURE__ */ jsxs("span", { title: `ip-hash ${s.ipHash}`, children: [
5459
- "ip ",
5460
- shortIp(s.ipHash)
5461
- ] })
6915
+ state.step === "loaded" && currentSession && /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-card", children: [
6916
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-icon-wrap", children: [
6917
+ /* @__PURE__ */ jsx(DeviceIcon, { kind: deviceKind(currentSession), size: "large" }),
6918
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-current-check", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
6919
+ ] }),
6920
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-current-label", children: "This device" }),
6921
+ /* @__PURE__ */ jsx("strong", { className: "pollar-sessions-current-device", children: describeDevice(currentSession) }),
6922
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-status", children: [
6923
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-status-dot" }),
6924
+ /* @__PURE__ */ jsx("span", { children: "Active now" }),
6925
+ currentSession.ipHash && /* @__PURE__ */ jsxs(Fragment, { children: [
6926
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
6927
+ /* @__PURE__ */ jsxs("span", { title: `ip-hash ${currentSession.ipHash}`, children: [
6928
+ "IP ",
6929
+ shortIp(currentSession.ipHash)
5462
6930
  ] })
5463
- ] }),
5464
- !s.current && /* @__PURE__ */ jsx(
5465
- "button",
5466
- {
5467
- className: "pollar-sessions-item-revoke",
5468
- onClick: () => onRevoke(s.familyId),
5469
- disabled: isRevoking || signingOutEverywhere,
5470
- children: isRevoking ? /* @__PURE__ */ jsxs(Fragment, { children: [
5471
- /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm pollar-spinner-current" }),
5472
- "Revoking\u2026"
5473
- ] }) : "Revoke"
5474
- }
5475
- )
5476
- ] }, s.familyId);
5477
- })
6931
+ ] })
6932
+ ] })
6933
+ ] }),
6934
+ state.step === "loaded" && otherSessions.length > 0 && /* @__PURE__ */ jsxs("section", { className: "pollar-sessions-other-section", "aria-labelledby": "pollar-other-devices-heading", children: [
6935
+ /* @__PURE__ */ jsxs("h3", { id: "pollar-other-devices-heading", className: "pollar-sessions-section-title", children: [
6936
+ "Other devices ",
6937
+ /* @__PURE__ */ jsx("span", { children: otherCount })
6938
+ ] }),
6939
+ /* @__PURE__ */ jsx("div", { className: "pollar-sessions-grid", children: otherSessions.map((s) => {
6940
+ const isRevoking = revokingFamilyId === s.familyId;
6941
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item", children: [
6942
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-top", children: [
6943
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-icon", children: /* @__PURE__ */ jsx(DeviceIcon, { kind: deviceKind(s) }) }),
6944
+ /* @__PURE__ */ jsx(
6945
+ "button",
6946
+ {
6947
+ type: "button",
6948
+ className: "pollar-sessions-item-revoke",
6949
+ onClick: () => onRevoke(s.familyId),
6950
+ disabled: isRevoking || signingOutEverywhere,
6951
+ "aria-label": `Sign out ${describeDevice(s)}`,
6952
+ title: isRevoking ? "Signing out\u2026" : "Sign out this device",
6953
+ children: isRevoking ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsx(CloseIcon, {})
6954
+ }
6955
+ )
6956
+ ] }),
6957
+ /* @__PURE__ */ jsx("strong", { className: "pollar-sessions-item-device", children: describeDevice(s) }),
6958
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-meta", children: formatRelative(s.lastUsedAt ?? s.createdAt) })
6959
+ ] }, s.familyId);
6960
+ }) })
6961
+ ] }),
6962
+ revokeError && /* @__PURE__ */ jsx("div", { className: "pollar-sessions-action-error", role: "alert", children: revokeError })
5478
6963
  ] }),
5479
6964
  state.step === "loaded" && sessions.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-sessions-actions", children: /* @__PURE__ */ jsx(
5480
6965
  "button",
@@ -5499,6 +6984,7 @@ function SessionsModal({ onClose }) {
5499
6984
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5500
6985
  const [revokingFamilyId, setRevokingFamilyId] = useState(null);
5501
6986
  const [signingOutEverywhere, setSigningOutEverywhere] = useState(false);
6987
+ const [revokeError, setRevokeError] = useState(null);
5502
6988
  const load = useCallback(() => {
5503
6989
  void getClient().fetchSessions();
5504
6990
  }, [getClient]);
@@ -5515,9 +7001,11 @@ function SessionsModal({ onClose }) {
5515
7001
  const handleRevoke = useCallback(
5516
7002
  async (familyId) => {
5517
7003
  setRevokingFamilyId(familyId);
7004
+ setRevokeError(null);
5518
7005
  try {
5519
7006
  await getClient().revokeSession(familyId);
5520
7007
  } catch {
7008
+ setRevokeError("Could not sign out that device. Please try again.");
5521
7009
  } finally {
5522
7010
  setRevokingFamilyId(null);
5523
7011
  load();
@@ -5543,6 +7031,7 @@ function SessionsModal({ onClose }) {
5543
7031
  state: sessions,
5544
7032
  revokingFamilyId,
5545
7033
  signingOutEverywhere,
7034
+ revokeError,
5546
7035
  onRefresh: () => load(),
5547
7036
  onRevoke: (familyId) => void handleRevoke(familyId),
5548
7037
  onLogoutEverywhere: () => void handleLogoutEverywhere(),
@@ -5937,14 +7426,32 @@ function faucetFor(record) {
5937
7426
  if (record.code === "USDC") return { url: "https://faucet.circle.com/", label: "Get test USDC" };
5938
7427
  return null;
5939
7428
  }
5940
- function BalanceItem({ record, faucet }) {
7429
+ function assetMetadataFor(record, metadata) {
7430
+ return metadata.find(
7431
+ (asset) => resolveChain(asset.chain) === resolveChain(record.chain) && asset.code === record.code && (asset.issuer ?? "") === (record.issuer ?? "")
7432
+ );
7433
+ }
7434
+ function AssetIcon3({ code }) {
7435
+ return /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
7436
+ }
7437
+ function BalanceItem({
7438
+ record,
7439
+ faucet,
7440
+ metadata
7441
+ }) {
5941
7442
  const balanceDiffers = record.balance !== record.available;
7443
+ const secondary = metadata?.name ?? (record.issuer ? cropAddress(record.issuer) : "Native asset");
5942
7444
  return /* @__PURE__ */ jsxs("div", { className: "pollar-bal-item", children: [
5943
7445
  /* @__PURE__ */ jsxs("div", { className: "pollar-bal-asset-info", children: [
5944
- /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset-code-row", children: /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset", children: record.code }) }),
5945
- record.issuer && /* @__PURE__ */ jsxs("span", { className: "pollar-issuer", children: [
5946
- /* @__PURE__ */ jsx("span", { className: "pollar-issuer-addr", children: cropAddress(record.issuer) }),
5947
- /* @__PURE__ */ jsx(CopyButton, { value: record.issuer, label: "Copy issuer address", className: "pollar-copy-btn-sm" })
7446
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-leading", children: [
7447
+ /* @__PURE__ */ jsx(AssetIcon3, { code: record.code }),
7448
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-copy", children: [
7449
+ /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset", children: record.code }),
7450
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-secondary", children: [
7451
+ secondary,
7452
+ record.issuer && /* @__PURE__ */ jsx(CopyButton, { value: record.issuer, label: "Copy issuer address", className: "pollar-copy-btn-sm" })
7453
+ ] })
7454
+ ] })
5948
7455
  ] }),
5949
7456
  faucet && /* @__PURE__ */ jsxs("span", { className: "pollar-bal-faucet-hint", children: [
5950
7457
  "Need more?",
@@ -5968,6 +7475,7 @@ function WalletBalanceModalTemplate({
5968
7475
  styleOverrides,
5969
7476
  walletBalance,
5970
7477
  walletAddress,
7478
+ assetMetadata = [],
5971
7479
  chains,
5972
7480
  selectedChain,
5973
7481
  network,
@@ -5994,33 +7502,13 @@ function WalletBalanceModalTemplate({
5994
7502
  disabled: isLoading,
5995
7503
  "aria-label": "Refresh",
5996
7504
  title: "Refresh",
5997
- children: /* @__PURE__ */ jsx(
5998
- "svg",
5999
- {
6000
- className: isLoading ? "pollar-modal-refresh-icon pollar-spinning" : "pollar-modal-refresh-icon",
6001
- width: "16",
6002
- height: "16",
6003
- viewBox: "0 0 16 16",
6004
- fill: "none",
6005
- "aria-hidden": true,
6006
- children: /* @__PURE__ */ jsx(
6007
- "path",
6008
- {
6009
- d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
6010
- stroke: "currentColor",
6011
- strokeWidth: "1.6",
6012
- strokeLinecap: "round",
6013
- strokeLinejoin: "round"
6014
- }
6015
- )
6016
- }
6017
- )
7505
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoading })
6018
7506
  }
6019
7507
  ),
6020
- /* @__PURE__ */ jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
7508
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
6021
7509
  ] })
6022
7510
  ] }),
6023
- /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoading }),
7511
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoading }),
6024
7512
  walletAddress && /* @__PURE__ */ jsxs("div", { className: "pollar-address-row", children: [
6025
7513
  /* @__PURE__ */ jsx("span", { className: "pollar-address", children: cropAddress(walletAddress) }),
6026
7514
  /* @__PURE__ */ jsx(CopyButton, { value: walletAddress, label: "Copy wallet address" })
@@ -6040,6 +7528,7 @@ function WalletBalanceModalTemplate({
6040
7528
  BalanceItem,
6041
7529
  {
6042
7530
  record: b,
7531
+ metadata: assetMetadataFor(b, assetMetadata),
6043
7532
  faucet: showFaucets ? faucetFor(b) : null
6044
7533
  },
6045
7534
  (b.chain ?? "") + b.code + (b.issuer ?? "")
@@ -6048,7 +7537,7 @@ function WalletBalanceModalTemplate({
6048
7537
  ] });
6049
7538
  }
6050
7539
  function WalletBalanceModal({ onClose }) {
6051
- const { walletBalance, refreshWalletBalance, wallets, network, styles } = usePollar();
7540
+ const { walletBalance, refreshWalletBalance, wallets, network, styles, enabledAssets } = usePollar();
6052
7541
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
6053
7542
  const { chains } = useChains();
6054
7543
  const [selectedChain, setSelectedChain] = useState(null);
@@ -6067,6 +7556,7 @@ function WalletBalanceModal({ onClose }) {
6067
7556
  styleOverrides,
6068
7557
  walletBalance,
6069
7558
  walletAddress,
7559
+ assetMetadata: enabledAssets.step === "loaded" ? enabledAssets.data.assets : [],
6070
7560
  chains,
6071
7561
  selectedChain,
6072
7562
  network,
@@ -6136,7 +7626,11 @@ var DEFAULT_APP_CONFIG = {
6136
7626
  function sessionsEqual(a, b) {
6137
7627
  if (a === b) return true;
6138
7628
  if (!a || !b) return false;
6139
- return a.clientSessionId === b.clientSessionId && a.userId === b.userId && a.status === b.status && a.token?.accessToken === b.token?.accessToken && a.token?.refreshToken === b.token?.refreshToken && a.token?.expiresAt === b.token?.expiresAt && a.wallet?.address === b.wallet?.address;
7629
+ return a.clientSessionId === b.clientSessionId && a.userId === b.userId && a.status === b.status && a.token?.accessToken === b.token?.accessToken && a.token?.refreshToken === b.token?.refreshToken && a.token?.expiresAt === b.token?.expiresAt && a.wallet?.address === b.wallet?.address && // The wallet's on-chain account can move from CREATING to READY without any
7630
+ // other field changing. Leaving it out of the comparison is what would make
7631
+ // the whole provisioning UI dead: the short-circuit below would swallow the
7632
+ // one emission that says the wait is over.
7633
+ a.wallet?.provisioning === b.wallet?.provisioning;
6140
7634
  }
6141
7635
  var PollarContext = createContext(null);
6142
7636
  var clientByConfig = /* @__PURE__ */ new WeakMap();
@@ -6302,6 +7796,7 @@ function PollarProvider({
6302
7796
  const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
6303
7797
  const [distributionRulesModalOpen, setDistributionRulesModalOpen] = useState(false);
6304
7798
  const walletAddress = sessionState?.wallet?.address || "";
7799
+ const walletProvisioning = sessionState?.wallet?.provisioning;
6305
7800
  const getClient = useCallback(() => pollarClient, [pollarClient]);
6306
7801
  const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
6307
7802
  const refreshAssets = useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
@@ -6393,6 +7888,7 @@ function PollarProvider({
6393
7888
  };
6394
7889
  }, [
6395
7890
  walletAddress,
7891
+ walletProvisioning,
6396
7892
  verified,
6397
7893
  pollarClient,
6398
7894
  getClient,
@@ -6413,11 +7909,24 @@ function PollarProvider({
6413
7909
  children,
6414
7910
  loginModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setLoginModalOpen(false), children: /* @__PURE__ */ jsx(LoginModal, { onClose: () => setLoginModalOpen(false) }) }),
6415
7911
  transactionModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setTransactionModalOpen(false), children: /* @__PURE__ */ jsx(TransactionModal, { onClose: () => setTransactionModalOpen(false) }) }),
6416
- kycModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setKycModalOpen(false), children: /* @__PURE__ */ jsx(
7912
+ kycModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setKycModalOpen(false), children: kycModalOptions.corridorId === void 0 && kycModalOptions.providerId === void 0 ? (
7913
+ // The app's own KYC: its configured steps, in order.
7914
+ /* @__PURE__ */ jsx(
7915
+ AppKycFlow,
7916
+ {
7917
+ onClose: () => setKycModalOpen(false),
7918
+ ...kycModalOptions.country !== void 0 && { country: kycModalOptions.country },
7919
+ ...kycModalOptions.level !== void 0 && { level: kycModalOptions.level },
7920
+ ...kycModalOptions.onApproved !== void 0 && { onApproved: kycModalOptions.onApproved }
7921
+ }
7922
+ )
7923
+ ) : /* @__PURE__ */ jsx(
6417
7924
  KycModal,
6418
7925
  {
6419
7926
  onClose: () => setKycModalOpen(false),
6420
7927
  ...kycModalOptions.country !== void 0 && { country: kycModalOptions.country },
7928
+ ...kycModalOptions.corridorId !== void 0 && { corridorId: kycModalOptions.corridorId },
7929
+ ...kycModalOptions.providerId !== void 0 && { providerId: kycModalOptions.providerId },
6421
7930
  ...kycModalOptions.level !== void 0 && { level: kycModalOptions.level },
6422
7931
  ...kycModalOptions.onApproved !== void 0 && { onApproved: kycModalOptions.onApproved }
6423
7932
  }
@@ -6479,6 +7988,7 @@ function WalletButtonTemplate({
6479
7988
  wrapperRef,
6480
7989
  isInProgress,
6481
7990
  showCreateAccount,
7991
+ notReadyReason,
6482
7992
  creatingAccount,
6483
7993
  onToggleOpen,
6484
7994
  onCreateAccount,
@@ -6543,6 +8053,10 @@ function WalletButtonTemplate({
6543
8053
  }
6544
8054
  ),
6545
8055
  open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
8056
+ notReadyReason && /* @__PURE__ */ jsxs(Fragment, { children: [
8057
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: notReadyReason }),
8058
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
8059
+ ] }),
6546
8060
  showCreateAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
6547
8061
  /* @__PURE__ */ jsxs(
6548
8062
  "button",
@@ -6812,7 +8326,7 @@ function WalletButton() {
6812
8326
  openDistributionRulesModal,
6813
8327
  tx: transaction
6814
8328
  } = usePollar();
6815
- const { primaryAddress } = useChains();
8329
+ const { primaryChain, primaryAddress } = useChains();
6816
8330
  const walletAddress = primaryAddress || (wallet?.address ?? "");
6817
8331
  const [open, setOpen] = useState(false);
6818
8332
  const [copied, setCopied] = useState(false);
@@ -6822,6 +8336,7 @@ function WalletButton() {
6822
8336
  const copyTimerRef = useRef(null);
6823
8337
  const isInProgress = transaction.step === "building" || transaction.step === "signing";
6824
8338
  const canCreateAccount = !created && wallet?.custody === "external" && wallet.existsOnStellar === false && wallet.fundingMode === "IMMEDIATE";
8339
+ const notReadyReason = walletNotReadyReason(wallet, primaryChain);
6825
8340
  const { theme = "light", accentColor = "#005DB4" } = styles;
6826
8341
  const isDark = theme === "dark";
6827
8342
  const dropdownBg = isDark ? "#18181b" : "#fff";
@@ -6915,6 +8430,7 @@ function WalletButton() {
6915
8430
  wrapperRef,
6916
8431
  isInProgress,
6917
8432
  showCreateAccount: canCreateAccount,
8433
+ notReadyReason,
6918
8434
  creatingAccount: creating,
6919
8435
  onToggleOpen: () => setOpen((v) => !v),
6920
8436
  onCreateAccount: handleCreateAccount,
@@ -6933,6 +8449,6 @@ function WalletButton() {
6933
8449
  );
6934
8450
  }
6935
8451
 
6936
- export { ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, usePollar };
8452
+ export { ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, usePollar, walletNotReadyReason };
6937
8453
  //# sourceMappingURL=index.mjs.map
6938
8454
  //# sourceMappingURL=index.mjs.map