@pollar/react 0.11.3-rc.1 → 0.11.4-rc.1

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, 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-rc.1"
1039
+ "0.11.4-rc.1"
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);
@@ -1217,7 +1275,7 @@ var REASON_LABEL = {
1217
1275
  DISTRIBUTION_RULE_NOT_STARTED: "Not started yet",
1218
1276
  DISTRIBUTION_RULE_EXPIRED: "Expired",
1219
1277
  DISTRIBUTION_RULE_EXHAUSTED: "Fully claimed",
1220
- // Per-user, per-window claim limit (resets next period) — not permanent.
1278
+ // Per-user, per-window claim limit (resets next period) - not permanent.
1221
1279
  DISTRIBUTION_RATE_LIMIT_EXCEEDED: "Claimed for this period"
1222
1280
  };
1223
1281
  function reasonLabel(reason) {
@@ -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,374 @@ 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
+ loadError: "Could not load your details. Please try again.",
3917
+ submitError: "Could not check your details. Please try again.",
3918
+ step: "Step {n} of {total}"
3919
+ },
3920
+ es: {
3921
+ title: "Confirma tus datos",
3922
+ 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.",
3923
+ givenNames: "Nombres",
3924
+ surname1: "Primer apellido",
3925
+ surname2: "Segundo apellido",
3926
+ birthdate: "Fecha de nacimiento",
3927
+ documentNumber: "N\xFAmero de CI",
3928
+ complement: "Complemento (si tu CI lo tiene)",
3929
+ submit: "Confirmar",
3930
+ submitting: "Validando\u2026",
3931
+ close: "Cerrar",
3932
+ approved: "Tus datos fueron confirmados.",
3933
+ review: "Tus datos est\xE1n en revisi\xF3n. Podr\xE1s continuar cuando se confirmen.",
3934
+ 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.",
3935
+ identityRequired: "Primero verifica tu identidad.",
3936
+ nameMismatch: "Los dos apellidos juntos deben leerse como en tu documento: {full}.",
3937
+ loadError: "No se pudieron cargar tus datos. Intenta de nuevo.",
3938
+ submitError: "No se pudieron validar tus datos. Intenta de nuevo.",
3939
+ step: "Paso {n} de {total}"
3940
+ }
3941
+ };
3942
+ var REGISTRATION_COPY = {
3943
+ en: {
3944
+ title: "Register with the payment provider",
3945
+ intro: "To use this route, the provider registers you as its customer. These details will be sent to it:",
3946
+ consent: "I agree to share these details with the provider.",
3947
+ submit: "Register",
3948
+ submitting: "Registering\u2026",
3949
+ close: "Close",
3950
+ notReady: "Complete the previous steps first.",
3951
+ missing: "Some details are missing: {fields}.",
3952
+ loadError: "Could not load the registration. Please try again.",
3953
+ submitError: "Could not register you. Please try again.",
3954
+ step: "Step {n} of {total}"
3955
+ },
3956
+ es: {
3957
+ title: "Registro con el proveedor de pagos",
3958
+ intro: "Para usar esta ruta, el proveedor te registra como su cliente. Se le enviar\xE1n estos datos:",
3959
+ consent: "Acepto compartir estos datos con el proveedor.",
3960
+ submit: "Registrarme",
3961
+ submitting: "Registrando\u2026",
3962
+ close: "Cerrar",
3963
+ notReady: "Primero completa los pasos anteriores.",
3964
+ missing: "Faltan algunos datos: {fields}.",
3965
+ loadError: "No se pudo cargar el registro. Intenta de nuevo.",
3966
+ submitError: "No se pudo completar el registro. Intenta de nuevo.",
3967
+ step: "Paso {n} de {total}"
3968
+ }
3969
+ };
3970
+ var SHARED_FIELD_LABELS = {
3971
+ en: {
3972
+ name: "Names",
3973
+ lastname: "Surnames",
3974
+ document_type: "Document type",
3975
+ document_number: "Document number",
3976
+ country: "Country",
3977
+ state_of_residence: "Department of residence",
3978
+ economic_activity: "Economic activity",
3979
+ source_of_funds: "Source of funds",
3980
+ destination_of_funds: "Use of funds",
3981
+ income_level: "Income level",
3982
+ doc_provider_id: "SEGIP validation"
3983
+ },
3984
+ es: {
3985
+ name: "Nombres",
3986
+ lastname: "Apellidos",
3987
+ document_type: "Tipo de documento",
3988
+ document_number: "N\xFAmero de documento",
3989
+ country: "Pa\xEDs",
3990
+ state_of_residence: "Departamento de residencia",
3991
+ economic_activity: "Actividad econ\xF3mica",
3992
+ source_of_funds: "Origen de los fondos",
3993
+ destination_of_funds: "Destino de los fondos",
3994
+ income_level: "Nivel de ingresos",
3995
+ doc_provider_id: "Validaci\xF3n SEGIP"
3996
+ }
3997
+ };
3998
+ var errorCode = (error) => error && typeof error === "object" && typeof error.code === "string" ? error.code : null;
3999
+ function missingFieldsOf(error) {
4000
+ const body = error && typeof error === "object" ? error.body : void 0;
4001
+ return Array.isArray(body?.missing) ? body.missing.filter((item) => typeof item === "string") : [];
4002
+ }
4003
+ function RegistryCheckModal({ optionId, progress, onClose, onApproved }) {
4004
+ const { getClient, styles } = usePollar();
4005
+ const client = getClient();
4006
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4007
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
4008
+ const language = useMemo(formLanguage, []);
4009
+ const copy = REGISTRY_COPY[language];
4010
+ const [check, setCheck] = useState(null);
4011
+ const [surname1, setSurname1] = useState("");
4012
+ const [surname2, setSurname2] = useState("");
4013
+ const [complement, setComplement] = useState("");
4014
+ const [error, setError] = useState(null);
4015
+ const [review, setReview] = useState(false);
4016
+ const [submitting, setSubmitting] = useState(false);
4017
+ const mounted = useRef(true);
4018
+ useEffect(() => {
4019
+ mounted.current = true;
4020
+ return () => {
4021
+ mounted.current = false;
4022
+ };
4023
+ }, []);
4024
+ useEffect(() => {
4025
+ client.getRegistryCheck(optionId).then((loaded) => {
4026
+ if (!mounted.current) return;
4027
+ if (loaded.status === "approved") return onApproved();
4028
+ setCheck(loaded);
4029
+ if (loaded.status === "pending") setReview(true);
4030
+ if (loaded.prefill.applies) {
4031
+ setSurname1(loaded.prefill.surname1);
4032
+ setSurname2(loaded.prefill.surname2 ?? "");
4033
+ setComplement(loaded.prefill.complementNumber ?? "");
4034
+ }
4035
+ }).catch((e) => {
4036
+ if (!mounted.current) return;
4037
+ setError(errorCode(e) === "KYC_REGISTRY_IDENTITY_REQUIRED" ? copy.identityRequired : copy.loadError);
4038
+ });
4039
+ }, [optionId]);
4040
+ const prefill = check?.prefill.applies ? check.prefill : null;
4041
+ async function submit() {
4042
+ if (!prefill) return;
4043
+ setSubmitting(true);
4044
+ setError(null);
4045
+ try {
4046
+ const result = await client.submitRegistryCheck(optionId, {
4047
+ surname1: surname1.trim(),
4048
+ ...surname2.trim() ? { surname2: surname2.trim() } : {},
4049
+ ...complement.trim() ? { complementNumber: complement.trim() } : {}
4050
+ });
4051
+ if (!mounted.current) return;
4052
+ if (result.status === "approved") onApproved();
4053
+ else setReview(true);
4054
+ } catch (e) {
4055
+ if (!mounted.current) return;
4056
+ const code = errorCode(e);
4057
+ if (code === "SDK_KYC_ALREADY_APPROVED") return onApproved();
4058
+ if (code === "KYC_REGISTRY_NAME_MISMATCH") {
4059
+ setError(copy.nameMismatch.replace("{full}", [prefill.surname1, prefill.surname2].filter(Boolean).join(" ")));
4060
+ } else if (code === "SDK_KYC_UNDER_REVIEW") setReview(true);
4061
+ else setError(copy.submitError);
4062
+ } finally {
4063
+ if (mounted.current) setSubmitting(false);
4064
+ }
4065
+ }
4066
+ const readOnly = (label, value) => /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4067
+ /* @__PURE__ */ jsx("span", { className: "pollar-form-label", children: label }),
4068
+ /* @__PURE__ */ jsx("input", { className: "pollar-input", value, readOnly: true, disabled: true })
4069
+ ] });
4070
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
4071
+ "div",
4072
+ {
4073
+ className: "pollar-modal-card pollar-form-modal",
4074
+ role: "dialog",
4075
+ "aria-modal": "true",
4076
+ "aria-label": copy.title,
4077
+ style: cssVars,
4078
+ onClick: (e) => e.stopPropagation(),
4079
+ children: [
4080
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4081
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-header-text", children: [
4082
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: copy.title }),
4083
+ progress && /* @__PURE__ */ jsx("p", { className: "pollar-form-subtitle", children: copy.step.replace("{n}", String(progress.position)).replace("{total}", String(progress.total)) })
4084
+ ] }),
4085
+ /* @__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" }) }) })
4086
+ ] }),
4087
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "alert", children: error }),
4088
+ !check && !error && /* @__PURE__ */ jsx("div", { className: "pollar-loading-block", children: /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }) }),
4089
+ check && (review || !prefill) && /* @__PURE__ */ jsxs("div", { className: "pollar-form-body", children: [
4090
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "status", children: review ? copy.review : copy.notApplicable }),
4091
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: copy.close }) })
4092
+ ] }),
4093
+ prefill && !review && /* @__PURE__ */ jsxs(
4094
+ "form",
4095
+ {
4096
+ className: "pollar-form-body",
4097
+ onSubmit: (e) => {
4098
+ e.preventDefault();
4099
+ void submit();
4100
+ },
4101
+ children: [
4102
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-description", children: copy.intro }),
4103
+ readOnly(copy.givenNames, prefill.givenNames),
4104
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4105
+ /* @__PURE__ */ jsxs("label", { htmlFor: "pollar-registry-surname1", className: "pollar-form-label", children: [
4106
+ copy.surname1,
4107
+ /* @__PURE__ */ jsx("span", { className: "pollar-form-required", children: " *" })
4108
+ ] }),
4109
+ /* @__PURE__ */ jsx(
4110
+ "input",
4111
+ {
4112
+ id: "pollar-registry-surname1",
4113
+ className: "pollar-input",
4114
+ value: surname1,
4115
+ required: true,
4116
+ disabled: submitting,
4117
+ onChange: (e) => setSurname1(e.target.value)
4118
+ }
4119
+ )
4120
+ ] }),
4121
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4122
+ /* @__PURE__ */ jsx("label", { htmlFor: "pollar-registry-surname2", className: "pollar-form-label", children: copy.surname2 }),
4123
+ /* @__PURE__ */ jsx(
4124
+ "input",
4125
+ {
4126
+ id: "pollar-registry-surname2",
4127
+ className: "pollar-input",
4128
+ value: surname2,
4129
+ disabled: submitting,
4130
+ onChange: (e) => setSurname2(e.target.value)
4131
+ }
4132
+ )
4133
+ ] }),
4134
+ readOnly(copy.birthdate, prefill.birthdate),
4135
+ readOnly(copy.documentNumber, prefill.documentNumber),
4136
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-field", children: [
4137
+ /* @__PURE__ */ jsx("label", { htmlFor: "pollar-registry-complement", className: "pollar-form-label", children: copy.complement }),
4138
+ /* @__PURE__ */ jsx(
4139
+ "input",
4140
+ {
4141
+ id: "pollar-registry-complement",
4142
+ className: "pollar-input",
4143
+ value: complement,
4144
+ maxLength: 3,
4145
+ disabled: submitting,
4146
+ onChange: (e) => setComplement(e.target.value.toUpperCase().replace(/[^0-9A-Z]/g, ""))
4147
+ }
4148
+ )
4149
+ ] }),
4150
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4151
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, disabled: submitting, children: copy.close }),
4152
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "pollar-btn-primary", disabled: submitting || !surname1.trim(), children: submitting ? copy.submitting : copy.submit })
4153
+ ] })
4154
+ ]
4155
+ }
4156
+ )
4157
+ ]
4158
+ }
4159
+ ) });
4160
+ }
4161
+ function ProviderRegistrationModal({ corridorId, progress, onClose, onRegistered }) {
4162
+ const { getClient, styles } = usePollar();
4163
+ const client = getClient();
4164
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4165
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
4166
+ const language = useMemo(formLanguage, []);
4167
+ const copy = REGISTRATION_COPY[language];
4168
+ const labels = SHARED_FIELD_LABELS[language];
4169
+ const [registration, setRegistration] = useState(null);
4170
+ const [consent, setConsent] = useState(false);
4171
+ const [error, setError] = useState(null);
4172
+ const [submitting, setSubmitting] = useState(false);
4173
+ const mounted = useRef(true);
4174
+ useEffect(() => {
4175
+ mounted.current = true;
4176
+ return () => {
4177
+ mounted.current = false;
4178
+ };
4179
+ }, []);
4180
+ useEffect(() => {
4181
+ client.getProviderRegistration(corridorId).then((loaded) => {
4182
+ if (!mounted.current) return;
4183
+ if (loaded.status === "registered") onRegistered();
4184
+ else setRegistration(loaded);
4185
+ }).catch(() => mounted.current && setError(copy.loadError));
4186
+ }, [corridorId]);
4187
+ async function submit() {
4188
+ setSubmitting(true);
4189
+ setError(null);
4190
+ try {
4191
+ await client.submitProviderRegistration(corridorId);
4192
+ if (mounted.current) onRegistered();
4193
+ } catch (e) {
4194
+ if (!mounted.current) return;
4195
+ const code = errorCode(e);
4196
+ if (code === "KYC_REGISTRATION_MISSING_DATA") {
4197
+ setError(
4198
+ copy.missing.replace(
4199
+ "{fields}",
4200
+ missingFieldsOf(e).map((key) => labels[key] ?? key).join(", ")
4201
+ )
4202
+ );
4203
+ } else if (code === "KYC_REGISTRATION_STEPS_PENDING") setError(copy.notReady);
4204
+ else setError(copy.submitError);
4205
+ } finally {
4206
+ if (mounted.current) setSubmitting(false);
4207
+ }
4208
+ }
4209
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
4210
+ "div",
4211
+ {
4212
+ className: "pollar-modal-card pollar-form-modal",
4213
+ role: "dialog",
4214
+ "aria-modal": "true",
4215
+ "aria-label": copy.title,
4216
+ style: cssVars,
4217
+ onClick: (e) => e.stopPropagation(),
4218
+ children: [
4219
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4220
+ /* @__PURE__ */ jsxs("div", { className: "pollar-form-header-text", children: [
4221
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: copy.title }),
4222
+ progress && /* @__PURE__ */ jsx("p", { className: "pollar-form-subtitle", children: copy.step.replace("{n}", String(progress.position)).replace("{total}", String(progress.total)) })
4223
+ ] }),
4224
+ /* @__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" }) }) })
4225
+ ] }),
4226
+ error && /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "alert", children: error }),
4227
+ !registration && !error && /* @__PURE__ */ jsx("div", { className: "pollar-loading-block", children: /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }) }),
4228
+ registration && /* @__PURE__ */ jsxs(
4229
+ "form",
4230
+ {
4231
+ className: "pollar-form-body",
4232
+ onSubmit: (e) => {
4233
+ e.preventDefault();
4234
+ void submit();
4235
+ },
4236
+ children: [
4237
+ registration.ready ? /* @__PURE__ */ jsxs(Fragment, { children: [
4238
+ /* @__PURE__ */ jsx("p", { className: "pollar-form-description", children: copy.intro }),
4239
+ /* @__PURE__ */ jsx("ul", { className: "pollar-form-description", children: registration.fields.map((key) => /* @__PURE__ */ jsx("li", { children: labels[key] ?? key }, key)) }),
4240
+ /* @__PURE__ */ jsxs("label", { className: "pollar-form-choice", children: [
4241
+ /* @__PURE__ */ jsx(
4242
+ "input",
4243
+ {
4244
+ type: "checkbox",
4245
+ checked: consent,
4246
+ disabled: submitting,
4247
+ onChange: (e) => setConsent(e.target.checked)
4248
+ }
4249
+ ),
4250
+ copy.consent
4251
+ ] })
4252
+ ] }) : /* @__PURE__ */ jsx("p", { className: "pollar-form-error", role: "status", children: copy.notReady }),
4253
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4254
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, disabled: submitting, children: copy.close }),
4255
+ registration.ready && /* @__PURE__ */ jsx("button", { type: "submit", className: "pollar-btn-primary", disabled: submitting || !consent, children: submitting ? copy.submitting : copy.submit })
4256
+ ] })
4257
+ ]
4258
+ }
4259
+ )
4260
+ ]
4261
+ }
4262
+ ) });
4263
+ }
3194
4264
  var TERMINAL = ["completed", "failed"];
3195
4265
  function requiredFieldsOf(quote) {
3196
4266
  return quote.requiredFields ?? [];
@@ -3223,7 +4293,12 @@ var RAMP_ERROR_MESSAGES = {
3223
4293
  SDK_RAMPS_WALLET_UNSUPPORTED: "This wallet type cannot be used for this ramp.",
3224
4294
  SDK_RAMPS_PROVIDER_NOT_CONFIGURED: "This ramp provider is not configured for this app yet.",
3225
4295
  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."
4296
+ SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment.",
4297
+ SDK_RAMPS_ETHERFUSE_ERROR: "The provider rejected the request. Please try again in a moment.",
4298
+ SDK_RAMPS_INSUFFICIENT_BALANCE: "This wallet does not hold enough to cover that amount. Try a smaller one.",
4299
+ // Nothing moved on-chain, so the balance is untouched. Almost always no XLM
4300
+ // for the network fee on a wallet the app does not sponsor.
4301
+ SDK_RAMPS_ONCHAIN_SUBMIT_FAILED: "The network rejected the transaction, so nothing was sent. The wallet may need XLM for the fee."
3227
4302
  };
3228
4303
  function rampErrorMessage(e, fallback) {
3229
4304
  const body = e?.body;
@@ -3253,14 +4328,25 @@ function RampWidget({ onClose }) {
3253
4328
  const [countriesLoading, setCountriesLoading] = useState(true);
3254
4329
  const [refreshing, setRefreshing] = useState(false);
3255
4330
  const [quotes, setQuotes] = useState([]);
4331
+ const [requirementsRequired, setRequirementsRequired] = useState([]);
3256
4332
  const [selectedQuote, setSelectedQuote] = useState(null);
3257
4333
  const [isLoading, setIsLoading] = useState(false);
4334
+ const [pendingKyc, setPendingKyc] = useState(null);
4335
+ const [noticeMsg, setNoticeMsg] = useState(null);
4336
+ const kycAttempt = useRef(null);
4337
+ useEffect(
4338
+ () => () => {
4339
+ kycAttempt.current = null;
4340
+ },
4341
+ []
4342
+ );
3258
4343
  const [txId, setTxId] = useState(null);
3259
4344
  const [provider, setProvider] = useState("");
3260
4345
  const [kycUrl, setKycUrl] = useState(null);
3261
4346
  const [tosUrl, setTosUrl] = useState(null);
3262
4347
  const [kycPending, setKycPending] = useState(false);
3263
4348
  const [kycApproved, setKycApproved] = useState(false);
4349
+ const [onboardingStatus, setOnboardingStatus] = useState(void 0);
3264
4350
  const [txStatus, setTxStatus] = useState(null);
3265
4351
  const [stellarTxHash, setStellarTxHash] = useState(null);
3266
4352
  const [depositInstructions, setDepositInstructions] = useState(null);
@@ -3291,6 +4377,7 @@ function RampWidget({ onClose }) {
3291
4377
  }, [step, txId, txStatus, client]);
3292
4378
  useEffect(() => {
3293
4379
  if (step !== "status" || !kycPending || kycApproved) return;
4380
+ if (onboardingStatus === "awaiting_provider") return;
3294
4381
  let active = true;
3295
4382
  const check = async () => {
3296
4383
  try {
@@ -3305,7 +4392,7 @@ function RampWidget({ onClose }) {
3305
4392
  active = false;
3306
4393
  clearInterval(id);
3307
4394
  };
3308
- }, [step, kycPending, kycApproved, client]);
4395
+ }, [step, kycPending, kycApproved, onboardingStatus, client]);
3309
4396
  async function loadCountries(resetSelection) {
3310
4397
  setCountriesLoading(true);
3311
4398
  try {
@@ -3339,6 +4426,7 @@ function RampWidget({ onClose }) {
3339
4426
  if (step === "select_route") {
3340
4427
  const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
3341
4428
  setQuotes(result.quotes ?? []);
4429
+ setRequirementsRequired(result.requirementsRequired ?? []);
3342
4430
  } else if (step === "status" && txId) {
3343
4431
  const tx = await client.getRampTransaction(txId);
3344
4432
  setTxStatus(tx.status);
@@ -3354,6 +4442,7 @@ function RampWidget({ onClose }) {
3354
4442
  function resetToInput({ keepMessage = false } = {}) {
3355
4443
  setStep("input");
3356
4444
  setQuotes([]);
4445
+ setRequirementsRequired([]);
3357
4446
  setSelectedQuote(null);
3358
4447
  setFieldValues({});
3359
4448
  setTxId(null);
@@ -3363,6 +4452,9 @@ function RampWidget({ onClose }) {
3363
4452
  setTxStatus(null);
3364
4453
  setStellarTxHash(null);
3365
4454
  setDepositInstructions(null);
4455
+ setKycPending(false);
4456
+ setKycApproved(false);
4457
+ setNoticeMsg(null);
3366
4458
  if (!keepMessage) setErrorMsg(null);
3367
4459
  }
3368
4460
  async function resumeWithSignature(id, ps) {
@@ -3387,6 +4479,7 @@ function RampWidget({ onClose }) {
3387
4479
  }
3388
4480
  setKycUrl(result.kycUrl ?? null);
3389
4481
  setTosUrl(result.tosUrl ?? null);
4482
+ setOnboardingStatus(result.onboardingStatus);
3390
4483
  setKycPending(result.kycRequired === true);
3391
4484
  if (result.kycRequired) setKycApproved(false);
3392
4485
  setTxStatus(result.status);
@@ -3394,19 +4487,28 @@ function RampWidget({ onClose }) {
3394
4487
  setDepositInstructions(result.depositInstructions ?? null);
3395
4488
  setStep("status");
3396
4489
  }
4490
+ async function loadQuotes() {
4491
+ const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
4492
+ const list = result.quotes ?? [];
4493
+ const locked = result.requirementsRequired ?? [];
4494
+ if (list.length === 0 && locked.length === 0) {
4495
+ const down = (result.unavailable ?? []).map((u) => u.provider);
4496
+ setErrorMsg(
4497
+ down.length > 0 ? `${down.join(", ")} ${down.length === 1 ? "is" : "are"} temporarily unavailable. Please try again later.` : `No ramp providers available for ${country} yet.`
4498
+ );
4499
+ setStep("error");
4500
+ return false;
4501
+ }
4502
+ setQuotes(list);
4503
+ setRequirementsRequired(locked);
4504
+ return true;
4505
+ }
3397
4506
  async function handleFindRoute() {
3398
4507
  setStep("loading_quote");
3399
4508
  setIsLoading(true);
3400
4509
  setErrorMsg(null);
3401
4510
  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);
4511
+ if (!await loadQuotes()) return;
3410
4512
  setStep("select_route");
3411
4513
  } catch (e) {
3412
4514
  setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
@@ -3418,6 +4520,7 @@ function RampWidget({ onClose }) {
3418
4520
  function handleSelectQuote(quote) {
3419
4521
  setSelectedQuote(quote);
3420
4522
  setErrorMsg(null);
4523
+ setNoticeMsg(null);
3421
4524
  const broken = brokenLimitOf(Number(amount), quote);
3422
4525
  if (broken) {
3423
4526
  setErrorMsg(limitMessage(broken.limit, broken.value, currency));
@@ -3453,12 +4556,47 @@ function RampWidget({ onClose }) {
3453
4556
  const result = direction === "onramp" ? await client.createOnRamp(base) : await client.createOffRamp(base);
3454
4557
  await applyResult(result);
3455
4558
  } catch (e) {
4559
+ const requirement = requiredRampKyc(e);
4560
+ if (requirement) {
4561
+ setStep(requiredFieldsOf(quote).length ? "contact" : "select_route");
4562
+ setErrorMsg(
4563
+ requirement.type === "KYC" ? "Identity verification is required before continuing." : "A few more details are required before continuing."
4564
+ );
4565
+ kycAttempt.current = requirement;
4566
+ setPendingKyc(requirement);
4567
+ return;
4568
+ }
3456
4569
  setErrorMsg(rampErrorMessage(e, "Failed to start the ramp."));
3457
4570
  setStep("error");
3458
4571
  } finally {
3459
4572
  setIsLoading(false);
3460
4573
  }
3461
4574
  }
4575
+ async function requoteAfterKyc(completed = "KYC") {
4576
+ setStep("loading_quote");
4577
+ setIsLoading(true);
4578
+ setErrorMsg(null);
4579
+ setSelectedQuote(null);
4580
+ try {
4581
+ if (!await loadQuotes()) return;
4582
+ setNoticeMsg(
4583
+ 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."
4584
+ );
4585
+ setStep("select_route");
4586
+ } catch (e) {
4587
+ setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
4588
+ setStep("error");
4589
+ } finally {
4590
+ setIsLoading(false);
4591
+ }
4592
+ }
4593
+ function handleVerifyRoute(requirement) {
4594
+ const attempt = pendingFromQuote(requirement);
4595
+ setErrorMsg(null);
4596
+ setNoticeMsg(null);
4597
+ kycAttempt.current = attempt;
4598
+ setPendingKyc(attempt);
4599
+ }
3462
4600
  function handleOpenKyc() {
3463
4601
  if (kycUrl) window.open(kycUrl, "_blank", "noopener,noreferrer");
3464
4602
  }
@@ -3487,9 +4625,61 @@ function RampWidget({ onClose }) {
3487
4625
  }
3488
4626
  }
3489
4627
  const kycBlocking = kycPending && !kycApproved;
3490
- const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycBlocking;
4628
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycPending;
3491
4629
  const flowSteps = flowStepsOf(quotes, selectedQuote);
3492
4630
  const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
4631
+ if (pendingKyc?.type === "FORM") {
4632
+ return /* @__PURE__ */ jsx(
4633
+ RequirementFormModal,
4634
+ {
4635
+ formId: pendingKyc.optionId,
4636
+ ...pendingKyc.progress ? { progress: pendingKyc.progress } : {},
4637
+ onClose: () => {
4638
+ kycAttempt.current = null;
4639
+ setPendingKyc(null);
4640
+ },
4641
+ onSubmitted: () => {
4642
+ if (kycAttempt.current !== pendingKyc) return;
4643
+ kycAttempt.current = null;
4644
+ setPendingKyc(null);
4645
+ void requoteAfterKyc("FORM");
4646
+ }
4647
+ }
4648
+ );
4649
+ }
4650
+ if (pendingKyc?.type === "REGISTRY_CHECK" || pendingKyc?.type === "PROVIDER_REGISTRATION") {
4651
+ const close = () => {
4652
+ kycAttempt.current = null;
4653
+ setPendingKyc(null);
4654
+ };
4655
+ const done = () => {
4656
+ if (kycAttempt.current !== pendingKyc) return;
4657
+ close();
4658
+ void requoteAfterKyc(pendingKyc.type);
4659
+ };
4660
+ const progress = pendingKyc.progress ? { progress: pendingKyc.progress } : {};
4661
+ 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 });
4662
+ }
4663
+ if (pendingKyc) {
4664
+ return /* @__PURE__ */ jsx(
4665
+ KycModal,
4666
+ {
4667
+ country,
4668
+ corridorId: pendingKyc.corridorId,
4669
+ providerId: pendingKyc.optionId,
4670
+ onClose: () => {
4671
+ kycAttempt.current = null;
4672
+ setPendingKyc(null);
4673
+ },
4674
+ onApproved: () => {
4675
+ if (kycAttempt.current !== pendingKyc) return;
4676
+ kycAttempt.current = null;
4677
+ setPendingKyc(null);
4678
+ void requoteAfterKyc();
4679
+ }
4680
+ }
4681
+ );
4682
+ }
3493
4683
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
3494
4684
  RampWidgetTemplate,
3495
4685
  {
@@ -3510,12 +4700,14 @@ function RampWidget({ onClose }) {
3510
4700
  countriesLoading,
3511
4701
  refreshing,
3512
4702
  quotes,
4703
+ kycRequired: requirementsRequired,
3513
4704
  isLoading,
3514
4705
  provider,
3515
4706
  txStatus,
3516
4707
  kycUrl,
3517
4708
  tosUrl,
3518
4709
  kycBlocking,
4710
+ onboardingStatus: onboardingStatus ?? null,
3519
4711
  kycJustApproved: kycPending && kycApproved,
3520
4712
  stellarTxHash,
3521
4713
  explorerUrl: stellarTxHash ? `https://stellar.expert/explorer/${network === "mainnet" ? "public" : "testnet"}/tx/${stellarTxHash}` : null,
@@ -3523,6 +4715,7 @@ function RampWidget({ onClose }) {
3523
4715
  canComplete,
3524
4716
  completing,
3525
4717
  errorMsg,
4718
+ noticeMsg,
3526
4719
  onDirectionChange: setDirection,
3527
4720
  onAmountChange: (next) => {
3528
4721
  setAmount(next);
@@ -3532,6 +4725,7 @@ function RampWidget({ onClose }) {
3532
4725
  onCountryChange: handleCountryChange,
3533
4726
  onFindRoute: handleFindRoute,
3534
4727
  onSelectQuote: handleSelectQuote,
4728
+ onVerifyRoute: handleVerifyRoute,
3535
4729
  onContactContinue: handleContactContinue,
3536
4730
  onOpenKyc: handleOpenKyc,
3537
4731
  onOpenTos: handleOpenTos,
@@ -3544,6 +4738,18 @@ function RampWidget({ onClose }) {
3544
4738
  ) });
3545
4739
  }
3546
4740
 
4741
+ // src/lib/wallet-provisioning.ts
4742
+ function walletNotReadyReason(wallet, chain) {
4743
+ if (chain !== "STELLAR") return null;
4744
+ if (wallet?.provisioning === "CREATING") {
4745
+ return "Your wallet is still being prepared on the network. This usually takes a few seconds.";
4746
+ }
4747
+ if (wallet?.provisioning === "FAILED") {
4748
+ return "Your wallet could not be created on the network. Sign in again to retry.";
4749
+ }
4750
+ return null;
4751
+ }
4752
+
3547
4753
  // src/lib/qr-code/index.tsx
3548
4754
  var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
3549
4755
  var import_QRCode = __toESM(require_QRCode());
@@ -3586,6 +4792,31 @@ var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#0000
3586
4792
  }
3587
4793
  );
3588
4794
  });
4795
+ function PollarLogo(props) {
4796
+ return /* @__PURE__ */ jsxs("svg", { width: "45", height: "48", viewBox: "0 0 45 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
4797
+ /* @__PURE__ */ jsx(
4798
+ "path",
4799
+ {
4800
+ d: "M27.2476 11.7526L23.3926 10.0529L24.6679 9.14844L26.8168 9.19868L27.7248 10.3873L27.2476 11.7526Z",
4801
+ fill: "#005DB4"
4802
+ }
4803
+ ),
4804
+ /* @__PURE__ */ jsx(
4805
+ "path",
4806
+ {
4807
+ 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",
4808
+ fill: "#005DB4"
4809
+ }
4810
+ ),
4811
+ /* @__PURE__ */ jsx(
4812
+ "path",
4813
+ {
4814
+ 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",
4815
+ fill: "#005DB4"
4816
+ }
4817
+ )
4818
+ ] });
4819
+ }
3589
4820
  var CHAIN_NAME = {
3590
4821
  STELLAR: "Stellar",
3591
4822
  POLYGON: "Polygon",
@@ -3600,10 +4831,10 @@ function ReceiveModalTemplate({
3600
4831
  selectedChain,
3601
4832
  onSelectChain,
3602
4833
  copied,
4834
+ notReadyReason,
3603
4835
  onCopy,
3604
4836
  onClose
3605
4837
  }) {
3606
- const isDark = theme === "dark";
3607
4838
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
3608
4839
  const chainName = selectedChain ? CHAIN_NAME[selectedChain] ?? selectedChain : "wallet";
3609
4840
  return /* @__PURE__ */ jsxs(
@@ -3618,18 +4849,32 @@ function ReceiveModalTemplate({
3618
4849
  /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Receive" }),
3619
4850
  /* @__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
4851
  ] }),
3621
- /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
4852
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain }),
3622
4853
  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."
4854
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr", children: [
4855
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-network-badge", children: [
4856
+ /* @__PURE__ */ jsx("span", { className: "pollar-receive-network-dot", "aria-hidden": true }),
4857
+ chainName,
4858
+ " network"
4859
+ ] }),
4860
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-qr-frame", children: [
4861
+ /* @__PURE__ */ jsx(
4862
+ QRCode,
4863
+ {
4864
+ value: walletAddress,
4865
+ size: 220,
4866
+ level: "H",
4867
+ fgColor: "#111827",
4868
+ bgColor: "#ffffff",
4869
+ title: `${chainName} wallet QR code`
4870
+ }
4871
+ ),
4872
+ /* @__PURE__ */ jsx("span", { className: "pollar-receive-qr-mark", "aria-hidden": true, children: /* @__PURE__ */ jsx(PollarLogo, { width: "22", height: "24" }) })
4873
+ ] })
3630
4874
  ] }),
3631
- /* @__PURE__ */ jsxs("div", { className: "pollar-receive-address-row", children: [
3632
- /* @__PURE__ */ jsx("span", { className: "pollar-receive-address", children: walletAddress }),
4875
+ notReadyReason && /* @__PURE__ */ jsx("div", { className: "pollar-receive-notice", children: notReadyReason }),
4876
+ /* @__PURE__ */ jsxs("div", { className: "pollar-receive-address-actions", children: [
4877
+ /* @__PURE__ */ jsx("div", { className: "pollar-receive-address-row", children: /* @__PURE__ */ jsx("span", { className: "pollar-receive-address", children: walletAddress }) }),
3633
4878
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-receive-copy-btn", onClick: onCopy, "aria-label": "Copy address", children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
3634
4879
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
3635
4880
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
@@ -3644,7 +4889,7 @@ function ReceiveModalTemplate({
3644
4889
  }
3645
4890
  )
3646
4891
  ] }),
3647
- "Copied!"
4892
+ "Copied"
3648
4893
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3649
4894
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
3650
4895
  /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
@@ -3658,17 +4903,28 @@ function ReceiveModalTemplate({
3658
4903
  }
3659
4904
  )
3660
4905
  ] }),
3661
- "Copy address"
4906
+ "Copy"
3662
4907
  ] }) })
4908
+ ] }),
4909
+ /* @__PURE__ */ jsxs("p", { className: "pollar-receive-warning", children: [
4910
+ /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
4911
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.25" }),
4912
+ /* @__PURE__ */ jsx("path", { d: "M7 6.25V9.25M7 4.5V4.75", stroke: "currentColor", strokeWidth: "1.25", strokeLinecap: "round" })
4913
+ ] }),
4914
+ /* @__PURE__ */ jsxs("span", { children: [
4915
+ "Only send ",
4916
+ chainName,
4917
+ " assets to this address"
4918
+ ] })
3663
4919
  ] })
3664
4920
  ] }) : /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No wallet connected." }),
3665
- /* @__PURE__ */ jsx(PollarModalFooter, {})
4921
+ /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
3666
4922
  ]
3667
4923
  }
3668
4924
  );
3669
4925
  }
3670
4926
  function ReceiveModal({ onClose }) {
3671
- const { wallets, styles } = usePollar();
4927
+ const { wallet, wallets, styles } = usePollar();
3672
4928
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
3673
4929
  const [copied, setCopied] = useState(false);
3674
4930
  const copyTimerRef = useRef(null);
@@ -3678,6 +4934,7 @@ function ReceiveModal({ onClose }) {
3678
4934
  if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
3679
4935
  }, [chains, selectedChain]);
3680
4936
  const walletAddress = addressForChain(wallets, selectedChain);
4937
+ const notReadyReason = walletNotReadyReason(wallet, selectedChain);
3681
4938
  useEffect(
3682
4939
  () => () => {
3683
4940
  if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
@@ -3708,53 +4965,13 @@ function ReceiveModal({ onClose }) {
3708
4965
  setCopied(false);
3709
4966
  setSelectedChain(chain);
3710
4967
  },
4968
+ notReadyReason,
3711
4969
  copied,
3712
4970
  onCopy: handleCopy,
3713
4971
  onClose
3714
4972
  }
3715
4973
  ) });
3716
4974
  }
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
4975
  var STATUS_MESSAGES = {
3759
4976
  idle: "",
3760
4977
  building: "Building transaction\u2026",
@@ -3960,6 +5177,97 @@ function formatBalance(balance) {
3960
5177
  function assetKey3(record) {
3961
5178
  return `${record.code}:${record.issuer ?? "native"}`;
3962
5179
  }
5180
+ function chainLabel(chain) {
5181
+ if (chain === "STELLAR") return "Stellar";
5182
+ if (chain === "SOLANA") return "Solana";
5183
+ if (chain === "POLYGON") return "Polygon";
5184
+ return "network";
5185
+ }
5186
+ function AssetIcon({ code }) {
5187
+ return /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
5188
+ }
5189
+ function SendAssetSelector({ assets, selectedAsset, loading, onSelectAsset }) {
5190
+ const [open, setOpen] = useState(false);
5191
+ const triggerRef = useRef(null);
5192
+ useEffect(() => {
5193
+ if (!open) return;
5194
+ const closeOnOutsideClick = (event) => {
5195
+ if (!(event.target instanceof Node) || !triggerRef.current?.parentElement?.contains(event.target)) setOpen(false);
5196
+ };
5197
+ document.addEventListener("mousedown", closeOnOutsideClick);
5198
+ return () => document.removeEventListener("mousedown", closeOnOutsideClick);
5199
+ }, [open]);
5200
+ function handleTriggerKeyDown(event) {
5201
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
5202
+ event.preventDefault();
5203
+ setOpen(true);
5204
+ }
5205
+ }
5206
+ if (loading) {
5207
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
5208
+ /* @__PURE__ */ jsx("span", { className: "pollar-send-label", children: "Asset" }),
5209
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-asset-selector pollar-send-asset-selector-loading", "aria-label": "Loading assets", children: [
5210
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
5211
+ /* @__PURE__ */ jsx("span", { children: "Loading assets\u2026" })
5212
+ ] })
5213
+ ] });
5214
+ }
5215
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
5216
+ /* @__PURE__ */ jsx("label", { className: "pollar-send-label", id: "pollar-send-asset-label", children: "Asset" }),
5217
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-asset-picker", children: [
5218
+ /* @__PURE__ */ jsxs(
5219
+ "button",
5220
+ {
5221
+ ref: triggerRef,
5222
+ type: "button",
5223
+ className: "pollar-send-asset-selector",
5224
+ "aria-labelledby": "pollar-send-asset-label",
5225
+ "aria-haspopup": "listbox",
5226
+ "aria-expanded": open,
5227
+ disabled: !selectedAsset,
5228
+ onClick: () => setOpen((value) => !value),
5229
+ onKeyDown: handleTriggerKeyDown,
5230
+ children: [
5231
+ selectedAsset ? /* @__PURE__ */ jsxs(Fragment, { children: [
5232
+ /* @__PURE__ */ jsx(AssetIcon, { code: selectedAsset.code }),
5233
+ /* @__PURE__ */ jsxs("span", { className: "pollar-send-asset-copy", children: [
5234
+ /* @__PURE__ */ jsx("strong", { children: selectedAsset.code }),
5235
+ /* @__PURE__ */ jsx("small", { children: selectedAsset.available === null ? "Balance unavailable" : `${formatBalance(selectedAsset.available)} available` })
5236
+ ] })
5237
+ ] }) : /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-copy", children: /* @__PURE__ */ jsx("strong", { children: "No assets available" }) }),
5238
+ /* @__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" }) })
5239
+ ]
5240
+ }
5241
+ ),
5242
+ open && assets.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-send-asset-menu", role: "listbox", "aria-label": "Assets", children: assets.map((asset) => {
5243
+ const key = assetKey3(asset);
5244
+ const selected = selectedAsset ? key === assetKey3(selectedAsset) : false;
5245
+ return /* @__PURE__ */ jsxs(
5246
+ "button",
5247
+ {
5248
+ type: "button",
5249
+ role: "option",
5250
+ "aria-selected": selected,
5251
+ className: `pollar-send-asset-option${selected ? " is-selected" : ""}`,
5252
+ onClick: () => {
5253
+ onSelectAsset(asset);
5254
+ setOpen(false);
5255
+ triggerRef.current?.focus();
5256
+ },
5257
+ children: [
5258
+ /* @__PURE__ */ jsx(AssetIcon, { code: asset.code }),
5259
+ /* @__PURE__ */ jsxs("span", { className: "pollar-send-asset-copy", children: [
5260
+ /* @__PURE__ */ jsx("strong", { children: asset.code }),
5261
+ /* @__PURE__ */ jsx("small", { children: asset.available === null ? "Balance unavailable" : `${formatBalance(asset.available)} available` })
5262
+ ] })
5263
+ ]
5264
+ },
5265
+ key
5266
+ );
5267
+ }) })
5268
+ ] })
5269
+ ] });
5270
+ }
3963
5271
  function SendModalTemplate({
3964
5272
  theme,
3965
5273
  accentColor,
@@ -3968,10 +5276,10 @@ function SendModalTemplate({
3968
5276
  txTitle,
3969
5277
  assets,
3970
5278
  selectedAsset,
3971
- chains,
3972
5279
  selectedChain,
3973
- walletAddress,
3974
5280
  canSendOnChain,
5281
+ notReadyReason,
5282
+ chains,
3975
5283
  onSelectChain,
3976
5284
  amount,
3977
5285
  destination,
@@ -3989,6 +5297,8 @@ function SendModalTemplate({
3989
5297
  onRefresh,
3990
5298
  onSelectAsset,
3991
5299
  onAmountChange,
5300
+ onMax,
5301
+ onPaste,
3992
5302
  onDestinationChange,
3993
5303
  onSubmit,
3994
5304
  onSignAndSend,
@@ -3998,8 +5308,9 @@ function SendModalTemplate({
3998
5308
  onDone
3999
5309
  }) {
4000
5310
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4001
- const selectedKey = selectedAsset ? assetKey3(selectedAsset) : "";
4002
- const canSubmit = canSendOnChain && !!selectedAsset && !!amount && !!destination.trim() && !isLoadingBalance;
5311
+ const parsedAmount = amount ? Number(amount) : 0;
5312
+ const availableAmount = selectedAsset?.available === null || selectedAsset?.available === void 0 ? 0 : Number(selectedAsset.available);
5313
+ const canSubmit = canSendOnChain && !notReadyReason && !!selectedAsset && Number.isFinite(parsedAmount) && parsedAmount > 0 && parsedAmount <= availableAmount && !!destination.trim() && !isLoadingBalance;
4003
5314
  const title = step === "form" ? "Send" : txTitle;
4004
5315
  return /* @__PURE__ */ jsxs(
4005
5316
  "div",
@@ -4012,7 +5323,14 @@ function SendModalTemplate({
4012
5323
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4013
5324
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-header-left", children: [
4014
5325
  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 })
5326
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-title-copy", children: [
5327
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title }),
5328
+ step === "form" && /* @__PURE__ */ jsxs("p", { className: "pollar-send-subtitle", children: [
5329
+ "Transfer assets to another ",
5330
+ chainLabel(selectedChain),
5331
+ " wallet"
5332
+ ] })
5333
+ ] })
4016
5334
  ] }),
4017
5335
  !isInProgress && /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
4018
5336
  step === "form" && /* @__PURE__ */ jsx(
@@ -4024,58 +5342,23 @@ function SendModalTemplate({
4024
5342
  disabled: isLoadingBalance,
4025
5343
  "aria-label": "Refresh",
4026
5344
  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
- )
5345
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoadingBalance })
4048
5346
  }
4049
5347
  ),
4050
5348
  /* @__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
5349
  ] })
4052
5350
  ] }),
4053
5351
  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
- ] }),
5352
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoadingBalance }),
4059
5353
  !canSendOnChain && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "Sending is not available on this network yet." }),
5354
+ canSendOnChain && notReadyReason && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: notReadyReason }),
4060
5355
  /* @__PURE__ */ jsx(
4061
- AssetSelect,
5356
+ SendAssetSelector,
4062
5357
  {
4063
- label: "Asset",
4064
- value: selectedKey,
5358
+ assets,
5359
+ selectedAsset,
4065
5360
  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
- }
5361
+ onSelectAsset
4079
5362
  }
4080
5363
  ),
4081
5364
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
@@ -4088,30 +5371,50 @@ function SendModalTemplate({
4088
5371
  selectedAsset.code
4089
5372
  ] })
4090
5373
  ] }),
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
- )
5374
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
5375
+ /* @__PURE__ */ jsx(
5376
+ "input",
5377
+ {
5378
+ className: "pollar-input pollar-send-amount-input",
5379
+ type: "text",
5380
+ inputMode: "decimal",
5381
+ placeholder: "0.00",
5382
+ value: amount,
5383
+ onChange: (e) => onAmountChange(e.target.value),
5384
+ "aria-label": "Amount"
5385
+ }
5386
+ ),
5387
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-suffix", children: [
5388
+ /* @__PURE__ */ jsx(
5389
+ "button",
5390
+ {
5391
+ type: "button",
5392
+ className: "pollar-send-inline-action",
5393
+ onClick: onMax,
5394
+ disabled: !selectedAsset?.available,
5395
+ children: "Max"
5396
+ }
5397
+ ),
5398
+ /* @__PURE__ */ jsx("span", { children: selectedAsset?.code ?? "" })
5399
+ ] })
5400
+ ] })
4102
5401
  ] }),
4103
5402
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
4104
5403
  /* @__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
- )
5404
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
5405
+ /* @__PURE__ */ jsx(
5406
+ "input",
5407
+ {
5408
+ className: "pollar-input pollar-send-destination-input",
5409
+ type: "text",
5410
+ placeholder: "G\u2026",
5411
+ value: destination,
5412
+ onChange: (e) => onDestinationChange(e.target.value),
5413
+ "aria-label": "Destination wallet"
5414
+ }
5415
+ ),
5416
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-send-inline-action", onClick: onPaste, children: "Paste" })
5417
+ ] })
4115
5418
  ] }),
4116
5419
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
4117
5420
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: "Continue" }) })
@@ -4152,7 +5455,6 @@ function SendModal({ onClose }) {
4152
5455
  signAndSubmitTx,
4153
5456
  tx: transaction,
4154
5457
  wallet,
4155
- wallets,
4156
5458
  network,
4157
5459
  styles
4158
5460
  } = usePollar();
@@ -4171,8 +5473,8 @@ function SendModal({ onClose }) {
4171
5473
  useEffect(() => {
4172
5474
  if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
4173
5475
  }, [chains, selectedChain]);
4174
- const walletAddress = addressForChain(wallets, selectedChain);
4175
5476
  const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA";
5477
+ const notReadyReason = walletNotReadyReason(wallet, selectedChain);
4176
5478
  const isBaseUnitChain = selectedChain === "SOLANA";
4177
5479
  useEffect(() => {
4178
5480
  void refreshWalletBalance();
@@ -4219,6 +5521,10 @@ function SendModal({ onClose }) {
4219
5521
  setFormError("Sending is not available on this network yet.");
4220
5522
  return;
4221
5523
  }
5524
+ if (notReadyReason) {
5525
+ setFormError(notReadyReason);
5526
+ return;
5527
+ }
4222
5528
  if (!selectedAsset) {
4223
5529
  setFormError("Select an asset");
4224
5530
  return;
@@ -4269,6 +5575,29 @@ function SendModal({ onClose }) {
4269
5575
  void signAndSubmitTx(transaction.buildData.unsignedXdr);
4270
5576
  }
4271
5577
  }
5578
+ function handleMax() {
5579
+ if (selectedAsset?.available !== null && selectedAsset?.available !== void 0) {
5580
+ setAmount(selectedAsset.available);
5581
+ setFormError("");
5582
+ }
5583
+ }
5584
+ async function handlePaste() {
5585
+ if (!navigator.clipboard?.readText) {
5586
+ setFormError("Clipboard access is not available. Paste the address manually.");
5587
+ return;
5588
+ }
5589
+ try {
5590
+ const value = (await navigator.clipboard.readText()).trim();
5591
+ if (!value) {
5592
+ setFormError("The clipboard is empty.");
5593
+ return;
5594
+ }
5595
+ setDestination(value);
5596
+ setFormError("");
5597
+ } catch {
5598
+ setFormError("Could not read the clipboard. Paste the address manually.");
5599
+ }
5600
+ }
4272
5601
  function handleCopyHash() {
4273
5602
  if (!hash) return;
4274
5603
  navigator.clipboard.writeText(hash).then(() => {
@@ -4302,8 +5631,8 @@ function SendModal({ onClose }) {
4302
5631
  selectedAsset,
4303
5632
  chains,
4304
5633
  selectedChain,
4305
- walletAddress,
4306
5634
  canSendOnChain,
5635
+ notReadyReason,
4307
5636
  onSelectChain: setSelectedChain,
4308
5637
  amount,
4309
5638
  destination,
@@ -4321,6 +5650,8 @@ function SendModal({ onClose }) {
4321
5650
  onRefresh: () => void refreshWalletBalance(),
4322
5651
  onSelectAsset: setSelectedAsset,
4323
5652
  onAmountChange: setAmount,
5653
+ onMax: handleMax,
5654
+ onPaste: handlePaste,
4324
5655
  onDestinationChange: setDestination,
4325
5656
  onSubmit: () => void handleSubmit(),
4326
5657
  onSignAndSend: handleSignAndSend,
@@ -4335,15 +5666,77 @@ function assetOptionKey(o) {
4335
5666
  return `${o.code}:${o.issuer ?? "native"}`;
4336
5667
  }
4337
5668
  var PROVIDER_LABELS = {
4338
- auto: "Best price (auto)",
5669
+ auto: "Best price",
4339
5670
  aquarius: "Aquarius",
4340
5671
  soroswap: "Soroswap",
4341
5672
  sdex: "Stellar DEX"
4342
5673
  };
4343
- function formatAmount3(value) {
5674
+ function formatAmount2(value) {
5675
+ if (value === void 0) return "\u2014";
4344
5676
  const n = parseFloat(value);
4345
5677
  return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
4346
5678
  }
5679
+ function formatRate(numerator, denominator) {
5680
+ const parse = (value) => {
5681
+ const trimmed = value.trim();
5682
+ if (!/^\d+(\.\d+)?$/.test(trimmed)) return null;
5683
+ const [whole2 = "0", fraction2 = ""] = trimmed.split(".");
5684
+ return { digits: BigInt(`${whole2}${fraction2}`), scale: fraction2.length };
5685
+ };
5686
+ const top = parse(numerator);
5687
+ const bottom = parse(denominator);
5688
+ if (!top || !bottom || bottom.digits === 0n || top.digits === 0n) return null;
5689
+ const precision = 8;
5690
+ const scaledNumerator = top.digits * 10n ** BigInt(bottom.scale + precision);
5691
+ const scaledDenominator = bottom.digits * 10n ** BigInt(top.scale);
5692
+ const scaled = scaledNumerator / scaledDenominator;
5693
+ const whole = scaled / 10n ** BigInt(precision);
5694
+ const fraction = (scaled % 10n ** BigInt(precision)).toString().padStart(precision, "0").replace(/0+$/, "");
5695
+ return fraction ? `${whole}.${fraction}` : whole.toString();
5696
+ }
5697
+ function AssetIcon2({ code }) {
5698
+ return /* @__PURE__ */ jsx("span", { className: "pollar-swap-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
5699
+ }
5700
+ function AssetPill({
5701
+ label,
5702
+ value,
5703
+ options,
5704
+ disabled,
5705
+ onChange
5706
+ }) {
5707
+ const selected = options.find((option) => assetOptionKey(option) === value);
5708
+ const appAssets = options.filter((option) => option.enabledInApp);
5709
+ const otherAssets = options.filter((option) => !option.enabledInApp);
5710
+ return /* @__PURE__ */ jsxs("label", { className: "pollar-swap-asset-pill-wrap", children: [
5711
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }),
5712
+ /* @__PURE__ */ jsx(AssetIcon2, { code: selected?.code ?? "\u2014" }),
5713
+ /* @__PURE__ */ jsxs(
5714
+ "select",
5715
+ {
5716
+ className: "pollar-swap-asset-pill",
5717
+ "aria-label": label,
5718
+ value,
5719
+ disabled,
5720
+ onChange: (event) => onChange(event.target.value),
5721
+ children: [
5722
+ value === "" && /* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true }),
5723
+ appAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: "App assets", children: appAssets.map((option) => /* @__PURE__ */ jsx("option", { value: assetOptionKey(option), children: option.code }, assetOptionKey(option))) }),
5724
+ otherAssets.length > 0 && /* @__PURE__ */ jsx("optgroup", { label: "Other assets", children: otherAssets.map((option) => /* @__PURE__ */ jsx("option", { value: assetOptionKey(option), children: option.code }, assetOptionKey(option))) })
5725
+ ]
5726
+ }
5727
+ ),
5728
+ /* @__PURE__ */ jsx("svg", { className: "pollar-swap-chevron", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
5729
+ "path",
5730
+ {
5731
+ d: "m3.5 5.25 3.5 3.5 3.5-3.5",
5732
+ stroke: "currentColor",
5733
+ strokeWidth: "1.5",
5734
+ strokeLinecap: "round",
5735
+ strokeLinejoin: "round"
5736
+ }
5737
+ ) })
5738
+ ] });
5739
+ }
4347
5740
  function SwapModalTemplate({
4348
5741
  theme,
4349
5742
  accentColor,
@@ -4379,6 +5772,8 @@ function SwapModalTemplate({
4379
5772
  onSelectSell,
4380
5773
  onSelectBuy,
4381
5774
  onAddCustomToken,
5775
+ onReverse,
5776
+ onMax,
4382
5777
  onAmountChange,
4383
5778
  onProviderChange,
4384
5779
  onSwap,
@@ -4405,19 +5800,29 @@ function SwapModalTemplate({
4405
5800
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
4406
5801
  const sellKey = selectedSell ? assetOptionKey(selectedSell) : "";
4407
5802
  const buyKey = selectedBuy ? assetOptionKey(selectedBuy) : "";
4408
- const canSwap = !smartUnsupported && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
5803
+ const sameAsset = !!selectedSell && !!selectedBuy && sellKey === buyKey;
5804
+ const canSwap = !smartUnsupported && !sameAsset && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
5805
+ const rate = quote && amount ? formatRate(quote.amountOut, amount) : null;
4409
5806
  const title = step === "form" ? "Swap" : txTitle;
5807
+ const selectSell = (key) => {
5808
+ const found = sellOptions.find((option) => assetOptionKey(option) === key);
5809
+ if (found) onSelectSell(found);
5810
+ };
5811
+ const selectBuy = (key) => {
5812
+ const found = buyOptions.find((option) => assetOptionKey(option) === key);
5813
+ if (found) onSelectBuy(found);
5814
+ };
4410
5815
  return /* @__PURE__ */ jsxs(
4411
5816
  "div",
4412
5817
  {
4413
- className: "pollar-modal-card pollar-send-modal",
5818
+ className: "pollar-modal-card pollar-swap-modal",
4414
5819
  "data-theme": theme,
4415
5820
  style: cssVars,
4416
- onClick: (e) => e.stopPropagation(),
5821
+ onClick: (event) => event.stopPropagation(),
4417
5822
  children: [
4418
5823
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
4419
5824
  /* @__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" }) }) }),
5825
+ 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
5826
  /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title })
4422
5827
  ] }),
4423
5828
  !isInProgress && /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
@@ -4430,16 +5835,7 @@ function SwapModalTemplate({
4430
5835
  disabled: configLoading || isLoadingData,
4431
5836
  "aria-label": "Refresh",
4432
5837
  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
- ) })
5838
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: configLoading || isLoadingData })
4443
5839
  }
4444
5840
  ),
4445
5841
  /* @__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 +5844,205 @@ function SwapModalTemplate({
4448
5844
  step === "form" && !configLoading && swapUnavailable && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Swap is not available for this app." }),
4449
5845
  step === "form" && !swapUnavailable && /* @__PURE__ */ jsxs(Fragment, { children: [
4450
5846
  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
- ),
5847
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panels", children: [
5848
+ /* @__PURE__ */ jsxs("section", { className: "pollar-swap-panel", "aria-labelledby": "pollar-swap-pay-label", children: [
5849
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-heading", children: [
5850
+ /* @__PURE__ */ jsx("span", { id: "pollar-swap-pay-label", className: "pollar-swap-panel-label", children: "You pay" }),
5851
+ /* @__PURE__ */ jsxs("span", { className: "pollar-swap-balance", children: [
5852
+ "Balance: ",
5853
+ formatAmount2(selectedSell?.available)
5854
+ ] })
5855
+ ] }),
5856
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-main", children: [
4522
5857
  /* @__PURE__ */ jsx(
4523
5858
  "input",
4524
5859
  {
4525
- className: "pollar-input",
5860
+ className: "pollar-swap-amount",
4526
5861
  type: "text",
4527
- placeholder: "Issuer (G...)",
4528
- value: customIssuer,
4529
- onChange: (e) => setCustomIssuer(e.target.value)
5862
+ inputMode: "decimal",
5863
+ "aria-label": "Amount to pay",
5864
+ placeholder: "0.00",
5865
+ value: amount,
5866
+ disabled: configLoading || isLoadingData,
5867
+ onChange: (event) => onAmountChange(event.target.value)
4530
5868
  }
4531
5869
  ),
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" }),
5870
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-selector", children: [
5871
+ /* @__PURE__ */ jsx(
5872
+ AssetPill,
5873
+ {
5874
+ label: "Asset to pay",
5875
+ value: sellKey,
5876
+ options: sellOptions,
5877
+ disabled: configLoading || isLoadingData,
5878
+ onChange: selectSell
5879
+ }
5880
+ ),
4535
5881
  /* @__PURE__ */ jsx(
4536
5882
  "button",
4537
5883
  {
4538
5884
  type: "button",
4539
- className: "pollar-swap-custom-cancel",
4540
- onClick: () => {
4541
- setCustomOpen(false);
4542
- setCustomError("");
4543
- },
4544
- children: "Cancel"
5885
+ className: "pollar-swap-max",
5886
+ onClick: onMax,
5887
+ disabled: !selectedSell?.available || configLoading || isLoadingData,
5888
+ children: "Max"
4545
5889
  }
4546
5890
  )
4547
5891
  ] })
4548
- ] }) : /* @__PURE__ */ jsx(
5892
+ ] })
5893
+ ] }),
5894
+ /* @__PURE__ */ jsx(
5895
+ "button",
5896
+ {
5897
+ type: "button",
5898
+ className: "pollar-swap-reverse",
5899
+ onClick: onReverse,
5900
+ disabled: !selectedSell || !selectedBuy,
5901
+ "aria-label": "Swap pay and receive assets",
5902
+ title: "Swap pay and receive assets",
5903
+ children: /* @__PURE__ */ jsx("svg", { width: "17", height: "17", viewBox: "0 0 17 17", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
5904
+ "path",
5905
+ {
5906
+ d: "M5 3v10M5 3 2.5 5.5M5 3l2.5 2.5M12 14V4M12 14l2.5-2.5M12 14 9.5 11.5",
5907
+ stroke: "currentColor",
5908
+ strokeWidth: "1.6",
5909
+ strokeLinecap: "round",
5910
+ strokeLinejoin: "round"
5911
+ }
5912
+ ) })
5913
+ }
5914
+ ),
5915
+ /* @__PURE__ */ jsxs("section", { className: "pollar-swap-panel", "aria-labelledby": "pollar-swap-receive-label", children: [
5916
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-heading", children: [
5917
+ /* @__PURE__ */ jsx("span", { id: "pollar-swap-receive-label", className: "pollar-swap-panel-label", children: "You receive" }),
5918
+ /* @__PURE__ */ jsxs("span", { className: "pollar-swap-balance", children: [
5919
+ "Balance: ",
5920
+ formatAmount2(selectedBuy?.available)
5921
+ ] })
5922
+ ] }),
5923
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-main", children: [
5924
+ /* @__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) }),
5925
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-selector", children: [
5926
+ /* @__PURE__ */ jsx(
5927
+ AssetPill,
5928
+ {
5929
+ label: "Asset to receive",
5930
+ value: buyKey,
5931
+ options: buyOptions,
5932
+ disabled: configLoading || isLoadingData,
5933
+ onChange: selectBuy
5934
+ }
5935
+ ),
5936
+ /* @__PURE__ */ jsx("span", { className: "pollar-swap-panel-note", children: "Quoted amount" })
5937
+ ] })
5938
+ ] })
5939
+ ] })
5940
+ ] }),
5941
+ /* @__PURE__ */ jsx("div", { className: "pollar-swap-custom-area", children: customOpen ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom", children: [
5942
+ /* @__PURE__ */ jsx(
5943
+ "input",
5944
+ {
5945
+ className: "pollar-input",
5946
+ type: "text",
5947
+ placeholder: "Code (e.g. USDC)",
5948
+ value: customCode,
5949
+ onChange: (e) => setCustomCode(e.target.value)
5950
+ }
5951
+ ),
5952
+ /* @__PURE__ */ jsx(
5953
+ "input",
5954
+ {
5955
+ className: "pollar-input",
5956
+ type: "text",
5957
+ placeholder: "Issuer (G...)",
5958
+ value: customIssuer,
5959
+ onChange: (e) => setCustomIssuer(e.target.value)
5960
+ }
5961
+ ),
5962
+ customError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: customError }),
5963
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom-actions", children: [
5964
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
5965
+ /* @__PURE__ */ jsx(
4549
5966
  "button",
4550
5967
  {
4551
5968
  type: "button",
4552
- className: "pollar-swap-custom-toggle",
4553
- disabled: configLoading,
4554
- onClick: () => setCustomOpen(true),
4555
- children: "+ Add a token by code / issuer"
5969
+ className: "pollar-swap-custom-cancel",
5970
+ onClick: () => {
5971
+ setCustomOpen(false);
5972
+ setCustomError("");
5973
+ },
5974
+ children: "Cancel"
4556
5975
  }
4557
5976
  )
5977
+ ] })
5978
+ ] }) : /* @__PURE__ */ jsx(
5979
+ "button",
5980
+ {
5981
+ type: "button",
5982
+ className: "pollar-swap-custom-toggle",
5983
+ disabled: configLoading,
5984
+ onClick: () => setCustomOpen(true),
5985
+ children: "+ Add a token by code / issuer"
4558
5986
  }
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: [
5987
+ ) }),
5988
+ /* @__PURE__ */ jsxs("div", { className: "pollar-swap-rate-row", children: [
5989
+ /* @__PURE__ */ jsx("div", { className: "pollar-swap-rate", "aria-live": "polite", children: rate && selectedSell && selectedBuy ? /* @__PURE__ */ jsxs(Fragment, { children: [
5990
+ "1 ",
5991
+ selectedSell.code,
5992
+ " \u2248 ",
5993
+ rate,
5994
+ " ",
5995
+ selectedBuy.code
5996
+ ] }) : quoteLoading ? "Updating rate\u2026" : "Exchange rate unavailable" }),
5997
+ configLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-route-pill", children: [
4563
5998
  /* @__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"
5999
+ " Loading"
6000
+ ] }) : /* @__PURE__ */ jsxs("label", { className: "pollar-swap-route-wrap", children: [
6001
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Route" }),
6002
+ /* @__PURE__ */ jsx(
6003
+ "select",
6004
+ {
6005
+ className: "pollar-swap-route-pill",
6006
+ "aria-label": "Route",
6007
+ value: provider,
6008
+ disabled: providers.length === 0,
6009
+ onChange: (event) => onProviderChange(event.target.value),
6010
+ 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))
6011
+ }
6012
+ )
6013
+ ] })
4579
6014
  ] }),
4580
6015
  !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
- ] }),
6016
+ !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
6017
+ quote && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote", children: [
4591
6018
  /* @__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),
6019
+ /* @__PURE__ */ jsx("span", { children: "Minimum received" }),
6020
+ /* @__PURE__ */ jsxs("strong", { children: [
6021
+ formatAmount2(quote.minReceived),
4595
6022
  " ",
4596
- selectedBuy.code
6023
+ selectedBuy?.code
4597
6024
  ] })
4598
6025
  ] }),
4599
6026
  /* @__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: [
6027
+ /* @__PURE__ */ jsx("span", { children: "Price impact" }),
6028
+ /* @__PURE__ */ jsxs("strong", { children: [
4602
6029
  quote.priceImpactPct,
4603
6030
  "%"
4604
6031
  ] })
4605
6032
  ] }),
4606
6033
  /* @__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] })
6034
+ /* @__PURE__ */ jsx("span", { children: "Route" }),
6035
+ /* @__PURE__ */ jsx("strong", { children: PROVIDER_LABELS[quote.provider] })
4609
6036
  ] })
4610
6037
  ] }),
4611
- !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
4612
6038
  buyNeedsTrustline && selectedBuy && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-trustline-notice", children: [
4613
6039
  "To receive ",
4614
6040
  selectedBuy.code,
4615
6041
  " your wallet needs a trustline. Swapping will create it first (~0.5 XLM reserve, refundable if you later remove it)."
4616
6042
  ] }),
6043
+ sameAsset && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Choose two different assets to swap." }),
4617
6044
  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" }) })
6045
+ /* @__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
6046
  ] }),
4620
6047
  step === "tx" && /* @__PURE__ */ jsx(
4621
6048
  TxStatusView,
@@ -4633,7 +6060,7 @@ function SwapModalTemplate({
4633
6060
  onDone
4634
6061
  }
4635
6062
  ),
4636
- /* @__PURE__ */ jsx(PollarModalFooter, {})
6063
+ /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
4637
6064
  ]
4638
6065
  }
4639
6066
  );
@@ -4714,6 +6141,7 @@ function SwapModal({ onClose }) {
4714
6141
  const balances = walletBalance.step === "loaded" ? walletBalance.data.balances : [];
4715
6142
  const assetRecords = enabledAssets.step === "loaded" ? enabledAssets.data.assets : [];
4716
6143
  const isLoadingData = walletBalance.step === "loading" || enabledAssets.step === "loading";
6144
+ const availableFor = (code, issuer) => balances.find((b) => b.code === code && (b.issuer ?? "") === (issuer ?? ""))?.available ?? void 0;
4717
6145
  const sellOptions = balances.filter(
4718
6146
  (b) => (b.chain === void 0 || b.chain === "STELLAR") && (b.type === "native" || (b.type === "credit_alphanum4" || b.type === "credit_alphanum12") && !b.trustlineRemoved)
4719
6147
  ).map((b) => ({
@@ -4731,6 +6159,7 @@ function SwapModal({ onClose }) {
4731
6159
  ref: toRef(a),
4732
6160
  code: a.code,
4733
6161
  issuer: a.issuer,
6162
+ available: availableFor(a.code, a.issuer),
4734
6163
  enabledInApp: a.enabledInApp
4735
6164
  }));
4736
6165
  const enabledKeys = new Set(enabledBuy.map(optKey));
@@ -4738,11 +6167,14 @@ function SwapModal({ onClose }) {
4738
6167
  ref: catalogRef(tk.code, tk.issuer),
4739
6168
  code: tk.code,
4740
6169
  issuer: tk.issuer,
6170
+ available: availableFor(tk.code, tk.issuer),
4741
6171
  enabledInApp: false
4742
6172
  })).filter((o) => !enabledKeys.has(optKey(o)));
4743
6173
  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);
6174
+ const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o))).map((o) => ({ ...o, available: availableFor(o.code, o.issuer) }));
6175
+ const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter(
6176
+ (o) => optKey(o) !== buyKeyOfSell || optKey(o) === (selectedBuy ? optKey(selectedBuy) : "")
6177
+ );
4746
6178
  useEffect(() => {
4747
6179
  if (sellOptions.length === 0) return;
4748
6180
  if (!selectedSell || !sellOptions.some((o) => optKey(o) === optKey(selectedSell))) {
@@ -4853,6 +6285,15 @@ function SwapModal({ onClose }) {
4853
6285
  async function handleRetry() {
4854
6286
  if (transaction.step === "error" && quote) await swap(quote);
4855
6287
  }
6288
+ function handleMax() {
6289
+ if (selectedSell?.available !== void 0) setAmount(selectedSell.available);
6290
+ }
6291
+ function handleReverse() {
6292
+ if (!selectedSell || !selectedBuy) return;
6293
+ const previousSell = selectedSell;
6294
+ setSelectedSell(selectedBuy);
6295
+ setSelectedBuy(previousSell);
6296
+ }
4856
6297
  function handleCopyHash() {
4857
6298
  if (!hash) return;
4858
6299
  navigator.clipboard.writeText(hash).then(() => {
@@ -4905,6 +6346,8 @@ function SwapModal({ onClose }) {
4905
6346
  onRefresh: handleRefresh,
4906
6347
  onSelectSell: setSelectedSell,
4907
6348
  onSelectBuy: setSelectedBuy,
6349
+ onReverse: handleReverse,
6350
+ onMax: handleMax,
4908
6351
  onAddCustomToken: addCustomToken,
4909
6352
  onAmountChange: setAmount,
4910
6353
  onProviderChange: setProvider,
@@ -4929,7 +6372,7 @@ var IN_FLIGHT_STEPS2 = [
4929
6372
  "signing-submitting",
4930
6373
  "building-signing-submitting"
4931
6374
  ];
4932
- function formatAmount4(value) {
6375
+ function formatAmount3(value) {
4933
6376
  const n = parseFloat(value);
4934
6377
  return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
4935
6378
  }
@@ -5243,7 +6686,7 @@ function EarnModal({ onClose }) {
5243
6686
  tab === "deposit" && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
5244
6687
  /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "In your wallet" }),
5245
6688
  /* @__PURE__ */ jsxs("span", { children: [
5246
- walletAvailable != null ? formatAmount4(walletAvailable) : "\u2026",
6689
+ walletAvailable != null ? formatAmount3(walletAvailable) : "\u2026",
5247
6690
  " ",
5248
6691
  assetCode
5249
6692
  ] })
@@ -5251,7 +6694,7 @@ function EarnModal({ onClose }) {
5251
6694
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-quote-row", children: [
5252
6695
  /* @__PURE__ */ jsx("span", { className: "pollar-send-hint", children: "Your position" }),
5253
6696
  /* @__PURE__ */ jsxs("span", { children: [
5254
- position ? formatAmount4(position.balance) : "\u2026",
6697
+ position ? formatAmount3(position.balance) : "\u2026",
5255
6698
  " ",
5256
6699
  assetCode
5257
6700
  ] })
@@ -5269,13 +6712,13 @@ function EarnModal({ onClose }) {
5269
6712
  /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: tab === "deposit" ? "Amount" : "Withdraw amount" }),
5270
6713
  tab === "deposit" && walletAvailable != null && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(walletAvailable), children: [
5271
6714
  "Max: ",
5272
- formatAmount4(walletAvailable),
6715
+ formatAmount3(walletAvailable),
5273
6716
  " ",
5274
6717
  amountUnitLabel
5275
6718
  ] }),
5276
6719
  tab === "withdraw" && position && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(position.withdrawable), children: [
5277
6720
  "Max: ",
5278
- formatAmount4(position.withdrawable),
6721
+ formatAmount3(position.withdrawable),
5279
6722
  " ",
5280
6723
  amountUnitLabel
5281
6724
  ] })
@@ -5325,10 +6768,15 @@ function EarnModal({ onClose }) {
5325
6768
  ) });
5326
6769
  }
5327
6770
  function describeDevice(s) {
5328
- if (s.deviceLabel) return s.deviceLabel;
6771
+ if (s.deviceLabel) return normalizeDeviceLabel(s.deviceLabel);
5329
6772
  if (!s.userAgent) return "Unknown device";
5330
6773
  return parseUserAgent(s.userAgent);
5331
6774
  }
6775
+ function normalizeDeviceLabel(label) {
6776
+ const parts = label.replace(/\s+/g, " ").split(/ (?:[—–-]|·) /);
6777
+ if (parts.length === 2 && parts[1] !== void 0) return `${parts[0]} \xB7 ${parts[1].toLowerCase()}`;
6778
+ return label;
6779
+ }
5332
6780
  function detectBrowser(ua) {
5333
6781
  if (/Edg\//.test(ua)) return "Edge";
5334
6782
  if (/OPR\//.test(ua)) return "Opera";
@@ -5348,7 +6796,7 @@ function detectOS(ua) {
5348
6796
  function parseUserAgent(ua) {
5349
6797
  const browser = detectBrowser(ua);
5350
6798
  const os = detectOS(ua);
5351
- if (browser && os) return `${os} \u2014 ${browser}`;
6799
+ if (browser && os) return `${os} \xB7 ${browser.toLowerCase()}`;
5352
6800
  if (os) return os;
5353
6801
  if (browser) return browser;
5354
6802
  return ua.slice(0, 48);
@@ -5372,6 +6820,37 @@ function shortIp(hash) {
5372
6820
  if (!hash) return "";
5373
6821
  return hash.slice(0, 8);
5374
6822
  }
6823
+ function deviceKind(session) {
6824
+ const value = `${session.deviceLabel ?? ""} ${session.userAgent ?? ""}`.toLowerCase();
6825
+ if (/ipad|tablet/.test(value)) return "tablet";
6826
+ if (/android|iphone|ipod|mobile|phone/.test(value)) return "phone";
6827
+ return "desktop";
6828
+ }
6829
+ function DeviceIcon({ kind, size = "small" }) {
6830
+ const dimensions = size === "large" ? 30 : 18;
6831
+ if (kind === "phone") {
6832
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6833
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "2.5", width: "10", height: "19", rx: "2.5", stroke: "currentColor", strokeWidth: "1.8" }),
6834
+ /* @__PURE__ */ jsx("path", { d: "M10.5 5h3M11 18.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
6835
+ ] });
6836
+ }
6837
+ if (kind === "tablet") {
6838
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6839
+ /* @__PURE__ */ jsx("rect", { x: "4.5", y: "2.5", width: "15", height: "19", rx: "2", stroke: "currentColor", strokeWidth: "1.8" }),
6840
+ /* @__PURE__ */ jsx("path", { d: "M11 18.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
6841
+ ] });
6842
+ }
6843
+ return /* @__PURE__ */ jsxs("svg", { width: dimensions, height: dimensions, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
6844
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "18", height: "12", rx: "1.8", stroke: "currentColor", strokeWidth: "1.8" }),
6845
+ /* @__PURE__ */ jsx("path", { d: "M8 20.5h8M12 15.5v5", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" })
6846
+ ] });
6847
+ }
6848
+ function CheckIcon() {
6849
+ 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" }) });
6850
+ }
6851
+ function CloseIcon() {
6852
+ 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" }) });
6853
+ }
5375
6854
  function SessionsModalTemplate({
5376
6855
  theme,
5377
6856
  accentColor,
@@ -5379,6 +6858,7 @@ function SessionsModalTemplate({
5379
6858
  state,
5380
6859
  revokingFamilyId,
5381
6860
  signingOutEverywhere,
6861
+ revokeError,
5382
6862
  onRefresh,
5383
6863
  onRevoke,
5384
6864
  onLogoutEverywhere,
@@ -5387,7 +6867,9 @@ function SessionsModalTemplate({
5387
6867
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5388
6868
  const isLoading = state.step === "loading";
5389
6869
  const sessions = state.step === "loaded" ? state.sessions : [];
5390
- const otherCount = sessions.filter((s) => !s.current).length;
6870
+ const currentSession = sessions.find((s) => s.current);
6871
+ const otherSessions = sessions.filter((s) => !s.current);
6872
+ const otherCount = otherSessions.length;
5391
6873
  return /* @__PURE__ */ jsxs(
5392
6874
  "div",
5393
6875
  {
@@ -5408,30 +6890,10 @@ function SessionsModalTemplate({
5408
6890
  disabled: isLoading,
5409
6891
  "aria-label": "Refresh",
5410
6892
  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
- )
6893
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoading })
5432
6894
  }
5433
6895
  ),
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" }) }) })
6896
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
5435
6897
  ] })
5436
6898
  ] }),
5437
6899
  /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-list", children: [
@@ -5441,40 +6903,54 @@ function SessionsModalTemplate({
5441
6903
  ] }),
5442
6904
  state.step === "error" && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: state.message }),
5443
6905
  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
- ] })
6906
+ state.step === "loaded" && currentSession && /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-card", children: [
6907
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-icon-wrap", children: [
6908
+ /* @__PURE__ */ jsx(DeviceIcon, { kind: deviceKind(currentSession), size: "large" }),
6909
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-current-check", children: /* @__PURE__ */ jsx(CheckIcon, {}) })
6910
+ ] }),
6911
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-current-label", children: "This device" }),
6912
+ /* @__PURE__ */ jsx("strong", { className: "pollar-sessions-current-device", children: describeDevice(currentSession) }),
6913
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-current-status", children: [
6914
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-status-dot" }),
6915
+ /* @__PURE__ */ jsx("span", { children: "Active now" }),
6916
+ currentSession.ipHash && /* @__PURE__ */ jsxs(Fragment, { children: [
6917
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
6918
+ /* @__PURE__ */ jsxs("span", { title: `ip-hash ${currentSession.ipHash}`, children: [
6919
+ "IP ",
6920
+ shortIp(currentSession.ipHash)
5462
6921
  ] })
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
- })
6922
+ ] })
6923
+ ] })
6924
+ ] }),
6925
+ state.step === "loaded" && otherSessions.length > 0 && /* @__PURE__ */ jsxs("section", { className: "pollar-sessions-other-section", "aria-labelledby": "pollar-other-devices-heading", children: [
6926
+ /* @__PURE__ */ jsxs("h3", { id: "pollar-other-devices-heading", className: "pollar-sessions-section-title", children: [
6927
+ "Other devices ",
6928
+ /* @__PURE__ */ jsx("span", { children: otherCount })
6929
+ ] }),
6930
+ /* @__PURE__ */ jsx("div", { className: "pollar-sessions-grid", children: otherSessions.map((s) => {
6931
+ const isRevoking = revokingFamilyId === s.familyId;
6932
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item", children: [
6933
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-top", children: [
6934
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-icon", children: /* @__PURE__ */ jsx(DeviceIcon, { kind: deviceKind(s) }) }),
6935
+ /* @__PURE__ */ jsx(
6936
+ "button",
6937
+ {
6938
+ type: "button",
6939
+ className: "pollar-sessions-item-revoke",
6940
+ onClick: () => onRevoke(s.familyId),
6941
+ disabled: isRevoking || signingOutEverywhere,
6942
+ "aria-label": `Sign out ${describeDevice(s)}`,
6943
+ title: isRevoking ? "Signing out\u2026" : "Sign out this device",
6944
+ children: isRevoking ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsx(CloseIcon, {})
6945
+ }
6946
+ )
6947
+ ] }),
6948
+ /* @__PURE__ */ jsx("strong", { className: "pollar-sessions-item-device", children: describeDevice(s) }),
6949
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-meta", children: formatRelative(s.lastUsedAt ?? s.createdAt) })
6950
+ ] }, s.familyId);
6951
+ }) })
6952
+ ] }),
6953
+ revokeError && /* @__PURE__ */ jsx("div", { className: "pollar-sessions-action-error", role: "alert", children: revokeError })
5478
6954
  ] }),
5479
6955
  state.step === "loaded" && sessions.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-sessions-actions", children: /* @__PURE__ */ jsx(
5480
6956
  "button",
@@ -5499,6 +6975,7 @@ function SessionsModal({ onClose }) {
5499
6975
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
5500
6976
  const [revokingFamilyId, setRevokingFamilyId] = useState(null);
5501
6977
  const [signingOutEverywhere, setSigningOutEverywhere] = useState(false);
6978
+ const [revokeError, setRevokeError] = useState(null);
5502
6979
  const load = useCallback(() => {
5503
6980
  void getClient().fetchSessions();
5504
6981
  }, [getClient]);
@@ -5515,9 +6992,11 @@ function SessionsModal({ onClose }) {
5515
6992
  const handleRevoke = useCallback(
5516
6993
  async (familyId) => {
5517
6994
  setRevokingFamilyId(familyId);
6995
+ setRevokeError(null);
5518
6996
  try {
5519
6997
  await getClient().revokeSession(familyId);
5520
6998
  } catch {
6999
+ setRevokeError("Could not sign out that device. Please try again.");
5521
7000
  } finally {
5522
7001
  setRevokingFamilyId(null);
5523
7002
  load();
@@ -5543,6 +7022,7 @@ function SessionsModal({ onClose }) {
5543
7022
  state: sessions,
5544
7023
  revokingFamilyId,
5545
7024
  signingOutEverywhere,
7025
+ revokeError,
5546
7026
  onRefresh: () => load(),
5547
7027
  onRevoke: (familyId) => void handleRevoke(familyId),
5548
7028
  onLogoutEverywhere: () => void handleLogoutEverywhere(),
@@ -5937,14 +7417,32 @@ function faucetFor(record) {
5937
7417
  if (record.code === "USDC") return { url: "https://faucet.circle.com/", label: "Get test USDC" };
5938
7418
  return null;
5939
7419
  }
5940
- function BalanceItem({ record, faucet }) {
7420
+ function assetMetadataFor(record, metadata) {
7421
+ return metadata.find(
7422
+ (asset) => resolveChain(asset.chain) === resolveChain(record.chain) && asset.code === record.code && (asset.issuer ?? "") === (record.issuer ?? "")
7423
+ );
7424
+ }
7425
+ function AssetIcon3({ code }) {
7426
+ return /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
7427
+ }
7428
+ function BalanceItem({
7429
+ record,
7430
+ faucet,
7431
+ metadata
7432
+ }) {
5941
7433
  const balanceDiffers = record.balance !== record.available;
7434
+ const secondary = metadata?.name ?? (record.issuer ? cropAddress(record.issuer) : "Native asset");
5942
7435
  return /* @__PURE__ */ jsxs("div", { className: "pollar-bal-item", children: [
5943
7436
  /* @__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" })
7437
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-leading", children: [
7438
+ /* @__PURE__ */ jsx(AssetIcon3, { code: record.code }),
7439
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-copy", children: [
7440
+ /* @__PURE__ */ jsx("span", { className: "pollar-bal-asset", children: record.code }),
7441
+ /* @__PURE__ */ jsxs("span", { className: "pollar-bal-asset-secondary", children: [
7442
+ secondary,
7443
+ record.issuer && /* @__PURE__ */ jsx(CopyButton, { value: record.issuer, label: "Copy issuer address", className: "pollar-copy-btn-sm" })
7444
+ ] })
7445
+ ] })
5948
7446
  ] }),
5949
7447
  faucet && /* @__PURE__ */ jsxs("span", { className: "pollar-bal-faucet-hint", children: [
5950
7448
  "Need more?",
@@ -5968,6 +7466,7 @@ function WalletBalanceModalTemplate({
5968
7466
  styleOverrides,
5969
7467
  walletBalance,
5970
7468
  walletAddress,
7469
+ assetMetadata = [],
5971
7470
  chains,
5972
7471
  selectedChain,
5973
7472
  network,
@@ -5994,33 +7493,13 @@ function WalletBalanceModalTemplate({
5994
7493
  disabled: isLoading,
5995
7494
  "aria-label": "Refresh",
5996
7495
  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
- )
7496
+ children: /* @__PURE__ */ jsx(RefreshIcon, { spinning: isLoading })
6018
7497
  }
6019
7498
  ),
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" }) }) })
7499
+ /* @__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
7500
  ] })
6022
7501
  ] }),
6023
- /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoading }),
7502
+ chains && onSelectChain && /* @__PURE__ */ jsx(ChainSelect, { value: selectedChain, options: chains, onChange: onSelectChain, disabled: isLoading }),
6024
7503
  walletAddress && /* @__PURE__ */ jsxs("div", { className: "pollar-address-row", children: [
6025
7504
  /* @__PURE__ */ jsx("span", { className: "pollar-address", children: cropAddress(walletAddress) }),
6026
7505
  /* @__PURE__ */ jsx(CopyButton, { value: walletAddress, label: "Copy wallet address" })
@@ -6040,6 +7519,7 @@ function WalletBalanceModalTemplate({
6040
7519
  BalanceItem,
6041
7520
  {
6042
7521
  record: b,
7522
+ metadata: assetMetadataFor(b, assetMetadata),
6043
7523
  faucet: showFaucets ? faucetFor(b) : null
6044
7524
  },
6045
7525
  (b.chain ?? "") + b.code + (b.issuer ?? "")
@@ -6048,7 +7528,7 @@ function WalletBalanceModalTemplate({
6048
7528
  ] });
6049
7529
  }
6050
7530
  function WalletBalanceModal({ onClose }) {
6051
- const { walletBalance, refreshWalletBalance, wallets, network, styles } = usePollar();
7531
+ const { walletBalance, refreshWalletBalance, wallets, network, styles, enabledAssets } = usePollar();
6052
7532
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
6053
7533
  const { chains } = useChains();
6054
7534
  const [selectedChain, setSelectedChain] = useState(null);
@@ -6067,6 +7547,7 @@ function WalletBalanceModal({ onClose }) {
6067
7547
  styleOverrides,
6068
7548
  walletBalance,
6069
7549
  walletAddress,
7550
+ assetMetadata: enabledAssets.step === "loaded" ? enabledAssets.data.assets : [],
6070
7551
  chains,
6071
7552
  selectedChain,
6072
7553
  network,
@@ -6104,7 +7585,7 @@ var browserPasskeyCeremony = async ({ challenge, mode }) => {
6104
7585
  challenge,
6105
7586
  rp: { id: rpId, name: rpId },
6106
7587
  user: { id: userId, name: "Smart Wallet", displayName: "Smart Wallet" },
6107
- // ES256 (secp256r1) — the curve the on-chain WebAuthn verifier expects.
7588
+ // ES256 (secp256r1) - the curve the on-chain WebAuthn verifier expects.
6108
7589
  pubKeyCredParams: [{ type: "public-key", alg: -7 }],
6109
7590
  authenticatorSelection: { residentKey: "required", userVerification: "required" },
6110
7591
  attestation: "none",
@@ -6136,9 +7617,14 @@ var DEFAULT_APP_CONFIG = {
6136
7617
  function sessionsEqual(a, b) {
6137
7618
  if (a === b) return true;
6138
7619
  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;
7620
+ 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
7621
+ // other field changing. Leaving it out of the comparison is what would make
7622
+ // the whole provisioning UI dead: the short-circuit below would swallow the
7623
+ // one emission that says the wait is over.
7624
+ a.wallet?.provisioning === b.wallet?.provisioning;
6140
7625
  }
6141
7626
  var PollarContext = createContext(null);
7627
+ var clientByConfig = /* @__PURE__ */ new WeakMap();
6142
7628
  function PollarProvider({
6143
7629
  client,
6144
7630
  appConfig: appConfigProp,
@@ -6147,20 +7633,29 @@ function PollarProvider({
6147
7633
  children
6148
7634
  }) {
6149
7635
  const [pollarClient] = useState(() => {
6150
- if (client instanceof PollarClient) {
7636
+ if (isPollarClient(client)) {
6151
7637
  client.setPasskeyDefaults({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner });
6152
7638
  return client;
6153
7639
  }
6154
- return new PollarClient({
7640
+ const alreadyBuilt = clientByConfig.get(client);
7641
+ if (alreadyBuilt) return alreadyBuilt;
7642
+ const built = new PollarClient({
6155
7643
  ...client,
6156
7644
  passkey: client.passkey ?? browserPasskeyCeremony,
6157
7645
  passkeySign: client.passkeySign ?? browserPasskeySigner
6158
7646
  });
7647
+ clientByConfig.set(client, built);
7648
+ return built;
6159
7649
  });
6160
- const ownsClientRef = useRef(!(client instanceof PollarClient));
7650
+ const ownsClientRef = useRef(!isPollarClient(client));
6161
7651
  const destroyTimerRef = useRef(null);
7652
+ const builtFromConfigRef = useRef(isPollarClient(client) ? null : client);
6162
7653
  useEffect(() => {
6163
7654
  if (!ownsClientRef.current) return;
7655
+ if (builtFromConfigRef.current) {
7656
+ clientByConfig.delete(builtFromConfigRef.current);
7657
+ builtFromConfigRef.current = null;
7658
+ }
6164
7659
  if (destroyTimerRef.current) {
6165
7660
  clearTimeout(destroyTimerRef.current);
6166
7661
  destroyTimerRef.current = null;
@@ -6292,6 +7787,7 @@ function PollarProvider({
6292
7787
  const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
6293
7788
  const [distributionRulesModalOpen, setDistributionRulesModalOpen] = useState(false);
6294
7789
  const walletAddress = sessionState?.wallet?.address || "";
7790
+ const walletProvisioning = sessionState?.wallet?.provisioning;
6295
7791
  const getClient = useCallback(() => pollarClient, [pollarClient]);
6296
7792
  const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
6297
7793
  const refreshAssets = useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
@@ -6383,6 +7879,7 @@ function PollarProvider({
6383
7879
  };
6384
7880
  }, [
6385
7881
  walletAddress,
7882
+ walletProvisioning,
6386
7883
  verified,
6387
7884
  pollarClient,
6388
7885
  getClient,
@@ -6403,11 +7900,24 @@ function PollarProvider({
6403
7900
  children,
6404
7901
  loginModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setLoginModalOpen(false), children: /* @__PURE__ */ jsx(LoginModal, { onClose: () => setLoginModalOpen(false) }) }),
6405
7902
  transactionModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setTransactionModalOpen(false), children: /* @__PURE__ */ jsx(TransactionModal, { onClose: () => setTransactionModalOpen(false) }) }),
6406
- kycModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setKycModalOpen(false), children: /* @__PURE__ */ jsx(
7903
+ kycModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setKycModalOpen(false), children: kycModalOptions.corridorId === void 0 && kycModalOptions.providerId === void 0 ? (
7904
+ // The app's own KYC: its configured steps, in order.
7905
+ /* @__PURE__ */ jsx(
7906
+ AppKycFlow,
7907
+ {
7908
+ onClose: () => setKycModalOpen(false),
7909
+ ...kycModalOptions.country !== void 0 && { country: kycModalOptions.country },
7910
+ ...kycModalOptions.level !== void 0 && { level: kycModalOptions.level },
7911
+ ...kycModalOptions.onApproved !== void 0 && { onApproved: kycModalOptions.onApproved }
7912
+ }
7913
+ )
7914
+ ) : /* @__PURE__ */ jsx(
6407
7915
  KycModal,
6408
7916
  {
6409
7917
  onClose: () => setKycModalOpen(false),
6410
7918
  ...kycModalOptions.country !== void 0 && { country: kycModalOptions.country },
7919
+ ...kycModalOptions.corridorId !== void 0 && { corridorId: kycModalOptions.corridorId },
7920
+ ...kycModalOptions.providerId !== void 0 && { providerId: kycModalOptions.providerId },
6411
7921
  ...kycModalOptions.level !== void 0 && { level: kycModalOptions.level },
6412
7922
  ...kycModalOptions.onApproved !== void 0 && { onApproved: kycModalOptions.onApproved }
6413
7923
  }
@@ -6469,6 +7979,7 @@ function WalletButtonTemplate({
6469
7979
  wrapperRef,
6470
7980
  isInProgress,
6471
7981
  showCreateAccount,
7982
+ notReadyReason,
6472
7983
  creatingAccount,
6473
7984
  onToggleOpen,
6474
7985
  onCreateAccount,
@@ -6533,6 +8044,10 @@ function WalletButtonTemplate({
6533
8044
  }
6534
8045
  ),
6535
8046
  open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
8047
+ notReadyReason && /* @__PURE__ */ jsxs(Fragment, { children: [
8048
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: notReadyReason }),
8049
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
8050
+ ] }),
6536
8051
  showCreateAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
6537
8052
  /* @__PURE__ */ jsxs(
6538
8053
  "button",
@@ -6802,9 +8317,8 @@ function WalletButton() {
6802
8317
  openDistributionRulesModal,
6803
8318
  tx: transaction
6804
8319
  } = usePollar();
6805
- const { primaryAddress } = useChains();
8320
+ const { primaryChain, primaryAddress } = useChains();
6806
8321
  const walletAddress = primaryAddress || (wallet?.address ?? "");
6807
- const walletType = wallet?.custody === "external" ? wallet.provider : null;
6808
8322
  const [open, setOpen] = useState(false);
6809
8323
  const [copied, setCopied] = useState(false);
6810
8324
  const [creating, setCreating] = useState(false);
@@ -6813,6 +8327,7 @@ function WalletButton() {
6813
8327
  const copyTimerRef = useRef(null);
6814
8328
  const isInProgress = transaction.step === "building" || transaction.step === "signing";
6815
8329
  const canCreateAccount = !created && wallet?.custody === "external" && wallet.existsOnStellar === false && wallet.fundingMode === "IMMEDIATE";
8330
+ const notReadyReason = walletNotReadyReason(wallet, primaryChain);
6816
8331
  const { theme = "light", accentColor = "#005DB4" } = styles;
6817
8332
  const isDark = theme === "dark";
6818
8333
  const dropdownBg = isDark ? "#18181b" : "#fff";
@@ -6905,8 +8420,8 @@ function WalletButton() {
6905
8420
  itemColor,
6906
8421
  wrapperRef,
6907
8422
  isInProgress,
6908
- walletType,
6909
8423
  showCreateAccount: canCreateAccount,
8424
+ notReadyReason,
6910
8425
  creatingAccount: creating,
6911
8426
  onToggleOpen: () => setOpen((v) => !v),
6912
8427
  onCreateAccount: handleCreateAccount,
@@ -6925,6 +8440,6 @@ function WalletButton() {
6925
8440
  );
6926
8441
  }
6927
8442
 
6928
- 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 };
8443
+ 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 };
6929
8444
  //# sourceMappingURL=index.mjs.map
6930
8445
  //# sourceMappingURL=index.mjs.map