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