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