@pollar/react 0.10.0-rc.8 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1035,7 +1035,7 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.10.0-rc.8"
1038
+ "0.10.0"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
@@ -1855,12 +1855,39 @@ var GoogleButton = ({ disabled, onClick }) => {
1855
1855
  function WalletAdapterButtons({
1856
1856
  walletAdapters,
1857
1857
  onConnect,
1858
- isLoading
1858
+ isLoading,
1859
+ variant = "list"
1859
1860
  }) {
1860
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-wallet-list", children: walletAdapters.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", disabled: isLoading, className: "pollar-wallet-list-btn", onClick: () => onConnect(a.id), children: [
1861
- a.meta.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.meta.iconUrl, alt: a.meta.label, className: "pollar-wallet-list-icon" }),
1862
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-wallet-list-name", children: a.meta.label })
1863
- ] }, a.id)) });
1861
+ if (variant === "entry") {
1862
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: walletAdapters.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
1863
+ "button",
1864
+ {
1865
+ type: "button",
1866
+ disabled: isLoading,
1867
+ className: "pollar-wallet-entry-btn",
1868
+ onClick: () => onConnect(a.id),
1869
+ children: [
1870
+ a.meta.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.meta.iconUrl, alt: a.meta.label, className: "pollar-wallet-icon" }),
1871
+ a.meta.label
1872
+ ]
1873
+ },
1874
+ a.id
1875
+ )) });
1876
+ }
1877
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-wallet-list", children: walletAdapters.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
1878
+ "button",
1879
+ {
1880
+ type: "button",
1881
+ disabled: isLoading,
1882
+ className: "pollar-wallet-list-btn",
1883
+ onClick: () => onConnect(a.id),
1884
+ children: [
1885
+ a.meta.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.meta.iconUrl, alt: a.meta.label, className: "pollar-wallet-list-icon" }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-wallet-list-name", children: a.meta.label })
1887
+ ]
1888
+ },
1889
+ a.id
1890
+ )) });
1864
1891
  }
1865
1892
  var AUTH_STATE_MESSAGES = {
1866
1893
  idle: "",
@@ -1917,7 +1944,6 @@ function LoginModalTemplate({
1917
1944
  onWalletConnect,
1918
1945
  onLoginSmartWallet,
1919
1946
  onCreateSmartWallet,
1920
- renderWallets,
1921
1947
  authState,
1922
1948
  codeInputKey,
1923
1949
  onCodeSubmit,
@@ -1925,10 +1951,19 @@ function LoginModalTemplate({
1925
1951
  onCancel,
1926
1952
  onRetry
1927
1953
  }) {
1928
- const [showWalletPicker, setShowWalletPicker] = react.useState(false);
1929
1954
  const [showPasskeyChooser, setShowPasskeyChooser] = react.useState(false);
1955
+ const [activeGroup, setActiveGroup] = react.useState(null);
1930
1956
  const isDark = theme === "dark";
1931
1957
  const enabledSocial = Object.entries(providers).filter(([, enabled]) => enabled);
1958
+ const rootAdapters = walletAdapters.filter((a) => !a.meta.group);
1959
+ const walletGroups = walletAdapters.filter((a) => a.meta.group).reduce((acc, a) => {
1960
+ const label = a.meta.group;
1961
+ const existing = acc.find((g) => g.label === label);
1962
+ if (existing) existing.adapters.push(a);
1963
+ else acc.push({ label, adapters: [a] });
1964
+ return acc;
1965
+ }, []);
1966
+ const activeGroupAdapters = walletGroups.find((g) => g.label === activeGroup)?.adapters ?? [];
1932
1967
  const cssVars = {
1933
1968
  "--pollar-accent": accentColor,
1934
1969
  "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
@@ -1992,11 +2027,17 @@ function LoginModalTemplate({
1992
2027
  /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: onBack }),
1993
2028
  /* @__PURE__ */ jsxRuntime.jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
1994
2029
  }) }, codeInputKey)
1995
- ] }) : showWalletPicker ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1996
- /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: () => setShowWalletPicker(false) }),
1997
- renderWallets ? renderWallets({ onConnect: onWalletConnect ?? (() => {
1998
- }), authState }) : /* @__PURE__ */ jsxRuntime.jsx(WalletAdapterButtons, { walletAdapters, onConnect: onWalletConnect ?? (() => {
1999
- }), isLoading })
2030
+ ] }) : activeGroup ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2031
+ /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: () => setActiveGroup(null) }),
2032
+ /* @__PURE__ */ jsxRuntime.jsx(
2033
+ WalletAdapterButtons,
2034
+ {
2035
+ walletAdapters: activeGroupAdapters,
2036
+ onConnect: onWalletConnect ?? (() => {
2037
+ }),
2038
+ isLoading
2039
+ }
2040
+ )
2000
2041
  ] }) : showPasskeyChooser ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2001
2042
  /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: () => setShowPasskeyChooser(false) }),
2002
2043
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-wallet-section", children: [
@@ -2038,32 +2079,45 @@ function LoginModalTemplate({
2038
2079
  enabledSocial.some(([key]) => key === "github") && /* @__PURE__ */ jsxRuntime.jsx(GithubButton, { disabled: isLoading, onClick: () => onSocialLogin?.("github") })
2039
2080
  ] }),
2040
2081
  (embeddedWallets || smartWallet) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-wallet-section", children: [
2041
- embeddedWallets && /* @__PURE__ */ jsxRuntime.jsxs(
2042
- "button",
2043
- {
2044
- type: "button",
2045
- disabled: isLoading,
2046
- className: "pollar-wallet-entry-btn",
2047
- onClick: () => setShowWalletPicker(true),
2048
- children: [
2049
- /* @__PURE__ */ jsxRuntime.jsx(
2050
- "svg",
2051
- {
2052
- width: "18",
2053
- height: "20",
2054
- viewBox: "0 0 24 24",
2055
- fill: "none",
2056
- stroke: "currentColor",
2057
- strokeWidth: "2",
2058
- strokeLinecap: "round",
2059
- strokeLinejoin: "round",
2060
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" })
2061
- }
2062
- ),
2063
- "Wallet"
2064
- ]
2065
- }
2066
- ),
2082
+ embeddedWallets && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2083
+ walletGroups.map((g) => /* @__PURE__ */ jsxRuntime.jsxs(
2084
+ "button",
2085
+ {
2086
+ type: "button",
2087
+ disabled: isLoading,
2088
+ className: "pollar-wallet-entry-btn",
2089
+ onClick: () => setActiveGroup(g.label),
2090
+ children: [
2091
+ /* @__PURE__ */ jsxRuntime.jsx(
2092
+ "svg",
2093
+ {
2094
+ width: "18",
2095
+ height: "20",
2096
+ viewBox: "0 0 24 24",
2097
+ fill: "none",
2098
+ stroke: "currentColor",
2099
+ strokeWidth: "2",
2100
+ strokeLinecap: "round",
2101
+ strokeLinejoin: "round",
2102
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" })
2103
+ }
2104
+ ),
2105
+ g.label
2106
+ ]
2107
+ },
2108
+ g.label
2109
+ )),
2110
+ rootAdapters.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2111
+ WalletAdapterButtons,
2112
+ {
2113
+ walletAdapters: rootAdapters,
2114
+ onConnect: onWalletConnect ?? (() => {
2115
+ }),
2116
+ isLoading,
2117
+ variant: "entry"
2118
+ }
2119
+ )
2120
+ ] }),
2067
2121
  smartWallet && /* @__PURE__ */ jsxRuntime.jsxs(
2068
2122
  "button",
2069
2123
  {
@@ -2104,13 +2158,142 @@ function LoginModalTemplate({
2104
2158
  /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
2105
2159
  ] });
2106
2160
  }
2161
+ function PrivyLoginSubmodal({
2162
+ adapter,
2163
+ theme,
2164
+ accentColor,
2165
+ logoUrl,
2166
+ appName,
2167
+ onBack,
2168
+ onCancel,
2169
+ onAuthenticated
2170
+ }) {
2171
+ const options = react.useMemo(() => adapter.getAuthOptions(), [adapter]);
2172
+ const [email, setEmail] = react.useState("");
2173
+ const [view, setView] = react.useState("root");
2174
+ const [status, setStatus] = react.useState("NONE");
2175
+ const [message, setMessage] = react.useState("");
2176
+ const isDark = theme === "dark";
2177
+ const isLoading = status === "LOADING";
2178
+ const cssVars = {
2179
+ "--pollar-accent": accentColor,
2180
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2181
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2182
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
2183
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2184
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2185
+ "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2186
+ "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2187
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2188
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2189
+ "--pollar-buttons-border-radius": "6px",
2190
+ "--pollar-buttons-height": "44px",
2191
+ "--pollar-input-height": "44px",
2192
+ "--pollar-input-border-radius": "0.5rem",
2193
+ "--pollar-card-border-radius": "10px",
2194
+ "--pollar-modal-padding": "2rem",
2195
+ "--pollar-modal-heading-size": "1.375rem",
2196
+ "--pollar-modal-subtitle-size": "0.9rem"
2197
+ };
2198
+ function fail(error) {
2199
+ setStatus("ERROR");
2200
+ setMessage(error instanceof Error ? error.message : "Something went wrong. Please try again.");
2201
+ }
2202
+ async function handleEmailSubmit() {
2203
+ if (!email) return;
2204
+ setStatus("LOADING");
2205
+ setMessage("");
2206
+ try {
2207
+ await adapter.sendEmailCode(email);
2208
+ setStatus("SUCCESS");
2209
+ setMessage("Code sent \u2014 check your inbox");
2210
+ setView("email-code");
2211
+ } catch (error) {
2212
+ fail(error);
2213
+ }
2214
+ }
2215
+ async function handleCodeSubmit(code) {
2216
+ setStatus("LOADING");
2217
+ setMessage("Verifying\u2026");
2218
+ try {
2219
+ await adapter.verifyEmailCode(code);
2220
+ onAuthenticated();
2221
+ } catch (error) {
2222
+ fail(error);
2223
+ }
2224
+ }
2225
+ async function handleOAuth(provider) {
2226
+ setStatus("LOADING");
2227
+ setMessage("");
2228
+ try {
2229
+ await adapter.loginWithOAuth(provider);
2230
+ onAuthenticated();
2231
+ } catch (error) {
2232
+ fail(error);
2233
+ }
2234
+ }
2235
+ const BackButton = ({ onClick }) => /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-back-btn", onClick, "aria-label": "Back", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 19l-7-7 7-7" }) }) });
2236
+ const showEmail = options.includes("email");
2237
+ const showGoogle = options.includes("google");
2238
+ const showGithub = options.includes("github");
2239
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2240
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-close-btn", onClick: onCancel, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) }),
2241
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-header", children: [
2242
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
2243
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-title", children: appName }),
2244
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-subtitle", children: adapter.meta.label })
2245
+ ] }),
2246
+ view === "email-code" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2247
+ /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: () => setView("root") }),
2248
+ /* @__PURE__ */ jsxRuntime.jsx(EmailCodeInput, { email, onSubmit: handleCodeSubmit })
2249
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2250
+ /* @__PURE__ */ jsxRuntime.jsx(BackButton, { onClick: onBack }),
2251
+ showEmail && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-email-section", children: [
2252
+ /* @__PURE__ */ jsxRuntime.jsx(
2253
+ "input",
2254
+ {
2255
+ type: "email",
2256
+ placeholder: "you@email.com",
2257
+ value: email,
2258
+ disabled: isLoading,
2259
+ className: "pollar-email-input",
2260
+ onChange: (e) => setEmail(e.target.value),
2261
+ onKeyDown: (e) => e.key === "Enter" && handleEmailSubmit()
2262
+ }
2263
+ ),
2264
+ /* @__PURE__ */ jsxRuntime.jsx(
2265
+ "button",
2266
+ {
2267
+ type: "button",
2268
+ disabled: isLoading || !email,
2269
+ className: "pollar-btn-primary",
2270
+ style: { marginTop: "0.75rem" },
2271
+ onClick: handleEmailSubmit,
2272
+ children: "Submit"
2273
+ }
2274
+ )
2275
+ ] }),
2276
+ showEmail && (showGoogle || showGithub) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-divider", children: [
2277
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-divider-line" }),
2278
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-divider-label", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-divider-text", children: "or continue with" }) })
2279
+ ] }),
2280
+ (showGoogle || showGithub) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-social-list", children: [
2281
+ showGoogle && /* @__PURE__ */ jsxRuntime.jsx(GoogleButton, { disabled: isLoading, onClick: () => handleOAuth("google") }),
2282
+ showGithub && /* @__PURE__ */ jsxRuntime.jsx(GithubButton, { disabled: isLoading, onClick: () => handleOAuth("github") })
2283
+ ] })
2284
+ ] }),
2285
+ /* @__PURE__ */ jsxRuntime.jsx(ModalStatusBanner, { message, status, onCancel, onRetry: void 0 }),
2286
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
2287
+ ] });
2288
+ }
2107
2289
  function LoginModal({ onClose }) {
2108
2290
  const [email, setEmail] = react.useState("");
2109
- const { getClient, styles, appConfig: config, renderWallets } = usePollar();
2291
+ const { getClient, styles, appConfig: config } = usePollar();
2110
2292
  const [authState, setAuthState] = react.useState(() => getClient().getAuthState());
2111
2293
  const walletAdapters = react.useMemo(() => getClient().listWalletAdapters(), [getClient]);
2112
2294
  const [codeInputKey, setCodeInputKey] = react.useState(0);
2113
2295
  const pendingEmail = react.useRef(null);
2296
+ const [interactiveAdapter, setInteractiveAdapter] = react.useState(null);
2114
2297
  const onCloseRef = react.useRef(onClose);
2115
2298
  onCloseRef.current = onClose;
2116
2299
  const autoCloseTimer = react.useRef(null);
@@ -2158,6 +2341,11 @@ function LoginModal({ onClose }) {
2158
2341
  getClient().login({ provider });
2159
2342
  }
2160
2343
  function handleWalletConnect(type) {
2344
+ const adapter = getClient().getWalletAdapter(type);
2345
+ if (core.isInteractiveAuthAdapter(adapter)) {
2346
+ setInteractiveAdapter(adapter);
2347
+ return;
2348
+ }
2161
2349
  getClient().login({ provider: type });
2162
2350
  }
2163
2351
  function handleLoginSmartWallet() {
@@ -2179,7 +2367,26 @@ function LoginModal({ onClose }) {
2179
2367
  getClient().beginEmailLogin();
2180
2368
  }
2181
2369
  }
2182
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: handleClose, children: /* @__PURE__ */ jsxRuntime.jsx(
2370
+ function handleInteractiveAuthenticated() {
2371
+ const provider = interactiveAdapter?.type;
2372
+ setInteractiveAdapter(null);
2373
+ if (provider) {
2374
+ getClient().login({ provider });
2375
+ }
2376
+ }
2377
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: handleClose, children: interactiveAdapter ? /* @__PURE__ */ jsxRuntime.jsx(
2378
+ PrivyLoginSubmodal,
2379
+ {
2380
+ adapter: interactiveAdapter,
2381
+ theme,
2382
+ accentColor,
2383
+ logoUrl: logoUrl ?? null,
2384
+ appName: config.application?.name ?? "Pollar",
2385
+ onBack: () => setInteractiveAdapter(null),
2386
+ onCancel: handleClose,
2387
+ onAuthenticated: handleInteractiveAuthenticated
2388
+ }
2389
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2183
2390
  LoginModalTemplate,
2184
2391
  {
2185
2392
  theme,
@@ -2204,7 +2411,6 @@ function LoginModal({ onClose }) {
2204
2411
  onWalletConnect: handleWalletConnect,
2205
2412
  onLoginSmartWallet: handleLoginSmartWallet,
2206
2413
  onCreateSmartWallet: handleCreateSmartWallet,
2207
- ...renderWallets !== void 0 && { renderWallets },
2208
2414
  authState,
2209
2415
  codeInputKey,
2210
2416
  onCodeSubmit: handleVerifyCode,
@@ -2261,6 +2467,12 @@ var COUNTRY_CURRENCIES = {
2261
2467
  PE: "PEN",
2262
2468
  AR: "ARS"
2263
2469
  };
2470
+ var STATUS_LABEL = {
2471
+ pending: "Pending",
2472
+ processing: "Processing",
2473
+ completed: "Completed",
2474
+ failed: "Failed"
2475
+ };
2264
2476
  function RampWidgetTemplate({
2265
2477
  theme,
2266
2478
  accentColor,
@@ -2270,15 +2482,23 @@ function RampWidgetTemplate({
2270
2482
  currency,
2271
2483
  country,
2272
2484
  quotes,
2273
- paymentInstructions,
2274
2485
  isLoading,
2486
+ provider,
2487
+ txStatus,
2488
+ kycUrl,
2489
+ stellarTxHash,
2490
+ canComplete,
2491
+ completing,
2492
+ errorMsg,
2275
2493
  onDirectionChange,
2276
2494
  onAmountChange,
2277
2495
  onCurrencyChange,
2278
2496
  onCountryChange,
2279
2497
  onFindRoute,
2280
2498
  onSelectQuote,
2281
- onCopy,
2499
+ onOpenKyc,
2500
+ onCompleteWithdraw,
2501
+ onRetry,
2282
2502
  onClose
2283
2503
  }) {
2284
2504
  const isDark = theme === "dark";
@@ -2306,13 +2526,15 @@ function RampWidgetTemplate({
2306
2526
  input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
2307
2527
  loading_quote: "Finding best route",
2308
2528
  select_route: "Select provider",
2309
- payment_instructions: "Payment instructions"
2529
+ status: direction === "onramp" ? "Complete your deposit" : "Complete your withdrawal",
2530
+ error: "Something went wrong"
2310
2531
  };
2311
2532
  const stepSubtitle = {
2312
2533
  input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
2313
2534
  loading_quote: "Comparing providers in real time\u2026",
2314
2535
  select_route: "All prices include fees",
2315
- payment_instructions: "Send the exact amount to complete your transaction"
2536
+ status: `Finish the flow at ${provider || "the provider"} to continue`,
2537
+ error: "Please try again"
2316
2538
  };
2317
2539
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2318
2540
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header", children: [
@@ -2408,183 +2630,183 @@ function RampWidgetTemplate({
2408
2630
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx(RouteDisplay, { quote: q, onSelect: onSelectQuote }, i)) }),
2409
2631
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
2410
2632
  ] }),
2411
- step === "payment_instructions" && paymentInstructions && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment", children: [
2412
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-payment-title", children: paymentInstructions.type }),
2633
+ step === "status" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment", children: [
2413
2634
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2414
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: paymentInstructions.type === "CLABE" ? "CLABE number" : paymentInstructions.type === "PIX" ? "PIX key" : "Account number" }),
2415
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-value", children: [
2416
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: paymentInstructions.value }),
2417
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-ramp-copy-btn", onClick: () => onCopy(paymentInstructions.value), children: "Copy" })
2418
- ] })
2635
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Provider" }),
2636
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: provider }) })
2419
2637
  ] }),
2420
2638
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2421
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Amount to send" }),
2422
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-value", children: [
2423
- /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
2424
- paymentInstructions.amount.toLocaleString(),
2425
- " ",
2426
- paymentInstructions.currency
2427
- ] }),
2428
- /* @__PURE__ */ jsxRuntime.jsx(
2429
- "button",
2430
- {
2431
- type: "button",
2432
- className: "pollar-ramp-copy-btn",
2433
- onClick: () => onCopy(`${paymentInstructions.amount} ${paymentInstructions.currency}`),
2434
- children: "Copy"
2435
- }
2436
- )
2437
- ] })
2639
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Status" }),
2640
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx(
2641
+ "code",
2642
+ {
2643
+ style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 },
2644
+ children: txStatus ? STATUS_LABEL[txStatus] : "Processing"
2645
+ }
2646
+ ) })
2438
2647
  ] }),
2439
- paymentInstructions.expiresAt && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pollar-ramp-payment-note", children: [
2440
- "Instructions expire at ",
2441
- new Date(paymentInstructions.expiresAt).toLocaleTimeString()
2648
+ stellarTxHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2649
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Stellar tx" }),
2650
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
2651
+ stellarTxHash.slice(0, 8),
2652
+ "\u2026",
2653
+ stellarTxHash.slice(-8)
2654
+ ] }) })
2442
2655
  ] }),
2443
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Done" })
2656
+ kycUrl && txStatus !== "completed" && /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenKyc, children: [
2657
+ "Continue at ",
2658
+ provider
2659
+ ] }),
2660
+ canComplete && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? "Submitting\u2026" : "I've completed KYC \u2014 withdraw" }),
2661
+ errorMsg && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
2662
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: txStatus === "completed" ? "Done" : "Close" })
2663
+ ] }),
2664
+ step === "error" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment", children: [
2665
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg ?? "Unexpected error." }),
2666
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2667
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Close" }),
2668
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onRetry, children: "Try again" })
2669
+ ] })
2444
2670
  ] })
2445
2671
  ] });
2446
2672
  }
2447
- var MOCK_DEFAULT_QUOTES = [
2448
- {
2449
- quoteId: "meld-default",
2450
- provider: "Meld",
2451
- fee: 1.2,
2452
- feeCurrency: "USD",
2453
- rate: 1,
2454
- rail: "ACH",
2455
- protocol: "REST",
2456
- estimatedTime: "~20 min",
2457
- recommended: true
2458
- }
2459
- ];
2460
- var MOCK_QUOTES = {
2461
- MX: [
2462
- {
2463
- quoteId: "etherfuse-mx",
2464
- provider: "Etherfuse",
2465
- fee: 0.5,
2466
- feeCurrency: "MXN",
2467
- rate: 17.2,
2468
- rail: "SPEI",
2469
- protocol: "SEP-24",
2470
- estimatedTime: "~10 min",
2471
- recommended: true
2472
- },
2473
- {
2474
- quoteId: "alfredpay-mx",
2475
- provider: "AlfredPay",
2476
- fee: 0.8,
2477
- feeCurrency: "MXN",
2478
- rate: 17.1,
2479
- rail: "SPEI",
2480
- protocol: "REST",
2481
- estimatedTime: "~15 min",
2482
- recommended: false
2483
- }
2484
- ],
2485
- BR: [
2486
- {
2487
- quoteId: "abroad-br",
2488
- provider: "Abroad",
2489
- fee: 0.6,
2490
- feeCurrency: "BRL",
2491
- rate: 5.1,
2492
- rail: "PIX",
2493
- protocol: "REST",
2494
- estimatedTime: "~5 min",
2495
- recommended: true
2496
- }
2497
- ],
2498
- CO: [
2499
- {
2500
- quoteId: "abroad-co",
2501
- provider: "Abroad",
2502
- fee: 0.7,
2503
- feeCurrency: "COP",
2504
- rate: 4100,
2505
- rail: "PSE",
2506
- protocol: "REST",
2507
- estimatedTime: "~10 min",
2508
- recommended: true
2509
- },
2510
- {
2511
- quoteId: "koywe-co",
2512
- provider: "Koywe",
2513
- fee: 0.9,
2514
- feeCurrency: "COP",
2515
- rate: 4095,
2516
- rail: "PSE",
2517
- protocol: "REST",
2518
- estimatedTime: "~15 min",
2519
- recommended: false
2520
- }
2521
- ],
2522
- DEFAULT: MOCK_DEFAULT_QUOTES
2523
- };
2524
- var MOCK_PAYMENT = {
2525
- type: "CLABE",
2526
- value: "646180157088723456",
2527
- amount: 1e3,
2528
- currency: "MXN",
2529
- expiresAt: new Date(Date.now() + 30 * 60 * 1e3).toISOString()
2530
- };
2673
+ var TERMINAL = ["completed", "failed"];
2531
2674
  function RampWidget({ onClose }) {
2532
- const { getClient, wallet, styles } = usePollar();
2675
+ const { getClient, signTx, wallet, styles } = usePollar();
2533
2676
  const walletAddress = wallet?.address ?? "";
2677
+ const client = getClient();
2678
+ const { theme = "light", accentColor = "#005DB4" } = styles;
2534
2679
  const [step, setStep] = react.useState("input");
2535
2680
  const [direction, setDirection] = react.useState("onramp");
2536
2681
  const [amount, setAmount] = react.useState("");
2537
- const [currency, setCurrency] = react.useState("MXN");
2538
- const [country, setCountry] = react.useState("MX");
2682
+ const [currency, setCurrency] = react.useState("ARS");
2683
+ const [country, setCountry] = react.useState("AR");
2539
2684
  const [quotes, setQuotes] = react.useState([]);
2540
- const [paymentInstructions, setPaymentInstructions] = react.useState(null);
2541
2685
  const [isLoading, setIsLoading] = react.useState(false);
2542
- const client = getClient();
2543
- const { theme = "light", accentColor = "#005DB4" } = styles;
2686
+ const [txId, setTxId] = react.useState(null);
2687
+ const [provider, setProvider] = react.useState("");
2688
+ const [kycUrl, setKycUrl] = react.useState(null);
2689
+ const [txStatus, setTxStatus] = react.useState(null);
2690
+ const [stellarTxHash, setStellarTxHash] = react.useState(null);
2691
+ const [completing, setCompleting] = react.useState(false);
2692
+ const [errorMsg, setErrorMsg] = react.useState(null);
2693
+ const directionRef = react.useRef(direction);
2694
+ directionRef.current = direction;
2695
+ react.useEffect(() => {
2696
+ if (step !== "status" || !txId) return;
2697
+ if (txStatus && TERMINAL.includes(txStatus)) return;
2698
+ let active = true;
2699
+ const id = setInterval(async () => {
2700
+ try {
2701
+ const tx = await client.getRampTransaction(txId);
2702
+ if (!active) return;
2703
+ setTxStatus(tx.status);
2704
+ if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
2705
+ if (tx.kycUrl) setKycUrl(tx.kycUrl);
2706
+ if (TERMINAL.includes(tx.status)) clearInterval(id);
2707
+ } catch {
2708
+ }
2709
+ }, 5e3);
2710
+ return () => {
2711
+ active = false;
2712
+ clearInterval(id);
2713
+ };
2714
+ }, [step, txId, txStatus, client]);
2715
+ function resetToInput() {
2716
+ setStep("input");
2717
+ setQuotes([]);
2718
+ setTxId(null);
2719
+ setProvider("");
2720
+ setKycUrl(null);
2721
+ setTxStatus(null);
2722
+ setStellarTxHash(null);
2723
+ setErrorMsg(null);
2724
+ }
2725
+ async function resumeWithSignature(id, ps) {
2726
+ const outcome = await signTx(ps.unsignedXdr);
2727
+ if (outcome.status !== "signed") {
2728
+ setErrorMsg(outcome.message ?? outcome.details ?? "Signing was cancelled.");
2729
+ setStep("error");
2730
+ return;
2731
+ }
2732
+ const result = await client.submitRampSignature(id, {
2733
+ signedXdr: outcome.signedXdr,
2734
+ action: ps.action
2735
+ });
2736
+ await applyResult(result);
2737
+ }
2738
+ async function applyResult(result) {
2739
+ setTxId(result.txId);
2740
+ setProvider(result.provider);
2741
+ if (result.pendingSignature) {
2742
+ await resumeWithSignature(result.txId, result.pendingSignature);
2743
+ return;
2744
+ }
2745
+ setKycUrl(result.kycUrl ?? null);
2746
+ setTxStatus(result.status);
2747
+ setStellarTxHash(result.stellarTxHash ?? null);
2748
+ setStep("status");
2749
+ }
2544
2750
  async function handleFindRoute() {
2545
2751
  setStep("loading_quote");
2546
2752
  setIsLoading(true);
2753
+ setErrorMsg(null);
2547
2754
  try {
2548
- const result = await client.getRampsQuote({
2549
- country,
2550
- amount: Number(amount),
2551
- currency,
2552
- direction
2553
- });
2554
- if (result.quotes) setQuotes(result.quotes);
2555
- } catch {
2556
- await new Promise((r) => setTimeout(r, 1500));
2557
- setQuotes(MOCK_QUOTES[country] ?? MOCK_DEFAULT_QUOTES);
2755
+ const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
2756
+ const list = result.quotes ?? [];
2757
+ if (list.length === 0) {
2758
+ setErrorMsg(`No ramp providers available for ${country} yet.`);
2759
+ setStep("error");
2760
+ return;
2761
+ }
2762
+ setQuotes(list);
2763
+ setStep("select_route");
2764
+ } catch (e) {
2765
+ setErrorMsg(e instanceof Error ? e.message : "Failed to fetch quotes.");
2766
+ setStep("error");
2558
2767
  } finally {
2559
2768
  setIsLoading(false);
2560
- setStep("select_route");
2561
2769
  }
2562
2770
  }
2563
2771
  async function handleSelectQuote(quote) {
2564
- if (!walletAddress) return;
2565
2772
  setIsLoading(true);
2566
- const body = {
2567
- quoteId: `${quote.provider}-${Date.now()}`,
2568
- amount: Number(amount),
2569
- currency,
2570
- country,
2571
- walletAddress
2572
- };
2773
+ setErrorMsg(null);
2573
2774
  try {
2574
- const result = await client.createOnRamp(body);
2575
- setPaymentInstructions(result.paymentInstructions);
2576
- } catch {
2577
- await new Promise((r) => setTimeout(r, 800));
2578
- setPaymentInstructions({ ...MOCK_PAYMENT, currency });
2775
+ const base = { quoteId: quote.quoteId, amount: Number(amount), currency, country };
2776
+ const result = direction === "onramp" ? await client.createOnRamp({ ...base, ...walletAddress ? { walletAddress } : {} }) : await client.createOffRamp({ ...base, ...walletAddress ? { walletAddress } : {} });
2777
+ await applyResult(result);
2778
+ } catch (e) {
2779
+ setErrorMsg(e instanceof Error ? e.message : "Failed to start the ramp.");
2780
+ setStep("error");
2579
2781
  } finally {
2580
2782
  setIsLoading(false);
2581
- setStep("payment_instructions");
2582
2783
  }
2583
2784
  }
2584
- function handleCopy(value) {
2585
- navigator.clipboard.writeText(value).catch(() => {
2586
- });
2785
+ function handleOpenKyc() {
2786
+ if (kycUrl) window.open(kycUrl, "_blank", "noopener,noreferrer");
2587
2787
  }
2788
+ async function handleCompleteWithdraw() {
2789
+ if (!txId) return;
2790
+ setCompleting(true);
2791
+ setErrorMsg(null);
2792
+ try {
2793
+ const result = await client.completeWithdraw(txId);
2794
+ if (result.pendingSignature) {
2795
+ await resumeWithSignature(txId, result.pendingSignature);
2796
+ return;
2797
+ }
2798
+ setTxStatus(result.status);
2799
+ setStellarTxHash(result.stellarTxHash ?? null);
2800
+ } catch (e) {
2801
+ const msg = e instanceof Error ? e.message : "";
2802
+ setErrorMsg(
2803
+ msg.includes("KYC") ? "Finish KYC at the provider first, then try again." : msg || "Failed to complete the withdrawal."
2804
+ );
2805
+ } finally {
2806
+ setCompleting(false);
2807
+ }
2808
+ }
2809
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash;
2588
2810
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
2589
2811
  RampWidgetTemplate,
2590
2812
  {
@@ -2596,15 +2818,23 @@ function RampWidget({ onClose }) {
2596
2818
  currency,
2597
2819
  country,
2598
2820
  quotes,
2599
- paymentInstructions,
2600
2821
  isLoading,
2822
+ provider,
2823
+ txStatus,
2824
+ kycUrl,
2825
+ stellarTxHash,
2826
+ canComplete,
2827
+ completing,
2828
+ errorMsg,
2601
2829
  onDirectionChange: setDirection,
2602
2830
  onAmountChange: setAmount,
2603
2831
  onCurrencyChange: setCurrency,
2604
2832
  onCountryChange: setCountry,
2605
2833
  onFindRoute: handleFindRoute,
2606
2834
  onSelectQuote: handleSelectQuote,
2607
- onCopy: handleCopy,
2835
+ onOpenKyc: handleOpenKyc,
2836
+ onCompleteWithdraw: handleCompleteWithdraw,
2837
+ onRetry: resetToInput,
2608
2838
  onClose
2609
2839
  }
2610
2840
  ) });
@@ -3268,6 +3498,415 @@ function SendModal({ onClose }) {
3268
3498
  }
3269
3499
  ) });
3270
3500
  }
3501
+ function assetOptionKey(o) {
3502
+ return `${o.code}:${o.issuer ?? "native"}`;
3503
+ }
3504
+ var PROVIDER_LABELS = {
3505
+ auto: "Best price (auto)",
3506
+ aquarius: "Aquarius",
3507
+ soroswap: "Soroswap",
3508
+ sdex: "Stellar DEX"
3509
+ };
3510
+ var IMPLEMENTED_PROVIDERS = ["auto", "aquarius"];
3511
+ function formatAmount2(value) {
3512
+ const n = parseFloat(value);
3513
+ return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
3514
+ }
3515
+ function SwapModalTemplate({
3516
+ theme,
3517
+ accentColor,
3518
+ step,
3519
+ txTitle,
3520
+ sellOptions,
3521
+ buyOptions,
3522
+ selectedSell,
3523
+ selectedBuy,
3524
+ amount,
3525
+ provider,
3526
+ providers,
3527
+ quote,
3528
+ quoteLoading,
3529
+ quoteError,
3530
+ formError,
3531
+ isLoadingData,
3532
+ smartUnsupported,
3533
+ transaction,
3534
+ showXdr,
3535
+ copied,
3536
+ explorerUrl,
3537
+ walletType,
3538
+ showBack,
3539
+ isInProgress,
3540
+ onClose,
3541
+ onBack,
3542
+ onSelectSell,
3543
+ onSelectBuy,
3544
+ onAmountChange,
3545
+ onProviderChange,
3546
+ onSwap,
3547
+ onToggleXdr,
3548
+ onCopyHash,
3549
+ onRetry,
3550
+ onDone
3551
+ }) {
3552
+ const isDark = theme === "dark";
3553
+ const cssVars = {
3554
+ "--pollar-accent": accentColor,
3555
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
3556
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
3557
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
3558
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
3559
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
3560
+ "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
3561
+ "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
3562
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
3563
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
3564
+ "--pollar-buttons-border-radius": "6px",
3565
+ "--pollar-buttons-height": "44px",
3566
+ "--pollar-input-height": "44px",
3567
+ "--pollar-input-border-radius": "0.5rem",
3568
+ "--pollar-card-border-radius": "10px"
3569
+ };
3570
+ const sellKey = selectedSell ? assetOptionKey(selectedSell) : "";
3571
+ const buyKey = selectedBuy ? assetOptionKey(selectedBuy) : "";
3572
+ const canSwap = !smartUnsupported && !!selectedSell && !!selectedBuy && !!amount && !!quote && !quoteLoading && !isLoadingData;
3573
+ const title = step === "form" ? "Swap" : txTitle;
3574
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-send-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
3575
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
3576
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-header-left", children: [
3577
+ showBack && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 3L5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
3578
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: title })
3579
+ ] }),
3580
+ !isInProgress && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) })
3581
+ ] }),
3582
+ step === "form" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3583
+ smartUnsupported && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "Swaps are not yet available for smart (passkey) wallets." }),
3584
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3585
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "You pay" }),
3586
+ isLoadingData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-skeleton" }) : /* @__PURE__ */ jsxRuntime.jsxs(
3587
+ "select",
3588
+ {
3589
+ className: "pollar-input pollar-send-select",
3590
+ value: sellKey,
3591
+ onChange: (e) => {
3592
+ const found = sellOptions.find((o) => assetOptionKey(o) === e.target.value);
3593
+ if (found) onSelectSell(found);
3594
+ },
3595
+ children: [
3596
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select asset to sell" }),
3597
+ sellOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: assetOptionKey(o), children: [
3598
+ o.code,
3599
+ o.available !== void 0 ? ` \u2014 ${formatAmount2(o.available)} available` : ""
3600
+ ] }, assetOptionKey(o)))
3601
+ ]
3602
+ }
3603
+ )
3604
+ ] }),
3605
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3606
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-label-row", children: [
3607
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "Amount" }),
3608
+ selectedSell?.available !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-send-hint", children: [
3609
+ "Available: ",
3610
+ formatAmount2(selectedSell.available),
3611
+ " ",
3612
+ selectedSell.code
3613
+ ] })
3614
+ ] }),
3615
+ /* @__PURE__ */ jsxRuntime.jsx(
3616
+ "input",
3617
+ {
3618
+ className: "pollar-input",
3619
+ type: "text",
3620
+ inputMode: "decimal",
3621
+ placeholder: "0.00",
3622
+ value: amount,
3623
+ onChange: (e) => onAmountChange(e.target.value)
3624
+ }
3625
+ )
3626
+ ] }),
3627
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3628
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "You receive" }),
3629
+ isLoadingData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-skeleton" }) : /* @__PURE__ */ jsxRuntime.jsxs(
3630
+ "select",
3631
+ {
3632
+ className: "pollar-input pollar-send-select",
3633
+ value: buyKey,
3634
+ onChange: (e) => {
3635
+ const found = buyOptions.find((o) => assetOptionKey(o) === e.target.value);
3636
+ if (found) onSelectBuy(found);
3637
+ },
3638
+ children: [
3639
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select asset to buy" }),
3640
+ buyOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: assetOptionKey(o), children: [
3641
+ o.code,
3642
+ o.enabledInApp ? "" : " (external)"
3643
+ ] }, assetOptionKey(o)))
3644
+ ]
3645
+ }
3646
+ )
3647
+ ] }),
3648
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3649
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "Route" }),
3650
+ /* @__PURE__ */ jsxRuntime.jsx(
3651
+ "select",
3652
+ {
3653
+ className: "pollar-input pollar-send-select",
3654
+ value: provider,
3655
+ onChange: (e) => onProviderChange(e.target.value),
3656
+ children: providers.map((p) => {
3657
+ const implemented = IMPLEMENTED_PROVIDERS.includes(p);
3658
+ return /* @__PURE__ */ jsxRuntime.jsxs("option", { value: p, disabled: !implemented, children: [
3659
+ PROVIDER_LABELS[p],
3660
+ implemented ? "" : " (coming soon)"
3661
+ ] }, p);
3662
+ })
3663
+ }
3664
+ )
3665
+ ] }),
3666
+ quoteLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-hint", children: "Fetching best price\u2026" }),
3667
+ !quoteLoading && quoteError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: quoteError }),
3668
+ !quoteLoading && !quoteError && quote && selectedBuy && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-quote", children: [
3669
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-quote-row", children: [
3670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-send-hint", children: "You receive" }),
3671
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3672
+ "~ ",
3673
+ formatAmount2(quote.amountOut),
3674
+ " ",
3675
+ selectedBuy.code
3676
+ ] })
3677
+ ] }),
3678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-quote-row", children: [
3679
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-send-hint", children: "Minimum received" }),
3680
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3681
+ formatAmount2(quote.minReceived),
3682
+ " ",
3683
+ selectedBuy.code
3684
+ ] })
3685
+ ] }),
3686
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-quote-row", children: [
3687
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-send-hint", children: "Price impact" }),
3688
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
3689
+ quote.priceImpactPct,
3690
+ "%"
3691
+ ] })
3692
+ ] }),
3693
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-quote-row", children: [
3694
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-send-hint", children: "Route" }),
3695
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: PROVIDER_LABELS[quote.provider] })
3696
+ ] })
3697
+ ] }),
3698
+ !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
3699
+ formError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: formError }),
3700
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: "Swap" }) })
3701
+ ] }),
3702
+ step === "tx" && /* @__PURE__ */ jsxRuntime.jsx(
3703
+ TxStatusView,
3704
+ {
3705
+ transaction,
3706
+ showXdr,
3707
+ copied,
3708
+ explorerUrl,
3709
+ walletType,
3710
+ onSignAndSend: () => {
3711
+ },
3712
+ onToggleXdr,
3713
+ onCopyHash,
3714
+ onRetry,
3715
+ onDone
3716
+ }
3717
+ ),
3718
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
3719
+ ] });
3720
+ }
3721
+ var QUOTE_DEBOUNCE_MS = 400;
3722
+ var ALL_PROVIDERS = ["auto", "aquarius", "soroswap", "sdex"];
3723
+ function toRef(a) {
3724
+ if (a.type === "native") return { type: "native" };
3725
+ if (a.type === "credit_alphanum4") return { type: "credit_alphanum4", code: a.code, issuer: a.issuer };
3726
+ return { type: "credit_alphanum12", code: a.code, issuer: a.issuer };
3727
+ }
3728
+ function SwapModal({ onClose }) {
3729
+ const {
3730
+ getSwapQuote,
3731
+ swap,
3732
+ walletBalance,
3733
+ refreshWalletBalance,
3734
+ enabledAssets,
3735
+ refreshAssets,
3736
+ tx: transaction,
3737
+ wallet,
3738
+ network,
3739
+ styles
3740
+ } = usePollar();
3741
+ const walletType = wallet?.custody === "external" ? wallet.provider : null;
3742
+ const smartUnsupported = wallet?.custody === "smart";
3743
+ const { theme = "light", accentColor = "#005DB4" } = styles;
3744
+ const [step, setStep] = react.useState("form");
3745
+ const [selectedSell, setSelectedSell] = react.useState(null);
3746
+ const [selectedBuy, setSelectedBuy] = react.useState(null);
3747
+ const [amount, setAmount] = react.useState("");
3748
+ const [provider, setProvider] = react.useState("auto");
3749
+ const [quotes, setQuotes] = react.useState([]);
3750
+ const [quoteLoading, setQuoteLoading] = react.useState(false);
3751
+ const [quoteError, setQuoteError] = react.useState("");
3752
+ const [formError, setFormError] = react.useState("");
3753
+ const [showXdr, setShowXdr] = react.useState(false);
3754
+ const [copied, setCopied] = react.useState(false);
3755
+ const copyTimerRef = react.useRef(null);
3756
+ const slippageBps = 50;
3757
+ react.useEffect(() => {
3758
+ void refreshWalletBalance();
3759
+ void refreshAssets();
3760
+ }, [refreshWalletBalance, refreshAssets]);
3761
+ react.useEffect(
3762
+ () => () => {
3763
+ if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
3764
+ },
3765
+ []
3766
+ );
3767
+ const balances = walletBalance.step === "loaded" ? walletBalance.data.balances : [];
3768
+ const assetRecords = enabledAssets.step === "loaded" ? enabledAssets.data.assets : [];
3769
+ const isLoadingData = walletBalance.step === "loading" || enabledAssets.step === "loading";
3770
+ const sellOptions = balances.filter((b) => parseFloat(b.available) > 0).map((b) => ({ ref: toRef(b), code: b.code, issuer: b.issuer, available: b.available, enabledInApp: b.enabledInApp }));
3771
+ const buyKeyOfSell = selectedSell ? `${selectedSell.code}:${selectedSell.issuer ?? "native"}` : "";
3772
+ const buyOptions = assetRecords.map((a) => ({ ref: toRef(a), code: a.code, issuer: a.issuer, enabledInApp: a.enabledInApp })).filter((o) => `${o.code}:${o.issuer ?? "native"}` !== buyKeyOfSell);
3773
+ const providers = smartUnsupported ? ALL_PROVIDERS.filter((p) => p !== "sdex") : ALL_PROVIDERS;
3774
+ const quote = quotes[0] ?? null;
3775
+ react.useEffect(() => {
3776
+ if (step !== "form" || !selectedSell || !selectedBuy || !amount || parseFloat(amount) <= 0) {
3777
+ setQuotes([]);
3778
+ setQuoteError("");
3779
+ setQuoteLoading(false);
3780
+ return;
3781
+ }
3782
+ let cancelled = false;
3783
+ setQuoteLoading(true);
3784
+ setQuoteError("");
3785
+ const t = setTimeout(async () => {
3786
+ try {
3787
+ const qs = await getSwapQuote({
3788
+ sellAsset: selectedSell.ref,
3789
+ buyAsset: selectedBuy.ref,
3790
+ amount,
3791
+ provider,
3792
+ slippageBps
3793
+ });
3794
+ if (!cancelled) setQuotes(qs);
3795
+ } catch (e) {
3796
+ if (!cancelled) {
3797
+ setQuotes([]);
3798
+ setQuoteError(e instanceof Error ? e.message : "Failed to fetch quote");
3799
+ }
3800
+ } finally {
3801
+ if (!cancelled) setQuoteLoading(false);
3802
+ }
3803
+ }, QUOTE_DEBOUNCE_MS);
3804
+ return () => {
3805
+ cancelled = true;
3806
+ clearTimeout(t);
3807
+ };
3808
+ }, [step, selectedSell, selectedBuy, amount, provider, getSwapQuote]);
3809
+ const hash = transaction.step === "success" ? transaction.hash : null;
3810
+ const buildData = "buildData" in transaction ? transaction.buildData : null;
3811
+ const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : buildData ? "public" : network === "testnet" ? "testnet" : "public";
3812
+ const explorerUrl = hash ? `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}` : null;
3813
+ const IN_FLIGHT_STEPS2 = [
3814
+ "building",
3815
+ "signing",
3816
+ "submitting",
3817
+ "submitted",
3818
+ "signing-submitting",
3819
+ "building-signing-submitting"
3820
+ ];
3821
+ const isInProgress = IN_FLIGHT_STEPS2.includes(transaction.step);
3822
+ const showBack = step === "tx" && (transaction.step === "error" || transaction.step === "success") && !isInProgress;
3823
+ const txTitle = isInProgress ? "Swapping\u2026" : transaction.step === "success" ? "Swapped!" : transaction.step === "error" ? "Swap failed" : "Confirm Swap";
3824
+ async function handleSwap() {
3825
+ setFormError("");
3826
+ if (smartUnsupported) {
3827
+ setFormError("Swaps are not yet supported for smart (passkey) wallets");
3828
+ return;
3829
+ }
3830
+ if (!selectedSell || !selectedBuy) {
3831
+ setFormError("Select both assets");
3832
+ return;
3833
+ }
3834
+ const parsed = parseFloat(amount);
3835
+ if (!amount || isNaN(parsed) || parsed <= 0) {
3836
+ setFormError("Enter a valid amount");
3837
+ return;
3838
+ }
3839
+ if (selectedSell.available !== void 0 && parsed > parseFloat(selectedSell.available)) {
3840
+ setFormError("Insufficient balance");
3841
+ return;
3842
+ }
3843
+ if (!quote) {
3844
+ setFormError("No route available for this pair");
3845
+ return;
3846
+ }
3847
+ setStep("tx");
3848
+ await swap(quote);
3849
+ }
3850
+ async function handleRetry() {
3851
+ if (transaction.step === "error" && quote) await swap(quote);
3852
+ }
3853
+ function handleCopyHash() {
3854
+ if (!hash) return;
3855
+ navigator.clipboard.writeText(hash).then(() => {
3856
+ setCopied(true);
3857
+ if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
3858
+ copyTimerRef.current = setTimeout(() => {
3859
+ copyTimerRef.current = null;
3860
+ setCopied(false);
3861
+ }, 2e3);
3862
+ });
3863
+ }
3864
+ function handleBack() {
3865
+ setStep("form");
3866
+ setShowXdr(false);
3867
+ setCopied(false);
3868
+ }
3869
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: !isInProgress ? onClose : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
3870
+ SwapModalTemplate,
3871
+ {
3872
+ theme,
3873
+ accentColor,
3874
+ step,
3875
+ txTitle,
3876
+ sellOptions,
3877
+ buyOptions,
3878
+ selectedSell,
3879
+ selectedBuy,
3880
+ amount,
3881
+ provider,
3882
+ providers,
3883
+ quote,
3884
+ quoteLoading,
3885
+ quoteError,
3886
+ formError,
3887
+ isLoadingData,
3888
+ smartUnsupported,
3889
+ transaction,
3890
+ showXdr,
3891
+ copied,
3892
+ explorerUrl,
3893
+ walletType,
3894
+ showBack,
3895
+ isInProgress,
3896
+ onClose,
3897
+ onBack: handleBack,
3898
+ onSelectSell: setSelectedSell,
3899
+ onSelectBuy: setSelectedBuy,
3900
+ onAmountChange: setAmount,
3901
+ onProviderChange: setProvider,
3902
+ onSwap: () => void handleSwap(),
3903
+ onToggleXdr: () => setShowXdr((v) => !v),
3904
+ onCopyHash: handleCopyHash,
3905
+ onRetry: () => void handleRetry(),
3906
+ onDone: onClose
3907
+ }
3908
+ ) });
3909
+ }
3271
3910
  function describeDevice(s) {
3272
3911
  if (s.deviceLabel) return s.deviceLabel;
3273
3912
  if (!s.userAgent) return "Unknown device";
@@ -3962,7 +4601,6 @@ var PollarContext = react.createContext(null);
3962
4601
  function PollarProvider({
3963
4602
  client,
3964
4603
  appConfig: appConfigProp,
3965
- ui,
3966
4604
  adapters,
3967
4605
  onStorageDegrade,
3968
4606
  children
@@ -4014,6 +4652,30 @@ function PollarProvider({
4014
4652
  }
4015
4653
  });
4016
4654
  }, [pollarClient]);
4655
+ const sessionRef = react.useRef(sessionState);
4656
+ sessionRef.current = sessionState;
4657
+ react.useEffect(() => {
4658
+ const unsubscribes = [];
4659
+ for (const { id } of pollarClient.listWalletAdapters()) {
4660
+ const adapter = pollarClient.getWalletAdapter(id);
4661
+ if (!core.isInteractiveAuthAdapter(adapter) || !adapter.onProviderAuthChange) continue;
4662
+ let triggered = false;
4663
+ unsubscribes.push(
4664
+ adapter.onProviderAuthChange((state) => {
4665
+ if (!state.authenticated || !state.address) {
4666
+ triggered = false;
4667
+ return;
4668
+ }
4669
+ if (triggered || sessionRef.current?.wallet?.address) return;
4670
+ triggered = true;
4671
+ pollarClient.login({ provider: id });
4672
+ })
4673
+ );
4674
+ }
4675
+ return () => {
4676
+ for (const unsubscribe of unsubscribes) unsubscribe();
4677
+ };
4678
+ }, [pollarClient]);
4017
4679
  react.useEffect(() => {
4018
4680
  setModalErrorLogger(pollarClient.getLogger());
4019
4681
  }, [pollarClient]);
@@ -4039,6 +4701,7 @@ function PollarProvider({
4039
4701
  const [walletBalanceModalOpen, setWalletBalanceModalOpen] = react.useState(false);
4040
4702
  const [enabledAssetsModalOpen, setEnabledAssetsModalOpen] = react.useState(false);
4041
4703
  const [sendModalOpen, setSendModalOpen] = react.useState(false);
4704
+ const [swapModalOpen, setSwapModalOpen] = react.useState(false);
4042
4705
  const [receiveModalOpen, setReceiveModalOpen] = react.useState(false);
4043
4706
  const [sessionsModalOpen, setSessionsModalOpen] = react.useState(false);
4044
4707
  const [distributionRulesModalOpen, setDistributionRulesModalOpen] = react.useState(false);
@@ -4046,7 +4709,6 @@ function PollarProvider({
4046
4709
  const getClient = react.useCallback(() => pollarClient, [pollarClient]);
4047
4710
  const refreshWalletBalance = react.useCallback(() => pollarClient.refreshBalance(), [pollarClient, walletAddress]);
4048
4711
  const refreshAssets = react.useCallback(() => pollarClient.refreshAssets(), [pollarClient, walletAddress]);
4049
- const renderWallets = ui?.renderWallets;
4050
4712
  const contextValue = react.useMemo(() => {
4051
4713
  const styles = resolvedConfig.styles ?? {};
4052
4714
  return {
@@ -4084,6 +4746,10 @@ function PollarProvider({
4084
4746
  // send / receive
4085
4747
  openSendModal: () => setSendModalOpen(true),
4086
4748
  openReceiveModal: () => setReceiveModalOpen(true),
4749
+ // swap
4750
+ getSwapQuote: (params) => pollarClient.getSwapQuote(params),
4751
+ swap: (quote, opts) => pollarClient.swap(quote, opts),
4752
+ openSwapModal: () => setSwapModalOpen(true),
4087
4753
  // sessions
4088
4754
  sessions,
4089
4755
  openSessionsModal: () => setSessionsModalOpen(true),
@@ -4102,7 +4768,6 @@ function PollarProvider({
4102
4768
  // config
4103
4769
  appConfig: resolvedConfig,
4104
4770
  styles,
4105
- renderWallets,
4106
4771
  adapters
4107
4772
  };
4108
4773
  }, [
@@ -4119,8 +4784,7 @@ function PollarProvider({
4119
4784
  refreshAssets,
4120
4785
  networkState,
4121
4786
  resolvedConfig,
4122
- adapters,
4123
- renderWallets
4787
+ adapters
4124
4788
  ]);
4125
4789
  return /* @__PURE__ */ jsxRuntime.jsxs(PollarContext.Provider, { value: contextValue, children: [
4126
4790
  children,
@@ -4140,6 +4804,7 @@ function PollarProvider({
4140
4804
  walletBalanceModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setWalletBalanceModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(WalletBalanceModal, { onClose: () => setWalletBalanceModalOpen(false) }) }),
4141
4805
  enabledAssetsModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setEnabledAssetsModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(EnabledAssetsModal, { onClose: () => setEnabledAssetsModalOpen(false) }) }),
4142
4806
  sendModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setSendModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(SendModal, { onClose: () => setSendModalOpen(false) }) }),
4807
+ swapModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setSwapModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(SwapModal, { onClose: () => setSwapModalOpen(false) }) }),
4143
4808
  receiveModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
4144
4809
  sessionsModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) }),
4145
4810
  distributionRulesModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setDistributionRulesModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(DistributionRulesModal, { onClose: () => setDistributionRulesModalOpen(false) }) })
@@ -4606,6 +5271,8 @@ exports.SendModal = SendModal;
4606
5271
  exports.SendModalTemplate = SendModalTemplate;
4607
5272
  exports.SessionsModal = SessionsModal;
4608
5273
  exports.SessionsModalTemplate = SessionsModalTemplate;
5274
+ exports.SwapModal = SwapModal;
5275
+ exports.SwapModalTemplate = SwapModalTemplate;
4609
5276
  exports.TransactionModalTemplate = TransactionModalTemplate;
4610
5277
  exports.TxHistoryModalTemplate = TxHistoryModalTemplate;
4611
5278
  exports.TxStatusView = TxStatusView;