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