@pollar/react 0.4.5 → 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, AUTH_ERROR_CODES, 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,26 +37,16 @@ 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 TRANSACTION_CODE_MESSAGES = {
46
- NONE: "",
47
- BUILD_TRANSACTION_START: "Building transaction\u2026",
48
- BUILD_TRANSACTION_SUCCESS: "Transaction built, ready to sign and send",
49
- BUILD_TRANSACTION_ERROR: "Failed to build transaction",
50
- BUILD_TRANSACTION_ERROR_NO_WALLET: "No wallet connected",
51
- SIGN_SEND_TRANSACTION_START: "Signing and sending transaction\u2026",
52
- SIGN_SEND_TRANSACTION_SUCCESS: "Transaction signed",
53
- SIGN_SEND_TRANSACTION_ERROR: "Signing rejected"
54
- };
55
45
  function ModalStatusBanner({ message, status, onCancel, onRetry }) {
56
- if (!message && status === StateStatus.NONE) {
46
+ if (!message && status === "NONE") {
57
47
  return /* @__PURE__ */ jsx("div", { className: "pollar-status" });
58
48
  }
59
- const isLoading = status === StateStatus.LOADING;
49
+ const isLoading = status === "LOADING";
60
50
  const icon = status === "ERROR" ? /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
61
51
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
62
52
  /* @__PURE__ */ jsx("path", { d: "M4.5 4.5l5 5M9.5 4.5l-5 5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round" })
@@ -68,9 +58,159 @@ function ModalStatusBanner({ message, status, onCancel, onRetry }) {
68
58
  icon,
69
59
  /* @__PURE__ */ jsx("span", { children: message }),
70
60
  isLoading && onCancel && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-status-cancel", onClick: onCancel, children: "Cancel" }),
71
- 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" })
72
62
  ] });
73
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
+ }
74
214
  function EmailCodeInput({ email, onSubmit }) {
75
215
  const [digits, setDigits] = useState(["", "", "", "", "", ""]);
76
216
  const inputRefs = useRef([]);
@@ -204,10 +344,10 @@ function authStateToStatus(step) {
204
344
  ];
205
345
  const success = ["authenticated", "entering_code"];
206
346
  const error = ["error", "wallet_not_installed"];
207
- if (loading.includes(step)) return StateStatus.LOADING;
208
- if (success.includes(step)) return StateStatus.SUCCESS;
209
- if (error.includes(step)) return StateStatus.ERROR;
210
- return StateStatus.NONE;
347
+ if (loading.includes(step)) return "LOADING";
348
+ if (success.includes(step)) return "SUCCESS";
349
+ if (error.includes(step)) return "ERROR";
350
+ return "NONE";
211
351
  }
212
352
  function LoginModalTemplate({
213
353
  theme,
@@ -246,7 +386,7 @@ function LoginModalTemplate({
246
386
  "--pollar-error-text": isDark ? "#f87171" : "#dc2626"
247
387
  };
248
388
  const status = authStateToStatus(authState.step);
249
- const isLoading = status === StateStatus.LOADING;
389
+ const isLoading = status === "LOADING";
250
390
  const isEmailCodeError = authState.step === "error" && (authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_EXPIRED || authState.errorCode === AUTH_ERROR_CODES.EMAIL_CODE_INVALID);
251
391
  const awaitingEmailCode = authState.step === "entering_code" || authState.step === "verifying_email_code" || isEmailCodeError;
252
392
  const statusMessage = authState.step === "error" ? authState.message : AUTH_STATE_MESSAGES[authState.step];
@@ -341,7 +481,7 @@ function LoginModal({ onClose }) {
341
481
  getClient().beginEmailLogin();
342
482
  }
343
483
  function handleSocialLogin(provider) {
344
- getClient().loginOAuth(provider);
484
+ getClient().login({ provider });
345
485
  }
346
486
  function handleWalletConnect(type) {
347
487
  getClient().loginWallet(type);
@@ -390,16 +530,316 @@ function LoginModal({ onClose }) {
390
530
  }
391
531
  ) });
392
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
+ }
393
837
  function TransactionModalTemplate({
394
838
  theme,
395
839
  accentColor,
396
- transactionStateCode,
397
- status,
398
- buildResult,
399
- submitResult,
840
+ transaction,
400
841
  onClose,
401
- onSignAndSend,
402
- onRetrySignAndSend
842
+ onSignAndSend
403
843
  }) {
404
844
  const isDark = theme === "dark";
405
845
  const cssVars = {
@@ -416,37 +856,49 @@ function TransactionModalTemplate({
416
856
  };
417
857
  const [showXdr, setShowXdr] = useState(false);
418
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;
419
869
  function handleCopyHash() {
420
- if (!submitResult) return;
421
- navigator.clipboard.writeText(submitResult.hash).then(() => {
870
+ if (!hash) return;
871
+ navigator.clipboard.writeText(hash).then(() => {
422
872
  setCopied(true);
423
873
  setTimeout(() => setCopied(false), 2e3);
424
874
  });
425
875
  }
426
- const explorerNetwork = buildResult?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : "public";
427
- const explorerUrl = submitResult ? `https://stellar.expert/explorer/${explorerNetwork}/tx/${submitResult.hash}` : null;
428
- transactionStateCode.includes("ERROR");
429
- transactionStateCode.includes("SUCCESS");
430
- const isBuilt = buildResult && transactionStateCode === "BUILD_TRANSACTION_SUCCESS";
431
- 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
+ };
432
884
  return /* @__PURE__ */ jsxs("div", { className: "pollar-tx-modal", "data-theme": theme, style: cssVars, onClick: (e) => e.stopPropagation(), children: [
433
- /* @__PURE__ */ jsxs("div", { className: "pollar-tx-header", children: [
434
- /* @__PURE__ */ jsx("h2", { className: "pollar-tx-title", children: "Transaction" }),
435
- /* @__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" }) }) })
436
888
  ] }),
437
- isBuilt && /* @__PURE__ */ jsxs(Fragment, { children: [
889
+ showDetails && buildData && /* @__PURE__ */ jsxs(Fragment, { children: [
438
890
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-summary", children: [
439
891
  /* @__PURE__ */ jsx("p", { className: "pollar-tx-summary-title", children: "Details" }),
440
- /* @__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)) })
441
893
  ] }),
442
894
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-meta", children: [
443
895
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-meta-item", children: [
444
896
  /* @__PURE__ */ jsx("span", { className: "pollar-tx-meta-label", children: "Network" }),
445
- /* @__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 })
446
898
  ] }),
447
899
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-meta-item", children: [
448
900
  /* @__PURE__ */ jsx("span", { className: "pollar-tx-meta-label", children: "Fee" }),
449
- /* @__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 })
450
902
  ] })
451
903
  ] }),
452
904
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-xdr", children: [
@@ -465,111 +917,52 @@ function TransactionModalTemplate({
465
917
  ),
466
918
  "Raw transaction (XDR)"
467
919
  ] }),
468
- 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 })
469
921
  ] })
470
922
  ] }),
471
- 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: [
472
924
  /* @__PURE__ */ jsx("span", { className: "pollar-tx-result-label", children: "Transaction hash" }),
473
- /* @__PURE__ */ jsx("span", { className: "pollar-tx-result-hash", children: submitResult.hash }),
925
+ /* @__PURE__ */ jsx("span", { className: "pollar-tx-result-hash", children: hash }),
474
926
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-result-actions", children: [
475
927
  /* @__PURE__ */ jsx("button", { className: "pollar-tx-result-btn", onClick: handleCopyHash, children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
476
928
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
477
929
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
478
- /* @__PURE__ */ jsx(
479
- "path",
480
- {
481
- d: "M3.5 7l2.5 2.5 4.5-5",
482
- stroke: "white",
483
- strokeWidth: "1.5",
484
- strokeLinecap: "round",
485
- strokeLinejoin: "round"
486
- }
487
- )
930
+ /* @__PURE__ */ jsx("path", { d: "M3.5 7l2.5 2.5 4.5-5", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })
488
931
  ] }),
489
932
  "Copied!"
490
933
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
491
934
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
492
935
  /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: "currentColor", strokeWidth: "1.5" }),
493
- /* @__PURE__ */ jsx(
494
- "path",
495
- {
496
- d: "M3 9H2a1 1 0 01-1-1V2a1 1 0 011-1h6a1 1 0 011 1v1",
497
- stroke: "currentColor",
498
- strokeWidth: "1.5",
499
- strokeLinecap: "round"
500
- }
501
- )
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" })
502
937
  ] }),
503
938
  "Copy hash"
504
939
  ] }) }),
505
940
  explorerUrl && /* @__PURE__ */ jsxs("a", { className: "pollar-tx-result-btn", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: [
506
941
  /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 13 13", fill: "none", "aria-hidden": true, children: [
507
- /* @__PURE__ */ jsx(
508
- "path",
509
- {
510
- d: "M5 2H2a1 1 0 00-1 1v8a1 1 0 001 1h8a1 1 0 001-1V8",
511
- stroke: "currentColor",
512
- strokeWidth: "1.5",
513
- strokeLinecap: "round"
514
- }
515
- ),
516
- /* @__PURE__ */ jsx(
517
- "path",
518
- {
519
- d: "M8 1h4m0 0v4m0-4L6 7",
520
- stroke: "currentColor",
521
- strokeWidth: "1.5",
522
- strokeLinecap: "round",
523
- strokeLinejoin: "round"
524
- }
525
- )
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" })
526
944
  ] }),
527
945
  "View on Explorer"
528
946
  ] })
529
947
  ] })
530
948
  ] }),
531
- isBuilt && /* @__PURE__ */ jsx("button", { className: "pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
532
- isDone && /* @__PURE__ */ jsx("button", { className: "pollar-tx-sign-btn", onClick: onClose, children: "Done" }),
533
- /* @__PURE__ */ jsx(
534
- ModalStatusBanner,
535
- {
536
- message: TRANSACTION_CODE_MESSAGES[transactionStateCode] ?? "",
537
- status
538
- }
539
- ),
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" }),
540
957
  /* @__PURE__ */ jsx(PollarModalFooter, {})
541
958
  ] });
542
959
  }
543
- var isTxBuildResponseContent = (data) => {
544
- if (!data || typeof data !== "object") return false;
545
- const d = data;
546
- return typeof d.unsignedXdr === "string" && typeof d.networkPassphrase === "string" && typeof d.estimatedFee === "string" && d.summary !== null && typeof d.summary === "object";
547
- };
548
- var isTxSignSendResponseContent = (data) => {
549
- if (!data || typeof data !== "object") return false;
550
- const d = data;
551
- return typeof d.hash === "string" && (d.status === "PENDING" || d.status === "SUCCESS" || d.status === "FAILED");
552
- };
553
960
  function TransactionModal({ onClose }) {
554
- const {
555
- getClient,
556
- styles,
557
- state: { transaction }
558
- } = usePollar();
961
+ const { getClient, styles, transaction } = usePollar();
559
962
  const { theme = "light", accentColor = "#005DB4" } = styles;
560
- let buildResult = null;
561
- const transactionStateCode = transaction.code;
562
- const content = transaction.data?.content;
563
- if (isTxBuildResponseContent(content)) {
564
- buildResult = content;
565
- }
566
- let submitResult = null;
567
- if (isTxSignSendResponseContent(content)) {
568
- submitResult = content;
569
- }
570
963
  async function handleSignAndSend() {
571
- if (buildResult) {
572
- await getClient().submitTx(buildResult.unsignedXdr);
964
+ if (transaction.step === "built") {
965
+ await getClient().signAndSubmitTx(transaction.buildData.unsignedXdr);
573
966
  }
574
967
  }
575
968
  return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
@@ -577,64 +970,262 @@ function TransactionModal({ onClose }) {
577
970
  {
578
971
  theme,
579
972
  accentColor,
580
- transactionStateCode,
581
- status: transaction.status,
582
- buildResult,
583
- submitResult,
973
+ transaction,
584
974
  onClose,
585
- onSignAndSend: handleSignAndSend,
586
- 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
587
1106
  }
588
1107
  ) });
589
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
+ }
590
1197
  var emptyResponse = {
591
1198
  application: {
592
1199
  name: ""
593
1200
  },
594
1201
  styles: {}
595
1202
  };
596
- async function fetchRemoteConfig(api) {
597
- try {
598
- const { data, error } = await api.GET(`/applications/config`);
599
- if (!data || error) {
600
- return emptyResponse;
601
- }
602
- return data.content;
603
- } catch {
604
- return emptyResponse;
605
- }
1203
+ async function fetchRemoteConfig(client) {
1204
+ const content = await client.getAppConfig();
1205
+ return content ?? emptyResponse;
606
1206
  }
607
1207
  var PollarContext = createContext(null);
608
1208
  function PollarProvider({ config, styles: propStyles, children }) {
609
1209
  const [pollarClient] = useState(() => new PollarClient(config));
610
- 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]);
611
1215
  const [sessionState, setSessionState] = useState(null);
612
- const [state, setState] = useState({
613
- network: {
614
- var: "network",
615
- code: STATE_VAR_CODES.network.NONE,
616
- status: StateStatus.NONE,
617
- level: "info",
618
- ts: 0
619
- },
620
- transaction: {
621
- var: "transaction",
622
- code: STATE_VAR_CODES.transaction.NONE,
623
- status: StateStatus.NONE,
624
- level: "info",
625
- ts: 0
626
- }
627
- });
1216
+ const [transaction, setTransaction] = useState({ step: "idle" });
1217
+ const [txHistory, setTxHistory] = useState({ step: "idle" });
628
1218
  const [remoteConfig, setRemoteConfig] = useState(emptyResponse);
629
1219
  const [styles, setStyles] = useState(propStyles ?? {});
630
1220
  useEffect(() => {
631
- return pollarClient.onStateChange((stateEntry) => {
632
- setState((prevState) => {
633
- if (JSON.stringify(prevState[stateEntry.var]) !== JSON.stringify(stateEntry)) {
634
- return { ...prevState, [stateEntry.var]: stateEntry };
635
- }
636
- return prevState;
637
- });
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);
638
1229
  });
639
1230
  }, [pollarClient]);
640
1231
  useEffect(() => {
@@ -647,7 +1238,7 @@ function PollarProvider({ config, styles: propStyles, children }) {
647
1238
  });
648
1239
  }, [pollarClient]);
649
1240
  useEffect(() => {
650
- fetchRemoteConfig(pollarClient.getApi()).then((fetched) => {
1241
+ fetchRemoteConfig(pollarClient).then((fetched) => {
651
1242
  setRemoteConfig(fetched);
652
1243
  setStyles({
653
1244
  ...fetched.styles,
@@ -658,40 +1249,62 @@ function PollarProvider({ config, styles: propStyles, children }) {
658
1249
  setStyles(propStyles ?? {});
659
1250
  });
660
1251
  }, [pollarClient]);
1252
+ useEffect(() => {
1253
+ if (transaction.step !== "idle") {
1254
+ setTransactionModalOpen(true);
1255
+ }
1256
+ }, [transaction.step]);
661
1257
  const [loginModalOpen, setLoginModalOpen] = useState(false);
662
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);
663
1264
  const contextValue = useMemo(
664
1265
  () => ({
665
1266
  walletAddress: sessionState?.wallet?.publicKey || "",
666
1267
  getClient: () => pollarClient,
667
- state,
1268
+ transaction,
668
1269
  login: (options) => pollarClient.login(options),
669
1270
  logout: () => pollarClient.logout(),
670
- isAuthenticated: pollarClient.isAuthenticated(),
1271
+ isAuthenticated: !!sessionState?.wallet?.publicKey,
671
1272
  buildTx: (operation, params, options) => pollarClient.buildTx(operation, params, options),
672
- submitTx: (signedXdr) => pollarClient.submitTx(signedXdr),
673
- sendTransaction: (operation, params, options) => {
674
- void pollarClient.buildTx(operation, params, options);
675
- setTransactionModalOpen(true);
676
- },
1273
+ signAndSubmitTx: (signedXdr) => pollarClient.signAndSubmitTx(signedXdr),
677
1274
  openTransactionModal: () => setTransactionModalOpen(true),
678
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),
679
1286
  config: remoteConfig,
680
1287
  styles,
681
- async getBalance(publicKey) {
682
- const pk = publicKey || sessionState?.wallet?.publicKey;
683
- if (pk) {
684
- return await stellarClient.getBalances(pk);
685
- }
686
- return { success: false, errorCode: "NO_WALLET_FOUND", balances: [] };
687
- }
1288
+ getBalance: (publicKey) => pollarClient.getWalletBalance(publicKey)
688
1289
  }),
689
- [sessionState, remoteConfig, styles, pollarClient, state]
1290
+ [sessionState, remoteConfig, styles, pollarClient, transaction, txHistory, networkState, stellarClient]
690
1291
  );
691
1292
  return /* @__PURE__ */ jsxs(PollarContext.Provider, { value: contextValue, children: [
692
1293
  children,
693
1294
  loginModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setLoginModalOpen(false), children: /* @__PURE__ */ jsx(LoginModal, { onClose: () => setLoginModalOpen(false) }) }),
694
- 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) }) })
695
1308
  ] });
696
1309
  }
697
1310
  function usePollar() {
@@ -709,7 +1322,7 @@ function ButtonLogo() {
709
1322
  return /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR, alt: "Pollar", width: 22, height: 22, className: "wallet-btn-logo" });
710
1323
  }
711
1324
  function WalletButton() {
712
- const { getClient, walletAddress, styles, openLoginModal } = usePollar();
1325
+ const { getClient, walletAddress, styles, openLoginModal, openTxHistoryModal, openWalletBalanceModal } = usePollar();
713
1326
  const [open, setOpen] = useState(false);
714
1327
  const [copied, setCopied] = useState(false);
715
1328
  const wrapperRef = useRef(null);
@@ -781,6 +1394,72 @@ function WalletButton() {
781
1394
  ),
782
1395
  copied ? "Copied!" : "Copy address"
783
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
+ ),
784
1463
  /* @__PURE__ */ jsxs("button", { className: "wallet-dropdown-item danger", onClick: handleLogout, children: [
785
1464
  /* @__PURE__ */ jsxs(
786
1465
  "svg",
@@ -806,6 +1485,6 @@ function WalletButton() {
806
1485
  ] });
807
1486
  }
808
1487
 
809
- export { PollarProvider, WalletButton, usePollar };
1488
+ export { KycModal, KycStatus, PollarProvider, RampWidget, RouteDisplay, WalletButton, usePollar };
810
1489
  //# sourceMappingURL=index.mjs.map
811
1490
  //# sourceMappingURL=index.mjs.map