@pollar/react 0.10.0 → 0.10.1-rc.1
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.css +47 -9
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +291 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +291 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1033,7 +1033,7 @@ var PollarModalFooter = () => {
|
|
|
1033
1033
|
/* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
|
|
1034
1034
|
/* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
|
|
1035
1035
|
"v",
|
|
1036
|
-
"0.10.
|
|
1036
|
+
"0.10.1-rc.1"
|
|
1037
1037
|
] })
|
|
1038
1038
|
] })
|
|
1039
1039
|
] });
|
|
@@ -2457,14 +2457,12 @@ function RouteDisplay({ quote, onSelect }) {
|
|
|
2457
2457
|
);
|
|
2458
2458
|
}
|
|
2459
2459
|
var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
AR: "ARS"
|
|
2467
|
-
};
|
|
2460
|
+
function flagEmoji(code) {
|
|
2461
|
+
const cc = code.trim().toUpperCase();
|
|
2462
|
+
if (!/^[A-Z]{2}$/.test(cc)) return "";
|
|
2463
|
+
const base = 127462;
|
|
2464
|
+
return String.fromCodePoint(base + (cc.charCodeAt(0) - 65), base + (cc.charCodeAt(1) - 65));
|
|
2465
|
+
}
|
|
2468
2466
|
var STATUS_LABEL = {
|
|
2469
2467
|
pending: "Pending",
|
|
2470
2468
|
processing: "Processing",
|
|
@@ -2479,6 +2477,9 @@ function RampWidgetTemplate({
|
|
|
2479
2477
|
amount,
|
|
2480
2478
|
currency,
|
|
2481
2479
|
country,
|
|
2480
|
+
countries,
|
|
2481
|
+
countriesLoading,
|
|
2482
|
+
refreshing,
|
|
2482
2483
|
quotes,
|
|
2483
2484
|
isLoading,
|
|
2484
2485
|
provider,
|
|
@@ -2497,6 +2498,7 @@ function RampWidgetTemplate({
|
|
|
2497
2498
|
onOpenKyc,
|
|
2498
2499
|
onCompleteWithdraw,
|
|
2499
2500
|
onRetry,
|
|
2501
|
+
onRefresh,
|
|
2500
2502
|
onClose
|
|
2501
2503
|
}) {
|
|
2502
2504
|
const isDark = theme === "dark";
|
|
@@ -2535,9 +2537,35 @@ function RampWidgetTemplate({
|
|
|
2535
2537
|
error: "Please try again"
|
|
2536
2538
|
};
|
|
2537
2539
|
return /* @__PURE__ */ jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
|
|
2538
|
-
/* @__PURE__ */ jsxs("div", { className: "pollar-
|
|
2539
|
-
/* @__PURE__ */
|
|
2540
|
-
|
|
2540
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
|
|
2541
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-header-text", children: [
|
|
2542
|
+
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: stepTitle[step] }),
|
|
2543
|
+
/* @__PURE__ */ jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
|
|
2544
|
+
] }),
|
|
2545
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
|
|
2546
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onRefresh, disabled: refreshing, "aria-label": "Refresh", children: /* @__PURE__ */ jsx(
|
|
2547
|
+
"svg",
|
|
2548
|
+
{
|
|
2549
|
+
className: refreshing ? "pollar-modal-refresh-icon spinning" : "pollar-modal-refresh-icon",
|
|
2550
|
+
width: "16",
|
|
2551
|
+
height: "16",
|
|
2552
|
+
viewBox: "0 0 16 16",
|
|
2553
|
+
fill: "none",
|
|
2554
|
+
"aria-hidden": true,
|
|
2555
|
+
children: /* @__PURE__ */ jsx(
|
|
2556
|
+
"path",
|
|
2557
|
+
{
|
|
2558
|
+
d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
|
|
2559
|
+
stroke: "currentColor",
|
|
2560
|
+
strokeWidth: "1.6",
|
|
2561
|
+
strokeLinecap: "round",
|
|
2562
|
+
strokeLinejoin: "round"
|
|
2563
|
+
}
|
|
2564
|
+
)
|
|
2565
|
+
}
|
|
2566
|
+
) }),
|
|
2567
|
+
/* @__PURE__ */ jsx("button", { type: "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" }) }) })
|
|
2568
|
+
] })
|
|
2541
2569
|
] }),
|
|
2542
2570
|
step === "input" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2543
2571
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-tabs", children: [
|
|
@@ -2594,30 +2622,25 @@ function RampWidgetTemplate({
|
|
|
2594
2622
|
] }),
|
|
2595
2623
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-field", children: [
|
|
2596
2624
|
/* @__PURE__ */ jsx("label", { className: "pollar-ramp-label", children: "Country" }),
|
|
2597
|
-
/* @__PURE__ */ jsxs(
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
const c = e.target.value;
|
|
2604
|
-
onCountryChange(c);
|
|
2605
|
-
if (COUNTRY_CURRENCIES[c]) onCurrencyChange(COUNTRY_CURRENCIES[c]);
|
|
2606
|
-
},
|
|
2607
|
-
children: [
|
|
2608
|
-
/* @__PURE__ */ jsx("option", { value: "MX", children: "\u{1F1F2}\u{1F1FD} Mexico" }),
|
|
2609
|
-
/* @__PURE__ */ jsx("option", { value: "BR", children: "\u{1F1E7}\u{1F1F7} Brazil" }),
|
|
2610
|
-
/* @__PURE__ */ jsx("option", { value: "CO", children: "\u{1F1E8}\u{1F1F4} Colombia" }),
|
|
2611
|
-
/* @__PURE__ */ jsx("option", { value: "CL", children: "\u{1F1E8}\u{1F1F1} Chile" }),
|
|
2612
|
-
/* @__PURE__ */ jsx("option", { value: "PE", children: "\u{1F1F5}\u{1F1EA} Peru" }),
|
|
2613
|
-
/* @__PURE__ */ jsx("option", { value: "AR", children: "\u{1F1E6}\u{1F1F7} Argentina" })
|
|
2614
|
-
]
|
|
2615
|
-
}
|
|
2616
|
-
)
|
|
2625
|
+
countriesLoading ? /* @__PURE__ */ jsx("div", { className: "pollar-ramp-input pollar-ramp-input-loading", children: "Loading countries\u2026" }) : countries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsx("select", { className: "pollar-ramp-input", value: country, onChange: (e) => onCountryChange(e.target.value), children: countries.map((c) => /* @__PURE__ */ jsxs("option", { value: c.code, children: [
|
|
2626
|
+
flagEmoji(c.code),
|
|
2627
|
+
" ",
|
|
2628
|
+
c.code,
|
|
2629
|
+
c.currency ? ` \u2014 ${c.currency}` : ""
|
|
2630
|
+
] }, c.code)) })
|
|
2617
2631
|
] }),
|
|
2618
2632
|
/* @__PURE__ */ jsxs("div", { className: "pollar-modal-actions", children: [
|
|
2619
2633
|
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
|
|
2620
|
-
/* @__PURE__ */ jsx(
|
|
2634
|
+
/* @__PURE__ */ jsx(
|
|
2635
|
+
"button",
|
|
2636
|
+
{
|
|
2637
|
+
type: "button",
|
|
2638
|
+
className: "pollar-btn-primary",
|
|
2639
|
+
disabled: !amount || isLoading || countriesLoading || countries.length === 0,
|
|
2640
|
+
onClick: onFindRoute,
|
|
2641
|
+
children: "Find best route"
|
|
2642
|
+
}
|
|
2643
|
+
)
|
|
2621
2644
|
] })
|
|
2622
2645
|
] }),
|
|
2623
2646
|
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: [
|
|
@@ -2635,13 +2658,7 @@ function RampWidgetTemplate({
|
|
|
2635
2658
|
] }),
|
|
2636
2659
|
/* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
2637
2660
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Status" }),
|
|
2638
|
-
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsx(
|
|
2639
|
-
"code",
|
|
2640
|
-
{
|
|
2641
|
-
style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 },
|
|
2642
|
-
children: txStatus ? STATUS_LABEL[txStatus] : "Processing"
|
|
2643
|
-
}
|
|
2644
|
-
) })
|
|
2661
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsx("code", { style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 }, children: txStatus ? STATUS_LABEL[txStatus] : "Processing" }) })
|
|
2645
2662
|
] }),
|
|
2646
2663
|
stellarTxHash && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
|
|
2647
2664
|
/* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Stellar tx" }),
|
|
@@ -2677,8 +2694,11 @@ function RampWidget({ onClose }) {
|
|
|
2677
2694
|
const [step, setStep] = useState("input");
|
|
2678
2695
|
const [direction, setDirection] = useState("onramp");
|
|
2679
2696
|
const [amount, setAmount] = useState("");
|
|
2680
|
-
const [currency, setCurrency] = useState("
|
|
2681
|
-
const [country, setCountry] = useState("
|
|
2697
|
+
const [currency, setCurrency] = useState("");
|
|
2698
|
+
const [country, setCountry] = useState("");
|
|
2699
|
+
const [countries, setCountries] = useState([]);
|
|
2700
|
+
const [countriesLoading, setCountriesLoading] = useState(true);
|
|
2701
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
2682
2702
|
const [quotes, setQuotes] = useState([]);
|
|
2683
2703
|
const [isLoading, setIsLoading] = useState(false);
|
|
2684
2704
|
const [txId, setTxId] = useState(null);
|
|
@@ -2710,6 +2730,50 @@ function RampWidget({ onClose }) {
|
|
|
2710
2730
|
clearInterval(id);
|
|
2711
2731
|
};
|
|
2712
2732
|
}, [step, txId, txStatus, client]);
|
|
2733
|
+
async function loadCountries(resetSelection) {
|
|
2734
|
+
setCountriesLoading(true);
|
|
2735
|
+
try {
|
|
2736
|
+
const { countries: list } = await client.getRampCountries();
|
|
2737
|
+
setCountries(list);
|
|
2738
|
+
const first = list[0];
|
|
2739
|
+
const stillValid = list.some((c) => c.code === country);
|
|
2740
|
+
if (first && (resetSelection || !stillValid)) {
|
|
2741
|
+
setCountry(first.code);
|
|
2742
|
+
if (first.currency) setCurrency(first.currency);
|
|
2743
|
+
}
|
|
2744
|
+
} catch {
|
|
2745
|
+
setCountries([]);
|
|
2746
|
+
} finally {
|
|
2747
|
+
setCountriesLoading(false);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
useEffect(() => {
|
|
2751
|
+
loadCountries(true);
|
|
2752
|
+
}, []);
|
|
2753
|
+
function handleCountryChange(code) {
|
|
2754
|
+
setCountry(code);
|
|
2755
|
+
const match = countries.find((c) => c.code === code);
|
|
2756
|
+
if (match?.currency) setCurrency(match.currency);
|
|
2757
|
+
}
|
|
2758
|
+
async function handleRefresh() {
|
|
2759
|
+
if (refreshing) return;
|
|
2760
|
+
setRefreshing(true);
|
|
2761
|
+
try {
|
|
2762
|
+
await loadCountries(false);
|
|
2763
|
+
if (step === "select_route") {
|
|
2764
|
+
const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
|
|
2765
|
+
setQuotes(result.quotes ?? []);
|
|
2766
|
+
} else if (step === "status" && txId) {
|
|
2767
|
+
const tx = await client.getRampTransaction(txId);
|
|
2768
|
+
setTxStatus(tx.status);
|
|
2769
|
+
if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
|
|
2770
|
+
if (tx.kycUrl) setKycUrl(tx.kycUrl);
|
|
2771
|
+
}
|
|
2772
|
+
} catch {
|
|
2773
|
+
} finally {
|
|
2774
|
+
setRefreshing(false);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2713
2777
|
function resetToInput() {
|
|
2714
2778
|
setStep("input");
|
|
2715
2779
|
setQuotes([]);
|
|
@@ -2815,6 +2879,9 @@ function RampWidget({ onClose }) {
|
|
|
2815
2879
|
amount,
|
|
2816
2880
|
currency,
|
|
2817
2881
|
country,
|
|
2882
|
+
countries,
|
|
2883
|
+
countriesLoading,
|
|
2884
|
+
refreshing,
|
|
2818
2885
|
quotes,
|
|
2819
2886
|
isLoading,
|
|
2820
2887
|
provider,
|
|
@@ -2827,12 +2894,13 @@ function RampWidget({ onClose }) {
|
|
|
2827
2894
|
onDirectionChange: setDirection,
|
|
2828
2895
|
onAmountChange: setAmount,
|
|
2829
2896
|
onCurrencyChange: setCurrency,
|
|
2830
|
-
onCountryChange:
|
|
2897
|
+
onCountryChange: handleCountryChange,
|
|
2831
2898
|
onFindRoute: handleFindRoute,
|
|
2832
2899
|
onSelectQuote: handleSelectQuote,
|
|
2833
2900
|
onOpenKyc: handleOpenKyc,
|
|
2834
2901
|
onCompleteWithdraw: handleCompleteWithdraw,
|
|
2835
2902
|
onRetry: resetToInput,
|
|
2903
|
+
onRefresh: handleRefresh,
|
|
2836
2904
|
onClose
|
|
2837
2905
|
}
|
|
2838
2906
|
) });
|
|
@@ -3505,7 +3573,6 @@ var PROVIDER_LABELS = {
|
|
|
3505
3573
|
soroswap: "Soroswap",
|
|
3506
3574
|
sdex: "Stellar DEX"
|
|
3507
3575
|
};
|
|
3508
|
-
var IMPLEMENTED_PROVIDERS = ["auto", "aquarius"];
|
|
3509
3576
|
function formatAmount2(value) {
|
|
3510
3577
|
const n = parseFloat(value);
|
|
3511
3578
|
return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
|
|
@@ -3528,6 +3595,9 @@ function SwapModalTemplate({
|
|
|
3528
3595
|
formError,
|
|
3529
3596
|
isLoadingData,
|
|
3530
3597
|
smartUnsupported,
|
|
3598
|
+
configLoading,
|
|
3599
|
+
swapUnavailable,
|
|
3600
|
+
buyNeedsTrustline,
|
|
3531
3601
|
transaction,
|
|
3532
3602
|
showXdr,
|
|
3533
3603
|
copied,
|
|
@@ -3537,8 +3607,10 @@ function SwapModalTemplate({
|
|
|
3537
3607
|
isInProgress,
|
|
3538
3608
|
onClose,
|
|
3539
3609
|
onBack,
|
|
3610
|
+
onRefresh,
|
|
3540
3611
|
onSelectSell,
|
|
3541
3612
|
onSelectBuy,
|
|
3613
|
+
onAddCustomToken,
|
|
3542
3614
|
onAmountChange,
|
|
3543
3615
|
onProviderChange,
|
|
3544
3616
|
onSwap,
|
|
@@ -3548,6 +3620,21 @@ function SwapModalTemplate({
|
|
|
3548
3620
|
onDone
|
|
3549
3621
|
}) {
|
|
3550
3622
|
const isDark = theme === "dark";
|
|
3623
|
+
const [customOpen, setCustomOpen] = useState(false);
|
|
3624
|
+
const [customCode, setCustomCode] = useState("");
|
|
3625
|
+
const [customIssuer, setCustomIssuer] = useState("");
|
|
3626
|
+
const [customError, setCustomError] = useState("");
|
|
3627
|
+
const submitCustomToken = () => {
|
|
3628
|
+
const err = onAddCustomToken(customCode, customIssuer);
|
|
3629
|
+
if (err) {
|
|
3630
|
+
setCustomError(err);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
setCustomCode("");
|
|
3634
|
+
setCustomIssuer("");
|
|
3635
|
+
setCustomError("");
|
|
3636
|
+
setCustomOpen(false);
|
|
3637
|
+
};
|
|
3551
3638
|
const cssVars = {
|
|
3552
3639
|
"--pollar-accent": accentColor,
|
|
3553
3640
|
"--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
|
|
@@ -3575,9 +3662,34 @@ function SwapModalTemplate({
|
|
|
3575
3662
|
showBack && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M10 3L5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
|
|
3576
3663
|
/* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: title })
|
|
3577
3664
|
] }),
|
|
3578
|
-
!isInProgress && /* @__PURE__ */
|
|
3665
|
+
!isInProgress && /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
|
|
3666
|
+
step === "form" && /* @__PURE__ */ jsx(
|
|
3667
|
+
"button",
|
|
3668
|
+
{
|
|
3669
|
+
type: "button",
|
|
3670
|
+
className: "pollar-modal-close",
|
|
3671
|
+
onClick: onRefresh,
|
|
3672
|
+
disabled: configLoading || isLoadingData,
|
|
3673
|
+
"aria-label": "Refresh",
|
|
3674
|
+
title: "Refresh balances and options",
|
|
3675
|
+
children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
3676
|
+
"path",
|
|
3677
|
+
{
|
|
3678
|
+
d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2.5v3h-3",
|
|
3679
|
+
stroke: "currentColor",
|
|
3680
|
+
strokeWidth: "1.6",
|
|
3681
|
+
strokeLinecap: "round",
|
|
3682
|
+
strokeLinejoin: "round"
|
|
3683
|
+
}
|
|
3684
|
+
) })
|
|
3685
|
+
}
|
|
3686
|
+
),
|
|
3687
|
+
/* @__PURE__ */ jsx("button", { type: "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" }) }) })
|
|
3688
|
+
] })
|
|
3579
3689
|
] }),
|
|
3580
|
-
step === "form" && /* @__PURE__ */
|
|
3690
|
+
step === "form" && configLoading && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "Loading swap options\u2026" }),
|
|
3691
|
+
step === "form" && !configLoading && swapUnavailable && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Swap is not available for this app." }),
|
|
3692
|
+
step === "form" && !configLoading && !swapUnavailable && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3581
3693
|
smartUnsupported && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: "Swaps are not yet available for smart (passkey) wallets." }),
|
|
3582
3694
|
/* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
|
|
3583
3695
|
/* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "You pay" }),
|
|
@@ -3641,7 +3753,45 @@ function SwapModalTemplate({
|
|
|
3641
3753
|
] }, assetOptionKey(o)))
|
|
3642
3754
|
]
|
|
3643
3755
|
}
|
|
3644
|
-
)
|
|
3756
|
+
),
|
|
3757
|
+
!isLoadingData && (customOpen ? /* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom", children: [
|
|
3758
|
+
/* @__PURE__ */ jsx(
|
|
3759
|
+
"input",
|
|
3760
|
+
{
|
|
3761
|
+
className: "pollar-input",
|
|
3762
|
+
type: "text",
|
|
3763
|
+
placeholder: "Code (e.g. USDC)",
|
|
3764
|
+
value: customCode,
|
|
3765
|
+
onChange: (e) => setCustomCode(e.target.value)
|
|
3766
|
+
}
|
|
3767
|
+
),
|
|
3768
|
+
/* @__PURE__ */ jsx(
|
|
3769
|
+
"input",
|
|
3770
|
+
{
|
|
3771
|
+
className: "pollar-input",
|
|
3772
|
+
type: "text",
|
|
3773
|
+
placeholder: "Issuer (G...)",
|
|
3774
|
+
value: customIssuer,
|
|
3775
|
+
onChange: (e) => setCustomIssuer(e.target.value)
|
|
3776
|
+
}
|
|
3777
|
+
),
|
|
3778
|
+
customError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: customError }),
|
|
3779
|
+
/* @__PURE__ */ jsxs("div", { className: "pollar-swap-custom-actions", children: [
|
|
3780
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
|
|
3781
|
+
/* @__PURE__ */ jsx(
|
|
3782
|
+
"button",
|
|
3783
|
+
{
|
|
3784
|
+
type: "button",
|
|
3785
|
+
className: "pollar-swap-custom-cancel",
|
|
3786
|
+
onClick: () => {
|
|
3787
|
+
setCustomOpen(false);
|
|
3788
|
+
setCustomError("");
|
|
3789
|
+
},
|
|
3790
|
+
children: "Cancel"
|
|
3791
|
+
}
|
|
3792
|
+
)
|
|
3793
|
+
] })
|
|
3794
|
+
] }) : /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setCustomOpen(true), children: "+ Add a token by code / issuer" }))
|
|
3645
3795
|
] }),
|
|
3646
3796
|
/* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
|
|
3647
3797
|
/* @__PURE__ */ jsx("label", { className: "pollar-send-label", children: "Route" }),
|
|
@@ -3651,13 +3801,7 @@ function SwapModalTemplate({
|
|
|
3651
3801
|
className: "pollar-input pollar-send-select",
|
|
3652
3802
|
value: provider,
|
|
3653
3803
|
onChange: (e) => onProviderChange(e.target.value),
|
|
3654
|
-
children: providers.map((p) => {
|
|
3655
|
-
const implemented = IMPLEMENTED_PROVIDERS.includes(p);
|
|
3656
|
-
return /* @__PURE__ */ jsxs("option", { value: p, disabled: !implemented, children: [
|
|
3657
|
-
PROVIDER_LABELS[p],
|
|
3658
|
-
implemented ? "" : " (coming soon)"
|
|
3659
|
-
] }, p);
|
|
3660
|
-
})
|
|
3804
|
+
children: providers.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: PROVIDER_LABELS[p] }, p))
|
|
3661
3805
|
}
|
|
3662
3806
|
)
|
|
3663
3807
|
] }),
|
|
@@ -3694,8 +3838,13 @@ function SwapModalTemplate({
|
|
|
3694
3838
|
] })
|
|
3695
3839
|
] }),
|
|
3696
3840
|
!quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
|
|
3841
|
+
buyNeedsTrustline && selectedBuy && /* @__PURE__ */ jsxs("div", { className: "pollar-swap-trustline-notice", children: [
|
|
3842
|
+
"To receive ",
|
|
3843
|
+
selectedBuy.code,
|
|
3844
|
+
" your wallet needs a trustline. Swapping will create it first (~0.5 XLM reserve, refundable if you later remove it)."
|
|
3845
|
+
] }),
|
|
3697
3846
|
formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
|
|
3698
|
-
/* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: "Swap" }) })
|
|
3847
|
+
/* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: buyNeedsTrustline ? "Create trustline & swap" : "Swap" }) })
|
|
3699
3848
|
] }),
|
|
3700
3849
|
step === "tx" && /* @__PURE__ */ jsx(
|
|
3701
3850
|
TxStatusView,
|
|
@@ -3717,14 +3866,18 @@ function SwapModalTemplate({
|
|
|
3717
3866
|
] });
|
|
3718
3867
|
}
|
|
3719
3868
|
var QUOTE_DEBOUNCE_MS = 400;
|
|
3720
|
-
var ALL_PROVIDERS = ["auto", "aquarius", "soroswap", "sdex"];
|
|
3721
3869
|
function toRef(a) {
|
|
3722
3870
|
if (a.type === "native") return { type: "native" };
|
|
3723
3871
|
if (a.type === "credit_alphanum4") return { type: "credit_alphanum4", code: a.code, issuer: a.issuer };
|
|
3724
3872
|
return { type: "credit_alphanum12", code: a.code, issuer: a.issuer };
|
|
3725
3873
|
}
|
|
3874
|
+
function catalogRef(code, issuer) {
|
|
3875
|
+
return code.length <= 4 ? { type: "credit_alphanum4", code, issuer } : { type: "credit_alphanum12", code, issuer };
|
|
3876
|
+
}
|
|
3726
3877
|
function SwapModal({ onClose }) {
|
|
3727
3878
|
const {
|
|
3879
|
+
getSwapConfig,
|
|
3880
|
+
getSwapTokens,
|
|
3728
3881
|
getSwapQuote,
|
|
3729
3882
|
swap,
|
|
3730
3883
|
walletBalance,
|
|
@@ -3744,6 +3897,9 @@ function SwapModal({ onClose }) {
|
|
|
3744
3897
|
const [selectedBuy, setSelectedBuy] = useState(null);
|
|
3745
3898
|
const [amount, setAmount] = useState("");
|
|
3746
3899
|
const [provider, setProvider] = useState("auto");
|
|
3900
|
+
const [venues, setVenues] = useState(null);
|
|
3901
|
+
const [catalogTokens, setCatalogTokens] = useState([]);
|
|
3902
|
+
const [customTokens, setCustomTokens] = useState([]);
|
|
3747
3903
|
const [quotes, setQuotes] = useState([]);
|
|
3748
3904
|
const [quoteLoading, setQuoteLoading] = useState(false);
|
|
3749
3905
|
const [quoteError, setQuoteError] = useState("");
|
|
@@ -3756,6 +3912,26 @@ function SwapModal({ onClose }) {
|
|
|
3756
3912
|
void refreshWalletBalance();
|
|
3757
3913
|
void refreshAssets();
|
|
3758
3914
|
}, [refreshWalletBalance, refreshAssets]);
|
|
3915
|
+
const loadConfig = useCallback(() => {
|
|
3916
|
+
setVenues(null);
|
|
3917
|
+
return getSwapConfig().then(setVenues).catch(() => setVenues([]));
|
|
3918
|
+
}, [getSwapConfig]);
|
|
3919
|
+
useEffect(() => {
|
|
3920
|
+
void loadConfig();
|
|
3921
|
+
}, [loadConfig]);
|
|
3922
|
+
const loadCatalog = useCallback(
|
|
3923
|
+
() => getSwapTokens().then(setCatalogTokens).catch(() => setCatalogTokens([])),
|
|
3924
|
+
[getSwapTokens]
|
|
3925
|
+
);
|
|
3926
|
+
useEffect(() => {
|
|
3927
|
+
void loadCatalog();
|
|
3928
|
+
}, [loadCatalog]);
|
|
3929
|
+
function handleRefresh() {
|
|
3930
|
+
void refreshWalletBalance();
|
|
3931
|
+
void refreshAssets();
|
|
3932
|
+
void loadConfig();
|
|
3933
|
+
void loadCatalog();
|
|
3934
|
+
}
|
|
3759
3935
|
useEffect(
|
|
3760
3936
|
() => () => {
|
|
3761
3937
|
if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
|
|
@@ -3767,11 +3943,47 @@ function SwapModal({ onClose }) {
|
|
|
3767
3943
|
const isLoadingData = walletBalance.step === "loading" || enabledAssets.step === "loading";
|
|
3768
3944
|
const sellOptions = balances.filter((b) => parseFloat(b.available) > 0).map((b) => ({ ref: toRef(b), code: b.code, issuer: b.issuer, available: b.available, enabledInApp: b.enabledInApp }));
|
|
3769
3945
|
const buyKeyOfSell = selectedSell ? `${selectedSell.code}:${selectedSell.issuer ?? "native"}` : "";
|
|
3770
|
-
const
|
|
3771
|
-
const
|
|
3946
|
+
const optKey = (o) => `${o.code}:${o.issuer ?? "native"}`;
|
|
3947
|
+
const enabledBuy = assetRecords.map((a) => ({
|
|
3948
|
+
ref: toRef(a),
|
|
3949
|
+
code: a.code,
|
|
3950
|
+
issuer: a.issuer,
|
|
3951
|
+
enabledInApp: a.enabledInApp
|
|
3952
|
+
}));
|
|
3953
|
+
const enabledKeys = new Set(enabledBuy.map(optKey));
|
|
3954
|
+
const catalogBuy = catalogTokens.map((tk) => ({
|
|
3955
|
+
ref: catalogRef(tk.code, tk.issuer),
|
|
3956
|
+
code: tk.code,
|
|
3957
|
+
issuer: tk.issuer,
|
|
3958
|
+
enabledInApp: false
|
|
3959
|
+
})).filter((o) => !enabledKeys.has(optKey(o)));
|
|
3960
|
+
const knownKeys = /* @__PURE__ */ new Set([...enabledKeys, ...catalogBuy.map(optKey)]);
|
|
3961
|
+
const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o)));
|
|
3962
|
+
const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter(
|
|
3963
|
+
(o) => optKey(o) !== buyKeyOfSell
|
|
3964
|
+
);
|
|
3965
|
+
function addCustomToken(code, issuer) {
|
|
3966
|
+
const c = code.trim().toUpperCase();
|
|
3967
|
+
const i = issuer.trim();
|
|
3968
|
+
if (c.length < 1 || c.length > 12) return "Code must be 1-12 characters";
|
|
3969
|
+
if (i.length !== 56 || !i.startsWith("G")) return "Issuer must be a Stellar address (G..., 56 chars)";
|
|
3970
|
+
const opt = { ref: catalogRef(c, i), code: c, issuer: i, enabledInApp: false };
|
|
3971
|
+
setCustomTokens((prev) => prev.some((o) => optKey(o) === optKey(opt)) ? prev : [...prev, opt]);
|
|
3972
|
+
setSelectedBuy(opt);
|
|
3973
|
+
return null;
|
|
3974
|
+
}
|
|
3975
|
+
const buyNeedsTrustline = (() => {
|
|
3976
|
+
if (!selectedBuy || smartUnsupported) return false;
|
|
3977
|
+
if (selectedBuy.ref.type === "native") return false;
|
|
3978
|
+
const rec = assetRecords.find((a) => a.code === selectedBuy.code && a.issuer === selectedBuy.issuer);
|
|
3979
|
+
return !rec?.trustlineEstablished;
|
|
3980
|
+
})();
|
|
3981
|
+
const configLoading = venues === null;
|
|
3982
|
+
const swapUnavailable = venues !== null && venues.length === 0;
|
|
3983
|
+
const providers = venues && venues.length > 0 ? ["auto", ...venues] : [];
|
|
3772
3984
|
const quote = quotes[0] ?? null;
|
|
3773
3985
|
useEffect(() => {
|
|
3774
|
-
if (step !== "form" || !selectedSell || !selectedBuy || !amount || parseFloat(amount) <= 0) {
|
|
3986
|
+
if (step !== "form" || swapUnavailable || configLoading || !selectedSell || !selectedBuy || !amount || parseFloat(amount) <= 0) {
|
|
3775
3987
|
setQuotes([]);
|
|
3776
3988
|
setQuoteError("");
|
|
3777
3989
|
setQuoteLoading(false);
|
|
@@ -3803,7 +4015,7 @@ function SwapModal({ onClose }) {
|
|
|
3803
4015
|
cancelled = true;
|
|
3804
4016
|
clearTimeout(t);
|
|
3805
4017
|
};
|
|
3806
|
-
}, [step, selectedSell, selectedBuy, amount, provider, getSwapQuote]);
|
|
4018
|
+
}, [step, swapUnavailable, configLoading, selectedSell, selectedBuy, amount, provider, getSwapQuote]);
|
|
3807
4019
|
const hash = transaction.step === "success" ? transaction.hash : null;
|
|
3808
4020
|
const buildData = "buildData" in transaction ? transaction.buildData : null;
|
|
3809
4021
|
const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : buildData ? "public" : network === "testnet" ? "testnet" : "public";
|
|
@@ -3884,6 +4096,9 @@ function SwapModal({ onClose }) {
|
|
|
3884
4096
|
formError,
|
|
3885
4097
|
isLoadingData,
|
|
3886
4098
|
smartUnsupported,
|
|
4099
|
+
configLoading,
|
|
4100
|
+
swapUnavailable,
|
|
4101
|
+
buyNeedsTrustline,
|
|
3887
4102
|
transaction,
|
|
3888
4103
|
showXdr,
|
|
3889
4104
|
copied,
|
|
@@ -3893,8 +4108,10 @@ function SwapModal({ onClose }) {
|
|
|
3893
4108
|
isInProgress,
|
|
3894
4109
|
onClose,
|
|
3895
4110
|
onBack: handleBack,
|
|
4111
|
+
onRefresh: handleRefresh,
|
|
3896
4112
|
onSelectSell: setSelectedSell,
|
|
3897
4113
|
onSelectBuy: setSelectedBuy,
|
|
4114
|
+
onAddCustomToken: addCustomToken,
|
|
3898
4115
|
onAmountChange: setAmount,
|
|
3899
4116
|
onProviderChange: setProvider,
|
|
3900
4117
|
onSwap: () => void handleSwap(),
|
|
@@ -4606,6 +4823,18 @@ function PollarProvider({
|
|
|
4606
4823
|
const [pollarClient] = useState(
|
|
4607
4824
|
() => client instanceof PollarClient ? client : new PollarClient({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner, ...client })
|
|
4608
4825
|
);
|
|
4826
|
+
const ownsClientRef = useRef(!(client instanceof PollarClient));
|
|
4827
|
+
const destroyTimerRef = useRef(null);
|
|
4828
|
+
useEffect(() => {
|
|
4829
|
+
if (!ownsClientRef.current) return;
|
|
4830
|
+
if (destroyTimerRef.current) {
|
|
4831
|
+
clearTimeout(destroyTimerRef.current);
|
|
4832
|
+
destroyTimerRef.current = null;
|
|
4833
|
+
}
|
|
4834
|
+
return () => {
|
|
4835
|
+
destroyTimerRef.current = setTimeout(() => pollarClient.destroy(), 0);
|
|
4836
|
+
};
|
|
4837
|
+
}, [pollarClient]);
|
|
4609
4838
|
const [networkState, setNetworkState] = useState(() => pollarClient.getNetworkState());
|
|
4610
4839
|
const [sessionState, setSessionState] = useState(null);
|
|
4611
4840
|
const [verified, setVerified] = useState(false);
|
|
@@ -4745,6 +4974,8 @@ function PollarProvider({
|
|
|
4745
4974
|
openSendModal: () => setSendModalOpen(true),
|
|
4746
4975
|
openReceiveModal: () => setReceiveModalOpen(true),
|
|
4747
4976
|
// swap
|
|
4977
|
+
getSwapConfig: () => pollarClient.getSwapConfig(),
|
|
4978
|
+
getSwapTokens: () => pollarClient.getSwapTokens(),
|
|
4748
4979
|
getSwapQuote: (params) => pollarClient.getSwapQuote(params),
|
|
4749
4980
|
swap: (quote, opts) => pollarClient.swap(quote, opts),
|
|
4750
4981
|
openSwapModal: () => setSwapModalOpen(true),
|