@pollar/react 0.12.0-rc.2 → 0.12.0-rc.5

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, describeRampAction, mergeRampCountries, transactionErrorMessage, WalletType, isPollarApiError, isPollarClient, PollarClient, isInteractiveAuthAdapter, mergeRampSnapshot, toBaseUnits, fromBaseUnits } from '@pollar/core';
2
+ import { AUTH_ERROR_CODES, rampLegTitle, rampRequirementCopy, rampTrustlineAsset, rampWalletStep, rampResolutionText, describeRampAction, rampLegStatus, rampNextApproval, rampOrderStopped, rampOrderReceive, rampRailLabel, rampNoRouteText, rampFieldError, rampFieldValid, rampLegNote, rampStepTitle, rampStepStatusText, rampLegTimeLeft, rampOrderResolutionText, rampCountries, rampQuoteFields, isPollarApiError, transactionErrorMessage, WalletType, isPollarClient, PollarClient, isInteractiveAuthAdapter, rampTrustlineErrorText, toBaseUnits, appUnavailableMessage, cosignRefusalMessage, fromBaseUnits, assertRampStellarSigner } from '@pollar/core';
3
3
  import { forwardRef, createContext, useState, useCallback, useEffect, useMemo, useRef, useContext, Component } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
@@ -999,7 +999,6 @@ var require_QRCode = __commonJS({
999
999
  });
1000
1000
 
1001
1001
  // src/constants.ts
1002
- var LOGO_POLLAR = "https://pollar.xyz/assets/logo_pollar.png";
1003
1002
  var LOGO_POLLAR_WORDMARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-light.png";
1004
1003
  var LOGO_POLLAR_WORDMARK_ON_DARK = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-dark.png";
1005
1004
  var LOGO_POLLAR_MARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-mark-on-light.png";
@@ -1029,6 +1028,13 @@ var ModalErrorBoundary = class extends Component {
1029
1028
  return this.props.children;
1030
1029
  }
1031
1030
  };
1031
+ function ModalLogo({ logoUrl }) {
1032
+ if (logoUrl) return /* @__PURE__ */ jsx("img", { src: logoUrl, alt: "Logo", className: "pollar-logo" });
1033
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1034
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_LIGHT, alt: "Pollar", className: "pollar-logo pollar-logo-on-light" }),
1035
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_DARK, alt: "Pollar", className: "pollar-logo pollar-logo-on-dark" })
1036
+ ] });
1037
+ }
1032
1038
  var PollarModalFooter = () => {
1033
1039
  return /* @__PURE__ */ jsxs("div", { className: "pollar-footer", children: [
1034
1040
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
@@ -1036,7 +1042,7 @@ var PollarModalFooter = () => {
1036
1042
  /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_DARK, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-dark" }),
1037
1043
  /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1038
1044
  "v",
1039
- "0.12.0-rc.2"
1045
+ "0.12.0-rc.5"
1040
1046
  ] })
1041
1047
  ] });
1042
1048
  };
@@ -1218,42 +1224,75 @@ function readableTextOn(background) {
1218
1224
  const [r, g, b] = rgb;
1219
1225
  return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.6 ? "#111827" : "#ffffff";
1220
1226
  }
1227
+ var PALETTES = {
1228
+ light: {
1229
+ bg: "#ffffff",
1230
+ border: "#e5e7eb",
1231
+ text: "#111827",
1232
+ muted: "#6b7280",
1233
+ "input-bg": "#f9fafb",
1234
+ "error-bg": "#fef2f2",
1235
+ "error-border": "#fecaca",
1236
+ "error-text": "#dc2626",
1237
+ "success-text": "#16a34a"
1238
+ },
1239
+ dark: {
1240
+ bg: "#1a1a1a",
1241
+ border: "#374151",
1242
+ text: "#ffffff",
1243
+ muted: "#9ca3af",
1244
+ "input-bg": "#374151",
1245
+ "error-bg": "#2a1515",
1246
+ "error-border": "#7f1d1d",
1247
+ "error-text": "#f87171",
1248
+ "success-text": "#4ade80"
1249
+ }
1250
+ };
1251
+ function mix(fg, percent, bg) {
1252
+ return `color-mix(in srgb, ${fg} ${percent}%, ${bg})`;
1253
+ }
1221
1254
  function buildModalCssVars(theme, accentColor, overrides = {}, variant = "compact") {
1222
1255
  const isDark = theme === "dark";
1223
- const textColor = color(overrides.textColor, isDark ? "#ffffff" : "#111827");
1256
+ const palette = isDark ? PALETTES.dark : PALETTES.light;
1257
+ const bg = color(overrides.backgroundColor, "");
1258
+ const text = color(overrides.textColor, "");
1259
+ const pinned = isDark || bg !== "" || text !== "";
1224
1260
  const secondaryBg = color(overrides.buttonColor, "");
1225
- return {
1226
- "--pollar-accent": accentColor,
1227
- "--pollar-bg": color(overrides.backgroundColor, isDark ? "#1a1a1a" : "#ffffff"),
1228
- "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1229
- "--pollar-text": textColor,
1230
- "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1261
+ const vars = {
1262
+ "--pollar-brand-accent": accentColor,
1231
1263
  // The primary button is the accent's, straight up; its label follows the
1232
1264
  // accent's luminance so a pale accent doesn't render white-on-white.
1233
- "--pollar-btn-primary-fg": readableTextOn(accentColor),
1234
- ...secondaryBg ? {
1235
- "--pollar-btn-secondary-bg": secondaryBg,
1236
- "--pollar-btn-secondary-fg": readableTextOn(secondaryBg)
1237
- } : {},
1238
- "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
1239
- "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
1240
- "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1241
- "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1242
- "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1243
- "--pollar-logo-on-light-display": isDark ? "none" : "block",
1244
- "--pollar-logo-on-dark-display": isDark ? "block" : "none",
1245
- "--pollar-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
1246
- "--pollar-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS),
1247
- "--pollar-buttons-height": "44px",
1248
- "--pollar-input-height": "44px",
1249
- "--pollar-input-border-radius": "0.5rem",
1250
- "--pollar-card-border-radius": "10px",
1251
- ...variant === "hero" ? {
1252
- "--pollar-modal-padding": "2rem",
1253
- "--pollar-modal-heading-size": "1.375rem",
1254
- "--pollar-modal-subtitle-size": "0.9rem"
1255
- } : {}
1265
+ "--pollar-brand-btn-primary-fg": readableTextOn(accentColor),
1266
+ "--pollar-brand-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
1267
+ "--pollar-brand-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS)
1256
1268
  };
1269
+ if (secondaryBg) {
1270
+ vars["--pollar-brand-btn-secondary-bg"] = secondaryBg;
1271
+ vars["--pollar-brand-btn-secondary-fg"] = readableTextOn(secondaryBg);
1272
+ }
1273
+ if (pinned) {
1274
+ for (const [name, value] of Object.entries(palette)) vars[`--pollar-brand-${name}`] = value;
1275
+ vars["--pollar-brand-logo-on-light-display"] = isDark ? "none" : "block";
1276
+ vars["--pollar-brand-logo-on-dark-display"] = isDark ? "block" : "none";
1277
+ }
1278
+ if (bg || text) {
1279
+ const surface = bg || palette.bg;
1280
+ const ink = text || palette.text;
1281
+ vars["--pollar-brand-bg"] = surface;
1282
+ vars["--pollar-brand-text"] = ink;
1283
+ vars["--pollar-brand-border"] = mix(ink, 14, surface);
1284
+ vars["--pollar-brand-muted"] = mix(ink, 62, surface);
1285
+ vars["--pollar-brand-input-bg"] = mix(ink, 4, surface);
1286
+ const darkSurface = readableTextOn(surface) === "#ffffff";
1287
+ vars["--pollar-brand-logo-on-light-display"] = darkSurface ? "none" : "block";
1288
+ vars["--pollar-brand-logo-on-dark-display"] = darkSurface ? "block" : "none";
1289
+ }
1290
+ if (variant === "hero") {
1291
+ vars["--pollar-brand-modal-padding"] = "2rem";
1292
+ vars["--pollar-brand-modal-heading-size"] = "1.375rem";
1293
+ vars["--pollar-brand-modal-subtitle-size"] = "0.9rem";
1294
+ }
1295
+ return vars;
1257
1296
  }
1258
1297
  function modalChrome(styles) {
1259
1298
  const zIndex = styles.modalZIndex;
@@ -1268,7 +1307,7 @@ function modalChrome(styles) {
1268
1307
  buttonBorderRadius: styles.buttonBorderRadius
1269
1308
  },
1270
1309
  overlayStyle: {
1271
- "--pollar-overlay-z-index": typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_OVERLAY_Z_INDEX
1310
+ "--pollar-brand-overlay-z-index": typeof zIndex === "number" && Number.isFinite(zIndex) ? zIndex : DEFAULT_OVERLAY_Z_INDEX
1272
1311
  }
1273
1312
  };
1274
1313
  }
@@ -1953,6 +1992,8 @@ function KycModalTemplate({
1953
1992
  reviewReason,
1954
1993
  processing = false,
1955
1994
  isLoading,
1995
+ startingProviderId = null,
1996
+ directOpen = false,
1956
1997
  error,
1957
1998
  onSelectProvider,
1958
1999
  onDoneVerifying,
@@ -1961,6 +2002,7 @@ function KycModalTemplate({
1961
2002
  onClose
1962
2003
  }) {
1963
2004
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
2005
+ const busy = isLoading || startingProviderId !== null;
1964
2006
  return /* @__PURE__ */ jsxs(
1965
2007
  "div",
1966
2008
  {
@@ -1988,7 +2030,7 @@ function KycModalTemplate({
1988
2030
  type: "button",
1989
2031
  className: "pollar-modal-close",
1990
2032
  onClick: onRefresh,
1991
- disabled: isLoading,
2033
+ disabled: busy,
1992
2034
  "aria-label": "Refresh",
1993
2035
  title: "Refresh providers",
1994
2036
  children: /* @__PURE__ */ jsx(
@@ -2018,7 +2060,14 @@ function KycModalTemplate({
2018
2060
  ] })
2019
2061
  ] }),
2020
2062
  error && /* @__PURE__ */ jsx("p", { className: "pollar-kyc-error", role: "alert", children: error }),
2021
- step === "select_provider" && (isLoading && providers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", children: [
2063
+ step === "select_provider" && (startingProviderId !== null && directOpen ? /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", "aria-live": "polite", children: [
2064
+ /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
2065
+ /* @__PURE__ */ jsxs("span", { children: [
2066
+ "Opening ",
2067
+ selectedProvider?.name ?? "verification",
2068
+ "\u2026"
2069
+ ] })
2070
+ ] }) : isLoading && providers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "pollar-loading-block", children: [
2022
2071
  /* @__PURE__ */ jsx("div", { className: "pollar-spinner" }),
2023
2072
  /* @__PURE__ */ jsx("span", { children: "Loading providers\u2026" })
2024
2073
  ] }) : /* @__PURE__ */ jsxs("div", { className: "pollar-kyc-providers", children: [
@@ -2028,11 +2077,15 @@ function KycModalTemplate({
2028
2077
  {
2029
2078
  type: "button",
2030
2079
  className: "pollar-kyc-provider-btn",
2031
- disabled: isLoading,
2080
+ disabled: busy,
2081
+ "aria-busy": startingProviderId === p.id,
2032
2082
  onClick: () => onSelectProvider(p),
2033
2083
  children: [
2034
2084
  /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-name", children: p.name }),
2035
- /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-flow", children: isLoading && selectedProvider?.id === p.id ? "Opening\u2026" : p.flow === "redirect" ? "Continue in a new tab" : "Continue here" })
2085
+ /* @__PURE__ */ jsx("span", { className: "pollar-kyc-provider-flow", children: startingProviderId === p.id ? /* @__PURE__ */ jsxs(Fragment, { children: [
2086
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
2087
+ "Opening\u2026"
2088
+ ] }) : p.flow === "redirect" ? "Continue in a new tab" : "Continue here" })
2036
2089
  ]
2037
2090
  },
2038
2091
  p.id
@@ -2083,7 +2136,7 @@ function KycModalTemplate({
2083
2136
  /* @__PURE__ */ jsx(KycStatus, { status: kycStatus, ...processing ? { label: "Approved" } : {} }),
2084
2137
  /* @__PURE__ */ jsx("p", { className: "pollar-kyc-result-text", children: kycStatus === "approved" ? "Your identity has been verified successfully." : kycStatus === "rejected" ? "Your verification was not approved. Contact support for the next steps." : kycStatus === "expired" ? "Your verification expired. Start again to verify your identity." : processing ? kycProcessingMessage() : kycReviewMessage(reviewReason) }),
2085
2138
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
2086
- kycStatus === "expired" && onStartAgain && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onStartAgain, disabled: isLoading, children: "Start again" }),
2139
+ kycStatus === "expired" && onStartAgain && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onStartAgain, disabled: busy, children: "Start again" }),
2087
2140
  kycStatus !== "approved" && kycStatus !== "rejected" && kycStatus !== "expired" && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onDoneVerifying, children: "Check again" }),
2088
2141
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Close" })
2089
2142
  ] })
@@ -2116,6 +2169,8 @@ function KycModal({
2116
2169
  const [reviewReason, setReviewReason] = useState(null);
2117
2170
  const [processing, setProcessing] = useState(false);
2118
2171
  const [isLoading, setIsLoading] = useState(false);
2172
+ const [startingProviderId, setStartingProviderId] = useState(null);
2173
+ const [directOpen, setDirectOpen] = useState(false);
2119
2174
  const [error, setError] = useState(null);
2120
2175
  const idempotencyKeys = useRef({});
2121
2176
  const autoOpened = useRef(false);
@@ -2143,7 +2198,7 @@ function KycModal({
2143
2198
  starting.current = true;
2144
2199
  setSelectedProvider(provider);
2145
2200
  setError(null);
2146
- setIsLoading(true);
2201
+ setStartingProviderId(provider.id);
2147
2202
  const key = (_a = idempotencyKeys.current)[_b = provider.id] ?? (_a[_b] = newIdempotencyKey());
2148
2203
  try {
2149
2204
  const result = await client.resolveKyc(
@@ -2181,7 +2236,7 @@ function KycModal({
2181
2236
  setStep("select_provider");
2182
2237
  } finally {
2183
2238
  starting.current = false;
2184
- setIsLoading(false);
2239
+ setStartingProviderId(null);
2185
2240
  }
2186
2241
  }
2187
2242
  const loadProviders = useCallback(() => {
@@ -2200,6 +2255,7 @@ function KycModal({
2200
2255
  void loadProviders().then((list) => {
2201
2256
  if (!providerId || autoOpened.current) return;
2202
2257
  autoOpened.current = true;
2258
+ setDirectOpen(true);
2203
2259
  const target = list.find((p) => p.id === providerId);
2204
2260
  if (target) void handleSelectProvider(target);
2205
2261
  else
@@ -2249,6 +2305,8 @@ function KycModal({
2249
2305
  reviewReason,
2250
2306
  processing,
2251
2307
  isLoading,
2308
+ startingProviderId,
2309
+ directOpen,
2252
2310
  error,
2253
2311
  onSelectProvider: handleSelectProvider,
2254
2312
  onDoneVerifying: handleDoneVerifying,
@@ -2938,7 +2996,7 @@ function LoginModalTemplate({
2938
2996
  }
2939
2997
  ) }),
2940
2998
  /* @__PURE__ */ jsxs("div", { className: "pollar-header", children: [
2941
- /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
2999
+ /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx(ModalLogo, { logoUrl }) }),
2942
3000
  /* @__PURE__ */ jsx("h2", { className: "pollar-title", children: appName }),
2943
3001
  /* @__PURE__ */ jsx("p", { className: "pollar-subtitle", children: "Log in or sign up" })
2944
3002
  ] }),
@@ -3104,7 +3162,7 @@ function LoginModalStatus({
3104
3162
  }
3105
3163
  ) }),
3106
3164
  /* @__PURE__ */ jsxs("div", { className: "pollar-header", children: [
3107
- /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
3165
+ /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx(ModalLogo, { logoUrl }) }),
3108
3166
  /* @__PURE__ */ jsx("h2", { className: "pollar-title", children: appName }),
3109
3167
  /* @__PURE__ */ jsx("p", { className: "pollar-subtitle", children: "Log in or sign up" })
3110
3168
  ] }),
@@ -3206,7 +3264,7 @@ function PrivyLoginSubmodal({
3206
3264
  }
3207
3265
  ) }),
3208
3266
  /* @__PURE__ */ jsxs("div", { className: "pollar-header", children: [
3209
- /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
3267
+ /* @__PURE__ */ jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsx(ModalLogo, { logoUrl }) }),
3210
3268
  /* @__PURE__ */ jsx("h2", { className: "pollar-title", children: appName }),
3211
3269
  /* @__PURE__ */ jsx("p", { className: "pollar-subtitle", children: adapter.meta.label })
3212
3270
  ] }),
@@ -3407,256 +3465,45 @@ function LoginModal({ onClose }) {
3407
3465
  }
3408
3466
  ) });
3409
3467
  }
3410
-
3411
- // src/lib/qr-code/index.tsx
3412
- var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
3413
- var import_QRCode = __toESM(require_QRCode());
3414
- var QRCodeSvg = forwardRef(function QRCodeSvg2({ bgColor, bgD, fgD, fgColor, size, title, viewBoxSize, xmlns = "http://www.w3.org/2000/svg", ...props }, ref) {
3415
- return /* @__PURE__ */ jsxs("svg", { ...props, height: size, ref, viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, width: size, xmlns, children: [
3416
- title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
3417
- /* @__PURE__ */ jsx("path", { d: bgD, fill: bgColor }),
3418
- /* @__PURE__ */ jsx("path", { d: fgD, fill: fgColor })
3419
- ] });
3420
- });
3421
- QRCodeSvg.displayName = "QRCodeSvg";
3422
- function bytesToBinaryString(bytes) {
3423
- return bytes.map((b) => String.fromCharCode(b & 255)).join("");
3424
- }
3425
- function encodeStringToUtf8Bytes(input) {
3426
- return Array.from(new TextEncoder().encode(input));
3427
- }
3428
- var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#000000", level = "L", size = 256, value, ...props }, ref) {
3429
- const qrcode = new import_QRCode.default(-1, import_ErrorCorrectLevel.default[level]);
3430
- const utf8Bytes = encodeStringToUtf8Bytes(value);
3431
- const binaryString = bytesToBinaryString(utf8Bytes);
3432
- qrcode.addData(binaryString, "Byte");
3433
- qrcode.make();
3434
- const cells = qrcode.modules;
3435
- return /* @__PURE__ */ jsx(
3436
- QRCodeSvg,
3437
- {
3438
- ...props,
3439
- bgColor,
3440
- bgD: cells.map(
3441
- (row, rowIndex) => row.map((cell, cellIndex) => !cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
3442
- ).join(" "),
3443
- fgColor,
3444
- fgD: cells.map(
3445
- (row, rowIndex) => row.map((cell, cellIndex) => cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
3446
- ).join(" "),
3447
- ref,
3448
- size,
3449
- viewBoxSize: cells.length
3450
- }
3451
- );
3452
- });
3453
- function RampWorkflow({
3454
- client,
3455
- snapshot,
3456
- onChange,
3457
- onBusyChange
3458
- }) {
3459
- const model = describeRampAction(snapshot);
3460
- const locked = useRef(false);
3461
- useEffect(() => {
3462
- setFields({});
3463
- setError(null);
3464
- }, [model.action?.actionId]);
3465
- const [busy, setBusy] = useState(false);
3466
- const [error, setError] = useState(null);
3467
- const [fields, setFields] = useState({});
3468
- const fieldsComplete = model.action?.kind !== "collect_information" || model.action.fields.every((field) => {
3469
- const value = (fields[field.key] ?? "").trim();
3470
- if (!value) return field.optional;
3471
- if (field.type === "email") return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
3472
- return field.type !== "select" || field.options?.some((option) => option.value === value);
3468
+ function findWalletBalance(balances, asset) {
3469
+ const { issuer } = asset;
3470
+ return balances.find((b) => {
3471
+ if (resolveChain(b.chain) !== asset.chain) return false;
3472
+ if (issuer === null) return b.type === "native" || !b.issuer && b.code === asset.code;
3473
+ if (b.code !== asset.code) return false;
3474
+ return issuer === void 0 || !b.issuer || b.issuer.toLowerCase() === issuer.toLowerCase();
3473
3475
  });
3474
- async function continueAction() {
3475
- if (!model.action || locked.current || !fieldsComplete) return;
3476
- locked.current = true;
3477
- setBusy(true);
3478
- onBusyChange?.(true);
3479
- setError(null);
3480
- try {
3481
- onChange(
3482
- model.canSign ? await client.signRampAction(snapshot.txId, snapshot) : await client.continueRamp(snapshot.txId, {
3483
- actionId: model.action.actionId,
3484
- transactionVersion: snapshot.transactionVersion,
3485
- ...model.action.kind === "collect_information" ? { fields } : {}
3486
- })
3487
- );
3488
- } catch (e) {
3489
- setError(e instanceof Error ? e.message : "Unable to continue.");
3490
- } finally {
3491
- locked.current = false;
3492
- setBusy(false);
3493
- onBusyChange?.(false);
3494
- }
3495
- }
3496
- return /* @__PURE__ */ jsxs(
3497
- "section",
3498
- {
3499
- className: "pollar-ramp-payment pollar-ramp-workflow",
3500
- "aria-label": "Ramp progress",
3501
- "aria-live": "polite",
3502
- "aria-busy": busy,
3503
- children: [
3504
- /* @__PURE__ */ jsx("h3", { className: "pollar-ramp-payment-title", children: model.title }),
3505
- snapshot.terms && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-terms", children: [
3506
- snapshot.terms.fiatAmount,
3507
- " ",
3508
- snapshot.terms.fiatCurrency,
3509
- " \xB7 ",
3510
- snapshot.terms.cryptoAmount,
3511
- " ",
3512
- snapshot.terms.assetCode,
3513
- /* @__PURE__ */ jsx("br", {}),
3514
- "Includes ",
3515
- snapshot.terms.feeAmount,
3516
- " ",
3517
- snapshot.terms.feeCurrency,
3518
- " fee"
3519
- ] }),
3520
- model.details.map((item, index) => /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-workflow-detail", children: [
3521
- /* @__PURE__ */ jsxs("strong", { children: [
3522
- item.label,
3523
- ": "
3524
- ] }),
3525
- item.value
3526
- ] }, index)),
3527
- model.links.map((link, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx("a", { className: "pollar-btn-primary pollar-ramp-hosted-link", href: link.url, target: "_blank", rel: "noopener noreferrer", children: link.label }) }, index)),
3528
- model.qr && /* @__PURE__ */ jsx(QRCode, { value: model.qr, size: 180 }),
3529
- model.qr && /* @__PURE__ */ jsx(
3530
- "button",
3531
- {
3532
- className: "pollar-btn-secondary",
3533
- type: "button",
3534
- onClick: () => void navigator.clipboard.writeText(model.qr).catch(() => setError("Unable to copy. Select the payment code to copy it manually.")),
3535
- children: "Copy payment code"
3536
- }
3537
- ),
3538
- model.action?.kind === "collect_information" && model.action.fields.map((field) => /* @__PURE__ */ jsxs("label", { className: "pollar-ramp-field", children: [
3539
- /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-label", children: [
3540
- field.label,
3541
- field.optional ? " (optional)" : ""
3542
- ] }),
3543
- field.type === "select" ? /* @__PURE__ */ jsxs(
3544
- "select",
3545
- {
3546
- className: "pollar-ramp-input",
3547
- disabled: busy,
3548
- value: fields[field.key] ?? "",
3549
- onChange: (e) => setFields((previous) => ({ ...previous, [field.key]: e.target.value })),
3550
- children: [
3551
- /* @__PURE__ */ jsx("option", { value: "", children: "Choose" }),
3552
- field.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
3553
- ]
3554
- }
3555
- ) : /* @__PURE__ */ jsx(
3556
- "input",
3557
- {
3558
- className: "pollar-ramp-input",
3559
- disabled: busy,
3560
- type: field.type,
3561
- placeholder: field.placeholder,
3562
- value: fields[field.key] ?? "",
3563
- onChange: (e) => setFields((previous) => ({ ...previous, [field.key]: e.target.value }))
3564
- }
3565
- ),
3566
- field.hint && /* @__PURE__ */ jsx("small", { className: "pollar-ramp-field-hint", children: field.hint })
3567
- ] }, field.key)),
3568
- (model.canContinue || model.canSign) && /* @__PURE__ */ jsx(
3569
- "button",
3570
- {
3571
- className: "pollar-btn-primary",
3572
- type: "button",
3573
- disabled: busy || !fieldsComplete,
3574
- onClick: () => void continueAction(),
3575
- children: busy ? model.canSign ? "Waiting for your wallet\u2026" : "Continuing\u2026" : model.canSign ? "Authorize" : "Continue"
3576
- }
3577
- ),
3578
- snapshot.milestones?.map((milestone, index) => /* @__PURE__ */ jsxs("p", { children: [
3579
- milestone.kind.replace(/_/g, " "),
3580
- " \xB7 ",
3581
- milestone.verifiedAt
3582
- ] }, index)),
3583
- error && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-field-error", role: "alert", children: error }),
3584
- /* @__PURE__ */ jsxs("small", { className: "pollar-ramp-reference", children: [
3585
- "Reference: ",
3586
- snapshot.txId
3587
- ] })
3588
- ]
3589
- }
3590
- );
3591
- }
3592
-
3593
- // src/components/ramp-widget/ramp-kyc.ts
3594
- var nonEmpty = (value) => typeof value === "string" && !!value.trim();
3595
- function requiredRampKyc(error) {
3596
- if (!error || typeof error !== "object") return null;
3597
- if (!("code" in error) || error.code !== "SDK_RAMPS_KYC_REQUIRED") return null;
3598
- const body = "body" in error ? error.body : void 0;
3599
- if (typeof body !== "object" || body === null) return null;
3600
- const rampProviderId = "rampProviderId" in body ? body.rampProviderId : void 0;
3601
- const corridorId = "corridorId" in body ? body.corridorId : void 0;
3602
- const requirementType = "requirementType" in body ? body.requirementType : void 0;
3603
- const optionId = "optionId" in body ? body.optionId : void 0;
3604
- const kycProviderId = "kycProviderId" in body ? body.kycProviderId : void 0;
3605
- if (!nonEmpty(rampProviderId) || !nonEmpty(corridorId)) return null;
3606
- if ((requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") && nonEmpty(optionId)) {
3607
- return { rampProviderId, corridorId, type: requirementType, optionId };
3608
- }
3609
- const kyc = nonEmpty(optionId) && requirementType === "KYC" ? optionId : kycProviderId;
3610
- return nonEmpty(kyc) ? { rampProviderId, corridorId, type: "KYC", optionId: kyc } : null;
3611
- }
3612
- function pendingFromQuote(requirement) {
3613
- return {
3614
- rampProviderId: requirement.rampProviderId,
3615
- corridorId: requirement.corridorId,
3616
- type: requirement.type,
3617
- optionId: requirement.optionId,
3618
- progress: { position: requirement.completed + 1, total: requirement.total }
3619
- };
3620
- }
3621
- function lockedRouteCopy(requirement) {
3622
- if (requirement.type === "FORM") return { message: "Answer a few questions to see this route", action: "Continue" };
3623
- if (requirement.type === "PROVIDER_REGISTRATION") {
3624
- return { message: "Register with the provider to see this route", action: "Continue" };
3625
- }
3626
- if (requirement.type === "REGISTRY_CHECK") {
3627
- return requirement.status === "pending" ? { message: "Your details are under review", action: null } : { message: "Confirm your details to see this route", action: "Confirm" };
3628
- }
3629
- switch (requirement.status) {
3630
- case "expired":
3631
- return { message: "Your verification expired", action: "Verify again" };
3632
- case "pending":
3633
- if (!requirement.reviewReason) return { message: "Verification in progress", action: "Continue" };
3634
- return {
3635
- message: requirement.reviewReason === "DUPLICATE_DOCUMENT" ? kycReviewMessage(requirement.reviewReason) : "Your verification is under review",
3636
- action: null
3637
- };
3638
- case "rejected":
3639
- return { message: "Verification was not approved", action: null };
3640
- default:
3641
- return { message: "Verify your identity to see this route", action: "Verify" };
3642
- }
3643
3476
  }
3644
- var RAIL_LABELS = {
3645
- SPEI: "SPEI (Mexico)",
3646
- PIX: "PIX (Brazil)",
3647
- PSE: "PSE (Colombia)",
3648
- ACH: "ACH (US)"
3649
- };
3650
- function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
3477
+ function useWalletAvailable(asset) {
3478
+ const { walletBalance, refreshWalletBalance } = usePollar();
3479
+ const wanted = !!asset;
3480
+ useEffect(() => {
3481
+ if (wanted && walletBalance.step === "idle") void refreshWalletBalance();
3482
+ }, [wanted, walletBalance.step, refreshWalletBalance]);
3483
+ if (!asset || walletBalance.step !== "loaded") return void 0;
3484
+ const record = findWalletBalance(walletBalance.data.balances, asset);
3485
+ return record ? record.available : "0";
3486
+ }
3487
+ var CUSTODY_LABEL = { COSIGNED: "co-signed", SELF_CUSTODY: "self-custody" };
3488
+ var assetOf = (node) => node.kind === "fiat" ? node.currency : node.code;
3489
+ function etaText(seconds) {
3490
+ if (seconds === null) return null;
3491
+ if (seconds < 90) return `~${Math.max(1, Math.round(seconds))} s`;
3492
+ if (seconds < 5400) return `~${Math.round(seconds / 60)} min`;
3493
+ return `~${Math.round(seconds / 3600)} h`;
3494
+ }
3495
+ function RouteDisplay({ quote, best = false, busy = false, disabled = false, onSelect }) {
3651
3496
  const inert = busy || disabled;
3652
3497
  const select = () => {
3653
3498
  if (!inert) onSelect(quote);
3654
3499
  };
3500
+ const first = quote.legs[0];
3501
+ const last = quote.legs[quote.legs.length - 1];
3655
3502
  return /* @__PURE__ */ jsxs(
3656
3503
  "div",
3657
3504
  {
3658
3505
  className: "pollar-ramp-route-card",
3659
- "data-recommended": quote.recommended,
3506
+ "data-recommended": best,
3660
3507
  "data-busy": busy || void 0,
3661
3508
  "data-disabled": disabled || void 0,
3662
3509
  role: "button",
@@ -3667,55 +3514,152 @@ function RouteDisplay({ quote, busy = false, disabled = false, onSelect }) {
3667
3514
  onKeyDown: (e) => e.key === "Enter" && select(),
3668
3515
  children: [
3669
3516
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
3670
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
3671
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" : [
3672
- quote.route ? `${quote.route.asset.code} on ${quote.route.asset.chain}` : null,
3673
- RAIL_LABELS[quote.rail] ?? quote.rail,
3674
- quote.estimatedTime
3675
- ].filter(Boolean).join(" \xB7 ") })
3517
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: first && last ? `${quote.amountIn} ${assetOf(first.from)} \u2192 ~${quote.amountOut} ${assetOf(last.to)}` : `~${quote.amountOut}` }),
3518
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: busy ? "Starting\u2026" : [quote.legs.map((leg) => rampLegTitle(leg)).join(" \xB7 "), etaText(quote.etaSeconds)].filter(Boolean).join(" \xB7 ") })
3676
3519
  ] }),
3677
3520
  /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: busy ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3678
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-fee", children: quote.terms ? `${quote.terms.fiatAmount} ${quote.terms.fiatCurrency} \xB7 ${quote.terms.cryptoAmount} ${quote.terms.assetCode} \xB7 Includes ${quote.terms.feeAmount} ${quote.terms.feeCurrency}` : `${quote.fee}% fee` }),
3679
- quote.recommended && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
3521
+ /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-route-fee", children: [
3522
+ quote.legs.length === 1 ? "1 step" : `${quote.legs.length} steps`,
3523
+ " \xB7 fees included"
3524
+ ] }),
3525
+ best && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
3680
3526
  ] }) })
3681
3527
  ]
3682
3528
  }
3683
3529
  );
3684
3530
  }
3685
- function LockedRouteDisplay({ requirement, disabled = false, onVerify }) {
3686
- const { message, action } = lockedRouteCopy(requirement);
3531
+ function RejectedRouteDisplay({
3532
+ rejection,
3533
+ disabled = false,
3534
+ trusting = false,
3535
+ walletBusy = false,
3536
+ custodyChoice = false,
3537
+ onVerify,
3538
+ onTrust,
3539
+ onWalletStep
3540
+ }) {
3541
+ const requirement = rejection.requirement;
3542
+ const copy = requirement ? rampRequirementCopy(requirement) : null;
3543
+ const trustAsset = !requirement && onTrust ? rampTrustlineAsset(rejection) : null;
3544
+ const step = !requirement ? rampWalletStep(rejection) : null;
3545
+ const fromMenu = step?.action === "create_wallet" && custodyChoice;
3546
+ const walletStep = onWalletStep && !fromMenu ? step : null;
3547
+ const picks = custodyChoice && step?.action !== "set_up_recovery" ? ["COSIGNED", "SELF_CUSTODY"] : [void 0];
3548
+ const meta = copy ? copy.message : fromMenu ? "You need a wallet to receive this. Create it from the wallet menu, then find the route again." : rampResolutionText(rejection.failure);
3549
+ const busy = trusting || !!walletStep && walletBusy;
3687
3550
  return /* @__PURE__ */ jsxs(
3688
3551
  "div",
3689
3552
  {
3690
3553
  className: "pollar-ramp-route-card",
3691
3554
  "data-locked": true,
3692
- "data-kyc-status": requirement.status,
3693
- "data-disabled": disabled || void 0,
3555
+ "data-kyc-status": requirement?.status,
3556
+ "data-disabled": !(copy?.action && onVerify) && !trustAsset && !walletStep || disabled && !busy || void 0,
3694
3557
  children: [
3695
3558
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-left", children: [
3696
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: requirement.provider }),
3697
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: message })
3559
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-provider", children: rejection.legs.map((leg) => rampLegTitle(leg)).join(" \xB7 ") }),
3560
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-route-meta", children: meta })
3698
3561
  ] }),
3699
- action && /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: /* @__PURE__ */ jsx(
3562
+ trustAsset && /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: /* @__PURE__ */ jsx(
3563
+ "button",
3564
+ {
3565
+ type: "button",
3566
+ className: "pollar-btn-secondary pollar-ramp-route-verify",
3567
+ disabled,
3568
+ "aria-busy": trusting,
3569
+ onClick: () => onTrust(trustAsset),
3570
+ children: trusting ? /* @__PURE__ */ jsxs(Fragment, { children: [
3571
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3572
+ " Adding\u2026"
3573
+ ] }) : `Add ${trustAsset.code} trustline`
3574
+ }
3575
+ ) }),
3576
+ walletStep && /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: walletBusy ? /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-btn-secondary pollar-ramp-route-verify", disabled: true, "aria-busy": true, children: [
3577
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3578
+ " ",
3579
+ walletStep.busyLabel
3580
+ ] }) : picks.map((custody) => /* @__PURE__ */ jsx(
3581
+ "button",
3582
+ {
3583
+ type: "button",
3584
+ className: "pollar-btn-secondary pollar-ramp-route-verify",
3585
+ disabled,
3586
+ onClick: () => onWalletStep(walletStep, custody),
3587
+ children: custody ? `${walletStep.label} (${CUSTODY_LABEL[custody]})` : walletStep.label
3588
+ },
3589
+ custody ?? "app"
3590
+ )) }),
3591
+ requirement && copy?.action && onVerify && /* @__PURE__ */ jsx("div", { className: "pollar-ramp-route-right", children: /* @__PURE__ */ jsx(
3700
3592
  "button",
3701
3593
  {
3702
3594
  type: "button",
3703
3595
  className: "pollar-btn-secondary pollar-ramp-route-verify",
3704
3596
  disabled,
3705
3597
  onClick: () => onVerify(requirement),
3706
- children: action
3598
+ children: copy.action
3707
3599
  }
3708
3600
  ) })
3709
3601
  ]
3710
3602
  }
3711
3603
  );
3712
3604
  }
3713
- var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3714
- function isFieldValid(spec, raw) {
3715
- const value = (raw ?? "").trim();
3716
- if (!value) return spec.optional === true;
3717
- if (spec.type === "email") return EMAIL_RE.test(value);
3718
- return true;
3605
+ function formatAvailable(available) {
3606
+ if (available === null) return "\u2014";
3607
+ const n = parseFloat(available);
3608
+ return isNaN(n) ? available : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
3609
+ }
3610
+ function AmountField({
3611
+ id,
3612
+ label,
3613
+ value,
3614
+ onChange,
3615
+ unit,
3616
+ available,
3617
+ onMax,
3618
+ placeholder = "0.00",
3619
+ disabled = false
3620
+ }) {
3621
+ const showBalance = available !== void 0;
3622
+ const canMax = showBalance && available !== null && parseFloat(available) > 0 && !disabled;
3623
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
3624
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-label-row", children: [
3625
+ /* @__PURE__ */ jsx("label", { className: "pollar-send-label", htmlFor: id, children: label }),
3626
+ showBalance && /* @__PURE__ */ jsxs("span", { className: "pollar-send-hint", children: [
3627
+ "Available: ",
3628
+ formatAvailable(available),
3629
+ " ",
3630
+ unit ?? ""
3631
+ ] })
3632
+ ] }),
3633
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
3634
+ /* @__PURE__ */ jsx(
3635
+ "input",
3636
+ {
3637
+ id,
3638
+ className: "pollar-input pollar-send-amount-input",
3639
+ type: "text",
3640
+ inputMode: "decimal",
3641
+ placeholder,
3642
+ value,
3643
+ disabled,
3644
+ onChange: (e) => onChange(e.target.value),
3645
+ "aria-label": id ? void 0 : label
3646
+ }
3647
+ ),
3648
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-suffix", children: [
3649
+ showBalance && /* @__PURE__ */ jsx(
3650
+ "button",
3651
+ {
3652
+ type: "button",
3653
+ className: "pollar-send-inline-action",
3654
+ disabled: !canMax,
3655
+ onClick: () => onMax ? onMax() : available && onChange(available),
3656
+ children: "Max"
3657
+ }
3658
+ ),
3659
+ unit && /* @__PURE__ */ jsx("span", { children: unit })
3660
+ ] })
3661
+ ] })
3662
+ ] });
3719
3663
  }
3720
3664
  function scannableImageSrc(image) {
3721
3665
  return image.encoding === "base64" ? `data:${image.mediaType};base64,${image.data}` : `data:${image.mediaType};charset=utf-8,${encodeURIComponent(image.data)}`;
@@ -3734,109 +3678,143 @@ function placeholderFor(field, fields, values) {
3734
3678
  }
3735
3679
  return field.placeholder;
3736
3680
  }
3737
- var CHAIN_NAME = { STELLAR: "Stellar", POLYGON: "Polygon", SOLANA: "Solana" };
3738
- var EXPLORER_NAME = { STELLAR: "Stellar Expert", POLYGON: "Polygonscan", SOLANA: "Solana Explorer" };
3739
- var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
3740
3681
  function flagEmoji(code) {
3741
3682
  const cc = code.trim().toUpperCase();
3742
3683
  if (!/^[A-Z]{2}$/.test(cc)) return "";
3743
3684
  const base = 127462;
3744
3685
  return String.fromCodePoint(base + (cc.charCodeAt(0) - 65), base + (cc.charCodeAt(1) - 65));
3745
3686
  }
3746
- var STATUS_LABEL = {
3747
- pending: "Pending",
3748
- processing: "Processing",
3749
- completed: "Completed",
3750
- failed: "Failed"
3751
- };
3752
- function displayValue(field) {
3753
- if (field.type !== "datetime") return field.value;
3754
- const at = new Date(field.value);
3755
- return Number.isNaN(at.getTime()) ? field.value : at.toLocaleString();
3687
+ var CHAIN_NAME = { STELLAR: "Stellar", POLYGON: "Polygon", SOLANA: "Solana" };
3688
+ function explorerTxUrl2(chain, hash, network) {
3689
+ const testnet = network === "testnet";
3690
+ if (chain === "SOLANA") return `https://explorer.solana.com/tx/${hash}${testnet ? "?cluster=devnet" : ""}`;
3691
+ if (chain === "POLYGON") return `https://${testnet ? "amoy." : ""}polygonscan.com/tx/${hash}`;
3692
+ return `https://stellar.expert/explorer/${testnet ? "testnet" : "public"}/tx/${hash}`;
3693
+ }
3694
+ function legChain(leg) {
3695
+ if (leg.from.kind === "asset") return leg.from.chain;
3696
+ return leg.to.kind === "asset" ? leg.to.chain : "STELLAR";
3697
+ }
3698
+ function stepChain(kind, leg) {
3699
+ if (kind === "mint" && leg.to.kind === "asset") return leg.to.chain;
3700
+ return legChain(leg);
3701
+ }
3702
+ var timeLeftText = (seconds) => seconds <= 0 ? "Taking a little longer than usual" : seconds < 60 ? `About ${seconds}s left` : `About ${Math.ceil(seconds / 60)} min left`;
3703
+ function useNowEverySecond(running) {
3704
+ const [now, setNow] = useState(() => Date.now());
3705
+ useEffect(() => {
3706
+ if (!running) return;
3707
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
3708
+ return () => clearInterval(timer);
3709
+ }, [running]);
3710
+ return now;
3711
+ }
3712
+ var fiatKey = (fiat) => `${fiat.currency}:${fiat.rail}`;
3713
+ var assetKey3 = (asset) => `${asset.code}:${asset.chain}:${asset.identifier ?? ""}`;
3714
+ function trustKey(rejection) {
3715
+ const asset = rampTrustlineAsset(rejection);
3716
+ return asset ? `${asset.code}:${asset.issuer}` : null;
3717
+ }
3718
+ var LOADING_STEPS = ["Checking routes\u2026", "Quoting each step\u2026", "Route found!"];
3719
+ function useRenderAt(at) {
3720
+ const [, setTick] = useState(0);
3721
+ useEffect(() => {
3722
+ if (at === null) return;
3723
+ const timer = setTimeout(() => setTick((n) => n + 1), Math.max(0, at - Date.now()) + 50);
3724
+ return () => clearTimeout(timer);
3725
+ }, [at]);
3756
3726
  }
3757
3727
  function RampWidgetTemplate({
3758
- workflowContent,
3759
- routeSelector,
3760
3728
  theme,
3761
3729
  accentColor,
3762
3730
  styleOverrides,
3731
+ network,
3763
3732
  step,
3764
3733
  flowSteps,
3765
3734
  flowStepIndex,
3766
- startingQuoteId,
3767
3735
  direction,
3768
- amount,
3769
- currency,
3770
- country,
3771
- requiredFields,
3772
- fieldValues,
3736
+ routesLoading,
3773
3737
  countries,
3774
- countriesLoading,
3775
- refreshing,
3738
+ country,
3739
+ fiats,
3740
+ fiat,
3741
+ assets,
3742
+ asset,
3743
+ amount,
3744
+ sellAvailable,
3776
3745
  quotes,
3777
- kycRequired,
3746
+ rejected,
3747
+ startingQuoteId,
3748
+ fields,
3749
+ fieldValues,
3750
+ order,
3751
+ pendingOrders,
3778
3752
  isLoading,
3779
- provider,
3780
- txStatus,
3781
- kycUrl,
3782
- tosUrl,
3783
- kycBlocking,
3784
- onboardingStatus,
3785
- kycJustApproved,
3786
- stellarTxHash,
3787
- txHash,
3788
- txChain,
3789
- explorerUrl,
3790
- fundsInTransit = false,
3791
- retrying = false,
3792
- onRetryTransit,
3793
- depositInstructions,
3794
- canComplete,
3795
- completing,
3796
- savedRamps = [],
3797
- onResumeRamp,
3798
- onDismissRamp,
3799
- onDismissCurrentRamp,
3800
- onExploreRoutes,
3753
+ refreshing,
3754
+ signing,
3801
3755
  errorMsg,
3802
- noticeMsg,
3756
+ notice = null,
3803
3757
  onDirectionChange,
3804
- onAmountChange,
3805
- onFieldChange,
3806
3758
  onCountryChange,
3759
+ onFiatChange,
3760
+ onAssetChange,
3761
+ onAmountChange,
3807
3762
  onFindRoute,
3808
3763
  onSelectQuote,
3809
3764
  onVerifyRoute,
3765
+ trustingAsset = null,
3766
+ onTrustAsset,
3767
+ walletBusy = null,
3768
+ custodyChoice = false,
3769
+ onWalletStep,
3770
+ noChains = false,
3771
+ retrying = false,
3772
+ onRetryOrder,
3773
+ onSetUpRecovery,
3774
+ recoveryMissing = null,
3775
+ checkingPayment = false,
3776
+ onCheckPayment,
3777
+ pollTrouble = false,
3778
+ onFieldChange,
3810
3779
  onContactContinue,
3811
- onOpenKyc,
3812
- onOpenTos,
3813
- onCompleteWithdraw,
3780
+ onSign,
3781
+ onApprove,
3782
+ onResumeOrder,
3783
+ onNewOrder,
3814
3784
  onBack,
3785
+ onStepSelect,
3815
3786
  onRetry,
3816
3787
  onRefresh,
3817
3788
  onClose
3818
3789
  }) {
3819
- const settledHash = txHash ?? stellarTxHash;
3820
- const chainName = CHAIN_NAME[txChain ?? "STELLAR"];
3821
3790
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
3791
+ const onramp = direction === "ONRAMP";
3792
+ const sendUnit = onramp ? fiat?.currency : asset?.code;
3793
+ const view = order ? describeRampAction(order) : null;
3794
+ const settled = order ? ["completed", "failed", "refunded"].includes(order.state) : false;
3795
+ const running = order?.legs.find((leg) => rampLegStatus(leg) === "active" && order.pendingAction?.legIndex !== leg.index);
3796
+ const now = useNowEverySecond(step === "status" && !settled && !!running && running.etaSeconds !== null);
3797
+ const nextApproval = order && !settled ? rampNextApproval(order) : null;
3798
+ const stopped = order ? rampOrderStopped(order) : false;
3799
+ const receive = order ? rampOrderReceive(order) : null;
3800
+ const waitsOnRecovery = order?.failure?.resolutionAction === "set_up_recovery";
3801
+ const payBy = view?.action?.kind === "FIAT_PAYMENT" && view.expiresAt ? Date.parse(view.expiresAt) : null;
3802
+ useRenderAt(view?.canSign && view.expiresAt ? Date.parse(view.expiresAt) : null);
3803
+ const stepsOpen = !!onStepSelect && step !== "status" && !isLoading && startingQuoteId === null && trustingAsset === null && walletBusy === null;
3822
3804
  const stepTitle = {
3823
- input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
3824
- loading_quote: "Finding best route",
3825
- select_route: "Select provider",
3805
+ input: onramp ? "Buy crypto" : "Sell crypto",
3806
+ loading_quote: "Finding the best route",
3807
+ select_route: "Choose a route",
3826
3808
  contact: "Your details",
3827
- status: direction === "onramp" ? "Complete your deposit" : "Complete your withdrawal",
3809
+ status: order?.state === "completed" ? "Transfer completed" : onramp ? "Your purchase" : "Your sale",
3828
3810
  error: "Something went wrong"
3829
3811
  };
3830
3812
  const stepSubtitle = {
3831
- input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
3832
- loading_quote: "Comparing providers in real time\u2026",
3833
- select_route: "All prices include fees",
3834
- // What this step actually asks for is whatever the route declared, and that
3835
- // is rarely a name and an email: an Abroad off-ramp asks where to send the
3836
- // money, not who you are. Say which, rather than asserting the Bridge case
3837
- // over every provider.
3838
- contact: `${provider || "This provider"} needs ${listOf(requiredFields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
3839
- status: workflowContent ? "Follow the next step below to continue" : `Finish the flow at ${provider || "the provider"} to continue`,
3813
+ input: onramp ? "Pick what you receive and how much you pay" : "Pick what you sell and how much",
3814
+ loading_quote: "Comparing routes in real time\u2026",
3815
+ select_route: "All amounts include fees",
3816
+ contact: `This route needs ${listOf(fields.filter((f) => !f.optional).map((f) => f.label.toLowerCase()))} to continue`,
3817
+ status: view?.title ?? "Processing",
3840
3818
  error: "Please try again"
3841
3819
  };
3842
3820
  return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
@@ -3877,128 +3855,124 @@ function RampWidgetTemplate({
3877
3855
  " of ",
3878
3856
  flowSteps.length
3879
3857
  ] }),
3880
- /* @__PURE__ */ jsx(
3881
- "div",
3858
+ /* @__PURE__ */ jsx("ol", { className: "pollar-ramp-steps-track", children: flowSteps.map((label, i) => /* @__PURE__ */ jsx("li", { className: "pollar-ramp-steps-item", "aria-current": i === flowStepIndex ? "step" : void 0, children: /* @__PURE__ */ jsxs(
3859
+ "button",
3882
3860
  {
3883
- className: "pollar-ramp-steps-track",
3884
- role: "progressbar",
3885
- "aria-valuenow": flowStepIndex + 1,
3886
- "aria-valuemin": 1,
3887
- "aria-valuemax": flowSteps.length,
3888
- "aria-label": flowSteps[flowStepIndex],
3889
- children: flowSteps.map((label, i) => /* @__PURE__ */ jsx("span", { className: "pollar-ramp-steps-segment", "data-done": i <= flowStepIndex || void 0 }, label))
3861
+ type: "button",
3862
+ className: "pollar-ramp-steps-step",
3863
+ "data-done": i <= flowStepIndex || void 0,
3864
+ disabled: !stepsOpen || i >= flowStepIndex,
3865
+ onClick: () => onStepSelect?.(label),
3866
+ children: [
3867
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-steps-segment" }),
3868
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-steps-name", children: label })
3869
+ ]
3890
3870
  }
3891
- )
3871
+ ) }, label)) })
3892
3872
  ] }),
3893
- step === "input" && savedRamps.length > 0 && /* @__PURE__ */ jsxs("section", { className: "pollar-ramp-payment pollar-ramp-saved", "aria-label": "Pending operations", children: [
3894
- /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your pending operations are saved. Starting another route does not cancel them." }),
3895
- savedRamps.map((ramp) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-saved-row", children: [
3896
- /* @__PURE__ */ jsxs(
3897
- "button",
3898
- {
3899
- type: "button",
3900
- className: "pollar-btn-secondary",
3901
- disabled: isLoading,
3902
- onClick: () => onResumeRamp?.(ramp.txId),
3903
- children: [
3904
- "Resume ",
3905
- ramp.provider,
3906
- " \xB7 ",
3907
- ramp.txId.slice(-8)
3908
- ]
3909
- }
3910
- ),
3911
- onDismissRamp && /* @__PURE__ */ jsx(
3912
- "button",
3913
- {
3914
- type: "button",
3915
- className: "pollar-btn-secondary",
3916
- disabled: isLoading,
3917
- "aria-label": `Remove ${ramp.provider} ${ramp.txId.slice(-8)} from pending list`,
3918
- onClick: () => onDismissRamp(ramp.txId),
3919
- children: "Remove"
3920
- }
3921
- )
3922
- ] }, ramp.txId))
3873
+ step === "input" && pendingOrders.length > 0 && /* @__PURE__ */ jsxs("section", { className: "pollar-ramp-payment pollar-ramp-saved", "aria-label": "Pending operations", children: [
3874
+ /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "These operations are waiting for you. Starting another does not cancel them." }),
3875
+ pendingOrders.map((pending) => /* @__PURE__ */ jsx("div", { className: "pollar-ramp-saved-row", children: /* @__PURE__ */ jsxs(
3876
+ "button",
3877
+ {
3878
+ type: "button",
3879
+ className: "pollar-btn-secondary",
3880
+ disabled: isLoading,
3881
+ onClick: () => onResumeOrder(pending.id),
3882
+ children: [
3883
+ "Resume ",
3884
+ pending.direction === "ONRAMP" ? "purchase" : "sale",
3885
+ " \xB7 ",
3886
+ pending.id.slice(-8)
3887
+ ]
3888
+ }
3889
+ ) }, pending.id))
3923
3890
  ] }),
3924
3891
  step === "input" && /* @__PURE__ */ jsxs(Fragment, { children: [
3925
- noticeMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", role: "status", children: noticeMsg }),
3926
3892
  /* @__PURE__ */ jsxs("div", { className: "pollar-tabs", children: [
3927
- /* @__PURE__ */ jsx(
3928
- "button",
3929
- {
3930
- type: "button",
3931
- className: "pollar-tab",
3932
- "data-active": direction === "onramp",
3933
- onClick: () => onDirectionChange("onramp"),
3934
- children: "Buy"
3935
- }
3936
- ),
3937
- /* @__PURE__ */ jsx(
3938
- "button",
3939
- {
3940
- type: "button",
3941
- className: "pollar-tab",
3942
- "data-active": direction === "offramp",
3943
- onClick: () => onDirectionChange("offramp"),
3944
- children: "Sell"
3945
- }
3946
- )
3893
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-tab", "data-active": onramp, onClick: () => onDirectionChange("ONRAMP"), children: "Buy" }),
3894
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-tab", "data-active": !onramp, onClick: () => onDirectionChange("OFFRAMP"), children: "Sell" })
3947
3895
  ] }),
3948
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3949
- /* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-country", children: "Country" }),
3950
- countriesLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-input pollar-select-loading", children: [
3951
- /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3952
- /* @__PURE__ */ jsx("span", { children: "Loading countries\u2026" })
3953
- ] }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsx(
3954
- "select",
3955
- {
3956
- id: "pollar-ramp-country",
3957
- className: "pollar-ramp-input",
3958
- value: country,
3959
- onChange: (e) => onCountryChange(e.target.value),
3960
- children: countries.map((c) => /* @__PURE__ */ jsxs("option", { value: c.code, children: [
3961
- flagEmoji(c.code),
3962
- " ",
3963
- c.code,
3964
- c.currency ? ` \u2014 ${c.currency}` : ""
3965
- ] }, c.code))
3966
- }
3967
- )
3968
- ] }),
3969
- routeSelector,
3970
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3971
- /* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-amount", children: [
3972
- "Amount",
3973
- currency ? ` (${currency})` : ""
3896
+ routesLoading ? /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-input pollar-select-loading", children: [
3897
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
3898
+ /* @__PURE__ */ jsx("span", { children: "Loading routes\u2026" })
3899
+ ] }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: noChains ? "This app has no chains enabled, so there is nothing to buy or sell yet." : "No ramp routes are available for this app yet." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3900
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3901
+ /* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-country", children: "Country" }),
3902
+ /* @__PURE__ */ jsx(
3903
+ "select",
3904
+ {
3905
+ id: "pollar-ramp-country",
3906
+ className: "pollar-ramp-input",
3907
+ value: country,
3908
+ onChange: (e) => onCountryChange(e.target.value),
3909
+ children: countries.map((c) => /* @__PURE__ */ jsxs("option", { value: c.country, children: [
3910
+ flagEmoji(c.country),
3911
+ " ",
3912
+ c.country
3913
+ ] }, c.country))
3914
+ }
3915
+ )
3974
3916
  ] }),
3975
- /* @__PURE__ */ jsx(
3976
- "input",
3977
- {
3978
- id: "pollar-ramp-amount",
3979
- type: "number",
3980
- className: "pollar-ramp-input",
3981
- placeholder: "0.00",
3982
- value: amount,
3983
- min: "0",
3984
- onChange: (e) => onAmountChange(e.target.value)
3985
- }
3986
- ),
3987
- errorMsg && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: errorMsg })
3917
+ fiats.length > 1 && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3918
+ /* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-fiat", children: onramp ? "Pay with" : "Receive in" }),
3919
+ /* @__PURE__ */ jsx(
3920
+ "select",
3921
+ {
3922
+ id: "pollar-ramp-fiat",
3923
+ className: "pollar-ramp-input",
3924
+ value: fiat ? fiatKey(fiat) : "",
3925
+ onChange: (e) => onFiatChange(e.target.value),
3926
+ children: fiats.map((f) => /* @__PURE__ */ jsxs("option", { value: fiatKey(f), children: [
3927
+ f.currency,
3928
+ " \xB7 ",
3929
+ rampRailLabel(f.rail, { language: formLanguage() })
3930
+ ] }, fiatKey(f)))
3931
+ }
3932
+ )
3933
+ ] }),
3934
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3935
+ /* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", htmlFor: "pollar-ramp-asset", children: onramp ? "Receive" : "Sell" }),
3936
+ /* @__PURE__ */ jsx(
3937
+ "select",
3938
+ {
3939
+ id: "pollar-ramp-asset",
3940
+ className: "pollar-ramp-input",
3941
+ value: asset ? assetKey3(asset) : "",
3942
+ onChange: (e) => onAssetChange(e.target.value),
3943
+ children: assets.map((a) => /* @__PURE__ */ jsxs("option", { value: assetKey3(a), children: [
3944
+ a.code,
3945
+ " on ",
3946
+ CHAIN_NAME[a.chain] ?? a.chain
3947
+ ] }, assetKey3(a)))
3948
+ }
3949
+ )
3950
+ ] }),
3951
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
3952
+ /* @__PURE__ */ jsx(
3953
+ AmountField,
3954
+ {
3955
+ id: "pollar-ramp-amount",
3956
+ label: onramp ? "You pay" : "You sell",
3957
+ value: amount,
3958
+ onChange: onAmountChange,
3959
+ unit: sendUnit,
3960
+ available: onramp ? void 0 : sellAvailable
3961
+ }
3962
+ ),
3963
+ errorMsg && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: errorMsg })
3964
+ ] })
3988
3965
  ] }),
3989
- /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
3990
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
3991
- /* @__PURE__ */ jsx(
3992
- "button",
3993
- {
3994
- type: "button",
3995
- className: "pollar-btn-primary",
3996
- disabled: !amount || isLoading || countriesLoading || countries.length === 0,
3997
- onClick: onFindRoute,
3998
- children: "Find best route"
3999
- }
4000
- )
4001
- ] })
3966
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx(
3967
+ "button",
3968
+ {
3969
+ type: "button",
3970
+ className: "pollar-btn-primary",
3971
+ disabled: !amount || !fiat || !asset || isLoading || routesLoading,
3972
+ onClick: onFindRoute,
3973
+ children: "Find best route"
3974
+ }
3975
+ ) })
4002
3976
  ] }),
4003
3977
  step === "loading_quote" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-loading", children: [
4004
3978
  /* @__PURE__ */ jsx("div", { className: "pollar-spinner pollar-ramp-loading-spinner" }),
@@ -4008,33 +3982,44 @@ function RampWidgetTemplate({
4008
3982
  ] }, i))
4009
3983
  ] }),
4010
3984
  step === "select_route" && /* @__PURE__ */ jsxs(Fragment, { children: [
4011
- noticeMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: noticeMsg }),
4012
3985
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-route-list", children: [
4013
3986
  quotes.map((q, i) => /* @__PURE__ */ jsx(
4014
3987
  RouteDisplay,
4015
3988
  {
4016
3989
  quote: q,
4017
- busy: startingQuoteId != null && q.quoteId === startingQuoteId,
3990
+ best: i === 0,
3991
+ busy: startingQuoteId === q.quoteId,
4018
3992
  disabled: startingQuoteId != null && q.quoteId !== startingQuoteId,
4019
3993
  onSelect: onSelectQuote
4020
3994
  },
4021
- i
3995
+ q.quoteId
4022
3996
  )),
4023
- kycRequired.map((r) => /* @__PURE__ */ jsx(
4024
- LockedRouteDisplay,
3997
+ rejected.map((r, i) => /* @__PURE__ */ jsx(
3998
+ RejectedRouteDisplay,
4025
3999
  {
4026
- requirement: r,
4027
- disabled: startingQuoteId != null,
4028
- onVerify: onVerifyRoute
4000
+ rejection: r,
4001
+ disabled: startingQuoteId != null || trustingAsset !== null || walletBusy !== null,
4002
+ trusting: trustKey(r) === trustingAsset,
4003
+ walletBusy: walletBusy === i,
4004
+ custodyChoice,
4005
+ onVerify: onVerifyRoute,
4006
+ onTrust: onTrustAsset,
4007
+ onWalletStep: onWalletStep && ((step2, custody) => onWalletStep(step2, i, custody))
4029
4008
  },
4030
- `${r.rampProviderId}:${r.corridorId}`
4009
+ i
4031
4010
  ))
4032
4011
  ] }),
4012
+ quotes.length === 0 && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: rampNoRouteText(rejected) }),
4013
+ notice && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
4014
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-hidden": true }),
4015
+ " ",
4016
+ notice
4017
+ ] }),
4033
4018
  errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
4034
- /* @__PURE__ */ jsx("button", { type: "button", className: errorMsg ? "pollar-btn-primary" : "pollar-btn-secondary", onClick: onBack, children: "Back" })
4019
+ (errorMsg || !quotes.length) && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onBack, children: "Back" })
4035
4020
  ] }),
4036
4021
  step === "contact" && /* @__PURE__ */ jsxs(Fragment, { children: [
4037
- requiredFields.map((f) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
4022
+ fields.map((f) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
4038
4023
  /* @__PURE__ */ jsxs("label", { className: "pollar-ramp-label", children: [
4039
4024
  f.label,
4040
4025
  f.optional && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-optional", children: " (optional)" })
@@ -4056,184 +4041,178 @@ function RampWidgetTemplate({
4056
4041
  type: f.type,
4057
4042
  className: "pollar-ramp-input",
4058
4043
  value: fieldValues[f.key] ?? "",
4059
- placeholder: placeholderFor(f, requiredFields, fieldValues),
4044
+ placeholder: placeholderFor(f, fields, fieldValues),
4060
4045
  autoComplete: f.type === "email" ? "email" : "off",
4061
4046
  onChange: (e) => onFieldChange(f.key, e.target.value)
4062
4047
  }
4063
4048
  ),
4064
4049
  f.hint && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-hint", children: f.hint }),
4065
- f.type === "email" && (fieldValues[f.key] ?? "").trim() !== "" && !isFieldValid(f, fieldValues[f.key]) && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: "Enter a valid email address." })
4050
+ rampFieldError(f, fieldValues[f.key]) && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-field-error", children: rampFieldError(f, fieldValues[f.key]) })
4066
4051
  ] }, f.key)),
4067
- /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4068
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onBack, children: "Back" }),
4069
- /* @__PURE__ */ jsx(
4052
+ errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-field-error", children: errorMsg }),
4053
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx(
4054
+ "button",
4055
+ {
4056
+ type: "button",
4057
+ className: "pollar-btn-primary",
4058
+ disabled: fields.some((f) => !rampFieldValid(f, fieldValues[f.key])) || isLoading,
4059
+ onClick: onContactContinue,
4060
+ children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
4061
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
4062
+ "Starting\u2026"
4063
+ ] }) : "Continue"
4064
+ }
4065
+ ) })
4066
+ ] }),
4067
+ step === "status" && order && view && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment", children: [
4068
+ /* @__PURE__ */ jsx("ol", { className: "pollar-ramp-legs", "aria-label": "Progress", children: order.legs.map((leg) => {
4069
+ const status = rampLegStatus(leg);
4070
+ const hash = leg.onchainTxHash;
4071
+ const note = rampLegNote(leg);
4072
+ return /* @__PURE__ */ jsxs("li", { className: "pollar-ramp-leg", "data-status": status, children: [
4073
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-leg-dot", "aria-hidden": true }),
4074
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-leg-title", children: rampLegTitle(leg) }),
4075
+ status === "active" && !settled && /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-label": "In progress" }),
4076
+ hash && /* @__PURE__ */ jsxs(
4077
+ "a",
4078
+ {
4079
+ className: "pollar-ramp-leg-link",
4080
+ href: explorerTxUrl2(legChain(leg), hash, network),
4081
+ target: "_blank",
4082
+ rel: "noopener noreferrer",
4083
+ children: [
4084
+ hash.slice(0, 6),
4085
+ "\u2026",
4086
+ hash.slice(-4)
4087
+ ]
4088
+ }
4089
+ ),
4090
+ note && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-leg-note", children: note }),
4091
+ status === "active" && !settled && !!leg.steps?.length && /* @__PURE__ */ jsx("ol", { className: "pollar-ramp-steps", children: leg.steps.map((s) => /* @__PURE__ */ jsxs("li", { className: "pollar-ramp-step", "data-status": s.status, children: [
4092
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-step-title", children: rampStepTitle(s, leg) }),
4093
+ /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-step-state", children: [
4094
+ s.status === "confirming" && /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-hidden": true }),
4095
+ s.hash ? /* @__PURE__ */ jsx(
4096
+ "a",
4097
+ {
4098
+ className: "pollar-ramp-leg-link",
4099
+ href: explorerTxUrl2(stepChain(s.kind, leg), s.hash, network),
4100
+ target: "_blank",
4101
+ rel: "noopener noreferrer",
4102
+ children: rampStepStatusText(s)
4103
+ }
4104
+ ) : rampStepStatusText(s)
4105
+ ] })
4106
+ ] }, s.kind)) }),
4107
+ running === leg && !settled && rampLegTimeLeft(leg, now) !== null && /* @__PURE__ */ jsx("span", { className: "pollar-ramp-leg-eta", children: timeLeftText(rampLegTimeLeft(leg, now)) })
4108
+ ] }, leg.index);
4109
+ }) }),
4110
+ nextApproval && !order.pendingAction && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-next", children: [
4111
+ "Next: ",
4112
+ nextApproval,
4113
+ ". You approve it on this device when it comes up."
4114
+ ] }),
4115
+ receive && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4116
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: order.state === "completed" ? "You received" : "You receive" }),
4117
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-value", children: [
4118
+ receive.estimated && "~",
4119
+ receive.amount,
4120
+ " ",
4121
+ receive.unit
4122
+ ] })
4123
+ ] }),
4124
+ order.failure && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4125
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "What to do" }),
4126
+ /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: rampOrderResolutionText(order) }),
4127
+ stopped && !settled && waitsOnRecovery && recoveryMissing !== false && onSetUpRecovery && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onSetUpRecovery, children: "Set up recovery" }),
4128
+ stopped && !settled && waitsOnRecovery && recoveryMissing === true && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Once it is set up, the order goes on by itself." }),
4129
+ stopped && !settled && order.failure.resolutionOwner === "END_USER" && onRetryOrder && /* @__PURE__ */ jsx(
4070
4130
  "button",
4071
4131
  {
4072
4132
  type: "button",
4073
4133
  className: "pollar-btn-primary",
4074
- disabled: requiredFields.some((f) => !isFieldValid(f, fieldValues[f.key])) || isLoading,
4075
- onClick: onContactContinue,
4076
- children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
4134
+ disabled: retrying || waitsOnRecovery && recoveryMissing === true,
4135
+ onClick: onRetryOrder,
4136
+ children: retrying ? /* @__PURE__ */ jsxs(Fragment, { children: [
4077
4137
  /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
4078
- "Starting\u2026"
4079
- ] }) : "Continue"
4138
+ " Trying again\u2026"
4139
+ ] }) : "Try again"
4080
4140
  }
4081
- )
4082
- ] })
4083
- ] }),
4084
- step === "status" && workflowContent,
4085
- step === "status" && onDismissCurrentRamp && txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-explore", children: [
4086
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", disabled: completing, onClick: onDismissCurrentRamp, children: "Remove from pending list" }),
4087
- /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "This only hides the operation in this tab. To request cancellation, open the provider link above when available or contact the provider\u2019s support. Payments and refunds must be confirmed by the provider." })
4088
- ] }),
4089
- step === "status" && onExploreRoutes && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-explore", children: [
4090
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", disabled: completing, onClick: onExploreRoutes, children: txStatus === "completed" || txStatus === "failed" ? "Start another operation" : "Try another route" }),
4091
- txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your current operation stays saved so you can resume it later." })
4092
- ] }),
4093
- step === "status" && !workflowContent && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment", children: [
4094
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4095
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Provider" }),
4096
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsx("code", { children: provider }) })
4097
- ] }),
4098
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4099
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Status" }),
4100
- /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-value", children: [
4101
- /* @__PURE__ */ jsx("code", { style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 }, children: txStatus ? STATUS_LABEL[txStatus] : "Processing" }),
4102
- txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-label": "Checking status" })
4103
- ] })
4104
- ] }),
4105
- fundsInTransit && txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4106
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Funds in transit" }),
4107
- /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your money is safe in your own wallet, waiting for the next step of the transfer." }),
4108
- onRetryTransit && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetryTransit, disabled: retrying, children: retrying ? "Retrying..." : "Retry now" })
4109
- ] }),
4110
- settledHash && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4111
- /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-payment-label", children: [
4112
- chainName,
4113
- " tx"
4114
- ] }),
4141
+ ),
4115
4142
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-value", style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4116
4143
  /* @__PURE__ */ jsxs("code", { style: { flex: 1, wordBreak: "break-all" }, children: [
4117
- settledHash.slice(0, 8),
4118
- "\u2026",
4119
- settledHash.slice(-8)
4144
+ "Order ",
4145
+ order.id
4120
4146
  ] }),
4121
- /* @__PURE__ */ jsx(CopyButton, { value: settledHash, label: "Copy transaction hash" }),
4122
- explorerUrl && /* @__PURE__ */ jsx(
4123
- "a",
4124
- {
4125
- href: explorerUrl,
4126
- target: "_blank",
4127
- rel: "noopener noreferrer",
4128
- className: "pollar-copy-btn",
4129
- "aria-label": `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
4130
- title: `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
4131
- children: /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
4132
- /* @__PURE__ */ jsx(
4133
- "path",
4134
- {
4135
- d: "M6 3H3v8h8V8",
4136
- stroke: "currentColor",
4137
- strokeWidth: "1.5",
4138
- strokeLinecap: "round",
4139
- strokeLinejoin: "round"
4140
- }
4141
- ),
4142
- /* @__PURE__ */ jsx(
4143
- "path",
4144
- {
4145
- d: "M8.5 2.5h3v3M11 3L6.5 7.5",
4146
- stroke: "currentColor",
4147
- strokeWidth: "1.5",
4148
- strokeLinecap: "round",
4149
- strokeLinejoin: "round"
4150
- }
4151
- )
4152
- ] })
4153
- }
4154
- )
4147
+ /* @__PURE__ */ jsx(CopyButton, { value: order.id, label: "Copy order id" })
4155
4148
  ] })
4156
4149
  ] }),
4157
- depositInstructions?.scannable && txStatus !== "completed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4150
+ !settled && payBy !== null && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: payBy > Date.now() ? `Pay before ${new Date(payBy).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}.` : "This payment window has closed. If you have not paid, start a new operation." }),
4151
+ !settled && view.scannable && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4158
4152
  /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Payment QR" }),
4159
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: depositInstructions.scannable.image.inlineSafe ? /* @__PURE__ */ jsx(
4153
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: view.scannable.image.inlineSafe ? /* @__PURE__ */ jsx(
4160
4154
  "div",
4161
4155
  {
4162
4156
  className: "pollar-ramp-qr",
4163
4157
  "aria-label": "Payment QR",
4164
- dangerouslySetInnerHTML: { __html: depositInstructions.scannable.image.data }
4158
+ dangerouslySetInnerHTML: { __html: view.scannable.image.data }
4165
4159
  }
4166
4160
  ) : /* @__PURE__ */ jsx(
4167
4161
  "img",
4168
4162
  {
4169
- src: scannableImageSrc(depositInstructions.scannable.image),
4163
+ src: scannableImageSrc(view.scannable.image),
4170
4164
  alt: "Payment QR",
4171
4165
  style: { width: "100%", maxWidth: 220, height: "auto", display: "block", margin: "0 auto" }
4172
4166
  }
4173
4167
  ) })
4174
4168
  ] }),
4175
- depositInstructions?.scannable?.payload && depositInstructions.scannable.payloadLabel && txStatus !== "completed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4176
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: depositInstructions.scannable.payloadLabel }),
4169
+ !settled && view.codeMissing && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: "The payment code could not be shown. Refresh; if it still does not appear, contact this app's support." }),
4170
+ !settled && view.scannable?.payload && view.scannable.payloadLabel && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4171
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: view.scannable.payloadLabel }),
4177
4172
  /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4178
- /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: depositInstructions.scannable.payload }),
4179
- /* @__PURE__ */ jsx(
4180
- CopyButton,
4181
- {
4182
- value: depositInstructions.scannable.payload,
4183
- label: `Copy ${depositInstructions.scannable.payloadLabel}`
4184
- }
4185
- )
4173
+ /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: view.scannable.payload }),
4174
+ /* @__PURE__ */ jsx(CopyButton, { value: view.scannable.payload, label: `Copy ${view.scannable.payloadLabel}` })
4186
4175
  ] }) })
4187
4176
  ] }),
4188
- depositInstructions && txStatus !== "completed" && depositInstructions.fields.map((f) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4189
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: f.label }),
4190
- /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: f.copyable ? /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4191
- /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: f.value }),
4192
- /* @__PURE__ */ jsx(CopyButton, { value: f.value, label: `Copy ${f.label}` })
4193
- ] }) : f.type === "url" ? /* @__PURE__ */ jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", children: f.value }) : /* @__PURE__ */ jsx("span", { children: displayValue(f) }) })
4194
- ] }, f.key)),
4195
- tosUrl && !depositInstructions && txStatus !== "completed" && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenTos, children: [
4196
- "Accept terms at ",
4197
- provider
4198
- ] }),
4199
- kycUrl && !depositInstructions && txStatus !== "completed" && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-btn-primary", onClick: onOpenKyc, children: [
4200
- "Continue at ",
4201
- provider
4202
- ] }),
4203
- kycBlocking && onboardingStatus === "awaiting_provider" && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
4204
- provider,
4205
- " is still setting up your account for this payment method. Your verification is complete and there is nothing left for you to do \u2014 nothing has been sent, and this will update on its own once ",
4206
- provider,
4207
- " is ready."
4208
- ] }),
4209
- kycBlocking && onboardingStatus !== "awaiting_provider" && /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
4210
- provider,
4211
- " needs to verify your identity before this payout. Nothing has been sent yet \u2014 complete verification with ",
4212
- provider,
4213
- ", and this will update on its own."
4214
- ] }),
4215
- kycJustApproved && /* @__PURE__ */ jsxs(Fragment, { children: [
4216
- /* @__PURE__ */ jsxs("p", { className: "pollar-ramp-payment-note", children: [
4217
- provider,
4218
- " approved your verification. Request a new quote to continue \u2014 the previous one was consumed."
4219
- ] }),
4220
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onRetry, children: "Request a new quote" })
4221
- ] }),
4222
- canComplete && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: completing, onClick: onCompleteWithdraw, children: completing ? /* @__PURE__ */ jsxs(Fragment, { children: [
4177
+ !settled && view.details.map((d) => /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
4178
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: d.label }),
4179
+ /* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4180
+ /* @__PURE__ */ jsx("code", { style: { flex: 1, wordBreak: "break-all" }, children: d.value }),
4181
+ /* @__PURE__ */ jsx(CopyButton, { value: d.value, label: `Copy ${d.label}` })
4182
+ ] }) })
4183
+ ] }, d.label)),
4184
+ !settled && view.action?.kind === "FIAT_PAYMENT" && onCheckPayment && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: checkingPayment, onClick: onCheckPayment, children: checkingPayment ? /* @__PURE__ */ jsxs(Fragment, { children: [
4223
4185
  /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }),
4224
- "Submitting\u2026"
4225
- ] }) : "Check verification and continue" }),
4226
- errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg }),
4227
- /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: txStatus === "completed" ? "Done" : "Close" })
4186
+ " Checking your payment..."
4187
+ ] }) : "I've paid" }),
4188
+ !settled && view.canSign && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", disabled: signing, onClick: onSign, children: signing ? "Waiting for your wallet\u2026" : "Authorize" }),
4189
+ !settled && view.action?.kind === "SIGNATURE" && !view.canSign && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Preparing a new signature request\u2026" }),
4190
+ !settled && view.canApprove && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onApprove, children: "Approve on this device" }),
4191
+ !settled && view.link && /* @__PURE__ */ jsx(
4192
+ "a",
4193
+ {
4194
+ className: "pollar-btn-primary pollar-ramp-hosted-link",
4195
+ href: view.link,
4196
+ target: "_blank",
4197
+ rel: "noopener noreferrer",
4198
+ children: view.linkLabel
4199
+ }
4200
+ ),
4201
+ !settled && order.pendingAction?.kind === "KYC" && !view.link && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: view.note ?? "The provider is reviewing your details. This updates on its own." }),
4202
+ pollTrouble && !settled && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Having trouble reaching the server. Retrying\u2026" }),
4203
+ errorMsg && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-field-error", children: errorMsg }),
4204
+ /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-explore", children: [
4205
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onNewOrder, children: "Start another operation" }),
4206
+ !stopped && /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "This one keeps going; you can resume it later." })
4207
+ ] })
4228
4208
  ] }),
4229
- step === "error" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment", children: [
4230
- /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", style: { color: "var(--pollar-error-text)" }, children: errorMsg ?? "Unexpected error." }),
4209
+ step === "error" && /* @__PURE__ */ jsxs(Fragment, { children: [
4210
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: errorMsg ?? "Something went wrong." }),
4231
4211
  /* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
4232
4212
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Close" }),
4233
4213
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onRetry, children: "Try again" })
4234
4214
  ] })
4235
- ] }),
4236
- /* @__PURE__ */ jsx(PollarModalFooter, {})
4215
+ ] })
4237
4216
  ] });
4238
4217
  }
4239
4218
 
@@ -4727,776 +4706,580 @@ function ProviderRegistrationModal({
4727
4706
  }
4728
4707
  ) });
4729
4708
  }
4730
-
4731
- // src/components/ramp-widget/ramp-quotes.ts
4732
- function unavailableRampMessage(country, unavailable) {
4733
- if (!unavailable.length) return `No ramp providers available for ${country} yet.`;
4734
- return unavailable.map(
4735
- ({ provider, code }) => code === "SDK_RAMPS_PROVIDER_NOT_CONFIGURED" ? `${provider} is not configured correctly. Contact the app administrator to enable this provider.` : `${provider} is temporarily unavailable. Please try again later.`
4736
- ).join(" ");
4737
- }
4738
- async function quoteRampOptions(client, query, routes) {
4739
- const matching = routes.filter(
4740
- (route) => route.country === query.country && route.direction === query.direction && route.fiatCurrency === query.currency
4741
- );
4742
- const responses = await Promise.allSettled([
4743
- client.getRampsQuote(query),
4744
- ...matching.map((route) => client.getRampsQuote({ ...query, routeId: route.routeId, chain: route.asset.chain }))
4745
- ]);
4746
- const results = responses.flatMap((response) => response.status === "fulfilled" ? [response.value] : []);
4747
- if (!results.length) throw responses[0].reason;
4748
- const quotes = /* @__PURE__ */ new Map();
4749
- const requirements = /* @__PURE__ */ new Map();
4750
- const unavailable = /* @__PURE__ */ new Map();
4751
- for (const result of results) {
4752
- for (const quote of result.quotes) {
4753
- const key = JSON.stringify([quote.provider, quote.route?.routeId, quote.route?.asset.chain, quote.rail, quote.protocol]);
4754
- if (!quotes.has(key)) quotes.set(key, quote);
4755
- }
4756
- for (const requirement of result.requirementsRequired ?? [])
4757
- requirements.set(`${requirement.rampProviderId}:${requirement.corridorId}`, requirement);
4758
- for (const item of result.unavailable ?? []) unavailable.set(item.provider, item);
4759
- }
4760
- const failed = responses.find((response) => response.status === "rejected");
4761
- if (!quotes.size && !requirements.size && failed?.status === "rejected") throw failed.reason;
4762
- return {
4763
- quotes: [...quotes.values()],
4764
- requirementsRequired: [...requirements.values()],
4765
- unavailable: [...unavailable.values()]
4766
- };
4767
- }
4768
- var TERMINAL = ["completed", "failed"];
4769
- var BANK_TYPES = ["CLABE", "PIX", "PSE", "ACH", "BREB"];
4770
- function isBankType(value) {
4771
- return BANK_TYPES.some((type) => type === value);
4772
- }
4773
- function requiredFieldsOf(quote) {
4774
- return "requiredFields" in quote ? quote.requiredFields : [];
4775
- }
4776
- function flowStepsOf(quotes, selected) {
4777
- const needsDetails = selected != null ? requiredFieldsOf(selected).length > 0 : quotes.some((q) => requiredFieldsOf(q).length > 0);
4778
- return needsDetails ? ["Amount", "Provider", "Details", "Complete"] : ["Amount", "Provider", "Complete"];
4779
- }
4780
- var STEP_LABEL = {
4781
- input: "Amount",
4782
- loading_quote: "Amount",
4783
- select_route: "Provider",
4784
- contact: "Details",
4785
- status: "Complete"
4786
- };
4787
- function brokenLimitOf(amount, quote) {
4788
- const minAmount = "minAmount" in quote ? quote.minAmount : void 0;
4789
- const maxAmount = "maxAmount" in quote ? quote.maxAmount : void 0;
4790
- if (!Number.isFinite(amount)) return null;
4791
- if (minAmount != null && amount < minAmount) return { limit: "min", value: minAmount };
4792
- if (maxAmount != null && amount > maxAmount) return { limit: "max", value: maxAmount };
4793
- return null;
4794
- }
4795
- function limitMessage(limit, value, currency) {
4796
- return `The ${limit === "min" ? "minimum" : "maximum"} amount for this route is ${value} ${currency}.`;
4797
- }
4709
+ var PAYMENT_CHECK_EVERY_MS = 1e4;
4710
+ var PAYMENT_CHECK_WINDOW_MS = 2 * 6e4;
4798
4711
  var RAMP_ERROR_MESSAGES = {
4799
- SDK_RAMPS_QUOTE_EXPIRED: "This quote expired. Request a new one and try again.",
4800
- SDK_RAMPS_ASSET_NOT_ENABLED: "This currency is not available for that route right now.",
4801
- SDK_RAMPS_KYC_REQUIRED: "The provider needs to verify your identity before continuing.",
4802
- SDK_RAMPS_WALLET_UNSUPPORTED: "This wallet type cannot be used for this ramp.",
4803
- SDK_RAMPS_PROVIDER_NOT_CONFIGURED: "This ramp provider is not configured for this app yet.",
4804
- SDK_RAMPS_ANCHOR_ERROR: "The provider rejected the request. Please try again in a moment.",
4805
- SDK_RAMPS_BRIDGE_ERROR: "The provider rejected the request. Please try again in a moment.",
4806
- SDK_RAMPS_ETHERFUSE_ERROR: "The provider rejected the request. Please try again in a moment.",
4807
- SDK_RAMPS_INSUFFICIENT_BALANCE: "This wallet does not hold enough to cover that amount. Try a smaller one.",
4808
- // Nothing moved on-chain, so the balance is untouched. Almost always no XLM
4809
- // for the network fee on a wallet the app does not sponsor.
4810
- SDK_RAMPS_ONCHAIN_SUBMIT_FAILED: "The network rejected the transaction, so nothing was sent. The wallet may need XLM for the fee."
4712
+ SDK_RAMPS_QUOTE_EXPIRED: "This quote expired. Find a route again.",
4713
+ SDK_RAMPS_QUOTE_NOT_FOUND: "This quote is no longer available. Find a route again.",
4714
+ SDK_RAMPS_ACTION_STALE: "This step changed. It was refreshed; try again.",
4715
+ SDK_RAMPS_TX_NOT_FOUND: "This operation was not found.",
4716
+ SDK_RAMPS_WALLET_NOT_READY: "Approve the new wallet account on your device, then try again."
4811
4717
  };
4812
- function rampErrorMessage(e, fallback) {
4813
- const code = typeof e === "object" && e !== null && "code" in e ? e.code : void 0;
4814
- if (typeof code !== "string") return e instanceof Error ? e.message : fallback;
4815
- const raw = typeof e === "object" && e !== null && "body" in e ? e.body : void 0;
4816
- const body = typeof raw === "object" && raw !== null ? raw : {};
4817
- const limit = "limit" in body ? body.limit : void 0;
4818
- const value = "limitAmount" in body ? body.limitAmount : void 0;
4819
- const currency = "limitCurrency" in body ? body.limitCurrency : void 0;
4820
- if ((limit === "min" || limit === "max") && typeof value === "number" && typeof currency === "string") {
4821
- return limitMessage(limit, value, currency);
4822
- }
4823
- return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
4824
- }
4825
- function readRampStorage(key) {
4826
- try {
4827
- return typeof window === "undefined" ? null : window.sessionStorage.getItem(key);
4828
- } catch {
4829
- return null;
4830
- }
4831
- }
4832
- function writeRampStorage(key, value) {
4833
- try {
4834
- if (typeof window === "undefined") return;
4835
- if (value === null) window.sessionStorage.removeItem(key);
4836
- else window.sessionStorage.setItem(key, value);
4837
- } catch {
4718
+ function rampErrorMessage(error, fallback) {
4719
+ const unavailable = appUnavailableMessage(error);
4720
+ if (unavailable) return unavailable;
4721
+ if (isPollarApiError(error)) {
4722
+ const known = RAMP_ERROR_MESSAGES[error.code];
4723
+ if (known) return known;
4724
+ const message = error.body.message;
4725
+ if (typeof message === "string" && message && !/^[A-Z0-9_]+$/.test(message)) return message;
4838
4726
  }
4727
+ return fallback;
4839
4728
  }
4840
- function savedRampsOf(key) {
4841
- try {
4842
- const value = JSON.parse(readRampStorage(`${key}:saved`) ?? "[]");
4843
- return Array.isArray(value) ? value.filter((item) => typeof item?.txId === "string" && typeof item?.provider === "string") : [];
4844
- } catch {
4845
- return [];
4846
- }
4729
+ function routeKey(quote) {
4730
+ const node = (n) => n.kind === "asset" ? `${n.code}:${n.chain}` : `${n.currency}:${n.rail}`;
4731
+ return quote.legs.map((leg) => `${leg.kind}:${node(leg.from)}>${node(leg.to)}`).join("|");
4847
4732
  }
4848
- function hiddenRampsOf(key) {
4733
+ var DECIMAL = /^\d+(\.\d+)?$/;
4734
+ async function signLegWithWallet(client, orderId, actionId) {
4735
+ const lent = [];
4736
+ const lend = (chain, encoding, handler) => {
4737
+ try {
4738
+ lent.push(client.registerRampSigningHandler(chain, encoding, handler));
4739
+ } catch {
4740
+ }
4741
+ };
4742
+ lend("STELLAR", "xdr", async (action) => {
4743
+ const wallet = client.getWallet();
4744
+ if (!wallet) throw new Error("Connect the Stellar wallet this step was built for.");
4745
+ assertRampStellarSigner(action, wallet.address);
4746
+ const outcome = await client.signTx(action.payload.value);
4747
+ if (client.getWallet()?.address !== wallet.address)
4748
+ throw new Error("The connected wallet changed. Refresh before signing.");
4749
+ if (outcome.status === "signed") return outcome.signedXdr;
4750
+ throw new Error(
4751
+ outcome.status === "error" ? outcome.message ?? outcome.details ?? "Signing was cancelled." : "Signing was cancelled."
4752
+ );
4753
+ });
4754
+ lend("SOLANA", "solana-v0-json", async (action) => {
4755
+ const payload = JSON.parse(action.payload.value);
4756
+ const solanaWallet = () => client.getWallets().find((w) => w.chain === "SOLANA" && w.custody === "external");
4757
+ const wallet = solanaWallet();
4758
+ if (!wallet || payload.from !== wallet.address || !payload.unsignedTransaction)
4759
+ throw new Error("This step must be signed by the Solana wallet it was built for. Reconnect that wallet.");
4760
+ const signed = await client.signSolanaTransaction(payload.unsignedTransaction);
4761
+ if (solanaWallet()?.address !== wallet.address) throw new Error("The connected wallet changed. Refresh before signing.");
4762
+ return signed;
4763
+ });
4849
4764
  try {
4850
- const value = JSON.parse(readRampStorage(`${key}:hidden`) ?? "[]");
4851
- return Array.isArray(value) ? value.filter((id) => typeof id === "string") : [];
4852
- } catch {
4853
- return [];
4765
+ return await client.signRampLeg(orderId, actionId);
4766
+ } finally {
4767
+ for (const release of lent) release();
4854
4768
  }
4855
4769
  }
4856
- function explorerTxUrl2(chain, hash, network) {
4857
- if (!hash) return null;
4858
- const testnet = network === "testnet";
4859
- if (chain === "SOLANA") return `https://explorer.solana.com/tx/${hash}${testnet ? "?cluster=devnet" : ""}`;
4860
- if (chain === "POLYGON") return `https://${testnet ? "amoy." : ""}polygonscan.com/tx/${hash}`;
4861
- return `https://stellar.expert/explorer/${testnet ? "testnet" : "public"}/tx/${hash}`;
4862
- }
4863
4770
  function RampWidget({ onClose }) {
4864
- const { getClient, wallet, styles, network } = usePollar();
4865
- const walletAddress = wallet?.address ?? "";
4771
+ const {
4772
+ getClient,
4773
+ styles,
4774
+ network,
4775
+ openCosignModal,
4776
+ setCosignNextStep,
4777
+ openRecoveryModal,
4778
+ recovery,
4779
+ recoveryLocked,
4780
+ appConfig,
4781
+ cosign,
4782
+ refreshWalletBalance
4783
+ } = usePollar();
4866
4784
  const client = getClient();
4867
4785
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
4868
4786
  const [step, setStep] = useState("input");
4869
- const [direction, setDirection] = useState("onramp");
4870
- const [amount, setAmount] = useState("");
4871
- const [currency, setCurrency] = useState("");
4787
+ const [direction, setDirection] = useState("ONRAMP");
4788
+ const [routes, setRoutes] = useState([]);
4789
+ const [routesLoading, setRoutesLoading] = useState(true);
4872
4790
  const [country, setCountry] = useState("");
4873
- const [fieldValues, setFieldValues] = useState({});
4874
- const setFieldValue = (key, value) => setFieldValues((v) => ({ ...v, [key]: value }));
4875
- const [legacyCountries, setCountries] = useState([]);
4876
- const [countriesLoading, setCountriesLoading] = useState(true);
4877
- const [refreshing, setRefreshing] = useState(false);
4791
+ const [fiatChoice, setFiatChoice] = useState("");
4792
+ const [assetChoice, setAssetChoice] = useState("");
4793
+ const [amount, setAmount] = useState("");
4878
4794
  const [quotes, setQuotes] = useState([]);
4879
- const [requirementsRequired, setRequirementsRequired] = useState([]);
4880
- const [selectedQuote, setSelectedQuote] = useState(null);
4795
+ const [rejected, setRejected] = useState([]);
4796
+ const [selected, setSelected] = useState(null);
4797
+ const [startingQuoteId, setStartingQuoteId] = useState(null);
4798
+ const [fieldValues, setFieldValues] = useState({});
4799
+ const [order, setOrder] = useState(null);
4800
+ const [pendingOrders, setPendingOrders] = useState([]);
4881
4801
  const [isLoading, setIsLoading] = useState(false);
4882
- const [pendingKyc, setPendingKyc] = useState(null);
4883
- const [noticeMsg, setNoticeMsg] = useState(null);
4884
- const kycAttempt = useRef(null);
4885
- useEffect(
4886
- () => () => {
4887
- kycAttempt.current = null;
4888
- },
4889
- []
4890
- );
4891
- const [txId, setTxId] = useState(null);
4892
- const [provider, setProvider] = useState("");
4893
- const [kycUrl, setKycUrl] = useState(null);
4894
- const [tosUrl, setTosUrl] = useState(null);
4895
- const [kycPending, setKycPending] = useState(false);
4896
- const [kycApproved, setKycApproved] = useState(false);
4897
- const [onboardingStatus, setOnboardingStatus] = useState(void 0);
4898
- const [txStatus, setTxStatus] = useState(null);
4899
- const [stellarTxHash, setStellarTxHash] = useState(null);
4900
- const [txHash, setTxHash] = useState(null);
4901
- const [txChain, setTxChain] = useState(null);
4902
- const [fundsInTransit, setFundsInTransit] = useState(false);
4903
- const [retrying, setRetrying] = useState(false);
4904
- const [depositInstructions, setDepositInstructions] = useState(null);
4905
- const [workflow, setWorkflow] = useState(null);
4906
- const workflowRef = useRef(null);
4907
- const storageKey = `pollar:ramp:${client.apiKey}:${walletAddress}`;
4908
- const [savedRamps, setSavedRamps] = useState([]);
4909
- const [legacySignature, setLegacySignature] = useState(void 0);
4910
- function acceptWorkflow(incoming) {
4911
- if (hiddenRampsOf(storageKey).includes(incoming.txId)) return false;
4912
- const next = mergeRampSnapshot(workflowRef.current, incoming);
4913
- workflowRef.current = next;
4914
- setWorkflow(next);
4915
- if (next === incoming) {
4916
- const pending = savedRampsOf(storageKey).filter((item) => item.txId !== incoming.txId);
4917
- if (!["completed", "failed", "refunded"].includes(incoming.lifecycleState ?? incoming.status))
4918
- pending.push({
4919
- txId: incoming.txId,
4920
- provider: "provider" in incoming && typeof incoming.provider === "string" ? incoming.provider : provider || "Ramp"
4921
- });
4922
- writeRampStorage(`${storageKey}:saved`, JSON.stringify(pending));
4923
- writeRampStorage(storageKey, incoming.txId);
4924
- writeRampStorage(`${storageKey}:selection`, null);
4925
- setSavedRamps(pending);
4926
- }
4927
- return next === incoming;
4928
- }
4929
- const [routes, setRoutes] = useState([]);
4930
- const countries = mergeRampCountries(legacyCountries, routes);
4931
- useEffect(() => {
4932
- if (!country && countries[0]) {
4933
- setCountry(countries[0].code);
4934
- setCurrency(countries[0].currency ?? "");
4935
- }
4936
- }, [countries, country]);
4937
- useEffect(() => {
4938
- let active = true;
4939
- void client.getRampRoutes().then((result) => {
4940
- if (active) setRoutes(result.routes);
4941
- }).catch(() => {
4942
- });
4943
- return () => {
4944
- active = false;
4945
- };
4946
- }, [client]);
4947
- const [completing, setCompleting] = useState(false);
4948
- const completionLocked = useRef(false);
4802
+ const [refreshing, setRefreshing] = useState(false);
4803
+ const [signing, setSigning] = useState(false);
4949
4804
  const [errorMsg, setErrorMsg] = useState(null);
4805
+ const [verifying, setVerifying] = useState(null);
4806
+ const [trustingAsset, setTrustingAsset] = useState(null);
4807
+ const [walletBusy, setWalletBusy] = useState(null);
4808
+ const [retrying, setRetrying] = useState(false);
4809
+ const [awaitingAccount, setAwaitingAccount] = useState(null);
4810
+ const [pollTrouble, setPollTrouble] = useState(false);
4811
+ const [pollKey, setPollKey] = useState(0);
4812
+ const [paymentCheckUntil, setPaymentCheckUntil] = useState(null);
4813
+ const promptedApproval = useRef(null);
4814
+ const custodyChoice = appConfig.application?.walletCustodyMode === "USER_CHOICE";
4815
+ const appChains = appConfig.application?.chains;
4816
+ const verifyAttempt = useRef(null);
4817
+ const countries = useMemo(() => rampCountries(routes), [routes]);
4818
+ const fiats = countries.find((c) => c.country === country)?.fiats ?? [];
4819
+ const fiat = fiats.find((f) => fiatKey(f) === fiatChoice) ?? fiats[0] ?? null;
4820
+ const assets = useMemo(() => {
4821
+ const seen = /* @__PURE__ */ new Map();
4822
+ for (const route of routes)
4823
+ if (fiat && route.fiat.country === country && fiatKey(route.fiat) === fiatKey(fiat))
4824
+ seen.set(assetKey3(route.asset), route.asset);
4825
+ return [...seen.values()];
4826
+ }, [routes, country, fiat]);
4827
+ const asset = assets.find((a) => assetKey3(a) === assetChoice) ?? assets[0] ?? null;
4828
+ const fields = selected ? rampQuoteFields(selected) : [];
4829
+ const sellAvailable = useWalletAvailable(
4830
+ direction === "OFFRAMP" && asset ? { chain: asset.chain, code: asset.code, issuer: asset.identifier } : null
4831
+ );
4950
4832
  useEffect(() => {
4951
- if (typeof window === "undefined" || !walletAddress) return;
4952
- setSavedRamps(savedRampsOf(storageKey));
4953
- if (readRampStorage(`${storageKey}:selection`) === "1") return;
4954
- const hidden = hiddenRampsOf(storageKey);
4955
- const savedId = [new URLSearchParams(window.location.search).get("rampTxId"), readRampStorage(storageKey)].find(
4956
- (id) => id && !hidden.includes(id)
4957
- );
4958
- if (!savedId) return;
4959
- let active = true;
4960
- void client.getRampTransaction(savedId).then(async (result) => {
4961
- if (!active || result.transactionVersion === void 0) return;
4962
- setDirection(result.direction);
4963
- await applyResult(result);
4964
- }).catch(() => {
4965
- if (active)
4966
- setErrorMsg("Could not restore the saved ramp. Retry opening the modal; do not create a replacement order.");
4967
- });
4968
- return () => {
4969
- active = false;
4970
- };
4971
- }, [client, storageKey, walletAddress]);
4972
- async function resumeSavedRamp(id) {
4973
- if (completionLocked.current) return;
4974
- setIsLoading(true);
4975
- setErrorMsg(null);
4833
+ if (direction === "OFFRAMP") void refreshWalletBalance();
4834
+ }, [direction, refreshWalletBalance]);
4835
+ async function loadRoutes(next) {
4836
+ setRoutesLoading(true);
4976
4837
  try {
4977
- const result = await client.getRampTransaction(id);
4978
- if (result.transactionVersion === void 0) throw new Error("This saved ramp cannot be restored.");
4979
- setDirection(result.direction);
4980
- await applyResult(result);
4838
+ const { routes: found } = await client.getRampRoutes(next);
4839
+ setRoutes(found);
4840
+ const first = rampCountries(found)[0]?.country ?? "";
4841
+ setCountry((current) => found.some((r) => r.fiat.country === current) ? current : first);
4981
4842
  } catch (error) {
4982
- setErrorMsg(rampErrorMessage(error, "Could not resume the saved ramp."));
4843
+ setRoutes([]);
4844
+ setErrorMsg(rampErrorMessage(error, "Could not load the ramp routes."));
4983
4845
  } finally {
4984
- setIsLoading(false);
4846
+ setRoutesLoading(false);
4985
4847
  }
4986
4848
  }
4987
- const directionRef = useRef(direction);
4988
- directionRef.current = direction;
4989
- function applySettlement(tx) {
4990
- if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
4991
- if (tx.txHash) setTxHash(tx.txHash);
4992
- if (tx.chain) setTxChain(tx.chain);
4993
- setFundsInTransit(tx.fundsInTransit === true);
4994
- }
4995
- async function handleRetryTransit() {
4996
- if (!txId || retrying) return;
4997
- setRetrying(true);
4998
- setErrorMsg(null);
4849
+ async function loadPendingOrders() {
4999
4850
  try {
5000
- const tx = await client.retryRampTransaction(txId);
5001
- setTxStatus(tx.status);
5002
- applySettlement(tx);
5003
- } catch (e) {
5004
- setErrorMsg(e instanceof Error && e.message ? e.message : "Could not retry the transfer. Try again in a moment.");
5005
- } finally {
5006
- setRetrying(false);
4851
+ const page = await client.listRampOrders({ open: "true", limit: 5 });
4852
+ setPendingOrders(page.orders);
4853
+ } catch {
4854
+ setPendingOrders([]);
5007
4855
  }
5008
4856
  }
5009
- const workflowTerminal = !!workflow && ["completed", "failed", "refunded"].includes(workflow.lifecycleState ?? workflow.status);
5010
4857
  useEffect(() => {
5011
- if (step !== "status" || !txId) return;
5012
- if (workflowTerminal || txStatus && TERMINAL.includes(txStatus)) return;
5013
- let active = true;
5014
- const id = setInterval(async () => {
5015
- try {
5016
- const tx = await client.getRampTransaction(txId);
5017
- if (!active) return;
5018
- if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
5019
- setTxStatus(tx.status);
5020
- applySettlement(tx);
5021
- const pending = "pendingSignature" in tx ? tx.pendingSignature : void 0;
5022
- if (pending) setLegacySignature(pending);
5023
- if (tx.kycUrl) setKycUrl(tx.kycUrl);
5024
- if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
5025
- if (TERMINAL.includes(tx.status)) clearInterval(id);
5026
- } catch {
5027
- }
5028
- }, 5e3);
5029
- return () => {
5030
- active = false;
5031
- clearInterval(id);
5032
- };
5033
- }, [step, txId, txStatus, workflowTerminal, client]);
4858
+ void loadRoutes(direction);
4859
+ }, [client, direction]);
5034
4860
  useEffect(() => {
5035
- if (workflow || step !== "status" || !kycPending || kycApproved || provider.toLowerCase() !== "abroad") return;
5036
- if (onboardingStatus === "awaiting_provider") return;
5037
- let active = true;
5038
- const check = async () => {
5039
- try {
5040
- const { hasApproved } = await client.getRampKycStatus();
5041
- if (active && hasApproved) setKycApproved(true);
5042
- } catch {
5043
- }
5044
- };
5045
- void check();
5046
- const id = setInterval(check, 1e4);
5047
- return () => {
5048
- active = false;
5049
- clearInterval(id);
5050
- };
5051
- }, [step, kycPending, kycApproved, onboardingStatus, workflow, client, provider]);
5052
- async function loadCountries(resetSelection) {
5053
- setCountriesLoading(true);
5054
- try {
5055
- const { countries: list } = await client.getRampCountries();
5056
- setCountries(list);
5057
- const available = mergeRampCountries(list, routes);
5058
- const first = available[0];
5059
- const stillValid = available.some((c) => c.code === country);
5060
- if (first && (resetSelection || !stillValid)) {
5061
- setCountry(first.code);
5062
- if (first.currency) setCurrency(first.currency);
5063
- }
5064
- } catch {
5065
- setCountries([]);
5066
- } finally {
5067
- setCountriesLoading(false);
5068
- }
5069
- }
4861
+ void loadPendingOrders();
4862
+ }, [client]);
4863
+ const orderId = order?.id ?? null;
5070
4864
  useEffect(() => {
5071
- loadCountries(true);
5072
- }, []);
5073
- function handleCountryChange(code) {
5074
- setCountry(code);
5075
- const match = countries.find((c) => c.code === code);
5076
- if (match?.currency) setCurrency(match.currency);
5077
- }
5078
- async function handleRefresh() {
5079
- if (refreshing) return;
5080
- setRefreshing(true);
5081
- try {
5082
- await loadCountries(false);
5083
- if (step === "select_route") {
5084
- const result = await quoteRampOptions(
5085
- client,
5086
- {
5087
- country,
5088
- amount: Number(amount),
5089
- amountExact: amount,
5090
- currency,
5091
- direction
5092
- },
5093
- routes
5094
- );
5095
- setQuotes(result.quotes ?? []);
5096
- setRequirementsRequired(result.requirementsRequired ?? []);
5097
- } else if (step === "status" && txId) {
5098
- const tx = await client.getRampTransaction(txId);
5099
- if (tx.transactionVersion !== void 0 && !acceptWorkflow(tx)) return;
5100
- setTxStatus(tx.status);
5101
- applySettlement(tx);
5102
- if (tx.kycUrl) setKycUrl(tx.kycUrl);
5103
- if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
5104
- }
5105
- } catch {
5106
- } finally {
5107
- setRefreshing(false);
5108
- }
5109
- }
5110
- function resetToInput({ keepMessage = false } = {}) {
5111
- writeRampStorage(storageKey, null);
5112
- writeRampStorage(`${storageKey}:selection`, "1");
5113
- setStep("input");
5114
- setQuotes([]);
5115
- setRequirementsRequired([]);
5116
- setSelectedQuote(null);
5117
- setFieldValues({});
5118
- setWorkflow(null);
5119
- workflowRef.current = null;
5120
- setLegacySignature(void 0);
5121
- setTxId(null);
5122
- setProvider("");
5123
- setKycUrl(null);
5124
- setTosUrl(null);
5125
- setTxStatus(null);
5126
- setStellarTxHash(null);
5127
- setTxHash(null);
5128
- setTxChain(null);
5129
- setFundsInTransit(false);
5130
- setDepositInstructions(null);
5131
- setKycPending(false);
5132
- setKycApproved(false);
5133
- setNoticeMsg(null);
5134
- if (!keepMessage) setErrorMsg(null);
5135
- }
5136
- function dismissSavedRamp(id) {
5137
- if (completionLocked.current) return;
5138
- const pending = savedRampsOf(storageKey).filter((ramp) => ramp.txId !== id);
5139
- writeRampStorage(`${storageKey}:saved`, JSON.stringify(pending));
5140
- writeRampStorage(`${storageKey}:hidden`, JSON.stringify([.../* @__PURE__ */ new Set([...hiddenRampsOf(storageKey), id])]));
5141
- if (readRampStorage(storageKey) === id) writeRampStorage(storageKey, null);
5142
- setSavedRamps(pending);
5143
- if (txId === id) resetToInput();
5144
- setNoticeMsg(
5145
- "Removed from this tab\u2019s pending list. The operation may still be active at the provider; no payment was cancelled or refunded."
5146
- );
5147
- }
5148
- async function resumeWithSignature(id, ps) {
5149
- const outcome = await client.signTx(ps.unsignedXdr, { skipSponsorship: ps.action === "sep10" });
5150
- if (outcome.status === "cosign_pending") {
5151
- setErrorMsg("Approve the signature on your device, then resume.");
5152
- setStep("error");
4865
+ if (step !== "status" || !orderId) return;
4866
+ const controller = new AbortController();
4867
+ client.pollRampOrder(orderId, {
4868
+ signal: controller.signal,
4869
+ onUpdate: (next) => {
4870
+ setOrder(next);
4871
+ setPollTrouble(false);
4872
+ },
4873
+ onError: () => setPollTrouble(true)
4874
+ }).catch((error) => {
4875
+ if (!controller.signal.aborted) setErrorMsg(rampErrorMessage(error, "This operation could not be read."));
4876
+ });
4877
+ return () => controller.abort();
4878
+ }, [client, step, orderId, pollKey]);
4879
+ const awaitingPayment = step === "status" && order?.pendingAction?.kind === "FIAT_PAYMENT";
4880
+ useEffect(() => {
4881
+ if (paymentCheckUntil === null || !orderId) return;
4882
+ if (!awaitingPayment) {
4883
+ setPaymentCheckUntil(null);
4884
+ setPollKey((key) => key + 1);
5153
4885
  return;
5154
4886
  }
5155
- if (outcome.status !== "signed") {
5156
- setErrorMsg(outcome.message ?? outcome.details ?? "Signing was cancelled.");
5157
- setStep("error");
4887
+ const timer = setInterval(() => {
4888
+ if (Date.now() >= paymentCheckUntil) {
4889
+ setPaymentCheckUntil(null);
4890
+ return;
4891
+ }
4892
+ client.checkRampOrder(orderId).then(setOrder).catch(() => void 0);
4893
+ }, PAYMENT_CHECK_EVERY_MS);
4894
+ return () => clearInterval(timer);
4895
+ }, [client, orderId, awaitingPayment, paymentCheckUntil]);
4896
+ const approval = order?.pendingAction?.kind === "COSIGN_APPROVAL" ? order.pendingAction.actionId : null;
4897
+ useEffect(() => {
4898
+ if (!approval || promptedApproval.current === approval) return;
4899
+ promptedApproval.current = approval;
4900
+ void client.cosign.pending().catch(() => void 0).then(() => openCosignModal());
4901
+ }, [approval, client, openCosignModal]);
4902
+ const nextApproval = step === "status" && order ? rampNextApproval(order) : null;
4903
+ useEffect(() => {
4904
+ setCosignNextStep(nextApproval);
4905
+ }, [nextApproval, setCosignNextStep]);
4906
+ useEffect(() => () => setCosignNextStep(null), [setCosignNextStep]);
4907
+ const resolvedRequest = cosign.lastResolved;
4908
+ useEffect(() => {
4909
+ if (!awaitingAccount || resolvedRequest?.id !== awaitingAccount.requestId) return;
4910
+ setAwaitingAccount(null);
4911
+ if (resolvedRequest.status !== "APPROVED") {
4912
+ setErrorMsg("The new account was not approved, so this route cannot start.");
5158
4913
  return;
5159
4914
  }
5160
- const result = await client.submitRampSignature(id, {
5161
- signedXdr: outcome.signedXdr,
5162
- action: ps.action
5163
- });
5164
- await applyResult(result);
5165
- }
5166
- async function applyResult(result) {
5167
- if ("transactionVersion" in result && result.transactionVersion !== void 0 && !acceptWorkflow(result)) return;
5168
- setTxId(result.txId);
5169
- setProvider(result.provider);
5170
- setLegacySignature(result.pendingSignature);
5171
- setKycUrl(result.kycUrl ?? null);
5172
- setTosUrl(result.tosUrl ?? null);
5173
- setOnboardingStatus(result.onboardingStatus);
5174
- setKycPending(result.kycRequired === true);
5175
- if (result.kycRequired) setKycApproved(false);
5176
- setTxStatus(result.status);
5177
- setStellarTxHash(result.stellarTxHash ?? null);
5178
- setDepositInstructions(result.depositInstructions ?? null);
5179
- setStep("status");
5180
- }
5181
- async function loadQuotes() {
5182
- const result = await quoteRampOptions(
5183
- client,
5184
- {
5185
- country,
5186
- amount: Number(amount),
5187
- amountExact: amount,
5188
- currency,
5189
- direction
5190
- },
5191
- routes
5192
- );
5193
- const list = result.quotes ?? [];
5194
- const locked = result.requirementsRequired ?? [];
5195
- if (list.length === 0 && locked.length === 0) {
5196
- setErrorMsg(unavailableRampMessage(country, result.unavailable ?? []));
5197
- setStep("error");
5198
- return false;
5199
- }
5200
- setQuotes(list);
5201
- setRequirementsRequired(locked);
5202
- return true;
4915
+ void resumeRoute(awaitingAccount.routeKey);
4916
+ }, [awaitingAccount, resolvedRequest]);
4917
+ function handleDirectionChange(next) {
4918
+ setDirection(next);
4919
+ setErrorMsg(null);
4920
+ setAwaitingAccount(null);
5203
4921
  }
5204
4922
  async function handleFindRoute() {
4923
+ if (!fiat || !asset) return [];
4924
+ if (!DECIMAL.test(amount.trim()) || Number(amount) <= 0) {
4925
+ setErrorMsg("Enter an amount greater than zero.");
4926
+ return [];
4927
+ }
5205
4928
  setStep("loading_quote");
5206
- setIsLoading(true);
5207
4929
  setErrorMsg(null);
4930
+ setSelected(null);
5208
4931
  try {
5209
- if (!await loadQuotes()) return;
4932
+ const result = await client.quoteRamp({
4933
+ direction,
4934
+ fiat: { currency: fiat.currency, rail: fiat.rail, country: fiat.country },
4935
+ asset: { code: asset.code, identifier: asset.identifier, chain: asset.chain },
4936
+ amount: { value: amount.trim(), denomination: "in" }
4937
+ });
4938
+ setQuotes(result.quotes);
4939
+ setRejected(result.rejected);
5210
4940
  setStep("select_route");
5211
- } catch (e) {
5212
- setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
4941
+ return result.quotes;
4942
+ } catch (error) {
4943
+ if (isPollarApiError(error) && error.code === "SDK_RAMPS_NO_ROUTE") {
4944
+ setQuotes([]);
4945
+ setRejected(Array.isArray(error.body.rejected) ? error.body.rejected : []);
4946
+ setStep("select_route");
4947
+ return [];
4948
+ }
4949
+ setErrorMsg(rampErrorMessage(error, "Could not find a route. Please try again."));
5213
4950
  setStep("error");
4951
+ return [];
4952
+ }
4953
+ }
4954
+ function handleVerifyRoute(requirement) {
4955
+ setErrorMsg(null);
4956
+ verifyAttempt.current = requirement;
4957
+ setVerifying(requirement);
4958
+ }
4959
+ function closeVerification() {
4960
+ verifyAttempt.current = null;
4961
+ setVerifying(null);
4962
+ }
4963
+ function verificationDone(requirement) {
4964
+ if (verifyAttempt.current !== requirement) return;
4965
+ closeVerification();
4966
+ void handleFindRoute();
4967
+ }
4968
+ async function handleTrustAsset(asset2) {
4969
+ if (trustingAsset) return;
4970
+ setTrustingAsset(`${asset2.code}:${asset2.issuer}`);
4971
+ setErrorMsg(null);
4972
+ try {
4973
+ const outcome = await client.setTrustline(asset2);
4974
+ if (outcome.status === "error") {
4975
+ setErrorMsg(rampTrustlineErrorText(outcome, asset2.code));
4976
+ return;
4977
+ }
4978
+ if (outcome.status === "cosign_pending") {
4979
+ setErrorMsg(`Approve the ${asset2.code} trustline, then refresh the routes.`);
4980
+ openCosignModal();
4981
+ return;
4982
+ }
4983
+ await client.refreshAssets().catch(() => {
4984
+ });
4985
+ await handleFindRoute();
4986
+ } catch (error) {
4987
+ setErrorMsg(rampErrorMessage(error, `Could not add the ${asset2.code} trustline. Please try again.`));
5214
4988
  } finally {
5215
- setIsLoading(false);
4989
+ setTrustingAsset(null);
5216
4990
  }
5217
4991
  }
5218
- function handleSelectQuote(quote) {
5219
- setSelectedQuote(quote);
4992
+ async function handleWalletStep(walletStep, index, custody) {
4993
+ if (walletBusy !== null) return;
4994
+ setWalletBusy(index);
5220
4995
  setErrorMsg(null);
5221
- setNoticeMsg(null);
5222
- const broken = brokenLimitOf(Number(amount), quote);
5223
- if (broken) {
5224
- setErrorMsg(limitMessage(broken.limit, broken.value, currency));
5225
- return;
4996
+ try {
4997
+ const { recoveryRequired } = await client.runRampWalletStep(walletStep, custody);
4998
+ if (recoveryRequired) {
4999
+ setErrorMsg("Add a way to recover your wallet, then find the route again.");
5000
+ openRecoveryModal();
5001
+ return;
5002
+ }
5003
+ await handleFindRoute();
5004
+ } catch (error) {
5005
+ const code = isPollarApiError(error) ? error.code : null;
5006
+ if (code === "SDK_RAMPS_WALLET_NOT_READY") {
5007
+ setErrorMsg("Approve the new account, then find the route again.");
5008
+ openCosignModal();
5009
+ return;
5010
+ }
5011
+ setErrorMsg(
5012
+ code === "SDK_WALLET_PROVISIONING_DISABLED" ? "This app does not create wallets." : rampErrorMessage(error, "Could not prepare the wallet. Please try again.")
5013
+ );
5014
+ } finally {
5015
+ setWalletBusy(null);
5226
5016
  }
5227
- const fields = requiredFieldsOf(quote);
5228
- const missing = fields.some((f) => !f.optional && !(fieldValues[f.key] ?? "").trim());
5229
- if (fields.length > 0 && missing) {
5230
- setStep("contact");
5017
+ }
5018
+ async function resumeRoute(key) {
5019
+ const same = (await handleFindRoute()).find((q) => routeKey(q) === key);
5020
+ if (same) handleSelectQuote(same, true);
5021
+ else setErrorMsg("That route is no longer available. Pick another one.");
5022
+ }
5023
+ function handleSelectQuote(quote, fresh = false) {
5024
+ if (!fresh && Date.parse(quote.expiresAt) <= Date.now()) {
5025
+ void resumeRoute(routeKey(quote));
5231
5026
  return;
5232
5027
  }
5233
- void startRamp(quote);
5234
- }
5235
- function handleContactContinue() {
5236
- if (selectedQuote) void startRamp(selectedQuote);
5028
+ setSelected(quote);
5029
+ setErrorMsg(null);
5030
+ const missing = rampQuoteFields(quote).some((f) => !f.optional && !(fieldValues[f.key] ?? "").trim());
5031
+ if (missing) setStep("contact");
5032
+ else void startOrder(quote, fresh);
5237
5033
  }
5238
- async function startRamp(quote) {
5034
+ async function startOrder(quote, fresh = false) {
5239
5035
  setIsLoading(true);
5036
+ setStartingQuoteId(quote.quoteId);
5240
5037
  setErrorMsg(null);
5038
+ setAwaitingAccount(null);
5241
5039
  try {
5242
- const body = {
5040
+ const payee = Object.fromEntries(
5041
+ rampQuoteFields(quote).map((f) => [f.key, (fieldValues[f.key] ?? "").trim()]).filter(([, value]) => value)
5042
+ );
5043
+ const created = await client.createRampOrder({
5243
5044
  quoteId: quote.quoteId,
5244
- amount: quote.fiatAmount ?? Number(amount),
5245
- ...quote.terms ? { amountExact: quote.terms.fiatAmount } : {},
5246
- currency,
5247
- country,
5248
- ...walletAddress ? { walletAddress } : {}
5249
- };
5250
- const extraFields = {};
5251
- for (const f of requiredFieldsOf(quote)) {
5252
- const val = (fieldValues[f.key] ?? "").trim();
5253
- if (!val) continue;
5254
- if (f.bankType) {
5255
- if (isBankType(f.bankType)) body.bankDetails = { type: f.bankType, value: val };
5256
- } else if (f.key === "email" || f.key === "fullName" || f.key === "taxId" || f.key === "qrCode") body[f.key] = val;
5257
- else extraFields[f.key] = val;
5045
+ ...Object.keys(payee).length ? { payee } : {}
5046
+ });
5047
+ setOrder(created);
5048
+ setStep("status");
5049
+ } catch (error) {
5050
+ if (isPollarApiError(error) && error.code === "SDK_RAMPS_WALLET_NOT_READY") {
5051
+ const pending = await client.cosign.pending().catch(() => []);
5052
+ const request = pending.find((r) => r.intent === "add_accounts");
5053
+ if (request) setAwaitingAccount({ routeKey: routeKey(quote), requestId: request.id });
5054
+ else setErrorMsg(rampErrorMessage(error, "Could not start the operation. Please try again."));
5055
+ openCosignModal();
5056
+ setStep("select_route");
5057
+ return;
5258
5058
  }
5259
- if (quote.route)
5260
- body.fields = Object.fromEntries(
5261
- requiredFieldsOf(quote).map((field) => [field.key, (fieldValues[field.key] ?? "").trim()]).filter(([, value]) => value)
5262
- );
5263
- else if (Object.keys(extraFields).length > 0) body.fields = extraFields;
5264
- const onrampBody = {
5265
- quoteId: body.quoteId,
5266
- amount: body.amount,
5267
- currency: body.currency,
5268
- country: body.country,
5269
- ...body.amountExact !== void 0 ? { amountExact: body.amountExact } : {},
5270
- ...body.walletAddress !== void 0 ? { walletAddress: body.walletAddress } : {},
5271
- ...body.email !== void 0 ? { email: body.email } : {},
5272
- ...body.fullName !== void 0 ? { fullName: body.fullName } : {},
5273
- ...body.fields !== void 0 ? { fields: body.fields } : {}
5274
- };
5275
- const result = direction === "onramp" ? await client.createOnRamp(onrampBody) : await client.createOffRamp(body);
5276
- await applyResult(result);
5277
- } catch (e) {
5278
- const requirement = requiredRampKyc(e);
5279
- if (requirement) {
5280
- setStep(requiredFieldsOf(quote).length ? "contact" : "select_route");
5281
- setErrorMsg(
5282
- requirement.type === "KYC" ? "Identity verification is required before continuing." : "A few more details are required before continuing."
5283
- );
5284
- kycAttempt.current = requirement;
5285
- setPendingKyc(requirement);
5059
+ if (isPollarApiError(error) && error.code === "SDK_RAMPS_QUOTE_EXPIRED" && !fresh) {
5060
+ void resumeRoute(routeKey(quote));
5286
5061
  return;
5287
5062
  }
5288
- setErrorMsg(rampErrorMessage(e, "Failed to start the ramp."));
5289
- setStep("error");
5063
+ setErrorMsg(rampErrorMessage(error, "Could not start the operation. Please try again."));
5064
+ if (isPollarApiError(error) && error.code === "VALIDATION_ERROR" && rampQuoteFields(quote).length) setStep("contact");
5065
+ else setStep("select_route");
5290
5066
  } finally {
5291
5067
  setIsLoading(false);
5068
+ setStartingQuoteId(null);
5292
5069
  }
5293
5070
  }
5294
- async function requoteAfterKyc(completed = "KYC") {
5295
- setStep("loading_quote");
5071
+ async function handleSign() {
5072
+ const action = order?.pendingAction;
5073
+ if (!order || action?.kind !== "SIGNATURE" || signing) return;
5074
+ setSigning(true);
5075
+ setErrorMsg(null);
5076
+ try {
5077
+ setOrder(await signLegWithWallet(client, order.id, action.actionId));
5078
+ } catch (error) {
5079
+ setErrorMsg(rampErrorMessage(error, error instanceof Error && error.message ? error.message : "Signing was cancelled."));
5080
+ setOrder(await client.getRampOrder(order.id).catch(() => order));
5081
+ } finally {
5082
+ setSigning(false);
5083
+ }
5084
+ }
5085
+ async function handleResumeOrder(id) {
5296
5086
  setIsLoading(true);
5297
5087
  setErrorMsg(null);
5298
- setSelectedQuote(null);
5299
5088
  try {
5300
- if (!await loadQuotes()) return;
5301
- setNoticeMsg(
5302
- completed === "KYC" ? "Your identity is verified. Prices may have changed, so choose a route to continue." : "Thanks, your details are saved. Prices may have changed, so choose a route to continue."
5303
- );
5304
- setStep("select_route");
5305
- } catch (e) {
5306
- setErrorMsg(rampErrorMessage(e, "Failed to fetch quotes."));
5307
- setStep("error");
5089
+ const found = await client.getRampOrder(id);
5090
+ setDirection(found.direction);
5091
+ setOrder(found);
5092
+ setStep("status");
5093
+ } catch (error) {
5094
+ setErrorMsg(rampErrorMessage(error, "Could not resume the operation."));
5308
5095
  } finally {
5309
5096
  setIsLoading(false);
5310
5097
  }
5311
5098
  }
5312
- function handleVerifyRoute(requirement) {
5313
- const attempt = pendingFromQuote(requirement);
5099
+ function handleNewOrder() {
5100
+ setAwaitingAccount(null);
5101
+ setOrder(null);
5102
+ setSelected(null);
5103
+ setQuotes([]);
5104
+ setRejected([]);
5314
5105
  setErrorMsg(null);
5315
- setNoticeMsg(null);
5316
- kycAttempt.current = attempt;
5317
- setPendingKyc(attempt);
5106
+ setStep("input");
5107
+ void loadPendingOrders();
5318
5108
  }
5319
- function handleOpenKyc() {
5320
- if (kycUrl) window.open(kycUrl, "_blank", "noopener,noreferrer");
5109
+ function handleBack() {
5110
+ setErrorMsg(null);
5111
+ setAwaitingAccount(null);
5112
+ setStep(step === "contact" ? "select_route" : "input");
5321
5113
  }
5322
- function handleOpenTos() {
5323
- if (tosUrl) window.open(tosUrl, "_blank", "noopener,noreferrer");
5114
+ function handleStepSelect(target) {
5115
+ setErrorMsg(null);
5116
+ setAwaitingAccount(null);
5117
+ setStep(target === "Route" ? "select_route" : "input");
5324
5118
  }
5325
- async function handleLegacySignature() {
5326
- if (!txId || !legacySignature || completionLocked.current) return;
5327
- completionLocked.current = true;
5328
- setCompleting(true);
5119
+ async function handleCheckPayment() {
5120
+ if (!order || paymentCheckUntil !== null) return;
5329
5121
  setErrorMsg(null);
5122
+ setPaymentCheckUntil(Date.now() + PAYMENT_CHECK_WINDOW_MS);
5330
5123
  try {
5331
- await resumeWithSignature(txId, legacySignature);
5332
- } catch (e) {
5333
- setErrorMsg(rampErrorMessage(e, "Failed to submit the signature."));
5334
- setStep("error");
5335
- } finally {
5336
- completionLocked.current = false;
5337
- setCompleting(false);
5124
+ setOrder(await client.checkRampOrder(order.id));
5125
+ } catch (error) {
5126
+ setPaymentCheckUntil(null);
5127
+ setErrorMsg(rampErrorMessage(error, "Could not check the payment. Please try again."));
5338
5128
  }
5339
5129
  }
5340
- async function handleCompleteWithdraw() {
5341
- if (!txId || completionLocked.current) return;
5342
- completionLocked.current = true;
5343
- setCompleting(true);
5130
+ async function handleRetryOrder() {
5131
+ if (!order || retrying) return;
5132
+ setRetrying(true);
5344
5133
  setErrorMsg(null);
5345
5134
  try {
5346
- const result = await client.completeWithdraw(txId);
5347
- if (result.pendingSignature) {
5348
- await resumeWithSignature(txId, result.pendingSignature);
5349
- return;
5350
- }
5351
- setTxStatus(result.status);
5352
- setStellarTxHash(result.stellarTxHash ?? null);
5353
- } catch (e) {
5354
- const msg = e instanceof Error ? e.message : "";
5355
- setErrorMsg(
5356
- msg.includes("KYC") ? "Finish KYC at the provider first, then try again." : msg || "Failed to complete the withdrawal."
5357
- );
5135
+ setOrder(await client.retryRampOrder(order.id));
5136
+ setPollKey((key) => key + 1);
5137
+ } catch (error) {
5138
+ setErrorMsg(rampErrorMessage(error, "Could not try again. Please try again."));
5358
5139
  } finally {
5359
- completionLocked.current = false;
5360
- setCompleting(false);
5140
+ setRetrying(false);
5361
5141
  }
5362
5142
  }
5363
- const kycBlocking = kycPending && !kycApproved;
5364
- const canComplete = !workflow && !legacySignature && direction === "offramp" && step === "status" && txStatus !== "completed" && txStatus !== "failed" && !stellarTxHash && !txHash && !kycPending;
5365
- const flowSteps = flowStepsOf(quotes, selectedQuote);
5366
- const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
5367
- if (pendingKyc?.type === "FORM") {
5368
- return /* @__PURE__ */ jsx(
5369
- RequirementFormModal,
5370
- {
5371
- formId: pendingKyc.optionId,
5372
- ...pendingKyc.progress ? { progress: pendingKyc.progress } : {},
5373
- onClose: () => {
5374
- kycAttempt.current = null;
5375
- setPendingKyc(null);
5376
- },
5377
- onSubmitted: () => {
5378
- if (kycAttempt.current !== pendingKyc) return;
5379
- kycAttempt.current = null;
5380
- setPendingKyc(null);
5381
- void requoteAfterKyc("FORM");
5382
- }
5143
+ const heldForRecovery = step === "status" && order?.failure?.resolutionAction === "set_up_recovery";
5144
+ const wasRecoveryLocked = useRef(recoveryLocked);
5145
+ useEffect(() => {
5146
+ if (heldForRecovery && wasRecoveryLocked.current && !recoveryLocked && recovery) void handleRetryOrder();
5147
+ wasRecoveryLocked.current = recoveryLocked;
5148
+ }, [heldForRecovery, recoveryLocked, recovery]);
5149
+ async function handleRefresh() {
5150
+ if (refreshing) return;
5151
+ setRefreshing(true);
5152
+ try {
5153
+ if (step === "status" && order) {
5154
+ setOrder(await client.getRampOrder(order.id));
5155
+ setPollKey((key) => key + 1);
5156
+ } else if (step === "select_route") await handleFindRoute();
5157
+ else {
5158
+ await loadRoutes(direction);
5159
+ await loadPendingOrders();
5160
+ if (direction === "OFFRAMP") await refreshWalletBalance();
5383
5161
  }
5384
- );
5385
- }
5386
- if (pendingKyc?.type === "REGISTRY_CHECK" || pendingKyc?.type === "PROVIDER_REGISTRATION") {
5387
- const close = () => {
5388
- kycAttempt.current = null;
5389
- setPendingKyc(null);
5390
- };
5391
- const done = () => {
5392
- if (kycAttempt.current !== pendingKyc) return;
5393
- close();
5394
- void requoteAfterKyc(pendingKyc.type);
5395
- };
5396
- const progress = pendingKyc.progress ? { progress: pendingKyc.progress } : {};
5397
- return pendingKyc.type === "REGISTRY_CHECK" ? /* @__PURE__ */ jsx(RegistryCheckModal, { optionId: pendingKyc.optionId, ...progress, onClose: close, onApproved: done }) : /* @__PURE__ */ jsx(ProviderRegistrationModal, { corridorId: pendingKyc.corridorId, ...progress, onClose: close, onRegistered: done });
5162
+ } catch (error) {
5163
+ setErrorMsg(rampErrorMessage(error, "Could not refresh."));
5164
+ } finally {
5165
+ setRefreshing(false);
5166
+ }
5398
5167
  }
5399
- if (pendingKyc) {
5168
+ const flowSteps = fields.length || quotes.some((q) => rampQuoteFields(q).length) ? ["Amount", "Route", "Details", "Complete"] : ["Amount", "Route", "Complete"];
5169
+ const stepLabel = {
5170
+ input: "Amount",
5171
+ loading_quote: "Amount",
5172
+ select_route: "Route",
5173
+ contact: "Details",
5174
+ status: "Complete"
5175
+ };
5176
+ const label = stepLabel[step];
5177
+ if (verifying) {
5178
+ const progress = { progress: { position: verifying.completed + 1, total: verifying.total } };
5179
+ const done = () => verificationDone(verifying);
5180
+ if (verifying.type === "FORM")
5181
+ return /* @__PURE__ */ jsx(RequirementFormModal, { formId: verifying.optionId, ...progress, onClose: closeVerification, onSubmitted: done });
5182
+ if (verifying.type === "REGISTRY_CHECK")
5183
+ return /* @__PURE__ */ jsx(RegistryCheckModal, { optionId: verifying.optionId, ...progress, onClose: closeVerification, onApproved: done });
5184
+ if (verifying.type === "PROVIDER_REGISTRATION")
5185
+ return /* @__PURE__ */ jsx(
5186
+ ProviderRegistrationModal,
5187
+ {
5188
+ corridorId: verifying.corridorId,
5189
+ ...progress,
5190
+ onClose: closeVerification,
5191
+ onRegistered: done
5192
+ }
5193
+ );
5400
5194
  return /* @__PURE__ */ jsx(
5401
5195
  KycModal,
5402
5196
  {
5403
5197
  country,
5404
- corridorId: pendingKyc.corridorId,
5405
- providerId: pendingKyc.optionId,
5406
- onClose: () => {
5407
- kycAttempt.current = null;
5408
- setPendingKyc(null);
5409
- },
5410
- onApproved: () => {
5411
- if (kycAttempt.current !== pendingKyc) return;
5412
- kycAttempt.current = null;
5413
- setPendingKyc(null);
5414
- void requoteAfterKyc();
5415
- }
5198
+ corridorId: verifying.corridorId,
5199
+ providerId: verifying.optionId,
5200
+ onClose: closeVerification,
5201
+ onApproved: done
5416
5202
  }
5417
5203
  );
5418
5204
  }
5419
5205
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
5420
5206
  RampWidgetTemplate,
5421
5207
  {
5422
- workflowContent: workflow ? /* @__PURE__ */ jsx(
5423
- RampWorkflow,
5424
- {
5425
- client,
5426
- snapshot: workflow,
5427
- onBusyChange: (busy) => {
5428
- completionLocked.current = busy;
5429
- setCompleting(busy);
5430
- },
5431
- onChange: (next) => {
5432
- if (acceptWorkflow(next)) setTxStatus(next.status);
5433
- }
5434
- }
5435
- ) : legacySignature && step === "status" && txStatus !== "completed" && txStatus !== "failed" ? /* @__PURE__ */ jsx("button", { type: "button", disabled: completing, onClick: () => void handleLegacySignature(), children: "Authorize wallet request" }) : void 0,
5436
5208
  theme,
5437
5209
  accentColor,
5438
- styleOverrides,
5210
+ ...styleOverrides ? { styleOverrides } : {},
5211
+ network,
5439
5212
  step,
5440
5213
  flowSteps,
5441
- flowStepIndex,
5442
- startingQuoteId: isLoading && selectedQuote ? selectedQuote.quoteId : null,
5214
+ flowStepIndex: label ? flowSteps.indexOf(label) : -1,
5443
5215
  direction,
5444
- amount,
5445
- currency,
5446
- country,
5447
- requiredFields: selectedQuote ? requiredFieldsOf(selectedQuote) : [],
5448
- fieldValues,
5216
+ routesLoading,
5449
5217
  countries,
5450
- countriesLoading,
5451
- refreshing,
5218
+ country,
5219
+ fiats,
5220
+ fiat,
5221
+ assets,
5222
+ asset,
5223
+ amount,
5224
+ sellAvailable,
5452
5225
  quotes,
5453
- kycRequired: requirementsRequired,
5226
+ rejected,
5227
+ startingQuoteId,
5228
+ fields,
5229
+ fieldValues,
5230
+ order,
5231
+ pendingOrders,
5454
5232
  isLoading,
5455
- provider,
5456
- txStatus,
5457
- kycUrl,
5458
- tosUrl,
5459
- kycBlocking,
5460
- onboardingStatus: onboardingStatus ?? null,
5461
- kycJustApproved: kycPending && kycApproved,
5462
- stellarTxHash,
5463
- txHash,
5464
- txChain,
5465
- explorerUrl: explorerTxUrl2(txChain ?? "STELLAR", txHash ?? stellarTxHash, network),
5466
- fundsInTransit,
5467
- retrying,
5468
- onRetryTransit: handleRetryTransit,
5469
- depositInstructions,
5470
- canComplete,
5471
- completing,
5472
- savedRamps,
5473
- onResumeRamp: resumeSavedRamp,
5474
- onDismissRamp: dismissSavedRamp,
5475
- onDismissCurrentRamp: workflow ? () => dismissSavedRamp(workflow.txId) : void 0,
5476
- onExploreRoutes: workflow ? () => {
5477
- if (!completionLocked.current) resetToInput();
5478
- } : void 0,
5233
+ refreshing,
5234
+ signing,
5479
5235
  errorMsg,
5480
- noticeMsg,
5481
- onDirectionChange: (next) => {
5482
- setDirection(next);
5236
+ notice: awaitingAccount ? "Approve the new account on your device. The order starts once it is approved." : null,
5237
+ onDirectionChange: handleDirectionChange,
5238
+ onCountryChange: (next) => {
5239
+ setCountry(next);
5240
+ setFiatChoice("");
5241
+ setAssetChoice("");
5242
+ },
5243
+ onFiatChange: (key) => {
5244
+ setFiatChoice(key);
5245
+ setAssetChoice("");
5483
5246
  },
5484
- onAmountChange: (next) => {
5485
- setAmount(next);
5247
+ onAssetChange: setAssetChoice,
5248
+ onAmountChange: (value) => {
5249
+ setAmount(value);
5486
5250
  setErrorMsg(null);
5487
5251
  },
5488
- onFieldChange: setFieldValue,
5489
- onCountryChange: handleCountryChange,
5490
- onFindRoute: handleFindRoute,
5491
- onSelectQuote: handleSelectQuote,
5252
+ onFindRoute: () => void handleFindRoute(),
5253
+ onSelectQuote: (quote) => handleSelectQuote(quote),
5492
5254
  onVerifyRoute: handleVerifyRoute,
5493
- onContactContinue: handleContactContinue,
5494
- onOpenKyc: handleOpenKyc,
5495
- onOpenTos: handleOpenTos,
5496
- onCompleteWithdraw: handleCompleteWithdraw,
5497
- onBack: () => resetToInput({ keepMessage: true }),
5498
- onRetry: () => resetToInput(),
5499
- onRefresh: handleRefresh,
5255
+ trustingAsset,
5256
+ onTrustAsset: (asset2) => void handleTrustAsset(asset2),
5257
+ walletBusy,
5258
+ custodyChoice,
5259
+ onWalletStep: (walletStep, index, custody) => void handleWalletStep(walletStep, index, custody),
5260
+ noChains: Array.isArray(appChains) && appChains.length === 0,
5261
+ onFieldChange: (key, value) => setFieldValues((current) => ({ ...current, [key]: value })),
5262
+ onContactContinue: () => {
5263
+ if (selected) void startOrder(selected);
5264
+ },
5265
+ onSign: () => void handleSign(),
5266
+ onApprove: openCosignModal,
5267
+ retrying,
5268
+ onRetryOrder: () => void handleRetryOrder(),
5269
+ onSetUpRecovery: openRecoveryModal,
5270
+ recoveryMissing: recovery === null ? null : recoveryLocked,
5271
+ pollTrouble,
5272
+ checkingPayment: paymentCheckUntil !== null,
5273
+ onCheckPayment: () => void handleCheckPayment(),
5274
+ onResumeOrder: (id) => void handleResumeOrder(id),
5275
+ onNewOrder: handleNewOrder,
5276
+ onBack: handleBack,
5277
+ onStepSelect: handleStepSelect,
5278
+ onRetry: () => {
5279
+ setErrorMsg(null);
5280
+ setStep("input");
5281
+ },
5282
+ onRefresh: () => void handleRefresh(),
5500
5283
  onClose
5501
5284
  }
5502
5285
  ) });
@@ -5513,6 +5296,49 @@ function walletNotReadyReason(wallet, chain) {
5513
5296
  }
5514
5297
  return null;
5515
5298
  }
5299
+
5300
+ // src/lib/qr-code/index.tsx
5301
+ var import_ErrorCorrectLevel = __toESM(require_ErrorCorrectLevel());
5302
+ var import_QRCode = __toESM(require_QRCode());
5303
+ var QRCodeSvg = forwardRef(function QRCodeSvg2({ bgColor, bgD, fgD, fgColor, size, title, viewBoxSize, xmlns = "http://www.w3.org/2000/svg", ...props }, ref) {
5304
+ return /* @__PURE__ */ jsxs("svg", { ...props, height: size, ref, viewBox: `0 0 ${viewBoxSize} ${viewBoxSize}`, width: size, xmlns, children: [
5305
+ title ? /* @__PURE__ */ jsx("title", { children: title }) : null,
5306
+ /* @__PURE__ */ jsx("path", { d: bgD, fill: bgColor }),
5307
+ /* @__PURE__ */ jsx("path", { d: fgD, fill: fgColor })
5308
+ ] });
5309
+ });
5310
+ QRCodeSvg.displayName = "QRCodeSvg";
5311
+ function bytesToBinaryString(bytes) {
5312
+ return bytes.map((b) => String.fromCharCode(b & 255)).join("");
5313
+ }
5314
+ function encodeStringToUtf8Bytes(input) {
5315
+ return Array.from(new TextEncoder().encode(input));
5316
+ }
5317
+ var QRCode = forwardRef(function QRCode2({ bgColor = "#FFFFFF", fgColor = "#000000", level = "L", size = 256, value, ...props }, ref) {
5318
+ const qrcode = new import_QRCode.default(-1, import_ErrorCorrectLevel.default[level]);
5319
+ const utf8Bytes = encodeStringToUtf8Bytes(value);
5320
+ const binaryString = bytesToBinaryString(utf8Bytes);
5321
+ qrcode.addData(binaryString, "Byte");
5322
+ qrcode.make();
5323
+ const cells = qrcode.modules;
5324
+ return /* @__PURE__ */ jsx(
5325
+ QRCodeSvg,
5326
+ {
5327
+ ...props,
5328
+ bgColor,
5329
+ bgD: cells.map(
5330
+ (row, rowIndex) => row.map((cell, cellIndex) => !cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
5331
+ ).join(" "),
5332
+ fgColor,
5333
+ fgD: cells.map(
5334
+ (row, rowIndex) => row.map((cell, cellIndex) => cell ? `M ${cellIndex} ${rowIndex} l 1 0 0 1 -1 0 Z` : "").join(" ")
5335
+ ).join(" "),
5336
+ ref,
5337
+ size,
5338
+ viewBoxSize: cells.length
5339
+ }
5340
+ );
5341
+ });
5516
5342
  function PollarLogo(props) {
5517
5343
  return /* @__PURE__ */ jsxs("svg", { width: "45", height: "48", viewBox: "0 0 45 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
5518
5344
  /* @__PURE__ */ jsx(
@@ -5912,7 +5738,7 @@ function formatBalance(balance) {
5912
5738
  const n = parseFloat(balance);
5913
5739
  return isNaN(n) ? balance : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
5914
5740
  }
5915
- function assetKey3(record) {
5741
+ function assetKey4(record) {
5916
5742
  return `${record.code}:${record.issuer ?? "native"}`;
5917
5743
  }
5918
5744
  function chainLabel(chain) {
@@ -5983,8 +5809,8 @@ function SendAssetSelector({ assets, selectedAsset, loading, onSelectAsset }) {
5983
5809
  }
5984
5810
  ),
5985
5811
  open && assets.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-send-asset-menu", role: "listbox", "aria-label": "Assets", children: assets.map((asset) => {
5986
- const key = assetKey3(asset);
5987
- const selected = selectedAsset ? key === assetKey3(selectedAsset) : false;
5812
+ const key = assetKey4(asset);
5813
+ const selected = selectedAsset ? key === assetKey4(selectedAsset) : false;
5988
5814
  return /* @__PURE__ */ jsxs(
5989
5815
  "button",
5990
5816
  {
@@ -6028,6 +5854,9 @@ function SendModalTemplate({
6028
5854
  destination,
6029
5855
  formError,
6030
5856
  isLoadingBalance,
5857
+ isCheckingDestination = false,
5858
+ minStartingBalance = 1,
5859
+ onCreateAccount,
6031
5860
  transaction,
6032
5861
  showXdr,
6033
5862
  copied,
@@ -6057,8 +5886,10 @@ function SendModalTemplate({
6057
5886
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
6058
5887
  const parsedAmount = amount ? Number(amount) : 0;
6059
5888
  const availableAmount = selectedAsset?.available === null || selectedAsset?.available === void 0 ? 0 : Number(selectedAsset.available);
6060
- const canSubmit = canSendOnChain && !notReadyReason && !!selectedAsset && Number.isFinite(parsedAmount) && parsedAmount > 0 && parsedAmount <= availableAmount && !!destination.trim() && !isLoadingBalance;
6061
- const title = step === "form" ? "Send" : txTitle;
5889
+ const canSubmit = canSendOnChain && !notReadyReason && !!selectedAsset && Number.isFinite(parsedAmount) && parsedAmount > 0 && parsedAmount <= availableAmount && !!destination.trim() && !isLoadingBalance && !isCheckingDestination;
5890
+ const title = step === "form" ? "Send" : step === "create-account" ? "Create account?" : txTitle;
5891
+ const createsWithNative = selectedAsset?.type === "native";
5892
+ const meetsMinimum = Number.isFinite(parsedAmount) && parsedAmount >= minStartingBalance;
6062
5893
  return /* @__PURE__ */ jsxs(
6063
5894
  "div",
6064
5895
  {
@@ -6108,44 +5939,18 @@ function SendModalTemplate({
6108
5939
  onSelectAsset
6109
5940
  }
6110
5941
  ),
6111
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
6112
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-label-row", children: [
6113
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Amount" }),
6114
- selectedAsset && /* @__PURE__ */ jsxs("span", { className: "pollar-send-hint", children: [
6115
- "Available: ",
6116
- formatBalance(selectedAsset.available),
6117
- " ",
6118
- selectedAsset.code
6119
- ] })
6120
- ] }),
6121
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
6122
- /* @__PURE__ */ jsx(
6123
- "input",
6124
- {
6125
- className: "pollar-input pollar-send-amount-input",
6126
- type: "text",
6127
- inputMode: "decimal",
6128
- placeholder: "0.00",
6129
- value: amount,
6130
- onChange: (e) => onAmountChange(e.target.value),
6131
- "aria-label": "Amount"
6132
- }
6133
- ),
6134
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-suffix", children: [
6135
- /* @__PURE__ */ jsx(
6136
- "button",
6137
- {
6138
- type: "button",
6139
- className: "pollar-send-inline-action",
6140
- onClick: onMax,
6141
- disabled: !selectedAsset?.available,
6142
- children: "Max"
6143
- }
6144
- ),
6145
- /* @__PURE__ */ jsx("span", { children: selectedAsset?.code ?? "" })
6146
- ] })
6147
- ] })
6148
- ] }),
5942
+ /* @__PURE__ */ jsx(
5943
+ AmountField,
5944
+ {
5945
+ id: "pollar-send-amount",
5946
+ label: "Amount",
5947
+ value: amount,
5948
+ onChange: onAmountChange,
5949
+ unit: selectedAsset?.code,
5950
+ available: selectedAsset ? selectedAsset.available : void 0,
5951
+ onMax
5952
+ }
5953
+ ),
6149
5954
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
6150
5955
  /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Destination wallet" }),
6151
5956
  /* @__PURE__ */ jsxs("div", { className: "pollar-send-input-shell", children: [
@@ -6197,7 +6002,41 @@ function SendModalTemplate({
6197
6002
  ] })
6198
6003
  ] }),
6199
6004
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
6200
- /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: "Continue" }) })
6005
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: isCheckingDestination ? "Checking destination\u2026" : "Continue" }) })
6006
+ ] }),
6007
+ step === "create-account" && /* @__PURE__ */ jsxs(Fragment, { children: [
6008
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-notice", children: [
6009
+ /* @__PURE__ */ jsx("p", { className: "pollar-send-notice-title", children: "This account doesn't exist yet" }),
6010
+ /* @__PURE__ */ jsx("p", { className: "pollar-send-notice-address", children: destination.trim() }),
6011
+ createsWithNative ? /* @__PURE__ */ jsxs("p", { children: [
6012
+ "A Stellar account has to be created and funded before it can receive payments. Instead of a payment, this will",
6013
+ " ",
6014
+ /* @__PURE__ */ jsx("strong", { children: "create the account" }),
6015
+ " with ",
6016
+ amount,
6017
+ " XLM as its starting balance."
6018
+ ] }) : /* @__PURE__ */ jsxs("p", { children: [
6019
+ "It can't receive ",
6020
+ selectedAsset?.code ?? "this asset",
6021
+ " yet: it has to be created and funded with XLM first, and then add a trustline for ",
6022
+ selectedAsset?.code ?? "the asset",
6023
+ "."
6024
+ ] }),
6025
+ createsWithNative && !meetsMinimum && /* @__PURE__ */ jsxs("p", { className: "pollar-send-notice-warning", children: [
6026
+ "A new account needs a starting balance of at least ",
6027
+ minStartingBalance,
6028
+ " XLM. Go back and raise the amount to create it."
6029
+ ] }),
6030
+ /* @__PURE__ */ jsx("p", { children: "Or wait until another wallet funds it, and send afterwards." })
6031
+ ] }),
6032
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-account-actions", children: [
6033
+ createsWithNative && onCreateAccount && /* @__PURE__ */ jsxs("button", { className: "pollar-btn-primary", onClick: onCreateAccount, disabled: !meetsMinimum, children: [
6034
+ "Create account with ",
6035
+ amount,
6036
+ " XLM"
6037
+ ] }),
6038
+ /* @__PURE__ */ jsx("button", { className: "pollar-btn-secondary", onClick: onBack, children: "I'll wait until it's funded" })
6039
+ ] })
6201
6040
  ] }),
6202
6041
  step === "tx" && /* @__PURE__ */ jsx(
6203
6042
  TxStatusView,
@@ -6234,6 +6073,7 @@ function parseMemo(type, raw) {
6234
6073
  if (!/^\d+$/.test(value) || BigInt(value) > MAX_MEMO_ID) return "The memo ID must be a whole number up to 2^64 - 1.";
6235
6074
  return { type, value };
6236
6075
  }
6076
+ var MIN_STARTING_BALANCE_XLM = 1;
6237
6077
  function assetParam(record) {
6238
6078
  if (record.type === "native") return { type: "native" };
6239
6079
  if (record.type === "credit_alphanum4") {
@@ -6251,11 +6091,14 @@ function SendModal({ onClose }) {
6251
6091
  tx: transaction,
6252
6092
  wallet,
6253
6093
  network,
6254
- styles
6094
+ styles,
6095
+ getClient
6255
6096
  } = usePollar();
6256
6097
  const walletType = wallet?.custody === "external" ? wallet.provider : null;
6257
6098
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
6258
6099
  const [step, setStep] = useState("form");
6100
+ const [createsAccount, setCreatesAccount] = useState(false);
6101
+ const [checkingDestination, setCheckingDestination] = useState(false);
6259
6102
  const [amount, setAmount] = useState("");
6260
6103
  const [destination, setDestination] = useState("");
6261
6104
  const [memo, setMemo] = useState("");
@@ -6311,8 +6154,15 @@ function SendModal({ onClose }) {
6311
6154
  "building-signing-submitting"
6312
6155
  ];
6313
6156
  const isInProgress = IN_FLIGHT_STEPS3.includes(transaction.step);
6314
- const showBack = step === "tx" && (transaction.step === "error" || transaction.step === "success") && !isInProgress;
6315
- const txTitle = isInProgress ? "Sending\u2026" : transaction.step === "success" ? "Sent!" : transaction.step === "error" ? "Send failed" : "Confirm Send";
6157
+ const showBack = step === "create-account" || step === "tx" && (transaction.step === "error" || transaction.step === "success") && !isInProgress;
6158
+ const txTitle = createsAccount ? isInProgress ? "Creating account\u2026" : transaction.step === "success" ? "Account created!" : transaction.step === "error" ? "Create account failed" : "Confirm Create Account" : isInProgress ? "Sending\u2026" : transaction.step === "success" ? "Sent!" : transaction.step === "error" ? "Send failed" : "Confirm Send";
6159
+ async function stellarAccountExists(address) {
6160
+ try {
6161
+ return (await getClient().getWalletBalance(address)).exists;
6162
+ } catch {
6163
+ return null;
6164
+ }
6165
+ }
6316
6166
  async function handleSubmit() {
6317
6167
  setFormError("");
6318
6168
  if (!canSendOnChain) {
@@ -6359,6 +6209,16 @@ function SendModal({ onClose }) {
6359
6209
  return;
6360
6210
  }
6361
6211
  }
6212
+ if (selectedChain === "STELLAR") {
6213
+ setCheckingDestination(true);
6214
+ const exists = await stellarAccountExists(destination.trim());
6215
+ setCheckingDestination(false);
6216
+ if (exists === false) {
6217
+ setStep("create-account");
6218
+ return;
6219
+ }
6220
+ }
6221
+ setCreatesAccount(false);
6362
6222
  setStep("tx");
6363
6223
  if (selectedChain === "SOLANA" || selectedChain === "POLYGON") {
6364
6224
  await sendPayment({
@@ -6377,6 +6237,17 @@ function SendModal({ onClose }) {
6377
6237
  parsedMemo ? { memo: parsedMemo } : void 0
6378
6238
  );
6379
6239
  }
6240
+ async function handleCreateAccount() {
6241
+ const parsedMemo = parseMemo(memoType, memo);
6242
+ if (typeof parsedMemo === "string" || parseFloat(amount) < MIN_STARTING_BALANCE_XLM) return;
6243
+ setCreatesAccount(true);
6244
+ setStep("tx");
6245
+ await buildTx(
6246
+ "create_account",
6247
+ { destination: destination.trim(), startingBalance: amount },
6248
+ parsedMemo ? { memo: parsedMemo } : void 0
6249
+ );
6250
+ }
6380
6251
  function handleSignAndSend() {
6381
6252
  if (transaction.step === "built") {
6382
6253
  void signAndSubmitTx(transaction.buildData.unsignedXdr);
@@ -6431,6 +6302,7 @@ function SendModal({ onClose }) {
6431
6302
  }
6432
6303
  function handleBack() {
6433
6304
  setStep("form");
6305
+ setCreatesAccount(false);
6434
6306
  setShowXdr(false);
6435
6307
  setCopied(false);
6436
6308
  }
@@ -6453,6 +6325,9 @@ function SendModal({ onClose }) {
6453
6325
  destination,
6454
6326
  formError,
6455
6327
  isLoadingBalance: walletBalance.step === "loading",
6328
+ isCheckingDestination: checkingDestination,
6329
+ minStartingBalance: MIN_STARTING_BALANCE_XLM,
6330
+ onCreateAccount: () => void handleCreateAccount(),
6456
6331
  transaction,
6457
6332
  showXdr,
6458
6333
  copied,
@@ -6740,7 +6615,7 @@ function SwapModalTemplate({
6740
6615
  ] })
6741
6616
  ] }),
6742
6617
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-main", children: [
6743
- /* @__PURE__ */ jsx("div", { className: `pollar-swap-output${quoteLoading ? " is-loading" : ""}`, "aria-live": "polite", children: quoteLoading ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : formatAmount2(quote?.amountOut) }),
6618
+ /* @__PURE__ */ jsx("div", { className: `pollar-swap-output${quoteLoading ? " is-loading" : ""}`, "aria-live": "polite", children: quoteLoading ? /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm" }) : quote ? `~${formatAmount2(quote.amountOut)}` : "\u2014" }),
6744
6619
  /* @__PURE__ */ jsxs("div", { className: "pollar-swap-panel-selector", children: [
6745
6620
  /* @__PURE__ */ jsx(
6746
6621
  AssetPill,
@@ -7204,7 +7079,6 @@ function EarnModal({ onClose }) {
7204
7079
  earnWithdraw,
7205
7080
  tx: transaction,
7206
7081
  wallet,
7207
- walletBalance,
7208
7082
  refreshWalletBalance,
7209
7083
  enabledAssets,
7210
7084
  refreshAssets,
@@ -7286,13 +7160,9 @@ function EarnModal({ onClose }) {
7286
7160
  const withdrawUnit = position?.withdrawUnit ?? "asset";
7287
7161
  const assetCode = selectedOpportunity?.asset.code ?? "";
7288
7162
  const amountUnitLabel = tab === "deposit" ? assetCode : withdrawUnit === "shares" ? "shares" : assetCode;
7289
- const walletAvailable = (() => {
7290
- if (!selectedOpportunity) return null;
7291
- const balances = walletBalance.step === "loaded" ? walletBalance.data.balances : [];
7292
- const issuer = selectedOpportunity.asset.issuer;
7293
- const rec = balances.find((b) => issuer === null ? b.type === "native" : b.code === assetCode && b.issuer === issuer);
7294
- return rec ? rec.available : null;
7295
- })();
7163
+ const walletAvailable = useWalletAvailable(
7164
+ selectedOpportunity ? { chain: "STELLAR", code: assetCode, issuer: selectedOpportunity.asset.issuer } : null
7165
+ );
7296
7166
  const depositAssetRecord = (() => {
7297
7167
  if (!selectedOpportunity?.asset.issuer || enabledAssets.step !== "loaded") return void 0;
7298
7168
  return enabledAssets.data.assets.find(
@@ -7526,37 +7396,17 @@ function EarnModal({ onClose }) {
7526
7396
  ] })
7527
7397
  ] })
7528
7398
  ] }),
7529
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
7530
- /* @__PURE__ */ jsxs("div", { className: "pollar-send-label-row", children: [
7531
- /* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: tab === "deposit" ? "Amount" : "Withdraw amount" }),
7532
- tab === "deposit" && walletAvailable != null && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(walletAvailable), children: [
7533
- "Max: ",
7534
- formatAmount3(walletAvailable),
7535
- " ",
7536
- amountUnitLabel
7537
- ] }),
7538
- tab === "withdraw" && position && /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setAmount(position.withdrawable), children: [
7539
- "Max: ",
7540
- formatAmount3(position.withdrawable),
7541
- " ",
7542
- amountUnitLabel
7543
- ] })
7544
- ] }),
7545
- /* @__PURE__ */ jsxs("div", { className: "pollar-earn-amount", children: [
7546
- /* @__PURE__ */ jsx(
7547
- "input",
7548
- {
7549
- className: "pollar-input",
7550
- type: "text",
7551
- inputMode: "decimal",
7552
- placeholder: "0.00",
7553
- value: amount,
7554
- onChange: (e) => setAmount(e.target.value)
7555
- }
7556
- ),
7557
- amountUnitLabel && /* @__PURE__ */ jsx("span", { className: "pollar-earn-amount-unit", children: amountUnitLabel })
7558
- ] })
7559
- ] }),
7399
+ /* @__PURE__ */ jsx(
7400
+ AmountField,
7401
+ {
7402
+ id: "pollar-earn-amount",
7403
+ label: tab === "deposit" ? "Amount" : "Withdraw amount",
7404
+ value: amount,
7405
+ onChange: setAmount,
7406
+ unit: amountUnitLabel,
7407
+ available: tab === "deposit" ? walletAvailable : position ? position.withdrawable : void 0
7408
+ }
7409
+ ),
7560
7410
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
7561
7411
  depositNeedsTrustline && selectedOpportunity && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-trustline-notice", children: [
7562
7412
  "To deposit ",
@@ -7588,7 +7438,7 @@ function EarnModal({ onClose }) {
7588
7438
  }
7589
7439
 
7590
7440
  // src/components/card-modal/card-kyc.ts
7591
- var nonEmpty2 = (value) => typeof value === "string" && !!value.trim();
7441
+ var nonEmpty = (value) => typeof value === "string" && !!value.trim();
7592
7442
  function requiredCardStep(error) {
7593
7443
  if (!error || typeof error !== "object") return null;
7594
7444
  if (!("code" in error) || error.code !== "SDK_CARDS_REQUIREMENT_REQUIRED") return null;
@@ -7597,7 +7447,7 @@ function requiredCardStep(error) {
7597
7447
  const cardProviderId = "cardProviderId" in body ? body.cardProviderId : void 0;
7598
7448
  const requirementType = "requirementType" in body ? body.requirementType : void 0;
7599
7449
  const optionId = "optionId" in body ? body.optionId : void 0;
7600
- if (!nonEmpty2(cardProviderId) || !nonEmpty2(optionId)) return null;
7450
+ if (!nonEmpty(cardProviderId) || !nonEmpty(optionId)) return null;
7601
7451
  if (requirementType === "KYC" || requirementType === "FORM" || requirementType === "REGISTRY_CHECK" || requirementType === "PROVIDER_REGISTRATION") {
7602
7452
  return { cardProviderId, type: requirementType, optionId };
7603
7453
  }
@@ -7752,6 +7602,8 @@ function CardModal({ onClose }) {
7752
7602
  const [error, setError] = useState("");
7753
7603
  const revealTimer = useRef(null);
7754
7604
  const provider = providers?.[0] ?? null;
7605
+ const fundingAsset = provider?.fundingAssets[0] ?? "USDC";
7606
+ const fundAvailable = useWalletAvailable(panel === "fund" ? { chain: "STELLAR", code: fundingAsset } : null);
7755
7607
  const card = cards?.[0] ?? null;
7756
7608
  const loadHolder = useCallback(async () => {
7757
7609
  const h = await getClient().getCardHolder();
@@ -8277,19 +8129,18 @@ function CardModal({ onClose }) {
8277
8129
  provider.fundingAssets.join(" or ") || "USDC",
8278
8130
  " from your wallet to your card. It takes a few minutes to cross to the card network."
8279
8131
  ] }),
8280
- /* @__PURE__ */ jsxs("label", { className: "pollar-send-field", children: [
8281
- /* @__PURE__ */ jsx("span", { className: "pollar-send-label", children: "Amount (USDC)" }),
8282
- /* @__PURE__ */ jsx(
8283
- "input",
8284
- {
8285
- className: "pollar-input",
8286
- inputMode: "decimal",
8287
- placeholder: "10",
8288
- value: fundAmount,
8289
- onChange: (e) => setFundAmount(e.target.value.replace(/[^0-9.]/g, ""))
8290
- }
8291
- )
8292
- ] }),
8132
+ /* @__PURE__ */ jsx(
8133
+ AmountField,
8134
+ {
8135
+ id: "pollar-card-fund-amount",
8136
+ label: "Amount",
8137
+ value: fundAmount,
8138
+ onChange: (value) => setFundAmount(value.replace(/[^0-9.]/g, "")),
8139
+ unit: fundingAsset,
8140
+ available: fundAvailable,
8141
+ placeholder: "10"
8142
+ }
8143
+ ),
8293
8144
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx(
8294
8145
  "button",
8295
8146
  {
@@ -8711,6 +8562,12 @@ function detailRows(request) {
8711
8562
  function CloseIcon2() {
8712
8563
  return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
8713
8564
  }
8565
+ function VerifiedIcon() {
8566
+ return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
8567
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
8568
+ /* @__PURE__ */ jsx("path", { d: "M3.5 7l2.5 2.5 4.5-5", stroke: "white", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" })
8569
+ ] });
8570
+ }
8714
8571
  function CosignModalTemplate({
8715
8572
  theme,
8716
8573
  accentColor,
@@ -8723,6 +8580,8 @@ function CosignModalTemplate({
8723
8580
  approveBlocked = false,
8724
8581
  lastResolved,
8725
8582
  explorerUrl,
8583
+ closesIn = null,
8584
+ nextStep = null,
8726
8585
  onSelect,
8727
8586
  onApprove,
8728
8587
  onReject,
@@ -8731,6 +8590,8 @@ function CosignModalTemplate({
8731
8590
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
8732
8591
  const verified = verification?.ok === true;
8733
8592
  const canVote = !!current && verified && !busy && !approveBlocked;
8593
+ const [verifiedOpenFor, setVerifiedOpenFor] = useState(null);
8594
+ const verifiedOpen = !!current && verifiedOpenFor === current.id;
8734
8595
  return /* @__PURE__ */ jsxs(
8735
8596
  "div",
8736
8597
  {
@@ -8743,27 +8604,48 @@ function CosignModalTemplate({
8743
8604
  /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm with your device" }),
8744
8605
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon2, {}) }) })
8745
8606
  ] }),
8746
- pending.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-empty", children: lastResolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
8747
- lastResolved.status === "APPROVED" && /* @__PURE__ */ jsxs("svg", { className: "pollar-cosign-approved-icon", width: "44", height: "44", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
8748
- /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
8749
- /* @__PURE__ */ jsx(
8750
- "path",
8751
- {
8752
- d: "M3.5 7l2.5 2.5 4.5-5",
8753
- stroke: "white",
8754
- strokeWidth: "1.2",
8755
- strokeLinecap: "round",
8756
- strokeLinejoin: "round"
8757
- }
8758
- )
8607
+ pending.length === 0 && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-empty", children: [
8608
+ lastResolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
8609
+ lastResolved.status === "APPROVED" && /* @__PURE__ */ jsxs("svg", { className: "pollar-cosign-approved-icon", width: "44", height: "44", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
8610
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
8611
+ /* @__PURE__ */ jsx(
8612
+ "path",
8613
+ {
8614
+ d: "M3.5 7l2.5 2.5 4.5-5",
8615
+ stroke: "white",
8616
+ strokeWidth: "1.2",
8617
+ strokeLinecap: "round",
8618
+ strokeLinejoin: "round"
8619
+ }
8620
+ )
8621
+ ] }),
8622
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-empty-title", children: lastResolved.status === "APPROVED" ? "Approved" : lastResolved.status === "REJECTED" ? "Rejected" : lastResolved.status.toLowerCase() }),
8623
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: lastResolved.submit?.hash ? /* @__PURE__ */ jsxs(Fragment, { children: [
8624
+ "Sent. Transaction",
8625
+ " ",
8626
+ explorerUrl ? /* @__PURE__ */ jsx("a", { className: "pollar-cosign-tx-link", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: cropAddress(lastResolved.submit.hash) }) : cropAddress(lastResolved.submit.hash),
8627
+ /* @__PURE__ */ jsx(
8628
+ CopyButton,
8629
+ {
8630
+ value: lastResolved.submit.hash,
8631
+ label: "Copy transaction hash",
8632
+ className: "pollar-copy-btn-sm pollar-cosign-copy"
8633
+ }
8634
+ )
8635
+ ] }) : lastResolved.signedXdr ? "Signed. The app will send it." : "Nothing was signed." })
8636
+ ] }) : /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Nothing waits for your approval." }),
8637
+ nextStep && /* @__PURE__ */ jsxs("p", { className: "pollar-cosign-next", children: [
8638
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-hidden": true }),
8639
+ " Next: ",
8640
+ nextStep,
8641
+ ". It appears here in a few seconds."
8759
8642
  ] }),
8760
- /* @__PURE__ */ jsx("p", { className: "pollar-cosign-empty-title", children: lastResolved.status === "APPROVED" ? "Approved" : lastResolved.status === "REJECTED" ? "Rejected" : lastResolved.status.toLowerCase() }),
8761
- /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: lastResolved.submit?.hash ? /* @__PURE__ */ jsxs(Fragment, { children: [
8762
- "Sent. Transaction",
8763
- " ",
8764
- explorerUrl ? /* @__PURE__ */ jsx("a", { className: "pollar-cosign-tx-link", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: cropAddress(lastResolved.submit.hash) }) : cropAddress(lastResolved.submit.hash)
8765
- ] }) : lastResolved.signedXdr ? "Signed. The app will send it." : "Nothing was signed." })
8766
- ] }) : /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Nothing waits for your approval." }) }),
8643
+ closesIn !== null && /* @__PURE__ */ jsxs("p", { className: "pollar-cosign-muted pollar-cosign-closing", children: [
8644
+ "Closing in ",
8645
+ closesIn,
8646
+ "s"
8647
+ ] })
8648
+ ] }),
8767
8649
  pending.length > 1 && /* @__PURE__ */ jsx("ul", { className: "pollar-cosign-list", children: pending.map((r) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
8768
8650
  "button",
8769
8651
  {
@@ -8777,7 +8659,26 @@ function CosignModalTemplate({
8777
8659
  }
8778
8660
  ) }, r.id)) }),
8779
8661
  current && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
8780
- /* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf(current) }),
8662
+ /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail-head", children: [
8663
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf(current) }),
8664
+ !verification && /* @__PURE__ */ jsxs("span", { className: "pollar-cosign-chip", children: [
8665
+ /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-hidden": true }),
8666
+ "Checking..."
8667
+ ] }),
8668
+ verification?.ok && /* @__PURE__ */ jsxs(
8669
+ "button",
8670
+ {
8671
+ type: "button",
8672
+ className: "pollar-cosign-chip pollar-cosign-chip-ok",
8673
+ "aria-expanded": verifiedOpen,
8674
+ onClick: () => setVerifiedOpenFor(verifiedOpen ? null : current.id),
8675
+ children: [
8676
+ /* @__PURE__ */ jsx(VerifiedIcon, {}),
8677
+ "Verified"
8678
+ ]
8679
+ }
8680
+ )
8681
+ ] }),
8781
8682
  current.summary && /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: current.summary }),
8782
8683
  /* @__PURE__ */ jsxs("dl", { className: "pollar-cosign-rows", children: [
8783
8684
  detailRows(current).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
@@ -8789,17 +8690,11 @@ function CosignModalTemplate({
8789
8690
  /* @__PURE__ */ jsx("dd", { children: expiresIn(current) })
8790
8691
  ] })
8791
8692
  ] }),
8792
- /* @__PURE__ */ jsxs(
8793
- "div",
8794
- {
8795
- className: `pollar-cosign-verify pollar-cosign-verify-${verification ? verification.ok ? "ok" : "bad" : "pending"}`,
8796
- children: [
8797
- !verification && "Checking the transaction against Turnkey...",
8798
- verification?.ok && "Verified: this is exactly what will be signed.",
8799
- verification && !verification.ok && `Not verified: ${verification.reason}`
8800
- ]
8801
- }
8802
- ),
8693
+ verification?.ok && verifiedOpen && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-verify pollar-cosign-verify-ok", children: "Checked against Turnkey: this is exactly what will be signed." }),
8694
+ verification && !verification.ok && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-verify pollar-cosign-verify-bad", children: [
8695
+ "Not verified: ",
8696
+ verification.reason
8697
+ ] }),
8803
8698
  error && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-error", children: error }),
8804
8699
  /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-actions", children: [
8805
8700
  /* @__PURE__ */ jsx(
@@ -8829,10 +8724,13 @@ function CosignModalTemplate({
8829
8724
  }
8830
8725
  );
8831
8726
  }
8832
- function CosignModal({ onClose }) {
8727
+ var CLOSE_AFTER_SECONDS = 3;
8728
+ var CLOSE_AFTER_SENT_SECONDS = 5;
8729
+ var NEXT_REQUEST_EVERY_MS = 2e3;
8730
+ function CosignModal({ nextStep = null, onClose }) {
8833
8731
  const { getClient, styles, network } = usePollar();
8834
8732
  const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
8835
- const { pending, lastResolved, approve, reject, verify, inspectQuorum } = useCosign();
8733
+ const { pending, lastResolved, loaded, refresh, approve, reject, verify, inspectQuorum } = useCosign();
8836
8734
  const [selectedId, setSelectedId] = useState(null);
8837
8735
  const [verification, setVerification] = useState(null);
8838
8736
  const [busy, setBusy] = useState(null);
@@ -8875,6 +8773,33 @@ function CosignModal({ onClose }) {
8875
8773
  if (authState.step === "idle") onCloseRef.current();
8876
8774
  });
8877
8775
  }, [getClient]);
8776
+ const silent = pending.length === 0 && (lastResolved ? lastResolved.status === "APPROVED" && !lastResolved.submit?.hash && !lastResolved.signedXdr : loaded);
8777
+ const sent = pending.length === 0 && lastResolved?.status === "APPROVED" && !!lastResolved.submit?.hash;
8778
+ const awaitingNext = sent && !!nextStep;
8779
+ const closeAfter = awaitingNext ? null : sent ? CLOSE_AFTER_SENT_SECONDS : silent ? CLOSE_AFTER_SECONDS : null;
8780
+ useEffect(() => {
8781
+ if (!awaitingNext) return;
8782
+ const timer = setInterval(() => void refresh().catch(() => void 0), NEXT_REQUEST_EVERY_MS);
8783
+ return () => clearInterval(timer);
8784
+ }, [awaitingNext, refresh]);
8785
+ const [closesIn, setClosesIn] = useState(null);
8786
+ useEffect(() => {
8787
+ if (closeAfter === null) {
8788
+ setClosesIn(null);
8789
+ return;
8790
+ }
8791
+ let left = closeAfter;
8792
+ setClosesIn(left);
8793
+ const timer = setInterval(() => {
8794
+ left -= 1;
8795
+ if (left > 0) setClosesIn(left);
8796
+ else {
8797
+ clearInterval(timer);
8798
+ onCloseRef.current();
8799
+ }
8800
+ }, 1e3);
8801
+ return () => clearInterval(timer);
8802
+ }, [closeAfter, lastResolved?.id]);
8878
8803
  const run = useCallback(
8879
8804
  async (kind, request, action) => {
8880
8805
  setBusy(kind);
@@ -8903,6 +8828,8 @@ function CosignModal({ onClose }) {
8903
8828
  error,
8904
8829
  approveBlocked: !!current && approveFailedFor === current.id,
8905
8830
  ...lastResolved ? { lastResolved } : {},
8831
+ closesIn,
8832
+ nextStep: awaitingNext ? nextStep : null,
8906
8833
  explorerUrl: lastResolved?.submit?.hash ? explorerTxUrl(lastResolved.network, lastResolved.submit.hash, network) : null,
8907
8834
  onSelect: (r) => setSelectedId(r.id),
8908
8835
  onApprove: (r) => void run("approving", r, approve),
@@ -9282,7 +9209,7 @@ function RecoveryModalTemplate(p) {
9282
9209
  );
9283
9210
  }
9284
9211
  function errorText2(err) {
9285
- return transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null);
9212
+ return cosignRefusalMessage(err) ?? transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null);
9286
9213
  }
9287
9214
  function RecoveryModal({ onClose }) {
9288
9215
  const { getClient, styles, refreshRecovery, appConfig } = usePollar();
@@ -10296,6 +10223,7 @@ function PollarProvider({
10296
10223
  const [receiveModalOpen, setReceiveModalOpen] = useState(false);
10297
10224
  const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
10298
10225
  const [cosignModalOpen, setCosignModalOpen] = useState(false);
10226
+ const [cosignNextStep, setCosignNextStep] = useState(null);
10299
10227
  const [recoveryModalOpen, setRecoveryModalOpen] = useState(false);
10300
10228
  const cosignPendingCount = useRef(0);
10301
10229
  useEffect(() => {
@@ -10446,6 +10374,7 @@ function PollarProvider({
10446
10374
  // cosign
10447
10375
  cosign,
10448
10376
  openCosignModal: () => setCosignModalOpen(true),
10377
+ setCosignNextStep,
10449
10378
  recovery,
10450
10379
  recoveryLocked,
10451
10380
  refreshRecovery,
@@ -10536,7 +10465,7 @@ function PollarProvider({
10536
10465
  /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCardModalOpen(false), children: /* @__PURE__ */ jsx(CardModal, { onClose: () => setCardModalOpen(false) }) }, `${cardModalKey}:${walletAddress}`),
10537
10466
  receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
10538
10467
  sessionsModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) }),
10539
- cosignModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCosignModalOpen(false), children: /* @__PURE__ */ jsx(CosignModal, { onClose: () => setCosignModalOpen(false) }) }),
10468
+ cosignModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCosignModalOpen(false), children: /* @__PURE__ */ jsx(CosignModal, { nextStep: cosignNextStep, onClose: () => setCosignModalOpen(false) }) }),
10540
10469
  custodyMigration?.required && !custodyMigrationLater && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: refreshCustodyMigration, children: /* @__PURE__ */ jsx(
10541
10470
  CustodyMigrationModal,
10542
10471
  {
@@ -10553,7 +10482,15 @@ function PollarProvider({
10553
10482
  }
10554
10483
  }
10555
10484
  ) }),
10556
- recoveryModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setRecoveryModalOpen(false), children: /* @__PURE__ */ jsx(RecoveryModal, { onClose: () => setRecoveryModalOpen(false) }) }),
10485
+ recoveryModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setRecoveryModalOpen(false), children: /* @__PURE__ */ jsx(
10486
+ RecoveryModal,
10487
+ {
10488
+ onClose: () => {
10489
+ setRecoveryModalOpen(false);
10490
+ refreshRecovery();
10491
+ }
10492
+ }
10493
+ ) }),
10557
10494
  firstConfirmation && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: firstConfirmation.reject, children: /* @__PURE__ */ jsx(ConfirmModal, { confirmation: firstConfirmation }, firstConfirmation.id) }),
10558
10495
  distributionRulesModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setDistributionRulesModalOpen(false), children: /* @__PURE__ */ jsx(DistributionRulesModal, { onClose: () => setDistributionRulesModalOpen(false) }) })
10559
10496
  ] });
@@ -10585,8 +10522,9 @@ function createPollarAdapterHook(key) {
10585
10522
  );
10586
10523
  };
10587
10524
  }
10588
- function ButtonLogo() {
10589
- return /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR, alt: "Pollar", width: 22, height: 22, className: "pollar-wallet-btn-logo" });
10525
+ function ButtonLogo({ accentColor }) {
10526
+ const src = readableTextOn(accentColor) === "#ffffff" ? LOGO_POLLAR_MARK_ON_DARK : LOGO_POLLAR_MARK_ON_LIGHT;
10527
+ return /* @__PURE__ */ jsx("img", { src, alt: "Pollar", width: 22, height: 22, className: "pollar-wallet-btn-logo" });
10590
10528
  }
10591
10529
  function cropWallet(address) {
10592
10530
  if (address.length <= 12) return address;
@@ -10594,12 +10532,11 @@ function cropWallet(address) {
10594
10532
  }
10595
10533
  function WalletButtonTemplate({
10596
10534
  walletAddress,
10535
+ theme,
10597
10536
  accentColor,
10537
+ styleOverrides,
10598
10538
  open,
10599
10539
  copied,
10600
- dropdownBg,
10601
- dropdownBorder,
10602
- itemColor,
10603
10540
  wrapperRef,
10604
10541
  isInProgress,
10605
10542
  showCreateAccount,
@@ -10628,9 +10565,10 @@ function WalletButtonTemplate({
10628
10565
  }) {
10629
10566
  const [selfCustodyNotice, setSelfCustodyNotice] = useState(false);
10630
10567
  const [risksAccepted, setRisksAccepted] = useState(false);
10568
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
10631
10569
  if (!walletAddress && signedIn) {
10632
- return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, children: [
10633
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-btn", style: { backgroundColor: accentColor }, onClick: onToggleOpen, children: [
10570
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, style: cssVars, children: [
10571
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-btn", onClick: onToggleOpen, children: [
10634
10572
  /* @__PURE__ */ jsx("span", { className: "pollar-wallet-btn-label", children: onCreateWallet ? "No wallet" : "Signed in" }),
10635
10573
  /* @__PURE__ */ jsx(
10636
10574
  "svg",
@@ -10646,22 +10584,13 @@ function WalletButtonTemplate({
10646
10584
  }
10647
10585
  )
10648
10586
  ] }),
10649
- open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
10587
+ open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", children: [
10650
10588
  createWalletError && /* @__PURE__ */ jsxs(Fragment, { children: [
10651
- /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: createWalletError }),
10589
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", role: "status", children: createWalletError }),
10652
10590
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10653
10591
  ] }),
10654
10592
  onCreateWallet && !custodyChoice && /* @__PURE__ */ jsxs(Fragment, { children: [
10655
- /* @__PURE__ */ jsx(
10656
- "button",
10657
- {
10658
- className: "pollar-wallet-dropdown-item",
10659
- style: { color: itemColor },
10660
- onClick: () => onCreateWallet(),
10661
- disabled: creatingWallet,
10662
- children: creatingWallet ? "Creating wallet\u2026" : "Create wallet"
10663
- }
10664
- ),
10593
+ /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", onClick: () => onCreateWallet(), disabled: creatingWallet, children: creatingWallet ? "Creating wallet\u2026" : "Create wallet" }),
10665
10594
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10666
10595
  ] }),
10667
10596
  onCreateWallet && custodyChoice && !selfCustodyNotice && /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -10669,29 +10598,27 @@ function WalletButtonTemplate({
10669
10598
  "button",
10670
10599
  {
10671
10600
  className: "pollar-wallet-dropdown-item",
10672
- style: { color: itemColor },
10673
10601
  onClick: () => onCreateWallet("COSIGNED"),
10674
10602
  disabled: creatingWallet,
10675
10603
  children: creatingWallet ? "Creating wallet\u2026" : "Create co-signed wallet"
10676
10604
  }
10677
10605
  ),
10678
- /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: "You and the app approve every signature together. Your wallet can be recovered with help." }),
10606
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", children: "You and the app approve every signature together. Your wallet can be recovered with help." }),
10679
10607
  /* @__PURE__ */ jsx(
10680
10608
  "button",
10681
10609
  {
10682
10610
  className: "pollar-wallet-dropdown-item",
10683
- style: { color: itemColor },
10684
10611
  onClick: () => setSelfCustodyNotice(true),
10685
10612
  disabled: creatingWallet,
10686
10613
  children: "Create self-custody wallet"
10687
10614
  }
10688
10615
  ),
10689
- /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: "Only your device signs. Nobody else can move or recover your funds." }),
10616
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", children: "Only your device signs. Nobody else can move or recover your funds." }),
10690
10617
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10691
10618
  ] }),
10692
10619
  onCreateWallet && custodyChoice && selfCustodyNotice && /* @__PURE__ */ jsxs(Fragment, { children: [
10693
- /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "alert", children: "In a self-custody wallet only your device can sign. If you lose this device and every recovery method, nobody can recover the wallet or its funds, not the app and not Pollar. Nobody can undo or freeze a transaction you sign." }),
10694
- /* @__PURE__ */ jsxs("label", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, children: [
10620
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", role: "alert", children: "In a self-custody wallet only your device can sign. If you lose this device and every recovery method, nobody can recover the wallet or its funds, not the app and not Pollar. Nobody can undo or freeze a transaction you sign." }),
10621
+ /* @__PURE__ */ jsxs("label", { className: "pollar-wallet-dropdown-notice", children: [
10695
10622
  /* @__PURE__ */ jsx(
10696
10623
  "input",
10697
10624
  {
@@ -10708,7 +10635,6 @@ function WalletButtonTemplate({
10708
10635
  "button",
10709
10636
  {
10710
10637
  className: "pollar-wallet-dropdown-item",
10711
- style: { color: itemColor },
10712
10638
  onClick: () => onCreateWallet("SELF_CUSTODY"),
10713
10639
  disabled: creatingWallet || !risksAccepted,
10714
10640
  children: creatingWallet ? "Creating wallet\u2026" : "Create self-custody wallet"
@@ -10718,7 +10644,6 @@ function WalletButtonTemplate({
10718
10644
  "button",
10719
10645
  {
10720
10646
  className: "pollar-wallet-dropdown-item",
10721
- style: { color: itemColor },
10722
10647
  onClick: () => {
10723
10648
  setSelfCustodyNotice(false);
10724
10649
  setRisksAccepted(false);
@@ -10729,24 +10654,23 @@ function WalletButtonTemplate({
10729
10654
  ),
10730
10655
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10731
10656
  ] }),
10732
- /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSessions, children: "Active sessions" }),
10657
+ /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item", onClick: onSessions, children: "Active sessions" }),
10733
10658
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
10734
10659
  /* @__PURE__ */ jsx("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: "Log out" })
10735
10660
  ] })
10736
10661
  ] });
10737
10662
  }
10738
10663
  if (!walletAddress) {
10739
- return /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-wallet-login-btn", style: { backgroundColor: accentColor }, onClick: onLogin, children: [
10740
- /* @__PURE__ */ jsx(ButtonLogo, {}),
10664
+ return /* @__PURE__ */ jsxs("button", { type: "button", className: "pollar-wallet-login-btn", style: cssVars, onClick: onLogin, children: [
10665
+ /* @__PURE__ */ jsx(ButtonLogo, { accentColor }),
10741
10666
  "Login with Pollar"
10742
10667
  ] });
10743
10668
  }
10744
- return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, children: [
10669
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-wrapper", ref: wrapperRef, style: cssVars, children: [
10745
10670
  /* @__PURE__ */ jsxs(
10746
10671
  "button",
10747
10672
  {
10748
10673
  className: "pollar-wallet-btn",
10749
- style: { backgroundColor: accentColor },
10750
10674
  onClick: isInProgress ? void 0 : onToggleOpen,
10751
10675
  disabled: isInProgress,
10752
10676
  "aria-busy": isInProgress,
@@ -10783,50 +10707,41 @@ function WalletButtonTemplate({
10783
10707
  ]
10784
10708
  }
10785
10709
  ),
10786
- open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", style: { backgroundColor: dropdownBg, borderColor: dropdownBorder }, children: [
10710
+ open && /* @__PURE__ */ jsxs("div", { className: "pollar-wallet-dropdown", children: [
10787
10711
  recoveryLocked && /* @__PURE__ */ jsxs(Fragment, { children: [
10788
10712
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice pollar-danger", role: "alert", children: "Locked: add a way to recover this wallet before sending or receiving. If you lose this device without one, the funds are lost." }),
10789
10713
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10790
10714
  ] }),
10791
10715
  notReadyReason && /* @__PURE__ */ jsxs(Fragment, { children: [
10792
- /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", style: { color: itemColor }, role: "status", children: notReadyReason }),
10716
+ /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-notice", role: "status", children: notReadyReason }),
10793
10717
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10794
10718
  ] }),
10795
10719
  showCreateAccount && /* @__PURE__ */ jsxs(Fragment, { children: [
10796
- /* @__PURE__ */ jsxs(
10797
- "button",
10798
- {
10799
- className: "pollar-wallet-dropdown-item",
10800
- style: { color: itemColor },
10801
- onClick: onCreateAccount,
10802
- disabled: creatingAccount,
10803
- children: [
10804
- /* @__PURE__ */ jsxs(
10805
- "svg",
10806
- {
10807
- width: "14",
10808
- height: "14",
10809
- viewBox: "0 0 24 24",
10810
- fill: "none",
10811
- stroke: "currentColor",
10812
- strokeWidth: "2",
10813
- strokeLinecap: "round",
10814
- strokeLinejoin: "round",
10815
- children: [
10816
- /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
10817
- /* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" }),
10818
- /* @__PURE__ */ jsx("line", { x1: "19", y1: "8", x2: "19", y2: "14" }),
10819
- /* @__PURE__ */ jsx("line", { x1: "22", y1: "11", x2: "16", y2: "11" })
10820
- ]
10821
- }
10822
- ),
10823
- creatingAccount ? "Creating account\u2026" : "Create account"
10824
- ]
10825
- }
10826
- ),
10720
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onCreateAccount, disabled: creatingAccount, children: [
10721
+ /* @__PURE__ */ jsxs(
10722
+ "svg",
10723
+ {
10724
+ width: "14",
10725
+ height: "14",
10726
+ viewBox: "0 0 24 24",
10727
+ fill: "none",
10728
+ stroke: "currentColor",
10729
+ strokeWidth: "2",
10730
+ strokeLinecap: "round",
10731
+ strokeLinejoin: "round",
10732
+ children: [
10733
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
10734
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" }),
10735
+ /* @__PURE__ */ jsx("line", { x1: "19", y1: "8", x2: "19", y2: "14" }),
10736
+ /* @__PURE__ */ jsx("line", { x1: "22", y1: "11", x2: "16", y2: "11" })
10737
+ ]
10738
+ }
10739
+ ),
10740
+ creatingAccount ? "Creating account\u2026" : "Create account"
10741
+ ] }),
10827
10742
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" })
10828
10743
  ] }),
10829
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSend, children: [
10744
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onSend, children: [
10830
10745
  /* @__PURE__ */ jsxs(
10831
10746
  "svg",
10832
10747
  {
@@ -10846,7 +10761,7 @@ function WalletButtonTemplate({
10846
10761
  ),
10847
10762
  "Send"
10848
10763
  ] }),
10849
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onReceive, children: [
10764
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onReceive, children: [
10850
10765
  /* @__PURE__ */ jsxs(
10851
10766
  "svg",
10852
10767
  {
@@ -10868,7 +10783,7 @@ function WalletButtonTemplate({
10868
10783
  "Receive"
10869
10784
  ] }),
10870
10785
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
10871
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onCopy, children: [
10786
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onCopy, children: [
10872
10787
  /* @__PURE__ */ jsxs(
10873
10788
  "svg",
10874
10789
  {
@@ -10888,7 +10803,7 @@ function WalletButtonTemplate({
10888
10803
  ),
10889
10804
  copied ? "Copied!" : "Copy address"
10890
10805
  ] }),
10891
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onWalletBalance, children: [
10806
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onWalletBalance, children: [
10892
10807
  /* @__PURE__ */ jsxs(
10893
10808
  "svg",
10894
10809
  {
@@ -10909,7 +10824,7 @@ function WalletButtonTemplate({
10909
10824
  ),
10910
10825
  "Wallet balance"
10911
10826
  ] }),
10912
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onTxHistory, children: [
10827
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onTxHistory, children: [
10913
10828
  /* @__PURE__ */ jsxs(
10914
10829
  "svg",
10915
10830
  {
@@ -10933,7 +10848,7 @@ function WalletButtonTemplate({
10933
10848
  "Transaction history"
10934
10849
  ] }),
10935
10850
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
10936
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onRamp, children: [
10851
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onRamp, children: [
10937
10852
  /* @__PURE__ */ jsxs(
10938
10853
  "svg",
10939
10854
  {
@@ -10955,7 +10870,7 @@ function WalletButtonTemplate({
10955
10870
  ),
10956
10871
  "Buy / Sell"
10957
10872
  ] }),
10958
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onKyc, children: [
10873
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onKyc, children: [
10959
10874
  /* @__PURE__ */ jsxs(
10960
10875
  "svg",
10961
10876
  {
@@ -10976,7 +10891,7 @@ function WalletButtonTemplate({
10976
10891
  ),
10977
10892
  "Identity verification"
10978
10893
  ] }),
10979
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onDistributionRules, children: [
10894
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onDistributionRules, children: [
10980
10895
  /* @__PURE__ */ jsxs(
10981
10896
  "svg",
10982
10897
  {
@@ -10999,7 +10914,7 @@ function WalletButtonTemplate({
10999
10914
  ),
11000
10915
  "Distribution rules"
11001
10916
  ] }),
11002
- /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", style: { color: itemColor }, onClick: onSessions, children: [
10917
+ /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item", onClick: onSessions, children: [
11003
10918
  /* @__PURE__ */ jsxs(
11004
10919
  "svg",
11005
10920
  {
@@ -11020,31 +10935,23 @@ function WalletButtonTemplate({
11020
10935
  ),
11021
10936
  "Active sessions"
11022
10937
  ] }),
11023
- onRecovery && /* @__PURE__ */ jsxs(
11024
- "button",
11025
- {
11026
- className: `pollar-wallet-dropdown-item${recoveryLocked ? " pollar-danger" : ""}`,
11027
- style: recoveryLocked ? void 0 : { color: itemColor },
11028
- onClick: onRecovery,
11029
- children: [
11030
- /* @__PURE__ */ jsx(
11031
- "svg",
11032
- {
11033
- width: "14",
11034
- height: "14",
11035
- viewBox: "0 0 24 24",
11036
- fill: "none",
11037
- stroke: "currentColor",
11038
- strokeWidth: "2",
11039
- strokeLinecap: "round",
11040
- strokeLinejoin: "round",
11041
- children: /* @__PURE__ */ jsx("path", { d: "M12 2 4 5v6c0 5 3.4 9.3 8 11 4.6-1.7 8-6 8-11V5l-8-3z" })
11042
- }
11043
- ),
11044
- recoveryLocked ? "Set up wallet recovery" : "Wallet recovery"
11045
- ]
11046
- }
11047
- ),
10938
+ onRecovery && /* @__PURE__ */ jsxs("button", { className: `pollar-wallet-dropdown-item${recoveryLocked ? " pollar-danger" : ""}`, onClick: onRecovery, children: [
10939
+ /* @__PURE__ */ jsx(
10940
+ "svg",
10941
+ {
10942
+ width: "14",
10943
+ height: "14",
10944
+ viewBox: "0 0 24 24",
10945
+ fill: "none",
10946
+ stroke: "currentColor",
10947
+ strokeWidth: "2",
10948
+ strokeLinecap: "round",
10949
+ strokeLinejoin: "round",
10950
+ children: /* @__PURE__ */ jsx("path", { d: "M12 2 4 5v6c0 5 3.4 9.3 8 11 4.6-1.7 8-6 8-11V5l-8-3z" })
10951
+ }
10952
+ ),
10953
+ recoveryLocked ? "Set up wallet recovery" : "Wallet recovery"
10954
+ ] }),
11048
10955
  /* @__PURE__ */ jsx("div", { className: "pollar-wallet-dropdown-divider" }),
11049
10956
  /* @__PURE__ */ jsxs("button", { className: "pollar-wallet-dropdown-item pollar-danger", onClick: onLogout, children: [
11050
10957
  /* @__PURE__ */ jsxs(
@@ -11105,11 +11012,7 @@ function WalletButton() {
11105
11012
  const isInProgress = transaction.step === "building" || transaction.step === "signing";
11106
11013
  const canCreateAccount = !created && wallet?.custody === "external" && wallet.existsOnStellar === false && wallet.fundingMode === "IMMEDIATE";
11107
11014
  const notReadyReason = walletNotReadyReason(wallet, primaryChain);
11108
- const { theme = "light", accentColor = "#005DB4" } = styles;
11109
- const isDark = theme === "dark";
11110
- const dropdownBg = isDark ? "#18181b" : "#fff";
11111
- const dropdownBorder = isDark ? "#3f3f46" : "#e4e4e7";
11112
- const itemColor = isDark ? "#fafafa" : "#18181b";
11015
+ const { theme, accentColor, styleOverrides } = modalChrome(styles);
11113
11016
  useEffect(() => {
11114
11017
  function handleClickOutside(e) {
11115
11018
  if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
@@ -11208,12 +11111,11 @@ function WalletButton() {
11208
11111
  WalletButtonTemplate,
11209
11112
  {
11210
11113
  walletAddress: walletAddress ?? null,
11114
+ theme,
11211
11115
  accentColor,
11116
+ styleOverrides,
11212
11117
  open,
11213
11118
  copied,
11214
- dropdownBg,
11215
- dropdownBorder,
11216
- itemColor,
11217
11119
  wrapperRef,
11218
11120
  isInProgress,
11219
11121
  showCreateAccount: canCreateAccount,
@@ -11243,6 +11145,6 @@ function WalletButton() {
11243
11145
  );
11244
11146
  }
11245
11147
 
11246
- export { CardModal, ChainSelect, CosignModal, CosignModalTemplate, CustodyMigrationModal, CustodyMigrationModalTemplate, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, RampWorkflow, ReceiveModal, ReceiveModalTemplate, RecoveryModal, RecoveryModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, browserRecoveryPasskey, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, useCosign, usePollar, walletNotReadyReason };
11148
+ export { CardModal, ChainSelect, CosignModal, CosignModalTemplate, CustodyMigrationModal, CustodyMigrationModalTemplate, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RecoveryModal, RecoveryModalTemplate, RegistryCheckModal, RejectedRouteDisplay, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, browserRecoveryPasskey, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, useCosign, usePollar, walletNotReadyReason };
11247
11149
  //# sourceMappingURL=index.mjs.map
11248
11150
  //# sourceMappingURL=index.mjs.map