@pollar/react 0.11.4 → 0.11.5

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 { AUTH_ERROR_CODES, WalletType, isPollarClient, PollarClient, isInteractiveAuthAdapter, isPollarApiError, toBaseUnits } from '@pollar/core';
2
+ import { AUTH_ERROR_CODES, transactionErrorMessage, WalletType, cosignGroupOf, cosignStepKeyOf, stepInGroup, isPollarApiError, isPollarClient, PollarClient, isInteractiveAuthAdapter, toBaseUnits } from '@pollar/core';
3
3
  import { forwardRef, createContext, useState, useCallback, useEffect, useMemo, useRef, useContext, Component } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
@@ -1000,6 +1000,10 @@ var require_QRCode = __commonJS({
1000
1000
 
1001
1001
  // src/constants.ts
1002
1002
  var LOGO_POLLAR = "https://pollar.xyz/assets/logo_pollar.png";
1003
+ var LOGO_POLLAR_WORDMARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-light.png";
1004
+ var LOGO_POLLAR_WORDMARK_ON_DARK = "https://www.pollar.xyz/brandkit/pollar-logo-horizontal-on-dark.png";
1005
+ var LOGO_POLLAR_MARK_ON_LIGHT = "https://www.pollar.xyz/brandkit/pollar-mark-on-light.png";
1006
+ var LOGO_POLLAR_MARK_ON_DARK = "https://www.pollar.xyz/brandkit/pollar-mark-on-dark.png";
1003
1007
  var LOGO_FREIGHTER = "https://pollar.xyz/assets/logo_freighter.png";
1004
1008
  var LOGO_ALBEDO = "https://pollar.xyz/assets/logo_albedo.svg";
1005
1009
  var _modalLog = console;
@@ -1025,22 +1029,26 @@ var ModalErrorBoundary = class extends Component {
1025
1029
  return this.props.children;
1026
1030
  }
1027
1031
  };
1028
- var PollarModalFooter = ({ shield = false }) => {
1032
+ var PollarModalFooter = () => {
1029
1033
  return /* @__PURE__ */ jsxs("div", { className: "pollar-footer", children: [
1030
1034
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-protected", children: "Protected by" }),
1031
- /* @__PURE__ */ jsxs("div", { className: "pollar-footer-brand", children: [
1032
- shield ? /* @__PURE__ */ jsxs("svg", { className: "pollar-footer-shield", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: [
1033
- /* @__PURE__ */ jsx("path", { d: "M12 3.2 19 6v5.3c0 4.4-2.9 7.8-7 9.5-4.1-1.7-7-5.1-7-9.5V6l7-2.8Z", fill: "var(--pollar-accent)" }),
1034
- /* @__PURE__ */ jsx("path", { d: "m8.5 12 2.2 2.2 4.8-5", stroke: "white", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" })
1035
- ] }) : /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR, alt: "Pollar", className: "pollar-footer-logo" }),
1036
- /* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1037
- /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1038
- "v",
1039
- "0.11.4"
1040
- ] })
1035
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_LIGHT, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-light" }),
1036
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_WORDMARK_ON_DARK, alt: "Pollar", className: "pollar-footer-logo pollar-logo-on-dark" }),
1037
+ /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1038
+ "v",
1039
+ "0.11.5"
1041
1040
  ] })
1042
1041
  ] });
1043
1042
  };
1043
+ function explorerTxUrl(chain, hash, network) {
1044
+ if (chain === "SOLANA") {
1045
+ return `https://explorer.solana.com/tx/${hash}${network === "testnet" ? "?cluster=devnet" : ""}`;
1046
+ }
1047
+ if (chain === "POLYGON") {
1048
+ return `https://${network === "testnet" ? "amoy." : ""}polygonscan.com/tx/${hash}`;
1049
+ }
1050
+ return `https://stellar.expert/explorer/${network === "testnet" ? "testnet" : "public"}/tx/${hash}`;
1051
+ }
1044
1052
  function RefreshIcon({ spinning = false, className = "" }) {
1045
1053
  return /* @__PURE__ */ jsxs(
1046
1054
  "svg",
@@ -1232,6 +1240,8 @@ function buildModalCssVars(theme, accentColor, overrides = {}, variant = "compac
1232
1240
  "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
1233
1241
  "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
1234
1242
  "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
1243
+ "--pollar-logo-on-light-display": isDark ? "none" : "block",
1244
+ "--pollar-logo-on-dark-display": isDark ? "block" : "none",
1235
1245
  "--pollar-modal-border-radius": px(overrides.modalBorderRadius, DEFAULT_MODAL_BORDER_RADIUS),
1236
1246
  "--pollar-buttons-border-radius": px(overrides.buttonBorderRadius, DEFAULT_BUTTON_BORDER_RADIUS),
1237
1247
  "--pollar-buttons-height": "44px",
@@ -3237,7 +3247,11 @@ function LoginModal({ onClose }) {
3237
3247
  const [email, setEmail] = useState("");
3238
3248
  const { getClient, styles, appConfig: config, configStatus, retryConfig } = usePollar();
3239
3249
  const [authState, setAuthState] = useState(() => getClient().getAuthState());
3240
- const walletAdapters = useMemo(() => getClient().listWalletAdapters(), [getClient]);
3250
+ const servedChains = config?.application?.chains;
3251
+ const walletAdapters = useMemo(
3252
+ () => getClient().listWalletAdapters().filter((a) => !servedChains || servedChains.includes(a.chain)),
3253
+ [getClient, servedChains]
3254
+ );
3241
3255
  const [codeInputKey, setCodeInputKey] = useState(0);
3242
3256
  const pendingEmail = useRef(null);
3243
3257
  const [interactiveAdapter, setInteractiveAdapter] = useState(null);
@@ -3522,6 +3536,8 @@ function placeholderFor(field, fields, values) {
3522
3536
  }
3523
3537
  return field.placeholder;
3524
3538
  }
3539
+ var CHAIN_NAME = { STELLAR: "Stellar", POLYGON: "Polygon", SOLANA: "Solana" };
3540
+ var EXPLORER_NAME = { STELLAR: "Stellar Expert", POLYGON: "Polygonscan", SOLANA: "Solana Explorer" };
3525
3541
  var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
3526
3542
  function flagEmoji(code) {
3527
3543
  const cc = code.trim().toUpperCase();
@@ -3568,7 +3584,12 @@ function RampWidgetTemplate({
3568
3584
  onboardingStatus,
3569
3585
  kycJustApproved,
3570
3586
  stellarTxHash,
3587
+ txHash,
3588
+ txChain,
3571
3589
  explorerUrl,
3590
+ fundsInTransit = false,
3591
+ retrying = false,
3592
+ onRetryTransit,
3572
3593
  depositInstructions,
3573
3594
  canComplete,
3574
3595
  completing,
@@ -3590,6 +3611,8 @@ function RampWidgetTemplate({
3590
3611
  onRefresh,
3591
3612
  onClose
3592
3613
  }) {
3614
+ const settledHash = txHash ?? stellarTxHash;
3615
+ const chainName = CHAIN_NAME[txChain ?? "STELLAR"];
3593
3616
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides, "hero");
3594
3617
  const stepTitle = {
3595
3618
  input: direction === "onramp" ? "Buy crypto" : "Sell crypto",
@@ -3822,15 +3845,23 @@ function RampWidgetTemplate({
3822
3845
  txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsx("span", { className: "pollar-spinner pollar-spinner-sm", "aria-label": "Checking status" })
3823
3846
  ] })
3824
3847
  ] }),
3825
- stellarTxHash && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3826
- /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Stellar tx" }),
3848
+ fundsInTransit && txStatus !== "completed" && txStatus !== "failed" && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3849
+ /* @__PURE__ */ jsx("span", { className: "pollar-ramp-payment-label", children: "Funds in transit" }),
3850
+ /* @__PURE__ */ jsx("p", { className: "pollar-ramp-payment-note", children: "Your money is safe in your own wallet, waiting for the next step of the transfer." }),
3851
+ onRetryTransit && /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onRetryTransit, disabled: retrying, children: retrying ? "Retrying..." : "Retry now" })
3852
+ ] }),
3853
+ settledHash && /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-field", children: [
3854
+ /* @__PURE__ */ jsxs("span", { className: "pollar-ramp-payment-label", children: [
3855
+ chainName,
3856
+ " tx"
3857
+ ] }),
3827
3858
  /* @__PURE__ */ jsxs("div", { className: "pollar-ramp-payment-value", style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3828
3859
  /* @__PURE__ */ jsxs("code", { style: { flex: 1, wordBreak: "break-all" }, children: [
3829
- stellarTxHash.slice(0, 8),
3860
+ settledHash.slice(0, 8),
3830
3861
  "\u2026",
3831
- stellarTxHash.slice(-8)
3862
+ settledHash.slice(-8)
3832
3863
  ] }),
3833
- /* @__PURE__ */ jsx(CopyButton, { value: stellarTxHash, label: "Copy transaction hash" }),
3864
+ /* @__PURE__ */ jsx(CopyButton, { value: settledHash, label: "Copy transaction hash" }),
3834
3865
  explorerUrl && /* @__PURE__ */ jsx(
3835
3866
  "a",
3836
3867
  {
@@ -3838,8 +3869,8 @@ function RampWidgetTemplate({
3838
3869
  target: "_blank",
3839
3870
  rel: "noopener noreferrer",
3840
3871
  className: "pollar-copy-btn",
3841
- "aria-label": "View on Stellar Expert",
3842
- title: "View on Stellar Expert",
3872
+ "aria-label": `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
3873
+ title: `View on ${EXPLORER_NAME[txChain ?? "STELLAR"]}`,
3843
3874
  children: /* @__PURE__ */ jsxs("svg", { width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
3844
3875
  /* @__PURE__ */ jsx(
3845
3876
  "path",
@@ -4483,6 +4514,13 @@ function rampErrorMessage(e, fallback) {
4483
4514
  }
4484
4515
  return RAMP_ERROR_MESSAGES[code] ?? (e instanceof Error ? e.message : fallback);
4485
4516
  }
4517
+ function explorerTxUrl2(chain, hash, network) {
4518
+ if (!hash) return null;
4519
+ const testnet = network === "testnet";
4520
+ if (chain === "SOLANA") return `https://explorer.solana.com/tx/${hash}${testnet ? "?cluster=devnet" : ""}`;
4521
+ if (chain === "POLYGON") return `https://${testnet ? "amoy." : ""}polygonscan.com/tx/${hash}`;
4522
+ return `https://stellar.expert/explorer/${testnet ? "testnet" : "public"}/tx/${hash}`;
4523
+ }
4486
4524
  function RampWidget({ onClose }) {
4487
4525
  const { getClient, signTx, wallet, styles, network } = usePollar();
4488
4526
  const walletAddress = wallet?.address ?? "";
@@ -4520,11 +4558,36 @@ function RampWidget({ onClose }) {
4520
4558
  const [onboardingStatus, setOnboardingStatus] = useState(void 0);
4521
4559
  const [txStatus, setTxStatus] = useState(null);
4522
4560
  const [stellarTxHash, setStellarTxHash] = useState(null);
4561
+ const [txHash, setTxHash] = useState(null);
4562
+ const [txChain, setTxChain] = useState(null);
4563
+ const [fundsInTransit, setFundsInTransit] = useState(false);
4564
+ const [retrying, setRetrying] = useState(false);
4523
4565
  const [depositInstructions, setDepositInstructions] = useState(null);
4524
4566
  const [completing, setCompleting] = useState(false);
4525
4567
  const [errorMsg, setErrorMsg] = useState(null);
4568
+ const signingRef = useRef(false);
4526
4569
  const directionRef = useRef(direction);
4527
4570
  directionRef.current = direction;
4571
+ function applySettlement(tx) {
4572
+ if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
4573
+ if (tx.txHash) setTxHash(tx.txHash);
4574
+ if (tx.chain) setTxChain(tx.chain);
4575
+ setFundsInTransit(tx.fundsInTransit === true);
4576
+ }
4577
+ async function handleRetryTransit() {
4578
+ if (!txId || retrying) return;
4579
+ setRetrying(true);
4580
+ setErrorMsg(null);
4581
+ try {
4582
+ const tx = await client.retryRampTransaction(txId);
4583
+ setTxStatus(tx.status);
4584
+ applySettlement(tx);
4585
+ } catch (e) {
4586
+ setErrorMsg(e instanceof Error && e.message ? e.message : "Could not retry the transfer. Try again in a moment.");
4587
+ } finally {
4588
+ setRetrying(false);
4589
+ }
4590
+ }
4528
4591
  useEffect(() => {
4529
4592
  if (step !== "status" || !txId) return;
4530
4593
  if (txStatus && TERMINAL.includes(txStatus)) return;
@@ -4534,7 +4597,14 @@ function RampWidget({ onClose }) {
4534
4597
  const tx = await client.getRampTransaction(txId);
4535
4598
  if (!active) return;
4536
4599
  setTxStatus(tx.status);
4537
- if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
4600
+ applySettlement(tx);
4601
+ const pending = tx.pendingSignature;
4602
+ if (pending && !signingRef.current) {
4603
+ signingRef.current = true;
4604
+ void resumeRef.current(txId, pending).finally(() => {
4605
+ signingRef.current = false;
4606
+ });
4607
+ }
4538
4608
  if (tx.kycUrl) setKycUrl(tx.kycUrl);
4539
4609
  if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
4540
4610
  if (TERMINAL.includes(tx.status)) clearInterval(id);
@@ -4601,7 +4671,7 @@ function RampWidget({ onClose }) {
4601
4671
  } else if (step === "status" && txId) {
4602
4672
  const tx = await client.getRampTransaction(txId);
4603
4673
  setTxStatus(tx.status);
4604
- if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
4674
+ applySettlement(tx);
4605
4675
  if (tx.kycUrl) setKycUrl(tx.kycUrl);
4606
4676
  if (tx.depositInstructions) setDepositInstructions(tx.depositInstructions);
4607
4677
  }
@@ -4622,6 +4692,9 @@ function RampWidget({ onClose }) {
4622
4692
  setTosUrl(null);
4623
4693
  setTxStatus(null);
4624
4694
  setStellarTxHash(null);
4695
+ setTxHash(null);
4696
+ setTxChain(null);
4697
+ setFundsInTransit(false);
4625
4698
  setDepositInstructions(null);
4626
4699
  setKycPending(false);
4627
4700
  setKycApproved(false);
@@ -4630,6 +4703,11 @@ function RampWidget({ onClose }) {
4630
4703
  }
4631
4704
  async function resumeWithSignature(id, ps) {
4632
4705
  const outcome = await signTx(ps.unsignedXdr);
4706
+ if (outcome.status === "cosign_pending") {
4707
+ setErrorMsg("Approve the signature on your device, then resume.");
4708
+ setStep("error");
4709
+ return;
4710
+ }
4633
4711
  if (outcome.status !== "signed") {
4634
4712
  setErrorMsg(outcome.message ?? outcome.details ?? "Signing was cancelled.");
4635
4713
  setStep("error");
@@ -4637,10 +4715,13 @@ function RampWidget({ onClose }) {
4637
4715
  }
4638
4716
  const result = await client.submitRampSignature(id, {
4639
4717
  signedXdr: outcome.signedXdr,
4718
+ // The CCTP actions are not in the generated schema until it is regenerated.
4640
4719
  action: ps.action
4641
4720
  });
4642
4721
  await applyResult(result);
4643
4722
  }
4723
+ const resumeRef = useRef(resumeWithSignature);
4724
+ resumeRef.current = resumeWithSignature;
4644
4725
  async function applyResult(result) {
4645
4726
  setTxId(result.txId);
4646
4727
  setProvider(result.provider);
@@ -4796,7 +4877,7 @@ function RampWidget({ onClose }) {
4796
4877
  }
4797
4878
  }
4798
4879
  const kycBlocking = kycPending && !kycApproved;
4799
- const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !kycPending;
4880
+ const canComplete = direction === "offramp" && step === "status" && txStatus !== "completed" && !stellarTxHash && !txHash && !kycPending;
4800
4881
  const flowSteps = flowStepsOf(quotes, selectedQuote);
4801
4882
  const flowStepIndex = flowSteps.indexOf(STEP_LABEL[step] ?? "");
4802
4883
  if (pendingKyc?.type === "FORM") {
@@ -4881,7 +4962,12 @@ function RampWidget({ onClose }) {
4881
4962
  onboardingStatus: onboardingStatus ?? null,
4882
4963
  kycJustApproved: kycPending && kycApproved,
4883
4964
  stellarTxHash,
4884
- explorerUrl: stellarTxHash ? `https://stellar.expert/explorer/${network === "mainnet" ? "public" : "testnet"}/tx/${stellarTxHash}` : null,
4965
+ txHash,
4966
+ txChain,
4967
+ explorerUrl: explorerTxUrl2(txChain ?? "STELLAR", txHash ?? stellarTxHash, network),
4968
+ fundsInTransit,
4969
+ retrying,
4970
+ onRetryTransit: handleRetryTransit,
4885
4971
  depositInstructions,
4886
4972
  canComplete,
4887
4973
  completing,
@@ -4988,7 +5074,7 @@ function PollarLogo(props) {
4988
5074
  )
4989
5075
  ] });
4990
5076
  }
4991
- var CHAIN_NAME = {
5077
+ var CHAIN_NAME2 = {
4992
5078
  STELLAR: "Stellar",
4993
5079
  POLYGON: "Polygon",
4994
5080
  SOLANA: "Solana"
@@ -5007,7 +5093,7 @@ function ReceiveModalTemplate({
5007
5093
  onClose
5008
5094
  }) {
5009
5095
  const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
5010
- const chainName = selectedChain ? CHAIN_NAME[selectedChain] ?? selectedChain : "wallet";
5096
+ const chainName = selectedChain ? CHAIN_NAME2[selectedChain] ?? selectedChain : "wallet";
5011
5097
  return /* @__PURE__ */ jsxs(
5012
5098
  "div",
5013
5099
  {
@@ -5089,7 +5175,7 @@ function ReceiveModalTemplate({
5089
5175
  ] })
5090
5176
  ] })
5091
5177
  ] }) : /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No wallet connected." }),
5092
- /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
5178
+ /* @__PURE__ */ jsx(PollarModalFooter, {})
5093
5179
  ]
5094
5180
  }
5095
5181
  );
@@ -5152,6 +5238,7 @@ var STATUS_MESSAGES = {
5152
5238
  submitting: "Submitting transaction\u2026",
5153
5239
  submitted: "Submitted \u2014 waiting for confirmation\u2026",
5154
5240
  "signing-submitting": "Signing and submitting transaction\u2026",
5241
+ awaiting_cosign: "Waiting for your approval on this device\u2026",
5155
5242
  "building-signing-submitting": "Processing transaction\u2026",
5156
5243
  success: "Transaction sent successfully",
5157
5244
  error: "Transaction failed"
@@ -5187,14 +5274,22 @@ function TxStatusView({
5187
5274
  }) {
5188
5275
  const buildData = "buildData" in transaction ? transaction.buildData : null;
5189
5276
  const hash = transaction.step === "success" ? transaction.hash : null;
5190
- const errorDetails = transaction.step === "error" ? transaction.details ?? null : null;
5277
+ const errorDetails = transaction.step === "error" ? transactionErrorMessage(transaction.code, transaction.details) : null;
5191
5278
  const isBuilt = transaction.step === "built";
5192
5279
  const isInFlight = IN_FLIGHT_STEPS.has(transaction.step);
5193
5280
  const isSuccess = transaction.step === "success";
5194
5281
  const isError = transaction.step === "error";
5195
5282
  const showDetails = buildData !== null && SHOW_DETAILS_STEPS.has(transaction.step);
5196
- const walletImg = walletType === WalletType.FREIGHTER ? LOGO_FREIGHTER : walletType === WalletType.ALBEDO ? LOGO_ALBEDO : LOGO_POLLAR;
5283
+ const walletImg = walletType === WalletType.FREIGHTER ? LOGO_FREIGHTER : walletType === WalletType.ALBEDO ? LOGO_ALBEDO : null;
5197
5284
  const walletAlt = walletType === WalletType.FREIGHTER ? "Freighter" : walletType === WalletType.ALBEDO ? "Albedo" : "Pollar";
5285
+ const statusBanner = /* @__PURE__ */ jsx(
5286
+ ModalStatusBanner,
5287
+ {
5288
+ message: STATUS_MESSAGES[transaction.step],
5289
+ status: isError ? "ERROR" : isInFlight ? "LOADING" : isSuccess ? "SUCCESS" : "NONE"
5290
+ }
5291
+ );
5292
+ const showSpinner = isInFlight || isSuccess || isError;
5198
5293
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5199
5294
  showDetails && buildData && /* @__PURE__ */ jsxs(Fragment, { children: [
5200
5295
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-summary", children: [
@@ -5235,7 +5330,7 @@ function TxStatusView({
5235
5330
  /* @__PURE__ */ jsx("pre", { className: "pollar-tx-error-details-content", children: errorDetails })
5236
5331
  ] }),
5237
5332
  isBuilt && /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onSignAndSend, children: "Sign & Send" }),
5238
- (isInFlight || isSuccess || isError) && /* @__PURE__ */ jsxs("div", { className: "pollar-tx-wallet-spinner", children: [
5333
+ showSpinner && /* @__PURE__ */ jsxs("div", { className: "pollar-tx-wallet-spinner", children: [
5239
5334
  /* @__PURE__ */ jsxs("div", { className: "pollar-tx-spinner-ring", children: [
5240
5335
  /* @__PURE__ */ jsxs(
5241
5336
  "svg",
@@ -5265,8 +5360,12 @@ function TxStatusView({
5265
5360
  ]
5266
5361
  }
5267
5362
  ),
5268
- /* @__PURE__ */ jsx("div", { className: "pollar-tx-wallet-icon", children: /* @__PURE__ */ jsx("img", { src: walletImg, alt: walletAlt, className: "pollar-tx-wallet-img" }) })
5363
+ /* @__PURE__ */ jsx("div", { className: "pollar-tx-wallet-icon", children: walletImg ? /* @__PURE__ */ jsx("img", { src: walletImg, alt: walletAlt, className: "pollar-tx-wallet-img" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5364
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_LIGHT, alt: walletAlt, className: "pollar-tx-wallet-img pollar-logo-on-light" }),
5365
+ /* @__PURE__ */ jsx("img", { src: LOGO_POLLAR_MARK_ON_DARK, alt: walletAlt, className: "pollar-tx-wallet-img pollar-logo-on-dark" })
5366
+ ] }) })
5269
5367
  ] }),
5368
+ statusBanner,
5270
5369
  isInFlight && /* @__PURE__ */ jsx("p", { className: "pollar-tx-spinner-label", children: walletType === WalletType.FREIGHTER ? "Waiting for Freighter\u2026" : walletType === WalletType.ALBEDO ? "Waiting for Albedo\u2026" : "Signing and sending\u2026" }),
5271
5370
  isError && onRetry && "buildData" in transaction && transaction.buildData && /* @__PURE__ */ jsx("button", { className: "pollar-btn-secondary pollar-tx-retry-btn", onClick: onRetry, children: "Try again" })
5272
5371
  ] }),
@@ -5331,13 +5430,7 @@ function TxStatusView({
5331
5430
  ] })
5332
5431
  ] }),
5333
5432
  isSuccess && /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary pollar-tx-sign-btn", onClick: onDone, children: "Done" }),
5334
- /* @__PURE__ */ jsx(
5335
- ModalStatusBanner,
5336
- {
5337
- message: STATUS_MESSAGES[transaction.step],
5338
- status: isError ? "ERROR" : isInFlight ? "LOADING" : isSuccess ? "SUCCESS" : "NONE"
5339
- }
5340
- )
5433
+ !showSpinner && statusBanner
5341
5434
  ] });
5342
5435
  }
5343
5436
  function formatBalance(balance) {
@@ -5354,6 +5447,11 @@ function chainLabel(chain) {
5354
5447
  if (chain === "POLYGON") return "Polygon";
5355
5448
  return "network";
5356
5449
  }
5450
+ function destinationPlaceholder(chain) {
5451
+ if (chain === "SOLANA") return "Solana address";
5452
+ if (chain === "POLYGON") return "0x\u2026";
5453
+ return "G\u2026";
5454
+ }
5357
5455
  function AssetIcon({ code }) {
5358
5456
  return /* @__PURE__ */ jsx("span", { className: "pollar-send-asset-icon", "aria-hidden": true, children: code.slice(0, 2).toUpperCase() });
5359
5457
  }
@@ -5471,6 +5569,10 @@ function SendModalTemplate({
5471
5569
  onMax,
5472
5570
  onPaste,
5473
5571
  onDestinationChange,
5572
+ memo = "",
5573
+ memoType = "text",
5574
+ onMemoChange,
5575
+ onMemoTypeChange,
5474
5576
  onSubmit,
5475
5577
  onSignAndSend,
5476
5578
  onToggleXdr,
@@ -5578,7 +5680,7 @@ function SendModalTemplate({
5578
5680
  {
5579
5681
  className: "pollar-input pollar-send-destination-input",
5580
5682
  type: "text",
5581
- placeholder: "G\u2026",
5683
+ placeholder: destinationPlaceholder(selectedChain),
5582
5684
  value: destination,
5583
5685
  onChange: (e) => onDestinationChange(e.target.value),
5584
5686
  "aria-label": "Destination wallet"
@@ -5587,6 +5689,39 @@ function SendModalTemplate({
5587
5689
  /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-send-inline-action", onClick: onPaste, children: "Paste" })
5588
5690
  ] })
5589
5691
  ] }),
5692
+ selectedChain === "STELLAR" && onMemoChange && /* @__PURE__ */ jsxs("div", { className: "pollar-send-field", children: [
5693
+ /* @__PURE__ */ jsxs("label", { className: "pollar-send-label", htmlFor: "pollar-send-memo", children: [
5694
+ "Memo ",
5695
+ /* @__PURE__ */ jsx("span", { className: "pollar-send-label-optional", children: "(optional)" })
5696
+ ] }),
5697
+ /* @__PURE__ */ jsxs("div", { className: "pollar-send-memo-row", children: [
5698
+ /* @__PURE__ */ jsxs(
5699
+ "select",
5700
+ {
5701
+ className: "pollar-input pollar-send-memo-type",
5702
+ value: memoType,
5703
+ onChange: (e) => onMemoTypeChange?.(e.target.value),
5704
+ "aria-label": "Memo type",
5705
+ children: [
5706
+ /* @__PURE__ */ jsx("option", { value: "text", children: "Text" }),
5707
+ /* @__PURE__ */ jsx("option", { value: "id", children: "ID" })
5708
+ ]
5709
+ }
5710
+ ),
5711
+ /* @__PURE__ */ jsx(
5712
+ "input",
5713
+ {
5714
+ id: "pollar-send-memo",
5715
+ className: "pollar-input pollar-send-memo-input",
5716
+ type: "text",
5717
+ inputMode: memoType === "id" ? "numeric" : "text",
5718
+ placeholder: memoType === "id" ? "123456789" : "Up to 28 characters",
5719
+ value: memo,
5720
+ onChange: (e) => onMemoChange(e.target.value)
5721
+ }
5722
+ )
5723
+ ] })
5724
+ ] }),
5590
5725
  formError && /* @__PURE__ */ jsx("div", { className: "pollar-modal-error", children: formError }),
5591
5726
  /* @__PURE__ */ jsx("div", { className: "pollar-modal-actions", children: /* @__PURE__ */ jsx("button", { className: "pollar-btn-primary", onClick: onSubmit, disabled: !canSubmit, children: "Continue" }) })
5592
5727
  ] }),
@@ -5610,6 +5745,16 @@ function SendModalTemplate({
5610
5745
  }
5611
5746
  );
5612
5747
  }
5748
+ var MAX_MEMO_ID = 18446744073709551615n;
5749
+ function parseMemo(type, raw) {
5750
+ const value = type === "id" ? raw.trim() : raw;
5751
+ if (value.trim() === "") return void 0;
5752
+ if (type === "text") {
5753
+ return new TextEncoder().encode(value).length > 28 ? "The memo text can be at most 28 bytes." : { type, value };
5754
+ }
5755
+ if (!/^\d+$/.test(value) || BigInt(value) > MAX_MEMO_ID) return "The memo ID must be a whole number up to 2^64 - 1.";
5756
+ return { type, value };
5757
+ }
5613
5758
  function assetParam(record) {
5614
5759
  if (record.type === "native") return { type: "native" };
5615
5760
  if (record.type === "credit_alphanum4") {
@@ -5634,6 +5779,8 @@ function SendModal({ onClose }) {
5634
5779
  const [step, setStep] = useState("form");
5635
5780
  const [amount, setAmount] = useState("");
5636
5781
  const [destination, setDestination] = useState("");
5782
+ const [memo, setMemo] = useState("");
5783
+ const [memoType, setMemoType] = useState("text");
5637
5784
  const [selectedAsset, setSelectedAsset] = useState(null);
5638
5785
  const [showXdr, setShowXdr] = useState(false);
5639
5786
  const [copied, setCopied] = useState(false);
@@ -5644,9 +5791,10 @@ function SendModal({ onClose }) {
5644
5791
  useEffect(() => {
5645
5792
  if (selectedChain === null && chains.length > 0) setSelectedChain(chains[0]);
5646
5793
  }, [chains, selectedChain]);
5647
- const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA";
5794
+ const canSendOnChain = selectedChain === "STELLAR" || selectedChain === "SOLANA" || selectedChain === "POLYGON";
5648
5795
  const notReadyReason = walletNotReadyReason(wallet, selectedChain);
5649
- const isBaseUnitChain = selectedChain === "SOLANA";
5796
+ const isBaseUnitChain = selectedChain === "SOLANA" || selectedChain === "POLYGON";
5797
+ const nativeDecimals = selectedChain === "POLYGON" ? 18 : 9;
5650
5798
  useEffect(() => {
5651
5799
  void refreshWalletBalance();
5652
5800
  }, [refreshWalletBalance]);
@@ -5674,7 +5822,7 @@ function SendModal({ onClose }) {
5674
5822
  const hash = transaction.step === "success" ? transaction.hash : null;
5675
5823
  const buildData = "buildData" in transaction ? transaction.buildData : null;
5676
5824
  const explorerNetwork = buildData?.summary.network?.toLowerCase().includes("testnet") ? "testnet" : buildData ? "public" : network === "testnet" ? "testnet" : "public";
5677
- const explorerUrl = !hash ? null : selectedChain === "SOLANA" ? `https://explorer.solana.com/tx/${hash}${explorerNetwork === "testnet" ? "?cluster=devnet" : ""}` : `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}`;
5825
+ const explorerUrl = !hash ? null : selectedChain === "SOLANA" ? `https://explorer.solana.com/tx/${hash}${explorerNetwork === "testnet" ? "?cluster=devnet" : ""}` : selectedChain === "POLYGON" ? `https://${explorerNetwork === "testnet" ? "amoy." : ""}polygonscan.com/tx/${hash}` : `https://stellar.expert/explorer/${explorerNetwork}/tx/${hash}`;
5678
5826
  const IN_FLIGHT_STEPS3 = [
5679
5827
  "building",
5680
5828
  "signing",
@@ -5713,33 +5861,42 @@ function SendModal({ onClose }) {
5713
5861
  setFormError("Enter a destination address");
5714
5862
  return;
5715
5863
  }
5864
+ const parsedMemo = selectedChain === "STELLAR" ? parseMemo(memoType, memo) : void 0;
5865
+ if (typeof parsedMemo === "string") {
5866
+ setFormError(parsedMemo);
5867
+ return;
5868
+ }
5716
5869
  const isNativeAsset = selectedAsset.type === "native";
5717
5870
  if (isBaseUnitChain && !isNativeAsset && (!selectedAsset.issuer || selectedAsset.decimals === void 0)) {
5718
- setFormError("This token is missing its mint or decimals and cannot be sent.");
5871
+ setFormError("This token is missing its contract or decimals and cannot be sent.");
5719
5872
  return;
5720
5873
  }
5721
5874
  let sendAmount = amount;
5722
5875
  if (isBaseUnitChain) {
5723
5876
  try {
5724
- sendAmount = toBaseUnits(amount, selectedAsset.decimals ?? 9);
5877
+ sendAmount = toBaseUnits(amount, selectedAsset.decimals ?? nativeDecimals);
5725
5878
  } catch (e) {
5726
5879
  setFormError(e instanceof Error ? e.message : "Invalid amount");
5727
5880
  return;
5728
5881
  }
5729
5882
  }
5730
5883
  setStep("tx");
5731
- if (selectedChain === "SOLANA") {
5884
+ if (selectedChain === "SOLANA" || selectedChain === "POLYGON") {
5732
5885
  await sendPayment({
5733
- chain: "SOLANA",
5886
+ chain: selectedChain,
5734
5887
  destination: destination.trim(),
5735
5888
  amount: sendAmount,
5736
- // Native SOL carries no mint; an SPL token's mint rides in `issuer`,
5737
- // which the guard above proved is present.
5889
+ // A native coin carries no token id; an SPL mint or ERC-20 contract rides
5890
+ // in `issuer`, which the guard above proved is present.
5738
5891
  ...isNativeAsset ? {} : { mint: selectedAsset.issuer }
5739
5892
  });
5740
5893
  return;
5741
5894
  }
5742
- await buildTx("payment", { destination: destination.trim(), amount, asset: assetParam(selectedAsset) });
5895
+ await buildTx(
5896
+ "payment",
5897
+ { destination: destination.trim(), amount, asset: assetParam(selectedAsset) },
5898
+ parsedMemo ? { memo: parsedMemo } : void 0
5899
+ );
5743
5900
  }
5744
5901
  function handleSignAndSend() {
5745
5902
  if (transaction.step === "built") {
@@ -5824,6 +5981,10 @@ function SendModal({ onClose }) {
5824
5981
  onMax: handleMax,
5825
5982
  onPaste: handlePaste,
5826
5983
  onDestinationChange: setDestination,
5984
+ memo,
5985
+ memoType,
5986
+ onMemoChange: setMemo,
5987
+ onMemoTypeChange: setMemoType,
5827
5988
  onSubmit: () => void handleSubmit(),
5828
5989
  onSignAndSend: handleSignAndSend,
5829
5990
  onToggleXdr: () => setShowXdr((v) => !v),
@@ -6231,7 +6392,7 @@ function SwapModalTemplate({
6231
6392
  onDone
6232
6393
  }
6233
6394
  ),
6234
- /* @__PURE__ */ jsx(PollarModalFooter, { shield: true })
6395
+ /* @__PURE__ */ jsx(PollarModalFooter, {})
6235
6396
  ]
6236
6397
  }
6237
6398
  );
@@ -7977,6 +8138,362 @@ function SessionsModal({ onClose }) {
7977
8138
  }
7978
8139
  ) });
7979
8140
  }
8141
+ function useCosign() {
8142
+ const { getClient, isAuthenticated } = usePollar();
8143
+ const [state, setState] = useState({ pending: [] });
8144
+ const [loaded, setLoaded] = useState(false);
8145
+ useEffect(() => getClient().onCosignStateChange(setState), [getClient]);
8146
+ const refresh = useCallback(async () => {
8147
+ if (!isAuthenticated) return;
8148
+ try {
8149
+ await getClient().cosign.pending();
8150
+ } finally {
8151
+ setLoaded(true);
8152
+ }
8153
+ }, [getClient, isAuthenticated]);
8154
+ useEffect(() => {
8155
+ void refresh();
8156
+ }, [refresh]);
8157
+ return {
8158
+ pending: state.pending,
8159
+ ...state.lastResolved ? { lastResolved: state.lastResolved } : {},
8160
+ loaded,
8161
+ refresh,
8162
+ approve: useCallback((request) => getClient().cosign.approve(request), [getClient]),
8163
+ reject: useCallback((request) => getClient().cosign.reject(request), [getClient]),
8164
+ verify: useCallback((request) => getClient().cosign.verify(request), [getClient]),
8165
+ inspectQuorum: useCallback(() => getClient().cosign.inspectQuorum(), [getClient]),
8166
+ devices: useCallback(() => getClient().cosign.devices(), [getClient]),
8167
+ revokeDevice: useCallback(
8168
+ (deviceId, options) => getClient().cosign.revokeDevice(deviceId, options),
8169
+ [getClient]
8170
+ )
8171
+ };
8172
+ }
8173
+ var OPERATION_LABELS = {
8174
+ payment: "Send",
8175
+ path_payment_strict_send: "Swap",
8176
+ change_trust: "Add asset",
8177
+ create_account: "Create account",
8178
+ invoke_contract: "Contract call",
8179
+ manage_data: "Manage data"
8180
+ };
8181
+ function labelOf(request) {
8182
+ const op = request.operation ?? "";
8183
+ return OPERATION_LABELS[op] ?? (op ? op.replace(/_/g, " ") : "Transaction");
8184
+ }
8185
+ function expiresIn(request) {
8186
+ const ms = new Date(request.expiresAt).getTime() - Date.now();
8187
+ if (!Number.isFinite(ms) || ms <= 0) return "expired";
8188
+ const h = Math.floor(ms / 36e5);
8189
+ const m = Math.floor(ms % 36e5 / 6e4);
8190
+ return h > 0 ? `${h}h ${m}m` : `${m}m`;
8191
+ }
8192
+ function detailRows(request) {
8193
+ const details = request.details ?? {};
8194
+ const rows = [];
8195
+ const push = (label, value) => {
8196
+ if (value === void 0 || value === null || value === "") return;
8197
+ rows.push([
8198
+ label,
8199
+ typeof value === "string" && value.length > 24 && /^[GC][A-Z2-7]{55}$/.test(value) ? cropAddress(value) : String(value)
8200
+ ]);
8201
+ };
8202
+ push("Destination", details.destination);
8203
+ push("Amount", details.amount);
8204
+ push("Asset", details.asset ?? details.assetCode);
8205
+ push("Network", request.network);
8206
+ return rows;
8207
+ }
8208
+ function CloseIcon2() {
8209
+ return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
8210
+ }
8211
+ function CosignModalTemplate({
8212
+ theme,
8213
+ accentColor,
8214
+ styleOverrides,
8215
+ pending,
8216
+ current,
8217
+ verification,
8218
+ busy,
8219
+ error,
8220
+ approveBlocked = false,
8221
+ lastResolved,
8222
+ explorerUrl,
8223
+ onSelect,
8224
+ onApprove,
8225
+ onReject,
8226
+ onClose
8227
+ }) {
8228
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
8229
+ const verified = verification?.ok === true;
8230
+ const canVote = !!current && verified && !busy && !approveBlocked;
8231
+ return /* @__PURE__ */ jsxs(
8232
+ "div",
8233
+ {
8234
+ className: "pollar-modal-card pollar-cosign-modal",
8235
+ "data-theme": theme,
8236
+ style: cssVars,
8237
+ onClick: (e) => e.stopPropagation(),
8238
+ children: [
8239
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
8240
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm with your device" }),
8241
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon2, {}) }) })
8242
+ ] }),
8243
+ pending.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-empty", children: lastResolved ? /* @__PURE__ */ jsxs(Fragment, { children: [
8244
+ lastResolved.status === "APPROVED" && /* @__PURE__ */ jsxs("svg", { className: "pollar-cosign-approved-icon", width: "44", height: "44", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: [
8245
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }),
8246
+ /* @__PURE__ */ jsx(
8247
+ "path",
8248
+ {
8249
+ d: "M3.5 7l2.5 2.5 4.5-5",
8250
+ stroke: "white",
8251
+ strokeWidth: "1.2",
8252
+ strokeLinecap: "round",
8253
+ strokeLinejoin: "round"
8254
+ }
8255
+ )
8256
+ ] }),
8257
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-empty-title", children: lastResolved.status === "APPROVED" ? "Approved" : lastResolved.status === "REJECTED" ? "Rejected" : lastResolved.status.toLowerCase() }),
8258
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: lastResolved.submit?.hash ? /* @__PURE__ */ jsxs(Fragment, { children: [
8259
+ "Sent. Transaction",
8260
+ " ",
8261
+ explorerUrl ? /* @__PURE__ */ jsx("a", { className: "pollar-cosign-tx-link", href: explorerUrl, target: "_blank", rel: "noopener noreferrer", children: cropAddress(lastResolved.submit.hash) }) : cropAddress(lastResolved.submit.hash)
8262
+ ] }) : lastResolved.signedXdr ? "Signed. The app will send it." : "Nothing was signed." })
8263
+ ] }) : /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Nothing waits for your approval." }) }),
8264
+ pending.length > 1 && /* @__PURE__ */ jsx("ul", { className: "pollar-cosign-list", children: pending.map((r) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
8265
+ "button",
8266
+ {
8267
+ type: "button",
8268
+ className: `pollar-cosign-item${current?.id === r.id ? " pollar-cosign-item-active" : ""}`,
8269
+ onClick: () => onSelect(r),
8270
+ children: [
8271
+ /* @__PURE__ */ jsx("span", { children: labelOf(r) }),
8272
+ /* @__PURE__ */ jsx("span", { className: "pollar-cosign-muted", children: expiresIn(r) })
8273
+ ]
8274
+ }
8275
+ ) }, r.id)) }),
8276
+ current && /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
8277
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf(current) }),
8278
+ current.summary && /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: current.summary }),
8279
+ /* @__PURE__ */ jsx(GroupSteps, { request: current }),
8280
+ /* @__PURE__ */ jsxs("dl", { className: "pollar-cosign-rows", children: [
8281
+ detailRows(current).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
8282
+ /* @__PURE__ */ jsx("dt", { children: k }),
8283
+ /* @__PURE__ */ jsx("dd", { children: v })
8284
+ ] }, k)),
8285
+ /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
8286
+ /* @__PURE__ */ jsx("dt", { children: "Expires in" }),
8287
+ /* @__PURE__ */ jsx("dd", { children: expiresIn(current) })
8288
+ ] })
8289
+ ] }),
8290
+ /* @__PURE__ */ jsxs(
8291
+ "div",
8292
+ {
8293
+ className: `pollar-cosign-verify pollar-cosign-verify-${verification ? verification.ok ? "ok" : "bad" : "pending"}`,
8294
+ children: [
8295
+ !verification && "Checking the transaction against Turnkey...",
8296
+ verification?.ok && "Verified: this is exactly what will be signed.",
8297
+ verification && !verification.ok && `Not verified: ${verification.reason}`
8298
+ ]
8299
+ }
8300
+ ),
8301
+ error && /* @__PURE__ */ jsx("div", { className: "pollar-cosign-error", children: error }),
8302
+ /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-actions", children: [
8303
+ /* @__PURE__ */ jsx(
8304
+ "button",
8305
+ {
8306
+ type: "button",
8307
+ className: "pollar-cosign-btn pollar-cosign-btn-secondary",
8308
+ disabled: !!busy,
8309
+ onClick: () => onReject(current),
8310
+ children: busy === "rejecting" ? "Rejecting..." : "Reject"
8311
+ }
8312
+ ),
8313
+ /* @__PURE__ */ jsx(
8314
+ "button",
8315
+ {
8316
+ type: "button",
8317
+ className: "pollar-cosign-btn pollar-cosign-btn-primary",
8318
+ disabled: !canVote,
8319
+ onClick: () => onApprove(current),
8320
+ children: busy === "approving" ? "Approving..." : "Approve"
8321
+ }
8322
+ )
8323
+ ] })
8324
+ ] }),
8325
+ /* @__PURE__ */ jsx(PollarModalFooter, {})
8326
+ ]
8327
+ }
8328
+ );
8329
+ }
8330
+ function GroupSteps({ request }) {
8331
+ const group = cosignGroupOf(request);
8332
+ if (!group || group.steps.length < 2) return null;
8333
+ const stepKey = cosignStepKeyOf(request);
8334
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-group", children: [
8335
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-group-title", children: group.title }),
8336
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "Approving covers these steps; each one is checked before it is signed." }),
8337
+ /* @__PURE__ */ jsx("ol", { className: "pollar-cosign-group-steps", children: group.steps.map((step) => {
8338
+ const isThis = stepKey !== null && stepInGroup({ steps: [step] }, stepKey);
8339
+ return /* @__PURE__ */ jsxs("li", { className: isThis ? "pollar-cosign-group-step-current" : void 0, children: [
8340
+ step.label,
8341
+ isThis && /* @__PURE__ */ jsx("span", { className: "pollar-cosign-muted", children: " (this one)" })
8342
+ ] }, step.key);
8343
+ }) })
8344
+ ] });
8345
+ }
8346
+ function CosignModal({ onClose }) {
8347
+ const { getClient, styles, network } = usePollar();
8348
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
8349
+ const { pending, lastResolved, approve, reject, verify, inspectQuorum } = useCosign();
8350
+ const [selectedId, setSelectedId] = useState(null);
8351
+ const [verification, setVerification] = useState(null);
8352
+ const [busy, setBusy] = useState(null);
8353
+ const [error, setError] = useState(null);
8354
+ const [approveFailedFor, setApproveFailedFor] = useState(null);
8355
+ const [quorumFailure, setQuorumFailure] = useState(null);
8356
+ useEffect(() => {
8357
+ let cancelled = false;
8358
+ inspectQuorum().then((q) => {
8359
+ if (!cancelled && !q.ok) setQuorumFailure(q.reason);
8360
+ }).catch(() => void 0);
8361
+ return () => {
8362
+ cancelled = true;
8363
+ };
8364
+ }, [inspectQuorum]);
8365
+ const current = pending.find((r) => r.id === selectedId) ?? pending[0] ?? null;
8366
+ const currentRef = useRef(current);
8367
+ currentRef.current = current;
8368
+ const currentId = current?.id ?? null;
8369
+ useEffect(() => {
8370
+ const request = currentRef.current;
8371
+ if (!request) return;
8372
+ setVerification(null);
8373
+ setError(null);
8374
+ setApproveFailedFor(null);
8375
+ let cancelled = false;
8376
+ verify(request).then((v) => {
8377
+ if (!cancelled) setVerification(v);
8378
+ }).catch((err) => {
8379
+ if (!cancelled) setVerification({ ok: false, reason: err instanceof Error ? err.message : String(err) });
8380
+ });
8381
+ return () => {
8382
+ cancelled = true;
8383
+ };
8384
+ }, [currentId, verify]);
8385
+ const onCloseRef = useRef(onClose);
8386
+ onCloseRef.current = onClose;
8387
+ useEffect(() => {
8388
+ return getClient().onAuthStateChange((authState) => {
8389
+ if (authState.step === "idle") onCloseRef.current();
8390
+ });
8391
+ }, [getClient]);
8392
+ const run = useCallback(
8393
+ async (kind, request, action) => {
8394
+ setBusy(kind);
8395
+ setError(null);
8396
+ try {
8397
+ await action(request);
8398
+ } catch (err) {
8399
+ setError(transactionErrorMessage(isPollarApiError(err) ? err.code : null, err instanceof Error ? err.message : null));
8400
+ if (kind === "approving") setApproveFailedFor(request.id);
8401
+ } finally {
8402
+ setBusy(null);
8403
+ }
8404
+ },
8405
+ []
8406
+ );
8407
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsx(
8408
+ CosignModalTemplate,
8409
+ {
8410
+ theme,
8411
+ accentColor,
8412
+ styleOverrides,
8413
+ pending,
8414
+ current,
8415
+ verification: current ? quorumFailure ? { ok: false, reason: quorumFailure } : verification : null,
8416
+ busy,
8417
+ error,
8418
+ approveBlocked: !!current && approveFailedFor === current.id,
8419
+ ...lastResolved ? { lastResolved } : {},
8420
+ explorerUrl: lastResolved?.submit?.hash ? explorerTxUrl(lastResolved.network, lastResolved.submit.hash, network) : null,
8421
+ onSelect: (r) => setSelectedId(r.id),
8422
+ onApprove: (r) => void run("approving", r, approve),
8423
+ onReject: (r) => void run("rejecting", r, reject),
8424
+ onClose
8425
+ }
8426
+ ) });
8427
+ }
8428
+ var OPERATION_LABELS2 = {
8429
+ payment: "Send",
8430
+ path_payment_strict_send: "Swap",
8431
+ change_trust: "Add asset",
8432
+ create_account: "Create account",
8433
+ invoke_contract: "Contract call",
8434
+ manage_data: "Manage data"
8435
+ };
8436
+ function labelOf2(confirmation) {
8437
+ const op = confirmation.operation ?? "";
8438
+ return OPERATION_LABELS2[op] ?? (op ? op.replace(/_/g, " ") : "Transaction");
8439
+ }
8440
+ function detailRows2(confirmation) {
8441
+ const details = confirmation.details ?? {};
8442
+ const rows = [];
8443
+ const push = (label, value) => {
8444
+ if (value === void 0 || value === null || value === "") return;
8445
+ const text = typeof value === "object" ? JSON.stringify(value) : String(value);
8446
+ rows.push([label, /^[GC][A-Z2-7]{55}$/.test(text) ? cropAddress(text) : text]);
8447
+ };
8448
+ push("Destination", details.destination);
8449
+ push("Amount", details.amount);
8450
+ push("Asset", details.asset ?? details.assetCode ?? details.mint);
8451
+ push("Network", confirmation.network);
8452
+ return rows;
8453
+ }
8454
+ function CloseIcon3() {
8455
+ return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m3 3 8 8M11 3l-8 8", stroke: "currentColor", strokeWidth: "1.7", strokeLinecap: "round" }) });
8456
+ }
8457
+ function ConfirmModal({ confirmation }) {
8458
+ const { getClient, styles } = usePollar();
8459
+ const { theme, accentColor, styleOverrides, overlayStyle } = modalChrome(styles);
8460
+ const cssVars = buildModalCssVars(theme, accentColor, styleOverrides);
8461
+ const rejectRef = useRef(confirmation.reject);
8462
+ rejectRef.current = confirmation.reject;
8463
+ useEffect(() => {
8464
+ return getClient().onAuthStateChange((authState) => {
8465
+ if (authState.step === "idle") rejectRef.current();
8466
+ });
8467
+ }, [getClient]);
8468
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", style: overlayStyle, onClick: confirmation.reject, children: /* @__PURE__ */ jsxs(
8469
+ "div",
8470
+ {
8471
+ className: "pollar-modal-card pollar-cosign-modal",
8472
+ "data-theme": theme,
8473
+ style: cssVars,
8474
+ onClick: (e) => e.stopPropagation(),
8475
+ children: [
8476
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
8477
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Confirm transaction" }),
8478
+ /* @__PURE__ */ jsx("div", { className: "pollar-modal-header-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-modal-close", onClick: confirmation.reject, "aria-label": "Close", title: "Close", children: /* @__PURE__ */ jsx(CloseIcon3, {}) }) })
8479
+ ] }),
8480
+ /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-detail", children: [
8481
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-detail-title", children: labelOf2(confirmation) }),
8482
+ /* @__PURE__ */ jsx("p", { className: "pollar-cosign-muted", children: "The app asks you to sign this transaction with your wallet." }),
8483
+ /* @__PURE__ */ jsx("dl", { className: "pollar-cosign-rows", children: detailRows2(confirmation).map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-row", children: [
8484
+ /* @__PURE__ */ jsx("dt", { children: k }),
8485
+ /* @__PURE__ */ jsx("dd", { children: v })
8486
+ ] }, k)) }),
8487
+ /* @__PURE__ */ jsxs("div", { className: "pollar-cosign-actions", children: [
8488
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-cosign-btn pollar-cosign-btn-secondary", onClick: confirmation.reject, children: "Cancel" }),
8489
+ /* @__PURE__ */ jsx("button", { type: "button", className: "pollar-cosign-btn pollar-cosign-btn-primary", onClick: confirmation.confirm, children: "Confirm" })
8490
+ ] })
8491
+ ] }),
8492
+ /* @__PURE__ */ jsx(PollarModalFooter, {})
8493
+ ]
8494
+ }
8495
+ ) });
8496
+ }
7980
8497
  function TransactionModalTemplate({
7981
8498
  theme,
7982
8499
  accentColor,
@@ -8086,12 +8603,6 @@ function TransactionModal({ onClose }) {
8086
8603
  ) });
8087
8604
  }
8088
8605
  var PAGE_SIZE = 10;
8089
- function explorerUrlFor(chain, hash, network) {
8090
- if (chain === "SOLANA") {
8091
- return `https://explorer.solana.com/tx/${hash}${network === "testnet" ? "?cluster=devnet" : ""}`;
8092
- }
8093
- return `https://stellar.expert/explorer/${network === "testnet" ? "testnet" : "public"}/tx/${hash}`;
8094
- }
8095
8606
  function StatusBadge({ status }) {
8096
8607
  return /* @__PURE__ */ jsx("span", { className: "pollar-hist-item-badge", "data-status": status, children: status });
8097
8608
  }
@@ -8208,7 +8719,7 @@ function TxHistoryModalTemplate({
8208
8719
  txHistory.step === "loaded" && records.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No transactions yet." }),
8209
8720
  records.map((record) => {
8210
8721
  const hash = typeof record.hash === "string" && record.hash.length > 0 ? record.hash : void 0;
8211
- const explorerUrl = hash ? explorerUrlFor(record.chain, hash, record.network) : void 0;
8722
+ const explorerUrl = hash ? explorerTxUrl(record.chain, hash, record.network) : void 0;
8212
8723
  const asset = typeof record.details?.asset === "string" ? record.details.asset : void 0;
8213
8724
  const colon = asset ? asset.indexOf(":") : -1;
8214
8725
  const trustline = record.operation === "change_trust" && asset && colon > 0 ? {
@@ -8617,6 +9128,9 @@ function PollarProvider({
8617
9128
  const [transaction, setTransaction] = useState({ step: "idle" });
8618
9129
  const [txHistory, setTxHistory] = useState({ step: "idle" });
8619
9130
  const [sessions, setSessions] = useState({ step: "idle" });
9131
+ const [cosign, setCosign] = useState({ pending: [] });
9132
+ const [confirmations, setConfirmations] = useState([]);
9133
+ const firstConfirmation = confirmations[0] ?? null;
8620
9134
  const [walletBalance, setWalletBalance] = useState({ step: "idle" });
8621
9135
  const [enabledAssets, setEnabledAssets] = useState({ step: "idle" });
8622
9136
  const [resolvedConfig, setResolvedConfig] = useState(() => appConfigProp ?? DEFAULT_APP_CONFIG);
@@ -8634,6 +9148,12 @@ function PollarProvider({
8634
9148
  useEffect(() => {
8635
9149
  return pollarClient.onTxHistoryStateChange(setTxHistory);
8636
9150
  }, [pollarClient]);
9151
+ useEffect(() => {
9152
+ return pollarClient.onCosignStateChange(setCosign);
9153
+ }, [pollarClient]);
9154
+ useEffect(() => {
9155
+ return pollarClient.onConfirmationRequest(setConfirmations);
9156
+ }, [pollarClient]);
8637
9157
  useEffect(() => {
8638
9158
  return pollarClient.onSessionsStateChange(setSessions);
8639
9159
  }, [pollarClient]);
@@ -8734,6 +9254,12 @@ function PollarProvider({
8734
9254
  const [cardModalKey, setCardModalKey] = useState(0);
8735
9255
  const [receiveModalOpen, setReceiveModalOpen] = useState(false);
8736
9256
  const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
9257
+ const [cosignModalOpen, setCosignModalOpen] = useState(false);
9258
+ const cosignPendingCount = useRef(0);
9259
+ useEffect(() => {
9260
+ if (cosign.pending.length > cosignPendingCount.current) setCosignModalOpen(true);
9261
+ cosignPendingCount.current = cosign.pending.length;
9262
+ }, [cosign.pending.length]);
8737
9263
  const [distributionRulesModalOpen, setDistributionRulesModalOpen] = useState(false);
8738
9264
  useEffect(
8739
9265
  () => pollarClient.onAuthStateChange((authState) => {
@@ -8750,6 +9276,7 @@ function PollarProvider({
8750
9276
  setCardModalOpen(false);
8751
9277
  setReceiveModalOpen(false);
8752
9278
  setSessionsModalOpen(false);
9279
+ setCosignModalOpen(false);
8753
9280
  setDistributionRulesModalOpen(false);
8754
9281
  }),
8755
9282
  [pollarClient]
@@ -8834,6 +9361,10 @@ function PollarProvider({
8834
9361
  // sessions
8835
9362
  sessions,
8836
9363
  openSessionsModal: () => setSessionsModalOpen(true),
9364
+ // cosign
9365
+ cosign,
9366
+ openCosignModal: () => setCosignModalOpen(true),
9367
+ confirmations,
8837
9368
  // distribution
8838
9369
  openDistributionRulesModal: () => setDistributionRulesModalOpen(true),
8839
9370
  // network
@@ -8863,6 +9394,8 @@ function PollarProvider({
8863
9394
  transaction,
8864
9395
  txHistory,
8865
9396
  sessions,
9397
+ cosign,
9398
+ confirmations,
8866
9399
  walletBalance,
8867
9400
  refreshWalletBalance,
8868
9401
  enabledAssets,
@@ -8910,6 +9443,8 @@ function PollarProvider({
8910
9443
  /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCardModalOpen(false), children: /* @__PURE__ */ jsx(CardModal, { onClose: () => setCardModalOpen(false) }) }, `${cardModalKey}:${walletAddress}`),
8911
9444
  receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
8912
9445
  sessionsModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) }),
9446
+ cosignModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setCosignModalOpen(false), children: /* @__PURE__ */ jsx(CosignModal, { onClose: () => setCosignModalOpen(false) }) }),
9447
+ firstConfirmation && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: firstConfirmation.reject, children: /* @__PURE__ */ jsx(ConfirmModal, { confirmation: firstConfirmation }, firstConfirmation.id) }),
8913
9448
  distributionRulesModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setDistributionRulesModalOpen(false), children: /* @__PURE__ */ jsx(DistributionRulesModal, { onClose: () => setDistributionRulesModalOpen(false) }) })
8914
9449
  ] });
8915
9450
  }
@@ -9489,6 +10024,6 @@ function WalletButton() {
9489
10024
  );
9490
10025
  }
9491
10026
 
9492
- export { CardModal, ChainSelect, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, usePollar, walletNotReadyReason };
10027
+ export { CardModal, ChainSelect, CosignModal, CosignModalTemplate, DistributionRulesModal, DistributionRulesModalTemplate, EarnModal, EnabledAssetsModal, EnabledAssetsModalTemplate, KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, ProviderRegistrationModal, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RegistryCheckModal, RequirementFormModal, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, SwapModal, SwapModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, WalletButtonTemplate, addressForChain, browserPasskeyCeremony, browserPasskeySigner, buildModalCssVars, chainsOf, createPollarAdapterHook, modalChrome, readableTextOn, resolveChain, useChains, useCosign, usePollar, walletNotReadyReason };
9493
10028
  //# sourceMappingURL=index.mjs.map
9494
10029
  //# sourceMappingURL=index.mjs.map