@pollar/react 0.10.0 → 0.10.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1035,7 +1035,7 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.10.0"
1038
+ "0.10.1-rc.0"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
@@ -3507,7 +3507,6 @@ var PROVIDER_LABELS = {
3507
3507
  soroswap: "Soroswap",
3508
3508
  sdex: "Stellar DEX"
3509
3509
  };
3510
- var IMPLEMENTED_PROVIDERS = ["auto", "aquarius"];
3511
3510
  function formatAmount2(value) {
3512
3511
  const n = parseFloat(value);
3513
3512
  return isNaN(n) ? value : n.toLocaleString(void 0, { maximumFractionDigits: 7 });
@@ -3530,6 +3529,9 @@ function SwapModalTemplate({
3530
3529
  formError,
3531
3530
  isLoadingData,
3532
3531
  smartUnsupported,
3532
+ configLoading,
3533
+ swapUnavailable,
3534
+ buyNeedsTrustline,
3533
3535
  transaction,
3534
3536
  showXdr,
3535
3537
  copied,
@@ -3539,6 +3541,7 @@ function SwapModalTemplate({
3539
3541
  isInProgress,
3540
3542
  onClose,
3541
3543
  onBack,
3544
+ onRefresh,
3542
3545
  onSelectSell,
3543
3546
  onSelectBuy,
3544
3547
  onAmountChange,
@@ -3577,9 +3580,34 @@ function SwapModalTemplate({
3577
3580
  showBack && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onBack, "aria-label": "Back", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 3L5 8l5 5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
3578
3581
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: title })
3579
3582
  ] }),
3580
- !isInProgress && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) }) })
3583
+ !isInProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header-actions", children: [
3584
+ step === "form" && /* @__PURE__ */ jsxRuntime.jsx(
3585
+ "button",
3586
+ {
3587
+ type: "button",
3588
+ className: "pollar-modal-close",
3589
+ onClick: onRefresh,
3590
+ disabled: configLoading || isLoadingData,
3591
+ "aria-label": "Refresh",
3592
+ title: "Refresh balances and options",
3593
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
3594
+ "path",
3595
+ {
3596
+ d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2.5v3h-3",
3597
+ stroke: "currentColor",
3598
+ strokeWidth: "1.6",
3599
+ strokeLinecap: "round",
3600
+ strokeLinejoin: "round"
3601
+ }
3602
+ ) })
3603
+ }
3604
+ ),
3605
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
3606
+ ] })
3581
3607
  ] }),
3582
- step === "form" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3608
+ step === "form" && configLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-hint", children: "Loading swap options\u2026" }),
3609
+ step === "form" && !configLoading && swapUnavailable && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "Swap is not available for this app." }),
3610
+ step === "form" && !configLoading && !swapUnavailable && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3583
3611
  smartUnsupported && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "Swaps are not yet available for smart (passkey) wallets." }),
3584
3612
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3585
3613
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "You pay" }),
@@ -3653,13 +3681,7 @@ function SwapModalTemplate({
3653
3681
  className: "pollar-input pollar-send-select",
3654
3682
  value: provider,
3655
3683
  onChange: (e) => onProviderChange(e.target.value),
3656
- children: providers.map((p) => {
3657
- const implemented = IMPLEMENTED_PROVIDERS.includes(p);
3658
- return /* @__PURE__ */ jsxRuntime.jsxs("option", { value: p, disabled: !implemented, children: [
3659
- PROVIDER_LABELS[p],
3660
- implemented ? "" : " (coming soon)"
3661
- ] }, p);
3662
- })
3684
+ children: providers.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p, children: PROVIDER_LABELS[p] }, p))
3663
3685
  }
3664
3686
  )
3665
3687
  ] }),
@@ -3696,8 +3718,13 @@ function SwapModalTemplate({
3696
3718
  ] })
3697
3719
  ] }),
3698
3720
  !quoteLoading && !quoteError && !quote && selectedSell && selectedBuy && !!amount && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-send-hint", children: "No route found for this pair." }),
3721
+ buyNeedsTrustline && selectedBuy && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-trustline-notice", children: [
3722
+ "To receive ",
3723
+ selectedBuy.code,
3724
+ " your wallet needs a trustline. Swapping will create it first (~0.5 XLM reserve, refundable if you later remove it)."
3725
+ ] }),
3699
3726
  formError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: formError }),
3700
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: "Swap" }) })
3727
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "pollar-btn-primary", onClick: onSwap, disabled: !canSwap, children: buyNeedsTrustline ? "Create trustline & swap" : "Swap" }) })
3701
3728
  ] }),
3702
3729
  step === "tx" && /* @__PURE__ */ jsxRuntime.jsx(
3703
3730
  TxStatusView,
@@ -3719,14 +3746,18 @@ function SwapModalTemplate({
3719
3746
  ] });
3720
3747
  }
3721
3748
  var QUOTE_DEBOUNCE_MS = 400;
3722
- var ALL_PROVIDERS = ["auto", "aquarius", "soroswap", "sdex"];
3723
3749
  function toRef(a) {
3724
3750
  if (a.type === "native") return { type: "native" };
3725
3751
  if (a.type === "credit_alphanum4") return { type: "credit_alphanum4", code: a.code, issuer: a.issuer };
3726
3752
  return { type: "credit_alphanum12", code: a.code, issuer: a.issuer };
3727
3753
  }
3754
+ function catalogRef(code, issuer) {
3755
+ return code.length <= 4 ? { type: "credit_alphanum4", code, issuer } : { type: "credit_alphanum12", code, issuer };
3756
+ }
3728
3757
  function SwapModal({ onClose }) {
3729
3758
  const {
3759
+ getSwapConfig,
3760
+ getSwapTokens,
3730
3761
  getSwapQuote,
3731
3762
  swap,
3732
3763
  walletBalance,
@@ -3746,6 +3777,8 @@ function SwapModal({ onClose }) {
3746
3777
  const [selectedBuy, setSelectedBuy] = react.useState(null);
3747
3778
  const [amount, setAmount] = react.useState("");
3748
3779
  const [provider, setProvider] = react.useState("auto");
3780
+ const [venues, setVenues] = react.useState(null);
3781
+ const [catalogTokens, setCatalogTokens] = react.useState([]);
3749
3782
  const [quotes, setQuotes] = react.useState([]);
3750
3783
  const [quoteLoading, setQuoteLoading] = react.useState(false);
3751
3784
  const [quoteError, setQuoteError] = react.useState("");
@@ -3758,6 +3791,26 @@ function SwapModal({ onClose }) {
3758
3791
  void refreshWalletBalance();
3759
3792
  void refreshAssets();
3760
3793
  }, [refreshWalletBalance, refreshAssets]);
3794
+ const loadConfig = react.useCallback(() => {
3795
+ setVenues(null);
3796
+ return getSwapConfig().then(setVenues).catch(() => setVenues([]));
3797
+ }, [getSwapConfig]);
3798
+ react.useEffect(() => {
3799
+ void loadConfig();
3800
+ }, [loadConfig]);
3801
+ const loadCatalog = react.useCallback(
3802
+ () => getSwapTokens().then(setCatalogTokens).catch(() => setCatalogTokens([])),
3803
+ [getSwapTokens]
3804
+ );
3805
+ react.useEffect(() => {
3806
+ void loadCatalog();
3807
+ }, [loadCatalog]);
3808
+ function handleRefresh() {
3809
+ void refreshWalletBalance();
3810
+ void refreshAssets();
3811
+ void loadConfig();
3812
+ void loadCatalog();
3813
+ }
3761
3814
  react.useEffect(
3762
3815
  () => () => {
3763
3816
  if (copyTimerRef.current !== null) clearTimeout(copyTimerRef.current);
@@ -3769,11 +3822,33 @@ function SwapModal({ onClose }) {
3769
3822
  const isLoadingData = walletBalance.step === "loading" || enabledAssets.step === "loading";
3770
3823
  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 }));
3771
3824
  const buyKeyOfSell = selectedSell ? `${selectedSell.code}:${selectedSell.issuer ?? "native"}` : "";
3772
- const buyOptions = assetRecords.map((a) => ({ ref: toRef(a), code: a.code, issuer: a.issuer, enabledInApp: a.enabledInApp })).filter((o) => `${o.code}:${o.issuer ?? "native"}` !== buyKeyOfSell);
3773
- const providers = smartUnsupported ? ALL_PROVIDERS.filter((p) => p !== "sdex") : ALL_PROVIDERS;
3825
+ const optKey = (o) => `${o.code}:${o.issuer ?? "native"}`;
3826
+ const enabledBuy = assetRecords.map((a) => ({
3827
+ ref: toRef(a),
3828
+ code: a.code,
3829
+ issuer: a.issuer,
3830
+ enabledInApp: a.enabledInApp
3831
+ }));
3832
+ const enabledKeys = new Set(enabledBuy.map(optKey));
3833
+ const catalogBuy = catalogTokens.map((tk) => ({
3834
+ ref: catalogRef(tk.code, tk.issuer),
3835
+ code: tk.code,
3836
+ issuer: tk.issuer,
3837
+ enabledInApp: false
3838
+ })).filter((o) => !enabledKeys.has(optKey(o)));
3839
+ const buyOptions = [...enabledBuy, ...catalogBuy].filter((o) => optKey(o) !== buyKeyOfSell);
3840
+ const buyNeedsTrustline = (() => {
3841
+ if (!selectedBuy || smartUnsupported) return false;
3842
+ if (selectedBuy.ref.type === "native") return false;
3843
+ const rec = assetRecords.find((a) => a.code === selectedBuy.code && a.issuer === selectedBuy.issuer);
3844
+ return !rec?.trustlineEstablished;
3845
+ })();
3846
+ const configLoading = venues === null;
3847
+ const swapUnavailable = venues !== null && venues.length === 0;
3848
+ const providers = venues && venues.length > 0 ? ["auto", ...venues] : [];
3774
3849
  const quote = quotes[0] ?? null;
3775
3850
  react.useEffect(() => {
3776
- if (step !== "form" || !selectedSell || !selectedBuy || !amount || parseFloat(amount) <= 0) {
3851
+ if (step !== "form" || swapUnavailable || configLoading || !selectedSell || !selectedBuy || !amount || parseFloat(amount) <= 0) {
3777
3852
  setQuotes([]);
3778
3853
  setQuoteError("");
3779
3854
  setQuoteLoading(false);
@@ -3805,7 +3880,7 @@ function SwapModal({ onClose }) {
3805
3880
  cancelled = true;
3806
3881
  clearTimeout(t);
3807
3882
  };
3808
- }, [step, selectedSell, selectedBuy, amount, provider, getSwapQuote]);
3883
+ }, [step, swapUnavailable, configLoading, selectedSell, selectedBuy, amount, provider, getSwapQuote]);
3809
3884
  const hash = transaction.step === "success" ? transaction.hash : null;
3810
3885
  const buildData = "buildData" in transaction ? transaction.buildData : null;
3811
3886
  const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : buildData ? "public" : network === "testnet" ? "testnet" : "public";
@@ -3886,6 +3961,9 @@ function SwapModal({ onClose }) {
3886
3961
  formError,
3887
3962
  isLoadingData,
3888
3963
  smartUnsupported,
3964
+ configLoading,
3965
+ swapUnavailable,
3966
+ buyNeedsTrustline,
3889
3967
  transaction,
3890
3968
  showXdr,
3891
3969
  copied,
@@ -3895,6 +3973,7 @@ function SwapModal({ onClose }) {
3895
3973
  isInProgress,
3896
3974
  onClose,
3897
3975
  onBack: handleBack,
3976
+ onRefresh: handleRefresh,
3898
3977
  onSelectSell: setSelectedSell,
3899
3978
  onSelectBuy: setSelectedBuy,
3900
3979
  onAmountChange: setAmount,
@@ -4608,6 +4687,18 @@ function PollarProvider({
4608
4687
  const [pollarClient] = react.useState(
4609
4688
  () => client instanceof core.PollarClient ? client : new core.PollarClient({ passkey: browserPasskeyCeremony, passkeySign: browserPasskeySigner, ...client })
4610
4689
  );
4690
+ const ownsClientRef = react.useRef(!(client instanceof core.PollarClient));
4691
+ const destroyTimerRef = react.useRef(null);
4692
+ react.useEffect(() => {
4693
+ if (!ownsClientRef.current) return;
4694
+ if (destroyTimerRef.current) {
4695
+ clearTimeout(destroyTimerRef.current);
4696
+ destroyTimerRef.current = null;
4697
+ }
4698
+ return () => {
4699
+ destroyTimerRef.current = setTimeout(() => pollarClient.destroy(), 0);
4700
+ };
4701
+ }, [pollarClient]);
4611
4702
  const [networkState, setNetworkState] = react.useState(() => pollarClient.getNetworkState());
4612
4703
  const [sessionState, setSessionState] = react.useState(null);
4613
4704
  const [verified, setVerified] = react.useState(false);
@@ -4747,6 +4838,8 @@ function PollarProvider({
4747
4838
  openSendModal: () => setSendModalOpen(true),
4748
4839
  openReceiveModal: () => setReceiveModalOpen(true),
4749
4840
  // swap
4841
+ getSwapConfig: () => pollarClient.getSwapConfig(),
4842
+ getSwapTokens: () => pollarClient.getSwapTokens(),
4750
4843
  getSwapQuote: (params) => pollarClient.getSwapQuote(params),
4751
4844
  swap: (quote, opts) => pollarClient.swap(quote, opts),
4752
4845
  openSwapModal: () => setSwapModalOpen(true),