@pollar/react 0.4.4 → 0.5.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
@@ -39,56 +39,15 @@ var PollarModalFooter = () => {
39
39
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
40
40
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
41
41
  "v",
42
- "0.4.4"
42
+ "0.4.5"
43
43
  ] })
44
44
  ] })
45
45
  ] });
46
46
  };
47
- var LOGIN_CODE_MESSAGES = {
48
- NONE: { text: "" },
49
- LOGOUT: { text: "Logged out" },
50
- CREATE_SESSION_START: { text: "Starting session\u2026" },
51
- CREATE_SESSION_ERROR: { text: "Failed to start session" },
52
- CREATE_SESSION_SUCCESS: { text: "Session ready" },
53
- EMAIL_AUTH_START: { text: "Sending code\u2026" },
54
- EMAIL_AUTH_START_ERROR: { text: "Failed to send code" },
55
- EMAIL_AUTH_START_SUCCESS: { text: "Code sent \u2014 check your inbox" },
56
- EMAIL_AUTH_CODE_ERROR: { text: "Invalid code \u2014 try again" },
57
- EMAIL_AUTH_CODE_SUCCESS: { text: "Code verified!" },
58
- WALLET_AUTH_START: { text: "Connecting wallet\u2026" },
59
- WALLET_AUTH_FREIGHTER_NOT_INSTALLED: { text: "Freighter is not installed" },
60
- WALLET_AUTH_ALBEDO_NOT_INSTALLED: { text: "Albedo is not installed" },
61
- WALLET_AUTH_CONNECTED: { text: "Wallet connected" },
62
- WALLET_AUTH_LOGIN_START: { text: "Signing in with wallet\u2026" },
63
- WALLET_AUTH_LOGIN_START_SUCCESS: { text: "Wallet signed in" },
64
- WALLET_AUTH_LOGIN_START_ERROR: { text: "Failed to sign in with wallet" },
65
- WALLET_AUTH_ERROR: { text: "Unknow wallet error" },
66
- STREAM_POLL_START: { text: "Waiting for authentication\u2026" },
67
- STREAM_POLL_EVENT: { text: "Waiting for authentication\u2026" },
68
- STREAM_POLL_READY: { text: "Authenticated!" },
69
- FETCH_SESSION_START: { text: "Loading session\u2026" },
70
- FETCH_SESSION_SUCCESS: { text: "Welcome back!" },
71
- FETCH_SESSION_ERROR: { text: "Failed to load session" },
72
- NO_RESTORED_SESSION: { text: "" },
73
- RESTORED_SESSION_SUCCESS: { text: "Session restored" },
74
- RESTORED_SESSION_ERROR: { text: "Failed to restore session" },
75
- SESSION_STORED: { text: "Session saved" },
76
- ERROR_UNKNOWN: { text: "Something went wrong" },
77
- ABORTED: { text: "Login cancelled" },
78
- // transaction
79
- BUILD_TRANSACTION_START: { text: "Building transaction\u2026" },
80
- BUILD_TRANSACTION_SUCCESS: { text: "Transaction built, ready to sign and send" },
81
- BUILD_TRANSACTION_ERROR: { text: "Failed to build transaction" },
82
- BUILD_TRANSACTION_ERROR_NO_WALLET: { text: "No wallet connected" },
83
- SIGN_SEND_TRANSACTION_START: { text: "Signing and sending transaction\u2026" },
84
- SIGN_SEND_TRANSACTION_SUCCESS: { text: "Transaction signed" },
85
- SIGN_SEND_TRANSACTION_ERROR: { text: "Signing rejected" }
86
- };
87
- function ModalStatusBanner({ code, status, onCancel, onRetry }) {
88
- if (!code) {
47
+ function ModalStatusBanner({ message, status, onCancel, onRetry }) {
48
+ if (!message && status === "NONE") {
89
49
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-status" });
90
50
  }
91
- const { text } = LOGIN_CODE_MESSAGES[code] || { text: "" };
92
51
  const isLoading = status === "LOADING";
93
52
  const icon = status === "ERROR" ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
94
53
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
@@ -99,11 +58,161 @@ function ModalStatusBanner({ code, status, onCancel, onRetry }) {
99
58
  ] }) : status === "LOADING" ? /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeDasharray: "22 10" }) }) : null;
100
59
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-status", "data-kind": status, children: [
101
60
  icon,
102
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: text }),
61
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: message }),
103
62
  isLoading && onCancel && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onCancel, children: "Cancel" }),
104
- status === core.StateStatus.ERROR && onRetry && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onRetry, children: "Retry" })
63
+ status === "ERROR" && onRetry && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onRetry, children: "Retry" })
105
64
  ] });
106
65
  }
66
+ var STATUS_CONFIG = {
67
+ none: { label: "Not started", color: "#6b7280", dot: false },
68
+ pending: { label: "Pending review", color: "#f59e0b", dot: true },
69
+ approved: { label: "Verified", color: "#10b981", dot: false },
70
+ rejected: { label: "Rejected", color: "#ef4444", dot: false }
71
+ };
72
+ function KycStatus({ status, className }) {
73
+ const config = STATUS_CONFIG[status] ?? STATUS_CONFIG.none;
74
+ return /* @__PURE__ */ jsxRuntime.jsxs(
75
+ "span",
76
+ {
77
+ className: `pollar-kyc-badge${className ? ` ${className}` : ""}`,
78
+ style: { "--pollar-kyc-color": config.color },
79
+ children: [
80
+ config.dot && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-kyc-badge-dot" }),
81
+ config.label
82
+ ]
83
+ }
84
+ );
85
+ }
86
+ function KycModalTemplate({
87
+ theme,
88
+ accentColor,
89
+ step,
90
+ providers,
91
+ selectedProvider,
92
+ session,
93
+ kycStatus,
94
+ isLoading,
95
+ onSelectProvider,
96
+ onDoneVerifying,
97
+ onClose
98
+ }) {
99
+ const isDark = theme === "dark";
100
+ const cssVars = {
101
+ "--pollar-accent": accentColor,
102
+ "--pollar-buttons-border-radius": "6px",
103
+ "--pollar-buttons-height": "44px",
104
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
105
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
106
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
107
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
108
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb"
109
+ };
110
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
111
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-header", children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-kyc-title", children: "Identity verification" }),
113
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pollar-kyc-subtitle", children: [
114
+ step === "select_provider" && "Choose your verification provider",
115
+ step === "verifying" && `Verifying with ${selectedProvider?.name}`,
116
+ step === "polling" && "Waiting for verification result",
117
+ step === "done" && "Verification complete"
118
+ ] })
119
+ ] }),
120
+ step === "select_provider" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-providers", children: [
121
+ providers.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--pollar-muted)", textAlign: "center" }, children: "No providers available for your country." }),
122
+ providers.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "pollar-kyc-provider-btn", disabled: isLoading, onClick: () => onSelectProvider(p), children: [
123
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-kyc-provider-name", children: p.name }),
124
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-kyc-provider-flow", children: p.flow })
125
+ ] }, p.id))
126
+ ] }),
127
+ step === "verifying" && selectedProvider && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
128
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-kyc-iframe-wrap", children: session?.kycUrl ? /* @__PURE__ */ jsxRuntime.jsx("iframe", { className: "pollar-kyc-iframe", src: session.kycUrl, title: "KYC verification", allow: "camera; microphone" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-iframe-mock", children: [
129
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u{1F512}" }),
130
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: selectedProvider.flow === "form" ? "Form-based KYC \u2014 fields will render here once backend is connected" : "KYC iframe will load here once backend is connected" }),
131
+ /* @__PURE__ */ jsxRuntime.jsxs("code", { style: { fontSize: "0.7rem", opacity: 0.6 }, children: [
132
+ "provider: ",
133
+ selectedProvider.id
134
+ ] })
135
+ ] }) }),
136
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
137
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
138
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onDoneVerifying, children: "I've completed verification" })
139
+ ] })
140
+ ] }),
141
+ step === "polling" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-polling", children: [
142
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-spinner" }),
143
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-kyc-polling-text", children: "Checking verification status\u2026" })
144
+ ] }),
145
+ step === "done" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-kyc-result", children: [
146
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-kyc-result-icon", children: kycStatus === "approved" ? "\u2705" : "\u274C" }),
147
+ /* @__PURE__ */ jsxRuntime.jsx(KycStatus, { status: kycStatus }),
148
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-kyc-result-text", children: kycStatus === "approved" ? "Your identity has been verified successfully." : "Verification was not approved. Please try again." }),
149
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Close" }) })
150
+ ] })
151
+ ] });
152
+ }
153
+ function KycModal({ onClose, country = "MX", level = "basic", onApproved }) {
154
+ const { getClient, styles } = usePollar();
155
+ const [step, setStep] = react.useState("select_provider");
156
+ const [providers, setProviders] = react.useState([]);
157
+ const [selectedProvider, setSelectedProvider] = react.useState(null);
158
+ const [session, setSession] = react.useState(null);
159
+ const [kycStatus, setKycStatus] = react.useState("none");
160
+ const [isLoading, setIsLoading] = react.useState(false);
161
+ const client = getClient();
162
+ const { theme = "light", accentColor = "#005DB4" } = styles;
163
+ react.useEffect(() => {
164
+ setIsLoading(true);
165
+ client.getKycProviders(country).then((result) => setProviders(result.providers)).catch(() => setProviders([])).finally(() => setIsLoading(false));
166
+ }, [country]);
167
+ async function handleSelectProvider(provider) {
168
+ setSelectedProvider(provider);
169
+ setIsLoading(true);
170
+ try {
171
+ const result = await client.resolveKyc(provider.id, level);
172
+ if (result.alreadyApproved) {
173
+ setKycStatus("approved");
174
+ setStep("done");
175
+ onApproved?.();
176
+ return;
177
+ }
178
+ setSession(result);
179
+ setStep("verifying");
180
+ } catch {
181
+ setStep("select_provider");
182
+ } finally {
183
+ setIsLoading(false);
184
+ }
185
+ }
186
+ async function handleDoneVerifying() {
187
+ if (!selectedProvider) return;
188
+ setStep("polling");
189
+ try {
190
+ const finalStatus = await client.pollKycStatus(selectedProvider.id, { intervalMs: 3e3, timeoutMs: 12e4 });
191
+ setKycStatus(finalStatus);
192
+ setStep("done");
193
+ if (finalStatus === "approved") onApproved?.();
194
+ } catch {
195
+ setKycStatus("rejected");
196
+ setStep("done");
197
+ }
198
+ }
199
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
200
+ KycModalTemplate,
201
+ {
202
+ theme,
203
+ accentColor,
204
+ step,
205
+ providers,
206
+ selectedProvider,
207
+ session,
208
+ kycStatus,
209
+ isLoading,
210
+ onSelectProvider: handleSelectProvider,
211
+ onDoneVerifying: handleDoneVerifying,
212
+ onClose
213
+ }
214
+ ) });
215
+ }
107
216
  function EmailCodeInput({ email, onSubmit }) {
108
217
  const [digits, setDigits] = react.useState(["", "", "", "", "", ""]);
109
218
  const inputRefs = react.useRef([]);
@@ -210,6 +319,38 @@ var GoogleButton = ({ disabled, onClick }) => {
210
319
  ] })
211
320
  ] });
212
321
  };
322
+ var AUTH_STATE_MESSAGES = {
323
+ idle: "",
324
+ creating_session: "Initializing\u2026",
325
+ entering_email: "",
326
+ sending_email: "Sending\u2026",
327
+ entering_code: "Code sent \u2014 check your inbox",
328
+ verifying_email_code: "Verifying\u2026",
329
+ opening_oauth: "Redirecting\u2026",
330
+ connecting_wallet: "Connecting wallet\u2026",
331
+ wallet_not_installed: "Wallet not installed",
332
+ authenticating_wallet: "Signing in with wallet\u2026",
333
+ authenticating: "Authenticating\u2026",
334
+ authenticated: "Welcome!",
335
+ error: ""
336
+ };
337
+ function authStateToStatus(step) {
338
+ const loading = [
339
+ "creating_session",
340
+ "sending_email",
341
+ "verifying_email_code",
342
+ "opening_oauth",
343
+ "connecting_wallet",
344
+ "authenticating_wallet",
345
+ "authenticating"
346
+ ];
347
+ const success = ["authenticated", "entering_code"];
348
+ const error = ["error", "wallet_not_installed"];
349
+ if (loading.includes(step)) return "LOADING";
350
+ if (success.includes(step)) return "SUCCESS";
351
+ if (error.includes(step)) return "ERROR";
352
+ return "NONE";
353
+ }
213
354
  function LoginModalTemplate({
214
355
  theme,
215
356
  accentColor,
@@ -219,17 +360,16 @@ function LoginModalTemplate({
219
360
  providers,
220
361
  appName,
221
362
  email = "",
222
- status,
223
- error,
224
363
  onEmailChange,
225
364
  onEmailSubmit,
226
365
  onSocialLogin,
227
366
  onFreighterConnect,
228
367
  onAlbedoConnect,
229
- loginStateCode,
230
- awaitingEmailCode = false,
368
+ authState,
369
+ codeInputKey,
231
370
  onCodeSubmit,
232
- cancelLoginRef,
371
+ onBack,
372
+ onCancel,
233
373
  onRetry
234
374
  }) {
235
375
  const isDark = theme === "dark";
@@ -247,16 +387,22 @@ function LoginModalTemplate({
247
387
  "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
248
388
  "--pollar-error-text": isDark ? "#f87171" : "#dc2626"
249
389
  };
250
- const isLoading = status === core.StateStatus.LOADING;
390
+ const status = authStateToStatus(authState.step);
391
+ const isLoading = status === "LOADING";
392
+ const isEmailCodeError = authState.step === "error" && (authState.errorCode === core.AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === core.AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
393
+ const awaitingEmailCode = authState.step === "entering_code" || authState.step === "verifying_email_code" || isEmailCodeError;
394
+ const statusMessage = authState.step === "error" ? authState.message : AUTH_STATE_MESSAGES[authState.step];
251
395
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
252
396
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-header", children: [
253
397
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-logo-wrap", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl ?? LOGO_POLLAR, alt: "Logo", className: "pollar-logo" }) }),
254
398
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-title", children: appName }),
255
399
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-subtitle", children: "Log in or sign up" })
256
400
  ] }),
257
- awaitingEmailCode ? /* @__PURE__ */ jsxRuntime.jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
258
- }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
259
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-error", children: error }),
401
+ awaitingEmailCode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
402
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-back-btn", onClick: onBack, children: "\u2190 Back" }),
403
+ /* @__PURE__ */ jsxRuntime.jsx(EmailCodeInput, { email, onSubmit: onCodeSubmit ?? (() => {
404
+ }) }, codeInputKey)
405
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
260
406
  emailEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-email-section", children: [
261
407
  /* @__PURE__ */ jsxRuntime.jsx(
262
408
  "input",
@@ -292,74 +438,68 @@ function LoginModalTemplate({
292
438
  ] })
293
439
  ] })
294
440
  ] }),
295
- /* @__PURE__ */ jsxRuntime.jsx(ModalStatusBanner, { code: loginStateCode, status, onCancel: () => cancelLoginRef.current?.(), onRetry }),
441
+ /* @__PURE__ */ jsxRuntime.jsx(
442
+ ModalStatusBanner,
443
+ {
444
+ message: statusMessage,
445
+ status,
446
+ onCancel,
447
+ onRetry: isEmailCodeError ? void 0 : onRetry
448
+ }
449
+ ),
296
450
  /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
297
451
  ] });
298
452
  }
299
- function isLoginCode(code) {
300
- return Object.values(core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION]).some((c) => code.startsWith(c));
301
- }
302
453
  function LoginModal({ onClose }) {
303
454
  const [email, setEmail] = react.useState("");
304
455
  const { getClient, styles, config } = usePollar();
305
- const [status, setStatus] = react.useState(core.StateStatus.NONE);
306
- const [error, setError] = react.useState(null);
307
- const [loginStateCode, setLoginStateCode] = react.useState(null);
308
- const [awaitingEmailCode, setAwaitingEmailCode] = react.useState(false);
309
- const [clientSessionId, setClientSessionId] = react.useState(null);
456
+ const [authState, setAuthState] = react.useState(() => getClient().getAuthState());
457
+ const [codeInputKey, setCodeInputKey] = react.useState(0);
458
+ const pendingEmail = react.useRef(null);
310
459
  react.useEffect(() => {
311
- return getClient().onStateChange((stateEntry) => {
312
- if (stateEntry.var === core.PollarStateVar.AUTHENTICATION && isLoginCode(stateEntry.code)) {
313
- setLoginStateCode(stateEntry.code);
314
- setStatus(stateEntry.status);
315
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].STREAM_POLL_START) {
316
- const data = stateEntry.data;
317
- setClientSessionId(data.clientSessionId);
318
- }
319
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].EMAIL_AUTH_START_SUCCESS) {
320
- const data = stateEntry.data;
321
- if (data?.code === "SDK_EMAIL_CODE_SENT") {
322
- setAwaitingEmailCode(true);
323
- setClientSessionId(data?.content?.clientSessionId);
324
- }
325
- }
326
- if (stateEntry.code === core.STATE_VAR_CODES[core.PollarStateVar.AUTHENTICATION].FETCH_SESSION_SUCCESS) {
327
- setAwaitingEmailCode(false);
328
- setTimeout(onClose, 1e3);
329
- }
460
+ return getClient().onAuthStateChange((next) => {
461
+ setAuthState(next);
462
+ if (next.step === "entering_email" && pendingEmail.current) {
463
+ getClient().sendEmailCode(pendingEmail.current);
464
+ pendingEmail.current = null;
465
+ }
466
+ if (next.step === "error" && next.errorCode === core.AUTH_ERROR_CODES.EMAIL_CODE_INVALID) {
467
+ setCodeInputKey((k) => k + 1);
468
+ }
469
+ if (next.step === "authenticated") {
470
+ setTimeout(onClose, 1e3);
330
471
  }
331
472
  });
332
473
  }, []);
333
474
  const { theme = "light", accentColor = "#005DB4", logoUrl, emailEnabled, embeddedWallets, providers } = styles;
334
475
  function handleClose() {
335
476
  setEmail("");
336
- setError(null);
337
- setAwaitingEmailCode(false);
338
- setClientSessionId(null);
477
+ getClient().cancelLogin();
339
478
  onClose();
340
479
  }
341
- const cancelLoginRef = react.useRef(null);
342
- function handleEmail() {
343
- if (!email) {
344
- return;
345
- }
346
- const { cancelLogin } = getClient().login({ provider: "email", email });
347
- cancelLoginRef.current = cancelLogin;
480
+ function handleEmailSubmit() {
481
+ if (!email) return;
482
+ pendingEmail.current = email;
483
+ getClient().beginEmailLogin();
348
484
  }
349
485
  function handleSocialLogin(provider) {
350
- const { cancelLogin } = getClient().login({ provider });
351
- cancelLoginRef.current = cancelLogin;
486
+ getClient().login({ provider });
352
487
  }
353
488
  function handleWalletConnect(type) {
354
- const { cancelLogin } = getClient().login({ provider: "wallet", type });
355
- cancelLoginRef.current = cancelLogin;
489
+ getClient().loginWallet(type);
356
490
  }
357
- async function handleVerifyCode(code) {
358
- if (!clientSessionId) return;
359
- void getClient().verifyEmailCode(clientSessionId, code);
491
+ function handleVerifyCode(code) {
492
+ getClient().verifyEmailCode(code);
493
+ }
494
+ function handleBack() {
495
+ setEmail("");
496
+ getClient().cancelLogin();
360
497
  }
361
498
  function handleRetry() {
362
499
  getClient().logout();
500
+ if (styles.emailEnabled) {
501
+ getClient().beginEmailLogin();
502
+ }
363
503
  }
364
504
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: handleClose, children: /* @__PURE__ */ jsxRuntime.jsx(
365
505
  LoginModalTemplate,
@@ -378,31 +518,330 @@ function LoginModal({ onClose }) {
378
518
  },
379
519
  appName: config.application?.name ?? "Pollar",
380
520
  email,
381
- status,
382
- error,
383
521
  onEmailChange: setEmail,
384
- onEmailSubmit: handleEmail,
522
+ onEmailSubmit: handleEmailSubmit,
385
523
  onSocialLogin: handleSocialLogin,
386
524
  onFreighterConnect: () => handleWalletConnect(core.WalletType.FREIGHTER),
387
525
  onAlbedoConnect: () => handleWalletConnect(core.WalletType.ALBEDO),
388
- loginStateCode,
389
- awaitingEmailCode,
526
+ authState,
527
+ codeInputKey,
390
528
  onCodeSubmit: handleVerifyCode,
391
- cancelLoginRef,
529
+ onBack: handleBack,
530
+ onCancel: () => getClient().cancelLogin(),
392
531
  onRetry: handleRetry
393
532
  }
394
533
  ) });
395
534
  }
535
+ var RAIL_LABELS = {
536
+ SPEI: "SPEI (Mexico)",
537
+ PIX: "PIX (Brazil)",
538
+ PSE: "PSE (Colombia)",
539
+ ACH: "ACH (US)"
540
+ };
541
+ function RouteDisplay({ quote, onSelect }) {
542
+ return /* @__PURE__ */ jsxRuntime.jsxs(
543
+ "div",
544
+ {
545
+ className: "pollar-ramp-route-card",
546
+ "data-recommended": quote.recommended,
547
+ role: "button",
548
+ tabIndex: 0,
549
+ onClick: () => onSelect(quote),
550
+ onKeyDown: (e) => e.key === "Enter" && onSelect(quote),
551
+ children: [
552
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-route-left", children: [
553
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-route-provider", children: quote.provider }),
554
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-ramp-route-meta", children: [
555
+ RAIL_LABELS[quote.rail] ?? quote.rail,
556
+ " \xB7 ",
557
+ quote.protocol,
558
+ " \xB7 ",
559
+ quote.estimatedTime
560
+ ] })
561
+ ] }),
562
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-route-right", children: [
563
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-ramp-route-fee", children: [
564
+ quote.fee,
565
+ "% fee"
566
+ ] }),
567
+ quote.recommended && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-route-badge", children: "Best rate" })
568
+ ] })
569
+ ]
570
+ }
571
+ );
572
+ }
573
+ var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
574
+ var COUNTRY_CURRENCIES = {
575
+ MX: "MXN",
576
+ BR: "BRL",
577
+ CO: "COP",
578
+ CL: "CLP",
579
+ PE: "PEN",
580
+ AR: "ARS"
581
+ };
582
+ function RampWidgetTemplate({
583
+ theme,
584
+ accentColor,
585
+ step,
586
+ direction,
587
+ amount,
588
+ currency,
589
+ country,
590
+ quotes,
591
+ paymentInstructions,
592
+ isLoading,
593
+ onDirectionChange,
594
+ onAmountChange,
595
+ onCurrencyChange,
596
+ onCountryChange,
597
+ onFindRoute,
598
+ onSelectQuote,
599
+ onCopy,
600
+ onClose
601
+ }) {
602
+ const isDark = theme === "dark";
603
+ const cssVars = {
604
+ "--pollar-accent": accentColor,
605
+ "--pollar-buttons-border-radius": "6px",
606
+ "--pollar-buttons-height": "44px",
607
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
608
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
609
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
610
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
611
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb"
612
+ };
613
+ const stepTitle = {
614
+ input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
615
+ loading_quote: "Finding best route",
616
+ select_route: "Select provider",
617
+ payment_instructions: "Payment instructions"
618
+ };
619
+ const stepSubtitle = {
620
+ input: direction === "onramp" ? "Enter the amount you want to deposit" : "Enter the amount you want to withdraw",
621
+ loading_quote: "Comparing providers in real time\u2026",
622
+ select_route: "All prices include fees",
623
+ payment_instructions: "Send the exact amount to complete your transaction"
624
+ };
625
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
626
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header", children: [
627
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-ramp-title", children: stepTitle[step] }),
628
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
629
+ ] }),
630
+ step === "input" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
631
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-tabs", children: [
632
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-ramp-tab", "data-active": direction === "onramp", onClick: () => onDirectionChange("onramp"), children: "Buy" }),
633
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-ramp-tab", "data-active": direction === "offramp", onClick: () => onDirectionChange("offramp"), children: "Sell" })
634
+ ] }),
635
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-input-row", children: [
636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Amount" }),
638
+ /* @__PURE__ */ jsxRuntime.jsx(
639
+ "input",
640
+ {
641
+ type: "number",
642
+ className: "pollar-ramp-input",
643
+ placeholder: "0.00",
644
+ value: amount,
645
+ min: "0",
646
+ onChange: (e) => onAmountChange(e.target.value)
647
+ }
648
+ )
649
+ ] }),
650
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", style: { maxWidth: 90 }, children: [
651
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Currency" }),
652
+ /* @__PURE__ */ jsxRuntime.jsx(
653
+ "input",
654
+ {
655
+ type: "text",
656
+ className: "pollar-ramp-input",
657
+ placeholder: "MXN",
658
+ value: currency,
659
+ maxLength: 5,
660
+ onChange: (e) => onCurrencyChange(e.target.value.toUpperCase())
661
+ }
662
+ )
663
+ ] })
664
+ ] }),
665
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
666
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Country" }),
667
+ /* @__PURE__ */ jsxRuntime.jsxs(
668
+ "select",
669
+ {
670
+ className: "pollar-ramp-input",
671
+ value: country,
672
+ onChange: (e) => {
673
+ const c = e.target.value;
674
+ onCountryChange(c);
675
+ if (COUNTRY_CURRENCIES[c]) onCurrencyChange(COUNTRY_CURRENCIES[c]);
676
+ },
677
+ children: [
678
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "MX", children: "\u{1F1F2}\u{1F1FD} Mexico" }),
679
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "BR", children: "\u{1F1E7}\u{1F1F7} Brazil" }),
680
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CO", children: "\u{1F1E8}\u{1F1F4} Colombia" }),
681
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CL", children: "\u{1F1E8}\u{1F1F1} Chile" }),
682
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "PE", children: "\u{1F1F5}\u{1F1EA} Peru" }),
683
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "AR", children: "\u{1F1E6}\u{1F1F7} Argentina" })
684
+ ]
685
+ }
686
+ )
687
+ ] }),
688
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
689
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
690
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", disabled: !amount || isLoading, onClick: onFindRoute, children: "Find best route" })
691
+ ] })
692
+ ] }),
693
+ step === "loading_quote" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-loading", children: LOADING_STEPS.map((text, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-loading-row", children: [
694
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-loading-dot" }),
695
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: text })
696
+ ] }, i)) }),
697
+ step === "select_route" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
698
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-route-list", children: quotes.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx(RouteDisplay, { quote: q, onSelect: onSelectQuote }, i)) }),
699
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" })
700
+ ] }),
701
+ step === "payment_instructions" && paymentInstructions && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment", children: [
702
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-payment-title", children: paymentInstructions.type }),
703
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
704
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: paymentInstructions.type === "CLABE" ? "CLABE number" : paymentInstructions.type === "PIX" ? "PIX key" : "Account number" }),
705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-value", children: [
706
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: paymentInstructions.value }),
707
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-ramp-copy-btn", onClick: () => onCopy(paymentInstructions.value), children: "Copy" })
708
+ ] })
709
+ ] }),
710
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Amount to send" }),
712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-value", children: [
713
+ /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
714
+ paymentInstructions.amount.toLocaleString(),
715
+ " ",
716
+ paymentInstructions.currency
717
+ ] }),
718
+ /* @__PURE__ */ jsxRuntime.jsx(
719
+ "button",
720
+ {
721
+ type: "button",
722
+ className: "pollar-ramp-copy-btn",
723
+ onClick: () => onCopy(`${paymentInstructions.amount} ${paymentInstructions.currency}`),
724
+ children: "Copy"
725
+ }
726
+ )
727
+ ] })
728
+ ] }),
729
+ paymentInstructions.expiresAt && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "pollar-ramp-payment-note", children: [
730
+ "Instructions expire at ",
731
+ new Date(paymentInstructions.expiresAt).toLocaleTimeString()
732
+ ] }),
733
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: onClose, children: "Done" })
734
+ ] })
735
+ ] });
736
+ }
737
+ var MOCK_DEFAULT_QUOTES = [
738
+ { quoteId: "meld-default", provider: "Meld", fee: 1.2, feeCurrency: "USD", rate: 1, rail: "ACH", protocol: "REST", estimatedTime: "~20 min", recommended: true }
739
+ ];
740
+ var MOCK_QUOTES = {
741
+ MX: [
742
+ { quoteId: "etherfuse-mx", provider: "Etherfuse", fee: 0.5, feeCurrency: "MXN", rate: 17.2, rail: "SPEI", protocol: "SEP-24", estimatedTime: "~10 min", recommended: true },
743
+ { quoteId: "alfredpay-mx", provider: "AlfredPay", fee: 0.8, feeCurrency: "MXN", rate: 17.1, rail: "SPEI", protocol: "REST", estimatedTime: "~15 min", recommended: false }
744
+ ],
745
+ BR: [{ quoteId: "abroad-br", provider: "Abroad", fee: 0.6, feeCurrency: "BRL", rate: 5.1, rail: "PIX", protocol: "REST", estimatedTime: "~5 min", recommended: true }],
746
+ CO: [
747
+ { quoteId: "abroad-co", provider: "Abroad", fee: 0.7, feeCurrency: "COP", rate: 4100, rail: "PSE", protocol: "REST", estimatedTime: "~10 min", recommended: true },
748
+ { quoteId: "koywe-co", provider: "Koywe", fee: 0.9, feeCurrency: "COP", rate: 4095, rail: "PSE", protocol: "REST", estimatedTime: "~15 min", recommended: false }
749
+ ],
750
+ DEFAULT: MOCK_DEFAULT_QUOTES
751
+ };
752
+ var MOCK_PAYMENT = {
753
+ type: "CLABE",
754
+ value: "646180157088723456",
755
+ amount: 1e3,
756
+ currency: "MXN",
757
+ expiresAt: new Date(Date.now() + 30 * 60 * 1e3).toISOString()
758
+ };
759
+ function RampWidget({ onClose }) {
760
+ const { getClient, walletAddress, styles } = usePollar();
761
+ const [step, setStep] = react.useState("input");
762
+ const [direction, setDirection] = react.useState("onramp");
763
+ const [amount, setAmount] = react.useState("");
764
+ const [currency, setCurrency] = react.useState("MXN");
765
+ const [country, setCountry] = react.useState("MX");
766
+ const [quotes, setQuotes] = react.useState([]);
767
+ const [paymentInstructions, setPaymentInstructions] = react.useState(null);
768
+ const [isLoading, setIsLoading] = react.useState(false);
769
+ const client = getClient();
770
+ const { theme = "light", accentColor = "#005DB4" } = styles;
771
+ async function handleFindRoute() {
772
+ setStep("loading_quote");
773
+ setIsLoading(true);
774
+ try {
775
+ const result = await client.getRampsQuote({
776
+ country,
777
+ amount: Number(amount),
778
+ currency,
779
+ direction
780
+ });
781
+ if (result.quotes) setQuotes(result.quotes);
782
+ } catch {
783
+ await new Promise((r) => setTimeout(r, 1500));
784
+ setQuotes(MOCK_QUOTES[country] ?? MOCK_DEFAULT_QUOTES);
785
+ } finally {
786
+ setIsLoading(false);
787
+ setStep("select_route");
788
+ }
789
+ }
790
+ async function handleSelectQuote(quote) {
791
+ if (!walletAddress) return;
792
+ setIsLoading(true);
793
+ const body = {
794
+ quoteId: `${quote.provider}-${Date.now()}`,
795
+ amount: Number(amount),
796
+ currency,
797
+ country,
798
+ walletAddress
799
+ };
800
+ try {
801
+ const result = await client.createOnRamp(body);
802
+ setPaymentInstructions(result.paymentInstructions);
803
+ } catch {
804
+ await new Promise((r) => setTimeout(r, 800));
805
+ setPaymentInstructions({ ...MOCK_PAYMENT, currency });
806
+ } finally {
807
+ setIsLoading(false);
808
+ setStep("payment_instructions");
809
+ }
810
+ }
811
+ function handleCopy(value) {
812
+ navigator.clipboard.writeText(value).catch(() => {
813
+ });
814
+ }
815
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
816
+ RampWidgetTemplate,
817
+ {
818
+ theme,
819
+ accentColor,
820
+ step,
821
+ direction,
822
+ amount,
823
+ currency,
824
+ country,
825
+ quotes,
826
+ paymentInstructions,
827
+ isLoading,
828
+ onDirectionChange: setDirection,
829
+ onAmountChange: setAmount,
830
+ onCurrencyChange: setCurrency,
831
+ onCountryChange: setCountry,
832
+ onFindRoute: handleFindRoute,
833
+ onSelectQuote: handleSelectQuote,
834
+ onCopy: handleCopy,
835
+ onClose
836
+ }
837
+ ) });
838
+ }
396
839
  function TransactionModalTemplate({
397
840
  theme,
398
841
  accentColor,
399
- transactionStateCode,
400
- status,
401
- buildResult,
402
- submitResult,
842
+ transaction,
403
843
  onClose,
404
- onSignAndSend,
405
- onRetrySignAndSend
844
+ onSignAndSend
406
845
  }) {
407
846
  const isDark = theme === "dark";
408
847
  const cssVars = {
@@ -419,37 +858,49 @@ function TransactionModalTemplate({
419
858
  };
420
859
  const [showXdr, setShowXdr] = react.useState(false);
421
860
  const [copied, setCopied] = react.useState(false);
861
+ const buildData = "buildData" in transaction ? transaction.buildData : null;
862
+ const hash = transaction.step === "success" ? transaction.hash : null;
863
+ const errorDetails = transaction.step === "error" ? transaction.details ?? null : null;
864
+ const isBuilt = transaction.step === "built";
865
+ const isSigning = transaction.step === "signing";
866
+ const isSuccess = transaction.step === "success";
867
+ const isError = transaction.step === "error";
868
+ const showDetails = buildData !== null && (isBuilt || isSigning || isSuccess);
869
+ const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : "public";
870
+ const explorerUrl = hash ? `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}` : null;
422
871
  function handleCopyHash() {
423
- if (!submitResult) return;
424
- navigator.clipboard.writeText(submitResult.hash).then(() => {
872
+ if (!hash) return;
873
+ navigator.clipboard.writeText(hash).then(() => {
425
874
  setCopied(true);
426
875
  setTimeout(() => setCopied(false), 2e3);
427
876
  });
428
877
  }
429
- const explorerNetwork = buildResult?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : "public";
430
- const explorerUrl = submitResult ? `https://stellar.expert/explorer/${explorerNetwork}/tx/${submitResult.hash}` : null;
431
- transactionStateCode.includes("ERROR");
432
- transactionStateCode.includes("SUCCESS");
433
- const isBuilt = buildResult && transactionStateCode === "BUILD_TRANSACTION_SUCCESS";
434
- const isDone = submitResult && transactionStateCode === "SIGN_SEND_TRANSACTION_START";
878
+ const statusMessage = {
879
+ idle: "",
880
+ building: "Building transaction\u2026",
881
+ built: "Ready to sign and send",
882
+ signing: "Signing and sending transaction\u2026",
883
+ success: "Transaction sent successfully",
884
+ error: "Transaction failed"
885
+ };
435
886
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
436
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-header", children: [
437
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-tx-title", children: "Transaction" }),
438
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
887
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
888
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: "Transaction" }),
889
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
439
890
  ] }),
440
- isBuilt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
891
+ showDetails && buildData && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
441
892
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-summary", children: [
442
893
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-tx-summary-title", children: "Details" }),
443
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "pollar-tx-summary-lines", children: buildResult.summary.lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "pollar-tx-summary-line", children: line }, i)) })
894
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "pollar-tx-summary-lines", children: buildData.summary.lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "pollar-tx-summary-line", children: line }, i)) })
444
895
  ] }),
445
896
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta", children: [
446
897
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta-item", children: [
447
898
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-label", children: "Network" }),
448
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildResult.summary.network })
899
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildData.summary.network })
449
900
  ] }),
450
901
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-meta-item", children: [
451
902
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-label", children: "Fee" }),
452
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildResult.summary.fee })
903
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-meta-value", children: buildData.summary.fee })
453
904
  ] })
454
905
  ] }),
455
906
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-xdr", children: [
@@ -468,111 +919,52 @@ function TransactionModalTemplate({
468
919
  ),
469
920
  "Raw transaction (XDR)"
470
921
  ] }),
471
- showXdr && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "pollar-tx-xdr-content", children: buildResult.unsignedXdr })
922
+ showXdr && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "pollar-tx-xdr-content", children: buildData.unsignedXdr })
472
923
  ] })
473
924
  ] }),
474
- submitResult && transactionStateCode === "SIGN_SEND_TRANSACTION_SUCCESS" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-result", children: [
925
+ isSuccess && hash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-result", children: [
475
926
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-result-label", children: "Transaction hash" }),
476
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-result-hash", children: submitResult.hash }),
927
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-tx-result-hash", children: hash }),
477
928
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-result-actions", children: [
478
929
  /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-result-btn", onClick: handleCopyHash, children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
479
930
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
480
931
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
481
- /* @__PURE__ */ jsxRuntime.jsx(
482
- "path",
483
- {
484
- d: "M3.5 7l2.5 2.5 4.5-5",
485
- stroke: "white",
486
- strokeWidth: "1.5",
487
- strokeLinecap: "round",
488
- strokeLinejoin: "round"
489
- }
490
- )
932
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3.5 7l2.5 2.5 4.5-5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
491
933
  ] }),
492
934
  "Copied!"
493
935
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
494
936
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
495
937
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
496
- /* @__PURE__ */ jsxRuntime.jsx(
497
- "path",
498
- {
499
- d: "M3 9H2a1 1 0 01-1-1V2a1 1 0 011-1h6a1 1 0 011 1v1",
500
- stroke: "currentColor",
501
- strokeWidth: "1.5",
502
- strokeLinecap: "round"
503
- }
504
- )
938
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9H2a1 1 0 01-1-1V2a1 1 0 011-1h6a1 1 0 011 1v1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
505
939
  ] }),
506
940
  "Copy hash"
507
941
  ] }) }),
508
942
  explorerUrl && /* @__PURE__ */ jsxRuntime.jsxs("a", { className: "pollar-tx-result-btn", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: [
509
943
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
510
- /* @__PURE__ */ jsxRuntime.jsx(
511
- "path",
512
- {
513
- d: "M5 2H2a1 1 0 00-1 1v8a1 1 0 001 1h8a1 1 0 001-1V8",
514
- stroke: "currentColor",
515
- strokeWidth: "1.5",
516
- strokeLinecap: "round"
517
- }
518
- ),
519
- /* @__PURE__ */ jsxRuntime.jsx(
520
- "path",
521
- {
522
- d: "M8 1h4m0 0v4m0-4L6 7",
523
- stroke: "currentColor",
524
- strokeWidth: "1.5",
525
- strokeLinecap: "round",
526
- strokeLinejoin: "round"
527
- }
528
- )
944
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 2H2a1 1 0 00-1 1v8a1 1 0 001 1h8a1 1 0 001-1V8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
945
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 1h4m0 0v4m0-4L6 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
529
946
  ] }),
530
947
  "View on Explorer"
531
948
  ] })
532
949
  ] })
533
950
  ] }),
534
- isBuilt && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
535
- isDone && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-tx-sign-btn", onClick: onClose, children: "Done" }),
536
- /* @__PURE__ */ jsxRuntime.jsx(
537
- ModalStatusBanner,
538
- {
539
- code: transactionStateCode,
540
- status
541
- }
542
- ),
951
+ isError && errorDetails && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-tx-error-details", children: [
952
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-tx-error-details-label", children: "Error details" }),
953
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "pollar-tx-error-details-content", children: errorDetails })
954
+ ] }),
955
+ isBuilt && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
956
+ isSigning && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", disabled: true, children: "Signing & sending\u2026" }),
957
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onClose, children: "Done" }),
958
+ /* @__PURE__ */ jsxRuntime.jsx(ModalStatusBanner, { message: statusMessage[transaction.step], status: isError ? "ERROR" : isSigning || transaction.step === "building" ? "LOADING" : isSuccess ? "SUCCESS" : "NONE" }),
543
959
  /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
544
960
  ] });
545
961
  }
546
- var isTxBuildResponseContent = (data) => {
547
- if (!data || typeof data !== "object") return false;
548
- const d = data;
549
- return typeof d.unsignedXdr === "string" && typeof d.networkPassphrase === "string" && typeof d.estimatedFee === "string" && d.summary !== null && typeof d.summary === "object";
550
- };
551
- var isTxSignSendResponseContent = (data) => {
552
- if (!data || typeof data !== "object") return false;
553
- const d = data;
554
- return typeof d.hash === "string" && (d.status === "PENDING" || d.status === "SUCCESS" || d.status === "FAILED");
555
- };
556
962
  function TransactionModal({ onClose }) {
557
- const {
558
- getClient,
559
- styles,
560
- state: { transaction }
561
- } = usePollar();
963
+ const { getClient, styles, transaction } = usePollar();
562
964
  const { theme = "light", accentColor = "#005DB4" } = styles;
563
- let buildResult = null;
564
- const transactionStateCode = transaction.code;
565
- const content = transaction.data?.content;
566
- if (isTxBuildResponseContent(content)) {
567
- buildResult = content;
568
- }
569
- let submitResult = null;
570
- if (isTxSignSendResponseContent(content)) {
571
- submitResult = content;
572
- }
573
965
  async function handleSignAndSend() {
574
- if (buildResult) {
575
- await getClient().submitTx(buildResult.unsignedXdr);
966
+ if (transaction.step === "built") {
967
+ await getClient().signAndSubmitTx(transaction.buildData.unsignedXdr);
576
968
  }
577
969
  }
578
970
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -580,91 +972,275 @@ function TransactionModal({ onClose }) {
580
972
  {
581
973
  theme,
582
974
  accentColor,
583
- transactionStateCode,
584
- status: transaction.status,
585
- buildResult,
586
- submitResult,
975
+ transaction,
587
976
  onClose,
588
- onSignAndSend: handleSignAndSend,
589
- onRetrySignAndSend: handleSignAndSend
977
+ onSignAndSend: handleSignAndSend
978
+ }
979
+ ) });
980
+ }
981
+ var PAGE_SIZE = 10;
982
+ function StatusBadge({ status }) {
983
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-hist-item-badge", "data-status": status, children: status });
984
+ }
985
+ function formatDate(iso) {
986
+ return new Date(iso).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
987
+ }
988
+ function TxHistoryModalTemplate({
989
+ theme,
990
+ accentColor,
991
+ txHistory,
992
+ offset,
993
+ onRefresh,
994
+ onPrev,
995
+ onNext,
996
+ onClose
997
+ }) {
998
+ const isDark = theme === "dark";
999
+ const cssVars = {
1000
+ "--pollar-accent": accentColor,
1001
+ "--pollar-buttons-border-radius": "8px",
1002
+ "--pollar-buttons-height": "44px",
1003
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1004
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1005
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
1006
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1007
+ "--pollar-input-bg": isDark ? "#374151" : "rgba(0,0,0,0.04)",
1008
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1009
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a"
1010
+ };
1011
+ const isLoading = txHistory.step === "loading";
1012
+ const records = txHistory.step === "loaded" ? txHistory.data.records : [];
1013
+ const total = txHistory.step === "loaded" ? txHistory.data.total : 0;
1014
+ const hasPrev = offset > 0;
1015
+ const hasNext = offset + PAGE_SIZE < total;
1016
+ const showPagination = txHistory.step === "loaded" && total > PAGE_SIZE;
1017
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
1018
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
1019
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: "Transaction History" }),
1020
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header-actions", children: [
1021
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "pollar-modal-refresh-btn", onClick: onRefresh, disabled: isLoading, children: [
1022
+ /* @__PURE__ */ jsxRuntime.jsxs(
1023
+ "svg",
1024
+ {
1025
+ className: `pollar-modal-refresh-icon${isLoading ? " spinning" : ""}`,
1026
+ width: "13",
1027
+ height: "13",
1028
+ viewBox: "0 0 13 13",
1029
+ fill: "none",
1030
+ "aria-hidden": true,
1031
+ children: [
1032
+ /* @__PURE__ */ jsxRuntime.jsx(
1033
+ "path",
1034
+ {
1035
+ d: "M11.5 6.5a5 5 0 11-1.5-3.536",
1036
+ stroke: "currentColor",
1037
+ strokeWidth: "1.5",
1038
+ strokeLinecap: "round"
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 1v3h-3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1042
+ ]
1043
+ }
1044
+ ),
1045
+ "Refresh"
1046
+ ] }),
1047
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
1048
+ ] })
1049
+ ] }),
1050
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-list", children: [
1051
+ txHistory.step === "idle" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "Click Refresh to load transactions." }),
1052
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "Loading\u2026" }),
1053
+ txHistory.step === "error" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: txHistory.message }),
1054
+ txHistory.step === "loaded" && records.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "No transactions yet." }),
1055
+ records.map((record) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-item", children: [
1056
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-hist-item-summary", children: record.summary }),
1057
+ /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: record.status }),
1058
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-hist-item-meta", children: [
1059
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: record.operation }),
1060
+ record.feeXlm && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1061
+ "\xB7 ",
1062
+ record.feeXlm,
1063
+ " XLM"
1064
+ ] }),
1065
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1066
+ "\xB7 ",
1067
+ formatDate(record.createdAt)
1068
+ ] })
1069
+ ] })
1070
+ ] }, record.id))
1071
+ ] }),
1072
+ showPagination && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-pagination", children: [
1073
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-hist-pagination-info", children: [
1074
+ offset + 1,
1075
+ "\u2013",
1076
+ Math.min(offset + PAGE_SIZE, total),
1077
+ " of ",
1078
+ total
1079
+ ] }),
1080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-hist-pagination-btns", children: [
1081
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-hist-page-btn", onClick: onPrev, disabled: !hasPrev, children: "\u2190 Prev" }),
1082
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-hist-page-btn", onClick: onNext, disabled: !hasNext, children: "Next \u2192" })
1083
+ ] })
1084
+ ] }),
1085
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
1086
+ ] });
1087
+ }
1088
+ var PAGE_SIZE2 = 10;
1089
+ function TxHistoryModal({ onClose }) {
1090
+ const { getClient, styles, txHistory } = usePollar();
1091
+ const { theme = "light", accentColor = "#005DB4" } = styles;
1092
+ const [offset, setOffset] = react.useState(0);
1093
+ function load(nextOffset) {
1094
+ setOffset(nextOffset);
1095
+ void getClient().fetchTxHistory({ limit: PAGE_SIZE2, offset: nextOffset });
1096
+ }
1097
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(
1098
+ TxHistoryModalTemplate,
1099
+ {
1100
+ theme,
1101
+ accentColor,
1102
+ txHistory,
1103
+ offset,
1104
+ onRefresh: () => load(offset),
1105
+ onPrev: () => load(Math.max(0, offset - PAGE_SIZE2)),
1106
+ onNext: () => load(offset + PAGE_SIZE2),
1107
+ onClose
590
1108
  }
591
1109
  ) });
592
1110
  }
1111
+ function formatBalance(balance) {
1112
+ const n = parseFloat(balance);
1113
+ return isNaN(n) ? balance : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
1114
+ }
1115
+ function cropAddress(address) {
1116
+ if (address.length <= 16) return address;
1117
+ return `${address.slice(0, 8)}...${address.slice(-8)}`;
1118
+ }
1119
+ function BalanceItem({ record }) {
1120
+ const balanceDiffers = record.balance !== record.available;
1121
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-item", children: [
1122
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-asset", children: record.code }),
1123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-amounts", children: [
1124
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-bal-amount", children: formatBalance(record.balance) }),
1125
+ balanceDiffers && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-bal-available", children: [
1126
+ formatBalance(record.available),
1127
+ " available"
1128
+ ] })
1129
+ ] })
1130
+ ] });
1131
+ }
1132
+ function WalletBalanceModal({ onClose }) {
1133
+ const { getBalance, walletAddress, styles } = usePollar();
1134
+ const { theme = "light", accentColor = "#005DB4" } = styles;
1135
+ const isDark = theme === "dark";
1136
+ const cssVars = {
1137
+ "--pollar-accent": accentColor,
1138
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
1139
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
1140
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
1141
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
1142
+ "--pollar-input-bg": isDark ? "#374151" : "rgba(0,0,0,0.04)",
1143
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626"
1144
+ };
1145
+ const [status, setStatus] = react.useState("loading");
1146
+ const [data, setData] = react.useState(null);
1147
+ async function load() {
1148
+ setStatus("loading");
1149
+ const result = await getBalance();
1150
+ if (result) {
1151
+ setData(result);
1152
+ setStatus("loaded");
1153
+ } else {
1154
+ setStatus("error");
1155
+ }
1156
+ }
1157
+ react.useEffect(() => {
1158
+ void load();
1159
+ }, []);
1160
+ const isLoading = status === "loading";
1161
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-bal-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
1162
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
1163
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: "Wallet Balance" }),
1164
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header-actions", children: [
1165
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "pollar-modal-refresh-btn", onClick: load, disabled: isLoading, children: [
1166
+ /* @__PURE__ */ jsxRuntime.jsxs(
1167
+ "svg",
1168
+ {
1169
+ className: `pollar-modal-refresh-icon${isLoading ? " spinning" : ""}`,
1170
+ width: "13",
1171
+ height: "13",
1172
+ viewBox: "0 0 13 13",
1173
+ fill: "none",
1174
+ "aria-hidden": true,
1175
+ children: [
1176
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M11.5 6.5a5 5 0 11-1.5-3.536", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
1177
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 1v3h-3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
1178
+ ]
1179
+ }
1180
+ ),
1181
+ "Refresh"
1182
+ ] }),
1183
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
1184
+ ] })
1185
+ ] }),
1186
+ walletAddress && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-bal-address", children: cropAddress(walletAddress) }),
1187
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "Loading\u2026" }),
1188
+ status === "error" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "Failed to load balances. Check your connection." }),
1189
+ status === "loaded" && data && !data.exists && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-empty", children: [
1190
+ "Account not found on ",
1191
+ data.network,
1192
+ "."
1193
+ ] }),
1194
+ status === "loaded" && data?.exists && data.balances.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-empty", children: "No balances found." }),
1195
+ status === "loaded" && data?.exists && data.balances.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-bal-list", children: data.balances.map((b) => /* @__PURE__ */ jsxRuntime.jsx(BalanceItem, { record: b }, b.code + (b.issuer ?? ""))) }),
1196
+ /* @__PURE__ */ jsxRuntime.jsx(PollarModalFooter, {})
1197
+ ] }) });
1198
+ }
593
1199
  var emptyResponse = {
594
1200
  application: {
595
1201
  name: ""
596
1202
  },
597
1203
  styles: {}
598
1204
  };
599
- async function fetchRemoteConfig(api) {
600
- try {
601
- const { data, error } = await api.GET(`/applications/config`);
602
- if (!data || error) {
603
- return emptyResponse;
604
- }
605
- return data.content;
606
- } catch {
607
- return emptyResponse;
608
- }
1205
+ async function fetchRemoteConfig(client) {
1206
+ const content = await client.getAppConfig();
1207
+ return content ?? emptyResponse;
609
1208
  }
610
1209
  var PollarContext = react.createContext(null);
611
1210
  function PollarProvider({ config, styles: propStyles, children }) {
612
1211
  const [pollarClient] = react.useState(() => new core.PollarClient(config));
613
- const [stellarClient] = react.useState(() => new core.StellarClient(config.stellarNetwork || "testnet"));
1212
+ const [networkState, setNetworkState] = react.useState(() => pollarClient.getNetworkState());
1213
+ const stellarClient = react.useMemo(() => {
1214
+ const network = networkState.step === "connected" ? networkState.network : "testnet";
1215
+ return new core.StellarClient(network);
1216
+ }, [networkState]);
614
1217
  const [sessionState, setSessionState] = react.useState(null);
615
- const [state, setState] = react.useState({
616
- network: {
617
- var: "network",
618
- code: core.STATE_VAR_CODES.network.NONE,
619
- status: core.StateStatus.NONE,
620
- level: "info",
621
- ts: 0
622
- },
623
- authentication: {
624
- var: "authentication",
625
- code: core.STATE_VAR_CODES.authentication.NONE,
626
- status: core.StateStatus.NONE,
627
- level: "info",
628
- ts: 0
629
- },
630
- transaction: {
631
- var: "transaction",
632
- code: core.STATE_VAR_CODES.transaction.NONE,
633
- status: core.StateStatus.NONE,
634
- level: "info",
635
- ts: 0
636
- }
637
- });
1218
+ const [transaction, setTransaction] = react.useState({ step: "idle" });
1219
+ const [txHistory, setTxHistory] = react.useState({ step: "idle" });
638
1220
  const [remoteConfig, setRemoteConfig] = react.useState(emptyResponse);
639
1221
  const [styles, setStyles] = react.useState(propStyles ?? {});
640
1222
  react.useEffect(() => {
641
- return pollarClient.onStateChange((stateEntry) => {
642
- setState((prevState) => {
643
- if (JSON.stringify(prevState[stateEntry.var]) !== JSON.stringify(stateEntry)) {
644
- return {
645
- ...prevState,
646
- [stateEntry.var]: stateEntry
647
- };
648
- }
649
- return prevState;
650
- });
651
- if (stateEntry.var === "authentication") {
652
- if ((stateEntry.code === core.STATE_VAR_CODES.authentication.SESSION_STORED || core.STATE_VAR_CODES.authentication.RESTORED_SESSION_SUCCESS) && core.isValidSession(stateEntry.data)) {
653
- setSessionState((prevState) => {
654
- if (JSON.stringify(prevState) !== JSON.stringify(stateEntry.data)) {
655
- return stateEntry.data;
656
- }
657
- return prevState;
658
- });
659
- }
660
- if (stateEntry.code === core.STATE_VAR_CODES.authentication.LOGOUT) {
661
- setSessionState(null);
662
- }
1223
+ return pollarClient.onTransactionStateChange(setTransaction);
1224
+ }, [pollarClient]);
1225
+ react.useEffect(() => {
1226
+ return pollarClient.onTxHistoryStateChange(setTxHistory);
1227
+ }, [pollarClient]);
1228
+ react.useEffect(() => {
1229
+ return pollarClient.onNetworkStateChange((state) => {
1230
+ setNetworkState(state);
1231
+ });
1232
+ }, [pollarClient]);
1233
+ react.useEffect(() => {
1234
+ return pollarClient.onAuthStateChange((authState) => {
1235
+ if (authState.step === "authenticated") {
1236
+ setSessionState((prev) => JSON.stringify(prev) !== JSON.stringify(authState.session) ? authState.session : prev);
1237
+ } else if (authState.step === "idle") {
1238
+ setSessionState(null);
663
1239
  }
664
1240
  });
665
1241
  }, [pollarClient]);
666
1242
  react.useEffect(() => {
667
- fetchRemoteConfig(pollarClient.getApi()).then((fetched) => {
1243
+ fetchRemoteConfig(pollarClient).then((fetched) => {
668
1244
  setRemoteConfig(fetched);
669
1245
  setStyles({
670
1246
  ...fetched.styles,
@@ -675,43 +1251,62 @@ function PollarProvider({ config, styles: propStyles, children }) {
675
1251
  setStyles(propStyles ?? {});
676
1252
  });
677
1253
  }, [pollarClient]);
1254
+ react.useEffect(() => {
1255
+ if (transaction.step !== "idle") {
1256
+ setTransactionModalOpen(true);
1257
+ }
1258
+ }, [transaction.step]);
678
1259
  const [loginModalOpen, setLoginModalOpen] = react.useState(false);
679
1260
  const [transactionModalOpen, setTransactionModalOpen] = react.useState(false);
1261
+ const [kycModalOpen, setKycModalOpen] = react.useState(false);
1262
+ const [kycModalOptions, setKycModalOptions] = react.useState({});
1263
+ const [rampWidgetOpen, setRampWidgetOpen] = react.useState(false);
1264
+ const [txHistoryModalOpen, setTxHistoryModalOpen] = react.useState(false);
1265
+ const [walletBalanceModalOpen, setWalletBalanceModalOpen] = react.useState(false);
680
1266
  const contextValue = react.useMemo(
681
1267
  () => ({
682
1268
  walletAddress: sessionState?.wallet?.publicKey || "",
683
1269
  getClient: () => pollarClient,
684
- // client
685
- state,
1270
+ transaction,
686
1271
  login: (options) => pollarClient.login(options),
687
1272
  logout: () => pollarClient.logout(),
688
- isAuthenticated: pollarClient.isAuthenticated(),
1273
+ isAuthenticated: !!sessionState?.wallet?.publicKey,
689
1274
  buildTx: (operation, params, options) => pollarClient.buildTx(operation, params, options),
690
- submitTx: (signedXdr) => pollarClient.submitTx(signedXdr),
691
- // react
692
- sendTransaction: (operation, params, options) => {
693
- void pollarClient.buildTx(operation, params, options);
694
- setTransactionModalOpen(true);
695
- },
1275
+ signAndSubmitTx: (signedXdr) => pollarClient.signAndSubmitTx(signedXdr),
696
1276
  openTransactionModal: () => setTransactionModalOpen(true),
697
1277
  openLoginModal: () => setLoginModalOpen(true),
1278
+ openKycModal: (options = {}) => {
1279
+ setKycModalOptions(options);
1280
+ setKycModalOpen(true);
1281
+ },
1282
+ openRampWidget: () => setRampWidgetOpen(true),
1283
+ txHistory,
1284
+ openTxHistoryModal: () => setTxHistoryModalOpen(true),
1285
+ openWalletBalanceModal: () => setWalletBalanceModalOpen(true),
1286
+ network: networkState.step === "connected" ? networkState.network : "testnet",
1287
+ setNetwork: (network) => pollarClient.setNetwork(network),
698
1288
  config: remoteConfig,
699
1289
  styles,
700
- // stellar
701
- async getBalance(publicKey) {
702
- const pk = publicKey || sessionState?.wallet?.publicKey;
703
- if (pk) {
704
- return await stellarClient.getBalances(pk);
705
- }
706
- return { success: false, errorCode: "NO_WALLET_FOUND", balances: [] };
707
- }
1290
+ getBalance: (publicKey) => pollarClient.getWalletBalance(publicKey)
708
1291
  }),
709
- [sessionState, remoteConfig, styles, pollarClient, state]
1292
+ [sessionState, remoteConfig, styles, pollarClient, transaction, txHistory, networkState, stellarClient]
710
1293
  );
711
1294
  return /* @__PURE__ */ jsxRuntime.jsxs(PollarContext.Provider, { value: contextValue, children: [
712
1295
  children,
713
1296
  loginModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setLoginModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(LoginModal, { onClose: () => setLoginModalOpen(false) }) }),
714
- transactionModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setTransactionModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(TransactionModal, { onClose: () => setTransactionModalOpen(false) }) })
1297
+ transactionModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setTransactionModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(TransactionModal, { onClose: () => setTransactionModalOpen(false) }) }),
1298
+ kycModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setKycModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(
1299
+ KycModal,
1300
+ {
1301
+ onClose: () => setKycModalOpen(false),
1302
+ ...kycModalOptions.country !== void 0 && { country: kycModalOptions.country },
1303
+ ...kycModalOptions.level !== void 0 && { level: kycModalOptions.level },
1304
+ ...kycModalOptions.onApproved !== void 0 && { onApproved: kycModalOptions.onApproved }
1305
+ }
1306
+ ) }),
1307
+ rampWidgetOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setRampWidgetOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(RampWidget, { onClose: () => setRampWidgetOpen(false) }) }),
1308
+ txHistoryModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setTxHistoryModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(TxHistoryModal, { onClose: () => setTxHistoryModalOpen(false) }) }),
1309
+ walletBalanceModalOpen && /* @__PURE__ */ jsxRuntime.jsx(ModalErrorBoundary, { onClose: () => setWalletBalanceModalOpen(false), children: /* @__PURE__ */ jsxRuntime.jsx(WalletBalanceModal, { onClose: () => setWalletBalanceModalOpen(false) }) })
715
1310
  ] });
716
1311
  }
717
1312
  function usePollar() {
@@ -729,7 +1324,7 @@ function ButtonLogo() {
729
1324
  return /* @__PURE__ */ jsxRuntime.jsx("img", { src: LOGO_POLLAR, alt: "Pollar", width: 22, height: 22, className: "wallet-btn-logo" });
730
1325
  }
731
1326
  function WalletButton() {
732
- const { getClient, walletAddress, styles, openLoginModal } = usePollar();
1327
+ const { getClient, walletAddress, styles, openLoginModal, openTxHistoryModal, openWalletBalanceModal } = usePollar();
733
1328
  const [open, setOpen] = react.useState(false);
734
1329
  const [copied, setCopied] = react.useState(false);
735
1330
  const wrapperRef = react.useRef(null);
@@ -801,6 +1396,72 @@ function WalletButton() {
801
1396
  ),
802
1397
  copied ? "Copied!" : "Copy address"
803
1398
  ] }),
1399
+ /* @__PURE__ */ jsxRuntime.jsxs(
1400
+ "button",
1401
+ {
1402
+ className: "wallet-dropdown-item",
1403
+ style: { color: itemColor },
1404
+ onClick: () => {
1405
+ setOpen(false);
1406
+ openWalletBalanceModal();
1407
+ },
1408
+ children: [
1409
+ /* @__PURE__ */ jsxRuntime.jsxs(
1410
+ "svg",
1411
+ {
1412
+ width: "14",
1413
+ height: "14",
1414
+ viewBox: "0 0 24 24",
1415
+ fill: "none",
1416
+ stroke: "currentColor",
1417
+ strokeWidth: "2",
1418
+ strokeLinecap: "round",
1419
+ strokeLinejoin: "round",
1420
+ children: [
1421
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "4", width: "22", height: "16", rx: "2", ry: "2" }),
1422
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16", cy: "12", r: "2" }),
1423
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22 8H12" })
1424
+ ]
1425
+ }
1426
+ ),
1427
+ "Wallet balance"
1428
+ ]
1429
+ }
1430
+ ),
1431
+ /* @__PURE__ */ jsxRuntime.jsxs(
1432
+ "button",
1433
+ {
1434
+ className: "wallet-dropdown-item",
1435
+ style: { color: itemColor },
1436
+ onClick: () => {
1437
+ setOpen(false);
1438
+ openTxHistoryModal();
1439
+ },
1440
+ children: [
1441
+ /* @__PURE__ */ jsxRuntime.jsxs(
1442
+ "svg",
1443
+ {
1444
+ width: "14",
1445
+ height: "14",
1446
+ viewBox: "0 0 24 24",
1447
+ fill: "none",
1448
+ stroke: "currentColor",
1449
+ strokeWidth: "2",
1450
+ strokeLinecap: "round",
1451
+ strokeLinejoin: "round",
1452
+ children: [
1453
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
1454
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "14,2 14,8 20,8" }),
1455
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
1456
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
1457
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "10,9 9,9 8,9" })
1458
+ ]
1459
+ }
1460
+ ),
1461
+ "Transaction history"
1462
+ ]
1463
+ }
1464
+ ),
804
1465
  /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "wallet-dropdown-item danger", onClick: handleLogout, children: [
805
1466
  /* @__PURE__ */ jsxRuntime.jsxs(
806
1467
  "svg",
@@ -826,7 +1487,11 @@ function WalletButton() {
826
1487
  ] });
827
1488
  }
828
1489
 
1490
+ exports.KycModal = KycModal;
1491
+ exports.KycStatus = KycStatus;
829
1492
  exports.PollarProvider = PollarProvider;
1493
+ exports.RampWidget = RampWidget;
1494
+ exports.RouteDisplay = RouteDisplay;
830
1495
  exports.WalletButton = WalletButton;
831
1496
  exports.usePollar = usePollar;
832
1497
  //# sourceMappingURL=index.js.map