@daimo/pay 1.8.6 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -2,7 +2,7 @@ import { http, useConnectors as useConnectors$1, useAccount, useSwitchChain, use
2
2
  import { mainnet, base as base$1, polygon, optimism, arbitrum, linea, bsc, sepolia, baseSepolia, worldchain, blast, mantle } from 'wagmi/chains';
3
3
  import { safe, injected, coinbaseWallet, walletConnect } from '@wagmi/connectors';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { DaimoPayIntentStatus, assert, DaimoPayOrderMode, readDaimoPayOrderID, getOrderDestChainId, assertNotNull, getChainName, arbitrum as arbitrum$1, base as base$2, blast as blast$1, bsc as bsc$1, ethereum, linea as linea$1, mantle as mantle$1, optimism as optimism$1, polygon as polygon$1, worldchain as worldchain$1, solana, getAddressContraction, supportedChains, getChainExplorerTxUrl, ExternalPaymentOptions, isCCTPV1Chain, debugJson, writeDaimoPayOrderID, getOrderSourceChainId, DaimoPayEventType, getDaimoPayOrderView } from '@daimo/pay-common';
5
+ import { DaimoPayIntentStatus, assert, DaimoPayOrderMode, readDaimoPayOrderID, getOrderDestChainId, assertNotNull, getChainName, arbitrum as arbitrum$1, base as base$2, blast as blast$1, bsc as bsc$1, ethereum, linea as linea$1, mantle as mantle$1, optimism as optimism$1, polygon as polygon$1, worldchain as worldchain$1, solana, getAddressContraction, supportedChains, getChainExplorerTxUrl, ExternalPaymentOptions, isCCTPV1Chain, debugJson, DepositAddressPaymentOptions, writeDaimoPayOrderID, getOrderSourceChainId, DaimoPayEventType, getDaimoPayOrderView } from '@daimo/pay-common';
6
6
  import { Buffer } from 'buffer';
7
7
  import React, { createContext, useRef, useState, useEffect, useLayoutEffect, useMemo, useContext, useSyncExternalStore, useCallback, createElement } from 'react';
8
8
  import styled$1, { css, keyframes, ThemeProvider } from 'styled-components';
@@ -22,7 +22,7 @@ import { VersionedTransaction } from '@solana/web3.js';
22
22
  import { normalize } from 'viem/ens';
23
23
 
24
24
  var name = "@daimo/pay";
25
- var version = "1.8.6";
25
+ var version = "1.9.0";
26
26
  var author = "Daimo";
27
27
  var homepage = "https://pay.daimo.com";
28
28
  var license = "BSD-2-Clause license";
@@ -61,7 +61,7 @@ var keywords = [
61
61
  "crypto"
62
62
  ];
63
63
  var dependencies = {
64
- "@daimo/pay-common": "1.8.6",
64
+ "@daimo/pay-common": "1.9.0",
65
65
  "@rollup/plugin-typescript": "^12.1.2",
66
66
  "@solana/wallet-adapter-base": "^0.9.23",
67
67
  "@solana/wallet-adapter-react": "^0.15.35",
@@ -2160,7 +2160,7 @@ function useLockBodyScroll(initialLocked) {
2160
2160
  useEffect(() => {
2161
2161
  if (locked !== initialLocked)
2162
2162
  setLocked(initialLocked);
2163
- }, [initialLocked]);
2163
+ }, [initialLocked]); // eslint-disable-line react-hooks/exhaustive-deps
2164
2164
  return [locked, setLocked];
2165
2165
  }
2166
2166
 
@@ -2600,11 +2600,12 @@ async function pollFindSourcePayment(store, trpc, orderId) {
2600
2600
  onResult: (found) => {
2601
2601
  const state = store.getState();
2602
2602
  // Check that we're still in the payment_unpaid state
2603
- if (state.type !== "payment_unpaid")
2604
- return;
2605
- if (found) {
2606
- store.dispatch({ type: "payment_started", order: state.order });
2603
+ if (state.type !== "payment_unpaid") {
2604
+ stopPolling();
2605
+ }
2606
+ else if (found) {
2607
2607
  stopPolling();
2608
+ store.dispatch({ type: "payment_started", order: state.order });
2608
2609
  }
2609
2610
  },
2610
2611
  onError: () => { },
@@ -2913,15 +2914,12 @@ function useFocusTrap() {
2913
2914
  }
2914
2915
  }
2915
2916
  useEffect(() => {
2916
- if (elRef.current) {
2917
- elRef.current.addEventListener("keydown", handleFocus);
2918
- elRef.current.focus({ preventScroll: true });
2919
- }
2920
- return () => {
2921
- if (elRef.current) {
2922
- elRef.current.removeEventListener("keydown", handleFocus);
2923
- }
2924
- };
2917
+ const el = elRef.current;
2918
+ if (el == null)
2919
+ return;
2920
+ el.addEventListener("keydown", handleFocus);
2921
+ el.focus({ preventScroll: true });
2922
+ return () => el.removeEventListener("keydown", handleFocus);
2925
2923
  }, []);
2926
2924
  return elRef;
2927
2925
  }
@@ -2931,7 +2929,7 @@ function FocusTrap(props) {
2931
2929
  if (!elRef.current)
2932
2930
  return;
2933
2931
  elRef.current.focus({ preventScroll: true });
2934
- }, []);
2932
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
2935
2933
  return (jsx("div", { ref: elRef, tabIndex: 0, children: props.children }));
2936
2934
  }
2937
2935
 
@@ -6171,9 +6169,8 @@ const About = () => {
6171
6169
  const animationDuration = 600;
6172
6170
  let interval;
6173
6171
  useEffect(() => {
6174
- //interval = setTimeout(nextSlide, autoplayDelay);
6175
6172
  return () => clearInterval(interval);
6176
- }, []);
6173
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
6177
6174
  const isSwipe = () => {
6178
6175
  if (sliderRef.current) {
6179
6176
  const { overflow } = getComputedStyle(sliderRef.current);
@@ -6225,19 +6222,18 @@ const About = () => {
6225
6222
  };
6226
6223
  const sliderRef = useRef(null);
6227
6224
  useEffect(() => {
6228
- if (!sliderRef.current)
6225
+ const slider = sliderRef.current;
6226
+ if (slider == null)
6229
6227
  return;
6230
- sliderRef.current.addEventListener("scroll", onScroll);
6231
- sliderRef.current.addEventListener("touchmove", onTouchMove);
6232
- sliderRef.current.addEventListener("touchend", onTouchEnd);
6228
+ slider.addEventListener("scroll", onScroll);
6229
+ slider.addEventListener("touchmove", onTouchMove);
6230
+ slider.addEventListener("touchend", onTouchEnd);
6233
6231
  return () => {
6234
- if (!sliderRef.current)
6235
- return;
6236
- sliderRef.current.removeEventListener("scroll", onScroll);
6237
- sliderRef.current.removeEventListener("touchmove", onTouchMove);
6238
- sliderRef.current.removeEventListener("touchend", onTouchEnd);
6232
+ slider.removeEventListener("scroll", onScroll);
6233
+ slider.removeEventListener("touchmove", onTouchMove);
6234
+ slider.removeEventListener("touchend", onTouchEnd);
6239
6235
  };
6240
- }, [sliderRef]);
6236
+ }, [sliderRef.current]); // eslint-disable-line react-hooks/exhaustive-deps
6241
6237
  const graphics = [
6242
6238
  jsx(SlideOne, { layoutId: "graphicCircle", duration: animationDuration, ease: animationEase }, "slideOne"),
6243
6239
  jsx(SlideTwo, { layoutId: "graphicCircle", duration: animationDuration, ease: animationEase }, "slideTwo"),
@@ -6597,7 +6593,7 @@ const useLastConnector = () => {
6597
6593
  setLastConnectorId(id ?? "");
6598
6594
  };
6599
6595
  init();
6600
- }, []);
6596
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
6601
6597
  const update = (id) => {
6602
6598
  storage?.setItem("recentConnectorId", id);
6603
6599
  };
@@ -6818,7 +6814,7 @@ const ScrollArea = ({ children, height, backgroundColor, mobileDirection, hideBo
6818
6814
  return () => {
6819
6815
  el.removeEventListener("scroll", handleScroll);
6820
6816
  };
6821
- }, [ref.current]);
6817
+ }, [ref.current]); // eslint-disable-line react-hooks/exhaustive-deps
6822
6818
  return (jsxs(ScrollContainer, { children: [jsx(ScrollAreaContainer, { ref: ref, "$mobile": isMobileFormat, "$height": height, "$backgroundColor": backgroundColor, "$mobileDirection": mobileDirection, "$hideBottomLine": hideBottomLine, "$totalItems": totalItems, children: children }), jsx(MoreIndicator, { ref: moreRef, className: "hide", onClick: () => {
6823
6819
  if (ref.current) {
6824
6820
  ref.current.scrollTo({
@@ -7329,7 +7325,6 @@ const BinanceSmartChain = ({ testnet, ...props }) => (jsx("svg", { ...props, "ar
7329
7325
  const Solana = ({ testnet, ...props }) => (jsxs("svg", { ...props, "aria-hidden": "true", width: "44", height: "44", viewBox: "0 0 200 200", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("rect", { width: "200", height: "200", rx: "100", fill: "#121212" }), jsx("g", { clipPath: "url(#clip0_295_42)", children: jsx("path", { d: "M149.48 125.382L132.807 142.801C132.444 143.18 132.006 143.482 131.519 143.688C131.031 143.894 130.505 144 129.974 144H50.9356C50.5585 144 50.1896 143.893 49.8742 143.691C49.5588 143.49 49.3107 143.203 49.1604 142.866C49.0101 142.529 48.9641 142.157 49.028 141.795C49.092 141.432 49.2631 141.096 49.5204 140.828L66.2061 123.408C66.5676 123.031 67.0047 122.729 67.4904 122.523C67.9762 122.317 68.5002 122.21 69.0301 122.21H148.064C148.441 122.21 148.81 122.317 149.126 122.518C149.441 122.72 149.689 123.007 149.84 123.344C149.99 123.681 150.036 124.053 149.972 124.415C149.908 124.777 149.737 125.113 149.48 125.382ZM132.807 90.3032C132.444 89.9248 132.006 89.6231 131.519 89.4169C131.031 89.2108 130.505 89.1045 129.974 89.1048H50.9356C50.5585 89.1048 50.1896 89.2121 49.8742 89.4136C49.5588 89.6151 49.3107 89.9019 49.1604 90.2388C49.0101 90.5758 48.9641 90.9482 49.028 91.3103C49.092 91.6723 49.2631 92.0083 49.5204 92.277L66.2061 109.697C66.5676 110.074 67.0047 110.375 67.4904 110.581C67.9762 110.788 68.5002 110.894 69.0301 110.895H148.064C148.441 110.895 148.81 110.788 149.126 110.586C149.441 110.385 149.689 110.098 149.84 109.761C149.99 109.424 150.036 109.052 149.972 108.69C149.908 108.328 149.737 107.992 149.48 107.723L132.807 90.3032ZM50.9356 77.7905H129.974C130.505 77.7907 131.031 77.6845 131.519 77.4783C132.006 77.2721 132.444 76.9704 132.807 76.592L149.48 59.1722C149.737 58.9036 149.908 58.5676 149.972 58.2055C150.036 57.8434 149.99 57.471 149.84 57.1341C149.689 56.7971 149.441 56.5103 149.126 56.3088C148.81 56.1073 148.441 56 148.064 56H69.0301C68.5002 56.0009 67.9762 56.1077 67.4904 56.3138C67.0047 56.52 66.5676 56.8211 66.2061 57.1985L49.5247 74.6183C49.2677 74.8866 49.0966 75.2223 49.0325 75.5839C48.9684 75.9456 49.0141 76.3177 49.1639 76.6545C49.3136 76.9913 49.5611 77.2781 49.8758 77.4799C50.1905 77.6817 50.5589 77.7896 50.9356 77.7905Z", fill: "url(#paint0_linear_295_42)" }) }), jsxs("defs", { children: [jsxs("linearGradient", { id: "paint0_linear_295_42", x1: "57.5256", y1: "146.097", x2: "137.993", y2: "52.9838", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { offset: "0.08", stopColor: "#9945FF" }), jsx("stop", { offset: "0.3", stopColor: "#8752F3" }), jsx("stop", { offset: "0.5", stopColor: "#5497D5" }), jsx("stop", { offset: "0.6", stopColor: "#43B4CA" }), jsx("stop", { offset: "0.72", stopColor: "#28E0B9" }), jsx("stop", { offset: "0.97", stopColor: "#19FB9B" })] }), jsx("clipPath", { id: "clip0_295_42", children: jsx("rect", { width: "101", height: "88", fill: "white", transform: "translate(49 56)" }) })] })] }));
7330
7326
  const Bitcoin = ({ testnet, ...props }) => (jsxs("svg", { ...props, "aria-hidden": "true", width: "44", height: "44", viewBox: "0 0 44 44", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M43.3388 27.3219C40.4005 39.1077 28.4634 46.2804 16.6762 43.3413C4.89383 40.4029 -2.27886 28.4652 0.660895 16.68C3.59789 4.89286 15.535 -2.28052 27.3187 0.657859C39.1052 3.59623 46.2772 15.5354 43.3388 27.3219Z", fill: "#F7931A" }), jsx("path", { d: "M31.6992 18.8656C32.1371 15.9382 29.9082 14.3645 26.8605 13.3147L27.8492 9.34922L25.4353 8.74765L24.4728 12.6087C23.8383 12.4505 23.1865 12.3013 22.5389 12.1535L23.5083 8.26709L21.0958 7.66553L20.1065 11.6297C19.5813 11.51 19.0657 11.3918 18.5652 11.2673L18.5679 11.255L15.239 10.4238L14.5969 13.0019C14.5969 13.0019 16.3878 13.4123 16.35 13.4378C17.3277 13.6818 17.5043 14.3288 17.4748 14.8417L16.3487 19.3592C16.416 19.3764 16.5033 19.4012 16.5996 19.4397C16.5192 19.4197 16.4332 19.3977 16.3445 19.3764L14.766 25.7048C14.6464 26.0018 14.3432 26.4473 13.6598 26.2782C13.6839 26.3133 11.9053 25.8403 11.9053 25.8403L10.707 28.6033L13.8482 29.3864C14.4326 29.5328 15.0053 29.6862 15.569 29.8305L14.5701 33.8414L16.9812 34.443L17.9705 30.4747C18.6291 30.6535 19.2685 30.8185 19.8941 30.9738L18.9082 34.9235L21.322 35.5251L22.321 31.5218C26.437 32.3007 29.5322 31.9865 30.835 28.2637C31.8848 25.2662 30.7827 23.5372 28.6171 22.4097C30.1942 22.046 31.3822 21.0085 31.6992 18.8656ZM26.184 26.5993C25.4381 29.5968 20.3912 27.9763 18.7549 27.57L20.0804 22.2563C21.7167 22.6647 26.9637 23.4732 26.184 26.5993ZM26.9307 18.8223C26.25 21.5489 22.0494 20.1636 20.6868 19.824L21.8885 15.0046C23.2512 15.3442 27.6395 15.9781 26.9307 18.8223Z", fill: "white" })] }));
7331
7327
  const Tron = ({ testnet, ...props }) => (jsxs("svg", { ...props, "aria-hidden": "true", width: "44", height: "44", viewBox: "0 0 44 44", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxs("g", { clipPath: "url(#clip0_450_171)", children: [jsx("path", { d: "M43.3388 27.3219C40.4005 39.1077 28.4634 46.2804 16.6762 43.3413C4.89383 40.4029 -2.27886 28.4652 0.660896 16.68C3.59789 4.89286 15.535 -2.28052 27.3187 0.65786C39.1052 3.59623 46.2772 15.5354 43.3388 27.3219Z", fill: "#FF060A" }), jsx("path", { d: "M36.3975 17.2381C35.0196 16.0232 33.1057 14.1724 31.5555 12.8625L31.4598 12.8056C31.3067 12.6917 31.1344 12.5968 30.9526 12.5303C27.2015 11.8659 9.74716 8.76218 9.41224 8.80015C9.31654 8.80964 9.22085 8.84761 9.1443 8.89507L9.05817 8.96151C8.95291 9.06591 8.86679 9.1893 8.81894 9.33167L8.7998 9.38862V9.70184V9.7493C10.7615 14.9507 18.5222 31.9785 20.0532 35.9839C20.1489 36.2591 20.3212 36.7717 20.6465 36.8001H20.7231C20.8953 36.8001 21.6417 35.8605 21.6417 35.8605C21.6417 35.8605 34.9717 20.5032 36.321 18.8707C36.4932 18.6713 36.6463 18.453 36.7803 18.2252C36.8186 18.0449 36.7994 17.8646 36.7324 17.6937C36.6655 17.5229 36.5411 17.3615 36.3975 17.2381ZM25.0484 19.032L30.7325 14.552L34.0722 17.4754L25.0484 19.032ZM22.8379 18.7378L13.0486 11.1066L28.8952 13.8876L22.8379 18.7378ZM23.7183 20.731L33.7373 19.1934L22.2829 32.3202L23.7183 20.731ZM11.7184 11.8754L22.0245 20.1805L20.5317 32.3296L11.7184 11.8754Z", fill: "white" })] }), jsx("defs", { children: jsx("clipPath", { id: "clip0_450_171", children: jsx("rect", { width: "44", height: "44", fill: "white" }) }) })] }));
7332
- const Zcash = ({ testnet, ...props }) => (jsxs("svg", { ...props, "aria-hidden": "true", width: "44", height: "44", viewBox: "0 0 44 44", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M43.3415 27.3221C40.4029 39.1078 28.4645 46.2804 16.676 43.3413C4.8924 40.403 -2.28106 28.4654 0.659014 16.6803C3.59632 4.8933 15.5347 -2.28001 27.3197 0.658341C39.1074 3.59668 46.2802 15.5357 43.3415 27.3221Z", fill: "white" }), jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M0 22C0 9.86741 9.86741 0 22 0C34.1326 0 44 9.86741 44 22C44 34.1326 34.1326 44 22 44C9.86741 44 0 34.1326 0 22ZM29.8475 11.7904V15.1384L20.5358 27.7681H29.8475V32.2088H23.8447V35.8885H20.1553V32.2088H14.1525V28.8607L23.4544 16.2311H14.1525V11.7904H20.1553V8.10089H23.8447V11.7904H29.8475Z", fill: "#F4B728" })] }));
7333
7328
  const Base = ({ testnet, ...props }) => (jsx("svg", { ...props, width: "44", height: "44", viewBox: "0 0 44 44", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: {
7334
7329
  background: testnet
7335
7330
  ? "linear-gradient(180deg, #8995A9 0%, #424D5F 99.48%)"
@@ -7972,21 +7967,21 @@ function QRCode({ ecl = "M", size: sizeProp = 200, uri, clearArea = false, image
7972
7967
  });
7973
7968
  });
7974
7969
  return dots;
7975
- }, [ecl, size, uri]);
7970
+ }, [ecl, size, uri]); // eslint-disable-line react-hooks/exhaustive-deps
7976
7971
  return (jsxs("svg", { height: size, width: size, viewBox: `0 0 ${size} ${size}`, style: {
7977
7972
  width: size,
7978
7973
  height: size,
7979
7974
  }, children: [jsx("rect", { fill: "transparent", height: size, width: size }), dots] }));
7980
7975
  }
7981
7976
 
7982
- function CustomQRCode({ value, image, imageBackground, imagePosition = "center", tooltipMessage, }) {
7977
+ function CustomQRCode({ value, image, imageBackground, imagePosition = "center", tooltipMessage, contentPadding = 13, }) {
7983
7978
  const windowSize = useWindowSize();
7984
7979
  const Logo = windowSize.width > 920 && tooltipMessage ? (jsx(Tooltip, { xOffset: 139, yOffset: 5, delay: 0.1, message: tooltipMessage, children: image })) : (image);
7985
- return (jsx(QRCodeContainer, { children: jsxs(QRCodeContent, { children: [image && (jsx(LogoContainer$3, { children: jsx(LogoIcon, { "$wcLogo": imagePosition !== "center", style: {
7980
+ return (jsx(QRCodeContainer, { children: jsxs(QRCodeContent, { style: { inset: contentPadding }, children: [image && (jsx(LogoContainer$3, { children: jsx(LogoIcon, { "$wcLogo": imagePosition !== "center", style: {
7986
7981
  background: imagePosition === "center" ? imageBackground : undefined,
7987
7982
  }, children: Logo }) })), jsx(AnimatePresence, { initial: false, children: value ? (jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0, position: "absolute", inset: [0, 0] }, transition: {
7988
7983
  duration: 0.2,
7989
- }, children: jsx(QRCode, { uri: value, size: 576, ecl: "H", clearArea: !!(imagePosition === "center" && image) }) }, value)) : (jsxs(QRPlaceholder, { initial: { opacity: 0.1 }, animate: { opacity: 0.1 }, exit: { opacity: 0, position: "absolute", inset: [0, 0] }, transition: {
7984
+ }, children: jsx(QRCode, { uri: value, size: 576, ecl: "H", clearArea: !!(imagePosition === "center" && image), image: imagePosition === "bottom right" ? image : undefined, imageBackground: imageBackground }) }, value)) : (jsxs(QRPlaceholder, { initial: { opacity: 0.1 }, animate: { opacity: 0.1 }, exit: { opacity: 0, position: "absolute", inset: [0, 0] }, transition: {
7990
7985
  duration: 0.2,
7991
7986
  }, children: [jsx("span", {}), jsx("span", {}), jsx("span", {}), jsx("div", {})] })) })] }) }));
7992
7987
  }
@@ -9184,7 +9179,7 @@ const ConnectWithInjector = ({ switchConnectMethod, forceState }) => {
9184
9179
  return () => {
9185
9180
  clearTimeout(connectTimeout);
9186
9181
  };
9187
- }, []);
9182
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
9188
9183
  /** Timeout functionality if necessary
9189
9184
  let expiryTimeout: any;
9190
9185
  useEffect(() => {
@@ -9484,7 +9479,7 @@ function useWalletConnectUri({ enabled } = {
9484
9479
  };
9485
9480
  }
9486
9481
  }
9487
- }, [enabled, connector, isConnected]);
9482
+ }, [enabled, connector, isConnected]); // eslint-disable-line react-hooks/exhaustive-deps
9488
9483
  return {
9489
9484
  uri,
9490
9485
  };
@@ -9598,7 +9593,7 @@ const ConnectUsing = () => {
9598
9593
  };
9599
9594
  if (status === states.INJECTOR)
9600
9595
  checkProvider();
9601
- }, []);
9596
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
9602
9597
  if (!wallet)
9603
9598
  return jsx(Alert, { children: "Connector not found" });
9604
9599
  return (jsxs(AnimatePresence, { children: [status === states.QRCODE && (jsx(motion.div, { initial: "initial", animate: "animate", exit: "exit", variants: contentVariants$2, children: jsx(ConnectWithQRCode, { switchConnectMethod: (id) => {
@@ -10113,7 +10108,7 @@ const MultiCurrencySelectAmount = ({ selectedTokenOption, setSelectedTokenOption
10113
10108
  const [continueDisabled, setContinueDisabled] = useState(true);
10114
10109
  useEffect(() => {
10115
10110
  triggerResize();
10116
- }, [message]);
10111
+ }, [message]); // eslint-disable-line react-hooks/exhaustive-deps
10117
10112
  /**
10118
10113
  * Update the editable value and secondary value, taking into account whether
10119
10114
  * the user is currently editing the USD value or the token value.
@@ -10250,19 +10245,17 @@ const ExternalPaymentSpinner = ({ logoURI, logoShape, }) => {
10250
10245
  return (jsx(LoadingContainer$2, { children: jsx(AnimationContainer$1, { "$circle": logoShape === "circle", children: jsx(AnimatePresence, { children: optionSpinner }) }) }));
10251
10246
  };
10252
10247
 
10253
- // TODO: min amount for deposit address should come from the backend
10254
- const MIN_USD_VALUE = 20;
10255
10248
  const SelectDepositAddressAmount = () => {
10256
10249
  const { paymentState, setRoute, triggerResize } = usePayContext();
10257
10250
  const { selectedDepositAddressOption } = paymentState;
10258
10251
  const maxUsdLimit = paymentState.getOrderUsdLimit();
10259
- const minimumMessage = `Minimum ${formatUsd(MIN_USD_VALUE, "up")}`;
10252
+ // const minimumMessage = `Minimum ${formatUsd(MIN_USD_VALUE, "up")}`;
10260
10253
  const [usdInput, setUsdInput] = useState("");
10261
- const [message, setMessage] = useState(minimumMessage);
10254
+ const [message, setMessage] = useState("");
10262
10255
  const [continueDisabled, setContinueDisabled] = useState(true);
10263
10256
  useEffect(() => {
10264
10257
  triggerResize();
10265
- }, [message]);
10258
+ }, [message]); // eslint-disable-line react-hooks/exhaustive-deps
10266
10259
  if (selectedDepositAddressOption == null) {
10267
10260
  return jsx(PageContent, {});
10268
10261
  }
@@ -10275,10 +10268,10 @@ const SelectDepositAddressAmount = () => {
10275
10268
  setMessage(`Maximum ${formatUsd(maxUsdLimit)}`);
10276
10269
  }
10277
10270
  else {
10278
- setMessage(minimumMessage);
10271
+ setMessage("");
10279
10272
  }
10280
10273
  const usd = Number(sanitizeNumber(value));
10281
- setContinueDisabled(usd <= 0 || usd < MIN_USD_VALUE || usd > maxUsdLimit);
10274
+ setContinueDisabled(usd <= 0 || usd > maxUsdLimit);
10282
10275
  };
10283
10276
  const handleKeyDown = (e) => {
10284
10277
  if (e.key === "Enter" && !continueDisabled) {
@@ -10452,7 +10445,7 @@ const OptionsList = ({ options, isLoading, requiredSkeletons, scrollHeight = 300
10452
10445
  const SkeletonOptionItem = () => {
10453
10446
  return (jsxs(OptionButton, { type: "button", children: [jsx(SkeletonIcon, {}), jsx(SkeletonLabel, {})] }));
10454
10447
  };
10455
- const pulse = keyframes `
10448
+ const pulse$1 = keyframes `
10456
10449
  0% {
10457
10450
  opacity: 0.6;
10458
10451
  }
@@ -10470,14 +10463,14 @@ const SkeletonIcon = styled.div `
10470
10463
  height: 32px;
10471
10464
  border-radius: 22.5%;
10472
10465
  background-color: rgba(0, 0, 0, 0.1);
10473
- animation: ${pulse} 1.5s ease-in-out infinite;
10466
+ animation: ${pulse$1} 1.5s ease-in-out infinite;
10474
10467
  `;
10475
10468
  const SkeletonLabel = styled.div `
10476
10469
  width: 100px;
10477
10470
  height: 16px;
10478
10471
  border-radius: 8px;
10479
10472
  background-color: rgba(0, 0, 0, 0.1);
10480
- animation: ${pulse} 1.5s ease-in-out infinite;
10473
+ animation: ${pulse$1} 1.5s ease-in-out infinite;
10481
10474
  `;
10482
10475
  const OptionItem = ({ option }) => {
10483
10476
  const hydratedIcons = option.icons.map((icon) => {
@@ -10637,7 +10630,7 @@ const SelectExternalAmount = () => {
10637
10630
  const [continueDisabled, setContinueDisabled] = useState(true);
10638
10631
  useEffect(() => {
10639
10632
  triggerResize();
10640
- }, [message]);
10633
+ }, [message]); // eslint-disable-line react-hooks/exhaustive-deps
10641
10634
  if (selectedExternalOption == null) {
10642
10635
  return jsx(PageContent, {});
10643
10636
  }
@@ -10699,9 +10692,7 @@ function SelectMethod() {
10699
10692
  const { connected: isSolanaConnected, wallet: solanaWallet, wallets: solanaWallets, disconnect: disconnectSolana, publicKey, } = useWallet$1();
10700
10693
  const { setRoute, paymentState, wcWallet, log } = usePayContext();
10701
10694
  const { disconnectAsync } = useDisconnect();
10702
- const { setSelectedExternalOption, externalPaymentOptions, showSolanaPaymentMethod, depositAddressOptions, senderEnsName, } = paymentState;
10703
- const { order } = useDaimoPay();
10704
- const paymentOptions = order?.metadata.payer?.paymentOptions;
10695
+ const { setSelectedExternalOption, externalPaymentOptions, showSolanaPaymentMethod, senderEnsName, } = paymentState;
10705
10696
  // Decide whether to show the connected eth account, solana account, or both.
10706
10697
  const showConnectedEth = isEthConnected;
10707
10698
  const showConnectedSolana = isSolanaConnected && showSolanaPaymentMethod;
@@ -10764,10 +10755,6 @@ function SelectMethod() {
10764
10755
  }
10765
10756
  return connectedOptions;
10766
10757
  };
10767
- // Deposit address options, e.g. Bitcoin, Tron, Zcash, etc.
10768
- // Include by default if paymentOptions not provided
10769
- const showDepositAddressMethod = paymentOptions == null ||
10770
- paymentOptions.includes(ExternalPaymentOptions.ExternalChains);
10771
10758
  const connectedWalletOptions = getConnectedWalletOptions();
10772
10759
  const unconnectedWalletOption = {
10773
10760
  id: "unconnectedWallet",
@@ -10790,20 +10777,6 @@ function SelectMethod() {
10790
10777
  options.push(solanaOption);
10791
10778
  }
10792
10779
  }
10793
- // ZKP2P is currently only available on desktop. Check if the user is on
10794
- // desktop and if any ZKP2P options are available.
10795
- const zkp2pOptions = externalPaymentOptions.options.get("zkp2p") ?? [];
10796
- const showZkp2pPaymentMethod = !isMobile && zkp2pOptions.length > 0;
10797
- if (showZkp2pPaymentMethod) {
10798
- options.push({
10799
- id: "ZKP2P",
10800
- title: "Pay via payment app",
10801
- icons: zkp2pOptions.slice(0, 3).map((option) => option.logoURI),
10802
- onClick: () => {
10803
- setRoute(ROUTES.SELECT_ZKP2P);
10804
- },
10805
- });
10806
- }
10807
10780
  // External payment options, e.g. Binance, Coinbase, etc.
10808
10781
  options.push(...(externalPaymentOptions.options.get("external") ?? []).map((option) => ({
10809
10782
  id: option.id,
@@ -10822,9 +10795,21 @@ function SelectMethod() {
10822
10795
  disabled: option.disabled,
10823
10796
  subtitle: option.message,
10824
10797
  })));
10825
- if (showDepositAddressMethod) {
10826
- const depositAddressOption = getDepositAddressOption(depositAddressOptions, setRoute, paymentState.isDepositFlow);
10827
- options.push(depositAddressOption);
10798
+ const depositAddressOption = getDepositAddressOption(setRoute);
10799
+ options.push(depositAddressOption);
10800
+ // ZKP2P is currently only available on desktop. Check if the user is on
10801
+ // desktop and if any ZKP2P options are available.
10802
+ const zkp2pOptions = externalPaymentOptions.options.get("zkp2p") ?? [];
10803
+ const showZkp2pPaymentMethod = !isMobile && zkp2pOptions.length > 0;
10804
+ if (showZkp2pPaymentMethod) {
10805
+ options.push({
10806
+ id: "ZKP2P",
10807
+ title: "Pay via payment app",
10808
+ icons: zkp2pOptions.slice(0, 2).map((option) => option.logoURI),
10809
+ onClick: () => {
10810
+ setRoute(ROUTES.SELECT_ZKP2P);
10811
+ },
10812
+ });
10828
10813
  }
10829
10814
  return (jsxs(PageContent, { children: [jsx(OrderHeader, {}), jsx(OptionsList, { requiredSkeletons: isMobile ? 4 : 3, isLoading: externalPaymentOptions.loading, options: externalPaymentOptions.loading ? [] : options }), jsx(PoweredByFooter, {})] }));
10830
10815
  }
@@ -10833,11 +10818,10 @@ function getBestUnconnectedWalletIcons(connector, isMobile) {
10833
10818
  const icons = [];
10834
10819
  const strippedId = connector?.id.toLowerCase(); // some connector ids can have weird casing and or suffixes and prefixes
10835
10820
  const [isRainbow, isTrust, isPhantom, isCoinbase] = [
10836
- strippedId?.includes("rainbow.me"),
10821
+ strippedId?.includes("rainbow"),
10837
10822
  strippedId?.includes("trust"),
10838
10823
  strippedId?.includes("phantom"),
10839
10824
  strippedId?.includes("coinbase"),
10840
- strippedId?.includes("rainbow"),
10841
10825
  ];
10842
10826
  if (isMobile) {
10843
10827
  if (!isTrust)
@@ -10846,7 +10830,7 @@ function getBestUnconnectedWalletIcons(connector, isMobile) {
10846
10830
  icons.push(jsx(Rainbow, {}));
10847
10831
  if (!isPhantom)
10848
10832
  icons.push(jsx(Phantom, {}));
10849
- if (icons.length < 3)
10833
+ if (!isCoinbase && icons.length < 3)
10850
10834
  icons.push(jsx(Coinbase, {}));
10851
10835
  }
10852
10836
  else {
@@ -10854,10 +10838,6 @@ function getBestUnconnectedWalletIcons(connector, isMobile) {
10854
10838
  icons.push(jsx(Rainbow, {}));
10855
10839
  if (!isPhantom)
10856
10840
  icons.push(jsx(Phantom, {}));
10857
- if (!isCoinbase)
10858
- icons.push(jsx(Coinbase, {}));
10859
- if (icons.length < 3)
10860
- icons.push(jsx(Rabby, {}));
10861
10841
  }
10862
10842
  return icons;
10863
10843
  }
@@ -10877,25 +10857,14 @@ function getSolanaOption(isIOS, isAndroid, solanaWallets, disconnectSolana, setR
10877
10857
  },
10878
10858
  };
10879
10859
  }
10880
- function getDepositAddressOption(depositAddressOptions, setRoute, isDepositFlow) {
10881
- // TODO: API should serve the subtitle and disabled
10882
- const disabled = !isDepositFlow &&
10883
- !depositAddressOptions.loading &&
10884
- depositAddressOptions.options.length === 0;
10885
- const subtitle = disabled ? "Minimum $20.00" : "Bitcoin, Tron, Zcash...";
10860
+ function getDepositAddressOption(setRoute) {
10886
10861
  return {
10887
10862
  id: "depositAddress",
10888
- title: "Pay on another chain",
10889
- subtitle,
10890
- icons: [
10891
- jsx(Bitcoin, {}, "bitcoin"),
10892
- jsx(Tron, {}, "tron"),
10893
- jsx(Zcash, {}, "zcash"),
10894
- ],
10863
+ title: "Pay to address",
10864
+ icons: [jsx(Ethereum, {}, "eth"), jsx(Tron, {}, "tron"), jsx(Base, {}, "base")],
10895
10865
  onClick: () => {
10896
10866
  setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN);
10897
10867
  },
10898
- disabled,
10899
10868
  };
10900
10869
  }
10901
10870
 
@@ -11272,10 +11241,10 @@ const PayWithSolanaToken = () => {
11272
11241
  // Give user time to see the UI before opening
11273
11242
  const transferTimeout = setTimeout(handleTransfer, 100);
11274
11243
  return () => clearTimeout(transferTimeout);
11275
- }, [selectedSolanaTokenOption]);
11244
+ }, [selectedSolanaTokenOption]); // eslint-disable-line react-hooks/exhaustive-deps
11276
11245
  useEffect(() => {
11277
11246
  triggerResize();
11278
- }, [payState]);
11247
+ }, [payState]); // eslint-disable-line react-hooks/exhaustive-deps
11279
11248
  return (jsxs(PageContent, { children: [selectedSolanaTokenOption && (jsx(TokenLogoSpinner, { token: selectedSolanaTokenOption.required.token })), jsxs(ModalContent, { style: { paddingBottom: 0 }, children: [jsx(ModalH1, { children: payState }), selectedSolanaTokenOption && (jsx(PaymentBreakdown, { paymentOption: selectedSolanaTokenOption })), payState === PayState.RequestCancelled && (jsx(Button, { onClick: handleTransfer, children: "Retry Payment" }))] })] }));
11280
11249
  };
11281
11250
 
@@ -11288,13 +11257,55 @@ const SelectSolanaAmount = () => {
11288
11257
  return (jsx(MultiCurrencySelectAmount, { selectedTokenOption: selectedSolanaTokenOption, setSelectedTokenOption: setSelectedSolanaTokenOption, nextPage: ROUTES.SOLANA_PAY_WITH_TOKEN }));
11289
11258
  };
11290
11259
 
11291
- const WaitingDepositAddress = () => {
11260
+ const CircleTimer = ({ total, size = 24, stroke = 3, currentTime, onTimeChange, children, }) => {
11261
+ // timestamp (ms) when timer ends
11262
+ const [target, setTarget] = useState(Date.now() + (currentTime ?? total) * 1000);
11263
+ const [left, setLeft] = useState(currentTime ?? total);
11264
+ // react to external currentTime updates
11265
+ useEffect(() => {
11266
+ if (currentTime !== undefined) {
11267
+ setTarget(Date.now() + currentTime * 1000);
11268
+ setLeft(currentTime);
11269
+ }
11270
+ }, [currentTime]);
11271
+ // interval tick
11272
+ useEffect(() => {
11273
+ const id = setInterval(() => {
11274
+ const secs = Math.max(0, Math.ceil((target - Date.now()) / 1000));
11275
+ setLeft(secs);
11276
+ onTimeChange?.(secs);
11277
+ if (secs === 0)
11278
+ clearInterval(id);
11279
+ }, 1000);
11280
+ return () => clearInterval(id);
11281
+ }, [target, onTimeChange]);
11282
+ const ratio = Math.round((left * 100) / total); // 0-100
11283
+ const radius = Math.round((size - stroke) / 2); // integer radius
11284
+ const circumference = Math.round((2 * 314 * radius) / 100); // 2πr, π≈3.14
11285
+ const dashoffset = Math.round((circumference * (100 - ratio)) / 100);
11286
+ // colour transition: green → orange → red
11287
+ const color = ratio <= 10
11288
+ ? "var(--timer-red, #D92D20)"
11289
+ : ratio <= 40
11290
+ ? "var(--timer-orange, #F79009)"
11291
+ : "var(--timer-green, #12D18E)";
11292
+ const center = Math.round(size / 2);
11293
+ return (jsxs("svg", { viewBox: `0 0 ${size} ${size}`, width: size, height: size, role: "img", "aria-label": `Timer: ${left}s left of ${total}s`, children: [jsx("circle", { cx: center, cy: center, r: radius, fill: "transparent", stroke: "var(--ck-body-background-secondary, #EEE)", strokeWidth: stroke }), jsx("circle", { cx: center, cy: center, r: radius, fill: "transparent", stroke: color, strokeWidth: stroke, strokeDasharray: circumference, strokeDashoffset: dashoffset, strokeLinecap: "round", style: { transition: "stroke-dashoffset 1s linear" }, transform: `rotate(-90 ${center} ${center})` }), children && (jsx("foreignObject", { x: "0", y: "0", width: size, height: size, children: jsx("div", { style: {
11294
+ display: "flex",
11295
+ alignItems: "center",
11296
+ justifyContent: "center",
11297
+ width: "100%",
11298
+ height: "100%",
11299
+ }, children: children }) }))] }));
11300
+ };
11301
+
11302
+ function WaitingDepositAddress() {
11292
11303
  const context = usePayContext();
11293
11304
  const { triggerResize, paymentState } = context;
11294
11305
  const { payWithDepositAddress, selectedDepositAddressOption } = paymentState;
11295
11306
  const [details, setDetails] = useState();
11296
11307
  const [failed, setFailed] = useState(false);
11297
- useEffect(() => {
11308
+ const generateDepositAddress = () => {
11298
11309
  if (!selectedDepositAddressOption)
11299
11310
  return;
11300
11311
  payWithDepositAddress(selectedDepositAddressOption.id).then((details) => {
@@ -11303,12 +11314,172 @@ const WaitingDepositAddress = () => {
11303
11314
  else
11304
11315
  setDetails(details);
11305
11316
  });
11306
- }, [selectedDepositAddressOption]);
11317
+ };
11318
+ // TODO: load payment status, show underpayment
11319
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11320
+ useEffect(generateDepositAddress, [selectedDepositAddressOption]);
11321
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11322
+ useEffect(triggerResize, [details]);
11323
+ return (jsx(PageContent, { children: selectedDepositAddressOption == null ? null : failed ? (jsx(DepositFailed, { meta: selectedDepositAddressOption })) : (jsx(DepositAddressInfo, { meta: selectedDepositAddressOption, details: details, refresh: generateDepositAddress })) }));
11324
+ }
11325
+ function DepositAddressInfo({ meta, details, refresh, }) {
11326
+ const { isMobile } = useIsMobile();
11327
+ const [remainingS, totalS] = useCountdown(details?.expirationS);
11328
+ const isExpired = details?.expirationS != null && remainingS === 0;
11329
+ return (jsxs(ModalContent, { children: [isExpired ? (jsx(LogoWrap, { children: jsx(Button, { onClick: refresh, style: { width: 128 }, children: "Refresh" }) })) : isMobile ? (jsx(LogoWrap, { children: jsx("img", { src: meta.logoURI, width: "64px", height: "64px" }) })) : (jsx(QRWrap, { children: jsx(CustomQRCode, { value: details?.uri, contentPadding: 24, size: 200, image: jsx("img", { src: meta.logoURI, width: "100%", height: "100%" }) }) })), jsx(CopyableInfo, { meta: meta, details: details, remainingS: remainingS, totalS: totalS })] }));
11330
+ }
11331
+ const LogoWrap = styled.div `
11332
+ padding: 32px 0;
11333
+ height: 128px;
11334
+ display: flex;
11335
+ align-items: center;
11336
+ justify-content: center;
11337
+ `;
11338
+ const QRWrap = styled.div `
11339
+ margin: 0 auto;
11340
+ width: 280px;
11341
+ `;
11342
+ function CopyableInfo({ meta, details, remainingS, totalS, }) {
11343
+ const currencies = details?.suffix;
11344
+ const isExpired = details?.expirationS != null && remainingS === 0;
11345
+ return (jsxs(CopyableInfoWrapper, { children: [jsx(CopyRowOrThrobber, { title: "Send Exactly", value: details?.amount, smallText: currencies, disabled: isExpired }), jsx(CopyRowOrThrobber, { title: "Receiving Address", value: details?.address, valueText: details && getAddressContraction(details.address), disabled: isExpired }), jsx(CountdownWrap, { children: jsx(CountdownTimer, { remainingS: remainingS, totalS: totalS }) })] }));
11346
+ }
11347
+ const CopyableInfoWrapper = styled.div `
11348
+ display: flex;
11349
+ flex-direction: column;
11350
+ justify-content: stretch;
11351
+ gap: 0;
11352
+ margin-top: 8px;
11353
+ `;
11354
+ const CountdownWrap = styled.div `
11355
+ margin-top: 24px;
11356
+ height: 16px;
11357
+ `;
11358
+ function useCountdown(expirationS) {
11359
+ const [initMs] = useState(Date.now());
11360
+ const [ms, setMs] = useState(initMs);
11307
11361
  useEffect(() => {
11308
- triggerResize();
11309
- }, [details]);
11310
- return (jsx(PageContent, { children: failed ? (jsxs(ModalContent, { style: { marginLeft: 24, marginRight: 24 }, children: [jsxs(ModalH1, { children: [selectedDepositAddressOption?.id, " unavailable"] }), jsxs(ModalBody, { children: ["We're unable to process ", selectedDepositAddressOption?.id, " ", "payments at this time. Please select another payment method."] }), jsx(SelectAnotherMethodButton, {})] })) : (jsxs(ModalContent, { children: [jsx(CustomQRCode, { value: details?.uri, image: jsx("img", { src: selectedDepositAddressOption?.logoURI, width: "100%", height: "100%" }), tooltipMessage: jsxs(Fragment, { children: [jsx(ScanIconWithLogos, { logo: jsx("img", { src: selectedDepositAddressOption?.logoURI }) }), jsxs("span", { children: ["Use a ", selectedDepositAddressOption?.id, " wallet to scan"] })] }) }), details && (jsxs(Fragment, { children: [jsx(OrDivider, {}), jsxs(ModalBody, { children: ["Send exactly ", details.amount, " ", details.suffix, " to", " ", getAddressContraction(details.address), " and return to this page. Confirmation should appear in a few minutes."] }), jsx(CopyToClipboard, { variant: "button", string: details.address, children: "Copy Address" }), jsx(CopyToClipboard, { variant: "left", string: details.amount, children: "Copy Amount" })] }))] })) }));
11362
+ const interval = setInterval(() => setMs(Date.now()), 1000);
11363
+ return () => clearInterval(interval);
11364
+ }, []);
11365
+ if (expirationS == null)
11366
+ return [0, 0];
11367
+ const remainingS = Math.max(0, (expirationS - ms / 1000) | 0);
11368
+ const totalS = Math.max(0, (expirationS - initMs / 1000) | 0);
11369
+ return [remainingS, totalS];
11370
+ }
11371
+ function CountdownTimer({ remainingS, totalS, }) {
11372
+ if (totalS == 0)
11373
+ return null;
11374
+ if (remainingS > 3600)
11375
+ return null;
11376
+ const isExpired = remainingS === 0;
11377
+ return (jsx(ModalBody, { children: jsxs(CountdownRow, { children: [jsx(CircleTimer, { total: totalS, currentTime: remainingS, size: 18, stroke: 3 }), jsx("strong", { children: isExpired ? "Expired" : formatTime(remainingS) })] }) }));
11378
+ }
11379
+ const CountdownRow = styled.div `
11380
+ display: flex;
11381
+ align-items: center;
11382
+ justify-content: center;
11383
+ gap: 8px;
11384
+ font-variant-numeric: tabular-nums;
11385
+ `;
11386
+ const formatTime = (sec) => {
11387
+ const m = `${Math.floor(sec / 60)}`.padStart(2, "0");
11388
+ const s = `${sec % 60}`.padStart(2, "0");
11389
+ return `${m}:${s}`;
11311
11390
  };
11391
+ function DepositFailed({ meta, }) {
11392
+ return (jsxs(ModalContent, { style: { marginLeft: 24, marginRight: 24 }, children: [jsxs(ModalH1, { children: [meta.id, " unavailable"] }), jsxs(ModalBody, { children: ["We're unable to process ", meta.id, " payments at this time. Please select another payment method."] }), jsx(SelectAnotherMethodButton, {})] }));
11393
+ }
11394
+ const CopyRow = styled.button `
11395
+ display: block;
11396
+ height: 64px;
11397
+ border-radius: 8px;
11398
+ padding: 8px 16px;
11399
+
11400
+ cursor: pointer;
11401
+
11402
+ display: flex;
11403
+ align-items: center;
11404
+ justify-content: space-between;
11405
+
11406
+ transition: all 100ms ease;
11407
+
11408
+ &:hover {
11409
+ opacity: 0.8;
11410
+ }
11411
+
11412
+ &:active {
11413
+ transform: scale(0.98);
11414
+ background-color: var(--ck-body-background-secondary);
11415
+ }
11416
+
11417
+ &:disabled {
11418
+ cursor: default;
11419
+ opacity: 0.5;
11420
+ transform: scale(0.98);
11421
+ background-color: var(--ck-body-background-secondary);
11422
+ }
11423
+ `;
11424
+ const LabelRow = styled.div `
11425
+ margin-bottom: 4px;
11426
+ `;
11427
+ const MainRow = styled.div `
11428
+ display: flex;
11429
+ align-items: center;
11430
+ justify-content: space-between;
11431
+ `;
11432
+ const ValueContainer = styled.div `
11433
+ display: flex;
11434
+ align-items: center;
11435
+ gap: 8px;
11436
+ `;
11437
+ const SmallText = styled.span `
11438
+ font-size: 14px;
11439
+ color: var(--ck-body-color-muted);
11440
+ `;
11441
+ const pulse = keyframes `
11442
+ 0% {
11443
+ opacity: 0.6;
11444
+ }
11445
+ 50% {
11446
+ opacity: 1;
11447
+ }
11448
+ 100% {
11449
+ opacity: 0.6;
11450
+ }
11451
+ `;
11452
+ const Skeleton = styled.div `
11453
+ width: 80px;
11454
+ height: 16px;
11455
+ border-radius: 8px;
11456
+ background-color: rgba(0, 0, 0, 0.1);
11457
+ animation: ${pulse} 1.5s ease-in-out infinite;
11458
+ `;
11459
+ function CopyRowOrThrobber({ title, value, valueText, smallText, disabled, }) {
11460
+ const [copied, setCopied] = useState(false);
11461
+ const handleCopy = () => {
11462
+ if (disabled)
11463
+ return;
11464
+ if (!value)
11465
+ return;
11466
+ const str = value.trim();
11467
+ if (navigator.clipboard) {
11468
+ navigator.clipboard.writeText(str);
11469
+ }
11470
+ setCopied(true);
11471
+ setTimeout(() => setCopied(false), 1000);
11472
+ };
11473
+ if (!value) {
11474
+ return (jsxs(CopyRow, { children: [jsx(LabelRow, { children: jsx(ModalBody, { style: { margin: 0, textAlign: "left" }, children: title }) }), jsx(MainRow, { children: jsx(Skeleton, {}) })] }));
11475
+ }
11476
+ const displayValue = valueText || value;
11477
+ return (jsxs(CopyRow, { as: "button", onClick: handleCopy, disabled: disabled, children: [jsxs("div", { children: [jsx(LabelRow, { children: jsx(ModalBody, { style: { margin: 0, textAlign: "left" }, children: title }) }), jsx(MainRow, { children: jsxs(ValueContainer, { children: [jsx("span", { style: { fontWeight: 600 }, children: displayValue }), smallText && jsx(SmallText, { children: smallText })] }) })] }), jsx(CopyIconWrap, { children: jsx(CopyToClipboardIcon, { copied: copied, dark: true }) })] }));
11478
+ }
11479
+ const CopyIconWrap = styled.div `
11480
+ --color: var(--ck-copytoclipboard-stroke);
11481
+ --bg: var(--ck-body-background);
11482
+ `;
11312
11483
 
11313
11484
  const WaitingExternal = () => {
11314
11485
  const context = usePayContext();
@@ -11332,7 +11503,7 @@ const WaitingExternal = () => {
11332
11503
  setExternalURL(url);
11333
11504
  openExternalWindow(url);
11334
11505
  });
11335
- }, [selectedExternalOption]);
11506
+ }, [selectedExternalOption]); // eslint-disable-line react-hooks/exhaustive-deps
11336
11507
  const openExternalWindow = (url) => {
11337
11508
  if (isMobile || isPaymentApp) {
11338
11509
  // on mobile: open in a new tab
@@ -11355,7 +11526,7 @@ const WaitingExternal = () => {
11355
11526
  const waitingMessageLength = paymentWaitingMessage?.length;
11356
11527
  useEffect(() => {
11357
11528
  triggerResize();
11358
- }, [waitingMessageLength, externalURL]);
11529
+ }, [waitingMessageLength, externalURL]); // eslint-disable-line react-hooks/exhaustive-deps
11359
11530
  if (!selectedExternalOption) {
11360
11531
  return jsx(PageContent, {});
11361
11532
  }
@@ -11714,7 +11885,7 @@ function useDepositAddressOptions({ trpc, usdRequired, mode, }) {
11714
11885
  if (usdRequired != null && mode != null) {
11715
11886
  refreshDepositAddressOptions(usdRequired, mode);
11716
11887
  }
11717
- }, [usdRequired, mode]);
11888
+ }, [usdRequired, mode]); // eslint-disable-line react-hooks/exhaustive-deps
11718
11889
  return { options, loading };
11719
11890
  }
11720
11891
 
@@ -11781,7 +11952,7 @@ function useOrderUsdLimits({ trpc }) {
11781
11952
  }
11782
11953
  };
11783
11954
  refreshOrderUsdLimits();
11784
- }, []);
11955
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
11785
11956
  return { limits, loading };
11786
11957
  }
11787
11958
 
@@ -11813,7 +11984,7 @@ function useSolanaPaymentOptions({ trpc, address, usdRequired, isDepositFlow, })
11813
11984
  if (address != null && usdRequired != null) {
11814
11985
  refreshWalletPaymentOptions();
11815
11986
  }
11816
- }, [address, usdRequired, isDepositFlow]);
11987
+ }, [address, usdRequired, isDepositFlow]); // eslint-disable-line react-hooks/exhaustive-deps
11817
11988
  return {
11818
11989
  options,
11819
11990
  isLoading,
@@ -11858,6 +12029,7 @@ function useWalletPaymentOptions({ trpc, address, usdRequired, destChainId, pref
11858
12029
  if (address != null && usdRequired != null && destChainId != null) {
11859
12030
  refreshWalletPaymentOptions();
11860
12031
  }
12032
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11861
12033
  }, [
11862
12034
  address,
11863
12035
  usdRequired,
@@ -12061,9 +12233,36 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
12061
12233
  return externalPaymentOptionData.url;
12062
12234
  };
12063
12235
  const payWithDepositAddress = async (option) => {
12064
- assert(pay.paymentState == "payment_unpaid", `[PAY DEPOSIT ADDRESS] paymentState is ${pay.paymentState}, must be payment_unpaid`);
12065
12236
  const { order: hydratedOrder } = await pay.hydrateOrder();
12066
- log(`[PAY DEPOSIT ADDRESS] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with deposit address: ${option}`);
12237
+ log(`[PAY DEPOSIT ADDRESS] hydrated order: ${debugJson(hydratedOrder)}, checking out with deposit address: ${option}`);
12238
+ // Special-case: USDT on Tron uses the Untron service rather than ChangeNow
12239
+ const payParams = currPayParams;
12240
+ if (option === DepositAddressPaymentOptions.TRON_USDT) {
12241
+ // Ensure we have an appId for auth to backend
12242
+ assert(payParams?.appId != null, "[PAY DEPOSIT ADDRESS] missing appId required for Tron USDT payments");
12243
+ // Round up to the nearest integer number of USDT to avoid fractional tokens.
12244
+ const usd = hydratedOrder.usdValue.toFixed(2);
12245
+ const amountTronUSDT = parseUnits(usd, 6);
12246
+ const untronResp = await trpc.untronTryCreateOrder.mutate({
12247
+ appId: payParams.appId,
12248
+ intentAddr: assertNotNull(hydratedOrder.intentAddr, `[PAY DEPOSIT ADDRESS] missing intentAddr on order ${hydratedOrder.id}`),
12249
+ amountTronUSDT: Number(amountTronUSDT),
12250
+ });
12251
+ if ("error" in untronResp) {
12252
+ log(`[PAY DEPOSIT ADDRESS] failed to create Untron order: ${untronResp.error}`);
12253
+ return null;
12254
+ }
12255
+ const untronOrder = untronResp.untronOrder;
12256
+ // Map Untron response to the generic deposit-address shape expected by the UI
12257
+ return {
12258
+ address: untronOrder.receiver,
12259
+ amount: usd,
12260
+ suffix: "USDT on Tron",
12261
+ uri: `tron:${untronOrder.receiver}`,
12262
+ expirationS: untronOrder.expiresAtS - 60,
12263
+ };
12264
+ }
12265
+ // Default behaviour for all other tokens via ChangeNow
12067
12266
  const depositAddressOption = await trpc.getDepositAddressOptionData.query({
12068
12267
  input: option,
12069
12268
  usdRequired: hydratedOrder.destFinalCallTokenAmount.usd,