@pollar/react 0.12.0-rc.2 → 0.12.0

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