@daimo/pay 1.8.1 → 1.8.3

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.d.ts CHANGED
@@ -339,6 +339,7 @@ type WalletConfigProps = {
339
339
  shouldDeeplinkDesktop?: boolean;
340
340
  showInMobileConnectors?: boolean;
341
341
  isWcMobileConnector?: boolean;
342
+ isSolanaOnly?: boolean;
342
343
  };
343
344
 
344
345
  type TrpcClient = CreateTRPCClient<AppRouter>;
package/build/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { ExternalPaymentOptions, assert, assertNotNull, debugJson, supportedChains, ethereum, isCCTPV1Chain, getOrderDestChainId, readDaimoPayOrderID, writeDaimoPayOrderID, getChainName, DaimoPayOrderMode, arbitrum as arbitrum$1, base as base$2, blast as blast$1, bsc as bsc$1, linea as linea$1, mantle as mantle$1, optimism as optimism$1, polygon as polygon$1, worldchain as worldchain$1, getAddressContraction, DaimoPayOrderStatusDest, getChainExplorerTxUrl, DaimoPayIntentStatus, retryBackoff, DaimoPayOrderStatusSource, DaimoPayEventType, getDaimoPayOrderView } from '@daimo/pay-common';
3
3
  import { Buffer } from 'buffer';
4
- import React, { useState, useEffect, createContext, useRef, useCallback, useLayoutEffect, useMemo, createElement } from 'react';
4
+ import React, { useState, useEffect, createContext, useMemo, useRef, useCallback, useLayoutEffect, createElement } from 'react';
5
5
  import styled$1, { css, keyframes, ThemeProvider } from 'styled-components';
6
6
  import { http, useConfig, useAccountEffect, useWriteContract, useSendTransaction, useAccount, useEnsName, useConnectors as useConnectors$1, useSwitchChain, useConnect as useConnect$1, useDisconnect, useChainId, WagmiContext, createConfig, useEnsAddress, useEnsAvatar } from 'wagmi';
7
7
  import { mainnet, base as base$1, polygon, optimism, arbitrum, linea, bsc, sepolia, baseSepolia, worldchain, blast, mantle } from 'wagmi/chains';
8
8
  import { safe, injected, coinbaseWallet, walletConnect } from '@wagmi/connectors';
9
9
  import { useConnection, useWallet as useWallet$1, ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
10
10
  import { hexToBytes, zeroAddress, erc20Abi, getAddress, formatUnits, parseUnits } from 'viem';
11
+ import { detect } from 'detect-browser';
11
12
  import { VersionedTransaction } from '@solana/web3.js';
12
13
  import { createTRPCClient, httpBatchLink } from '@trpc/client';
13
14
  import { motion, AnimatePresence, MotionConfig } from 'framer-motion';
14
15
  import { createPortal } from 'react-dom';
15
- import { detect } from 'detect-browser';
16
16
  import { useTransition } from 'react-transition-state';
17
17
  import ResizeObserver from 'resize-observer-polyfill';
18
18
  import useMeasure from 'react-use-measure';
@@ -22,7 +22,7 @@ import { WalletSignTransactionError, WalletSendTransactionError } from '@solana/
22
22
  import { normalize } from 'viem/ens';
23
23
 
24
24
  var name = "@daimo/pay";
25
- var version = "1.8.1";
25
+ var version = "1.8.3";
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.1",
64
+ "@daimo/pay-common": "1.8.3",
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",
@@ -797,6 +797,7 @@ const walletConfigs = {
797
797
  const ref = encodeURIComponent(window.location.origin);
798
798
  return `https://solflare.com/ul/v1/browse/${url}?ref=${ref}`;
799
799
  },
800
+ isSolanaOnly: true,
800
801
  },
801
802
  // steak: {
802
803
  // name: "Steak",
@@ -948,7 +949,6 @@ function extractWcWalletFromProvider(p, log) {
948
949
  let deeplinkUrl;
949
950
  try {
950
951
  deeplinkUrl = JSON.parse(deeplinkJson).href;
951
- console.log(`[WCWALLET] deeplinkUrl: ${deeplinkUrl}`);
952
952
  }
953
953
  catch { }
954
954
  wallet = {
@@ -1055,6 +1055,47 @@ function useExternalPaymentOptions({ trpc, filterIds, platform, usdRequired, mod
1055
1055
  return { options, loading };
1056
1056
  }
1057
1057
 
1058
+ const detectBrowser = () => {
1059
+ const browser = detect();
1060
+ return browser?.name ?? "";
1061
+ };
1062
+ const detectOS = () => {
1063
+ const browser = detect();
1064
+ return browser?.os ?? "";
1065
+ };
1066
+ const isIOS = () => {
1067
+ const os = detectOS();
1068
+ return os.toLowerCase().includes("ios");
1069
+ };
1070
+ const isAndroid = () => {
1071
+ const os = detectOS();
1072
+ return os.toLowerCase().includes("android");
1073
+ };
1074
+ const isMobile = () => {
1075
+ const os = detectOS().toLowerCase();
1076
+ return os.includes("android") || os.includes("ios");
1077
+ };
1078
+ function flattenChildren(children) {
1079
+ const childrenArray = React.Children.toArray(children);
1080
+ return childrenArray.reduce((flatChildren, child) => {
1081
+ if (child.type === React.Fragment) {
1082
+ return flatChildren.concat(flattenChildren(child.props.children));
1083
+ }
1084
+ flatChildren.push(child);
1085
+ return flatChildren;
1086
+ }, []);
1087
+ }
1088
+ const isWalletConnectConnector = (connectorId) => connectorId === "walletConnect";
1089
+ const isCoinbaseWalletConnector = (connectorId) => connectorId === "coinbaseWalletSDK";
1090
+ const isSafeConnector = (connectorId) => connectorId === "safe";
1091
+ const isInjectedConnector = (connectorId) => connectorId === "injected";
1092
+
1093
+ function useIsMobile() {
1094
+ const isI = useMemo(isIOS, []);
1095
+ const isA = useMemo(isAndroid, []);
1096
+ return { isMobile: isI || isA, isIOS: isI, isAndroid: isA };
1097
+ }
1098
+
1058
1099
  function useOrderUsdLimits({ trpc }) {
1059
1100
  const [limits, setLimits] = useState({});
1060
1101
  const [loading, setLoading] = useState(false);
@@ -1398,6 +1439,7 @@ function usePaymentState({ trpc, lockPayParams, daimoPayOrder, setDaimoPayOrder,
1398
1439
  assert(!!platform, "[PAY EXTERNAL] platform cannot be null");
1399
1440
  const { hydratedOrder, externalPaymentOptionData } = await createOrHydrate({
1400
1441
  order: daimoPayOrder,
1442
+ refundAddress: payParams?.refundAddress,
1401
1443
  externalPaymentOption: option,
1402
1444
  });
1403
1445
  assert(!!externalPaymentOptionData, "[PAY EXTERNAL] missing externalPaymentOptionData");
@@ -1410,6 +1452,7 @@ function usePaymentState({ trpc, lockPayParams, daimoPayOrder, setDaimoPayOrder,
1410
1452
  assert(!!daimoPayOrder, "[PAY DEPOSIT ADDRESS] missing daimoPayOrder");
1411
1453
  const { hydratedOrder } = await createOrHydrate({
1412
1454
  order: daimoPayOrder,
1455
+ refundAddress: payParams?.refundAddress,
1413
1456
  });
1414
1457
  setDaimoPayOrder(hydratedOrder);
1415
1458
  log(`[PAY DEPOSIT ADDRESS] hydrated order: ${JSON.stringify(hydratedOrder)}, checking out with deposit address: ${option}`);
@@ -1420,6 +1463,7 @@ function usePaymentState({ trpc, lockPayParams, daimoPayOrder, setDaimoPayOrder,
1420
1463
  });
1421
1464
  return depositAddressOption;
1422
1465
  };
1466
+ const { isIOS } = useIsMobile();
1423
1467
  /// Hydrates an order to prepare for paying in an in-wallet browser via
1424
1468
  /// deeplink. Then, if wallet is specified, opens in that wallet.
1425
1469
  const payWithWallet = async (wallet, amountUsd) => {
@@ -1433,7 +1477,10 @@ function usePaymentState({ trpc, lockPayParams, daimoPayOrder, setDaimoPayOrder,
1433
1477
  }
1434
1478
  // Hydrate the order
1435
1479
  log(`payWithWallet: hydrating order ${order.id}${amountUsd && ` for $${amountUsd}`}`);
1436
- const { hydratedOrder } = await createOrHydrate({ order });
1480
+ const { hydratedOrder } = await createOrHydrate({
1481
+ order,
1482
+ refundAddress: payParams?.refundAddress,
1483
+ });
1437
1484
  setDaimoPayOrder(hydratedOrder);
1438
1485
  // If we already picked a wallet, open in that wallet.
1439
1486
  if (wallet == null)
@@ -1441,7 +1488,10 @@ function usePaymentState({ trpc, lockPayParams, daimoPayOrder, setDaimoPayOrder,
1441
1488
  assert(wallet.getDaimoPayDeeplink != null, "payWithWallet: missing deeplink");
1442
1489
  const payId = writeDaimoPayOrderID(hydratedOrder.id);
1443
1490
  const deeplink = wallet.getDaimoPayDeeplink(payId);
1444
- window.open(deeplink, "_blank");
1491
+ // if we are in IOS, we don't open the deeplink in a new window, because it will not work, the link will be opened in the page WAITING_WALLET
1492
+ if (!isIOS) {
1493
+ window.open(deeplink, "_blank");
1494
+ }
1445
1495
  setSelectedWalletDeepLink(deeplink);
1446
1496
  setRoute(ROUTES.WAITING_WALLET, {
1447
1497
  amountUsd,
@@ -2862,41 +2912,6 @@ const Portal = (props) => {
2862
2912
  return mounted ? createPortal(children, ref.current) : null;
2863
2913
  };
2864
2914
 
2865
- const detectBrowser = () => {
2866
- const browser = detect();
2867
- return browser?.name ?? "";
2868
- };
2869
- const detectOS = () => {
2870
- const browser = detect();
2871
- return browser?.os ?? "";
2872
- };
2873
- const isIOS = () => {
2874
- const os = detectOS();
2875
- return os.toLowerCase().includes("ios");
2876
- };
2877
- const isAndroid = () => {
2878
- const os = detectOS();
2879
- return os.toLowerCase().includes("android");
2880
- };
2881
- const isMobile = () => {
2882
- const os = detectOS().toLowerCase();
2883
- return os.includes("android") || os.includes("ios");
2884
- };
2885
- function flattenChildren(children) {
2886
- const childrenArray = React.Children.toArray(children);
2887
- return childrenArray.reduce((flatChildren, child) => {
2888
- if (child.type === React.Fragment) {
2889
- return flatChildren.concat(flattenChildren(child.props.children));
2890
- }
2891
- flatChildren.push(child);
2892
- return flatChildren;
2893
- }, []);
2894
- }
2895
- const isWalletConnectConnector = (connectorId) => connectorId === "walletConnect";
2896
- const isCoinbaseWalletConnector = (connectorId) => connectorId === "coinbaseWalletSDK";
2897
- const isSafeConnector = (connectorId) => connectorId === "safe";
2898
- const isInjectedConnector = (connectorId) => connectorId === "injected";
2899
-
2900
2915
  var defaultTheme = {
2901
2916
  mobileWidth: 560,
2902
2917
  };
@@ -6663,12 +6678,6 @@ styled(motion.div) `
6663
6678
  }
6664
6679
  `;
6665
6680
 
6666
- function useIsMobile() {
6667
- const isI = useMemo(isIOS, []);
6668
- const isA = useMemo(isAndroid, []);
6669
- return { isMobile: isI || isA, isIOS: isI, isAndroid: isA };
6670
- }
6671
-
6672
6681
  /**
6673
6682
  * This is a wrapper around wagmi's useConnect hook that adds some
6674
6683
  * additional functionality.
@@ -8446,6 +8455,9 @@ const MobileConnectors = () => {
8446
8455
  return false;
8447
8456
  if (!wallet.showInMobileConnectors)
8448
8457
  return false;
8458
+ // If the mobile wallet supports solana only, don't show it if we are not supporting solana has a payment method
8459
+ if (wallet.isSolanaOnly && !context.paymentState.showSolanaPaymentMethod)
8460
+ return false;
8449
8461
  return true;
8450
8462
  }) ?? [];
8451
8463
  const goToWallet = (wallet) => {
@@ -10139,6 +10151,7 @@ const Container$2 = styled.button `
10139
10151
  gap: 4px;
10140
10152
  cursor: pointer;
10141
10153
  width: fit-content;
10154
+ background: transparent;
10142
10155
  `;
10143
10156
  const IconWrapper = styled.div `
10144
10157
  opacity: ${({ $isFlipped }) => ($isFlipped ? 1 : 1)};
@@ -10169,6 +10182,7 @@ const Container$1 = styled.button `
10169
10182
  align-items: center;
10170
10183
  justify-content: center;
10171
10184
  gap: 4px;
10185
+ background: transparent;
10172
10186
  `;
10173
10187
  const InputField = styled.input `
10174
10188
  margin: 0;
@@ -10816,7 +10830,7 @@ function SelectMethod() {
10816
10830
  const { connected: isSolanaConnected, wallet: solanaWallet, wallets: solanaWallets, disconnect: disconnectSolana, publicKey, } = useWallet$1();
10817
10831
  const { setRoute, paymentState, wcWallet, log } = usePayContext();
10818
10832
  const { disconnectAsync } = useDisconnect();
10819
- const { daimoPayOrder, setSelectedExternalOption, externalPaymentOptions, showSolanaPaymentMethod, depositAddressOptions, senderEnsName, } = paymentState;
10833
+ const { setSelectedExternalOption, externalPaymentOptions, showSolanaPaymentMethod, depositAddressOptions, senderEnsName, } = paymentState;
10820
10834
  // Decide whether to show the connected eth account, solana account, or both.
10821
10835
  const showConnectedEth = isEthConnected;
10822
10836
  const showConnectedSolana = isSolanaConnected && showSolanaPaymentMethod;
@@ -11037,7 +11051,6 @@ function SelectToken() {
11037
11051
  const { isDepositFlow, walletPaymentOptions, setSelectedTokenOption } = paymentState;
11038
11052
  const { connector } = useAccount();
11039
11053
  const { connected: isSolanaConnected } = useWallet$1();
11040
- console.log("connector", connector);
11041
11054
  const optionsList = walletPaymentOptions.options?.map((option) => {
11042
11055
  const chainName = getChainName(option.balance.token.chainId);
11043
11056
  const titlePrice = isDepositFlow
@@ -11083,10 +11096,11 @@ function SelectToken() {
11083
11096
  disabled,
11084
11097
  };
11085
11098
  }) ?? [];
11086
- // IsAnotherMethodButtonVisible is true when there are token options and we are in desktop mode or in mobile mode using a wallet connect connector
11087
- const isAnotherMethodButtonVisible = (optionsList.length != 0 &&
11088
- (!isMobileFormat || isWalletConnectConnector(connector?.id))) ||
11089
- (optionsList.length == 0 && isSolanaConnected);
11099
+ // IsAnotherMethodButtonVisible is true when there are token options and we are in desktop mode or in mobile mode using a wallet connect connector or if we are connected to solana and evm in mobile mode
11100
+ const isAnotherMethodButtonVisible = optionsList.length > 0 &&
11101
+ (!isMobileFormat ||
11102
+ isWalletConnectConnector(connector?.id) ||
11103
+ isSolanaConnected);
11090
11104
  return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true, showEth: true }), !walletPaymentOptions.isLoading && optionsList.length === 0 && (jsxs(ModalContent, { style: {
11091
11105
  display: "flex",
11092
11106
  alignItems: "center",
@@ -11352,7 +11366,9 @@ function getDaimoSolanaTokenKey(token) {
11352
11366
  const SelectSolanaToken = () => {
11353
11367
  const { paymentState, setRoute } = usePayContext();
11354
11368
  const { isDepositFlow, solanaPaymentOptions, setSelectedSolanaTokenOption } = paymentState;
11355
- const isMobileFormat = useIsMobile() || window?.innerWidth < defaultTheme.mobileWidth;
11369
+ const { isMobile } = useIsMobile();
11370
+ const isMobileFormat = isMobile || window?.innerWidth < defaultTheme.mobileWidth;
11371
+ const { isConnected: isEvmConnected } = useAccount();
11356
11372
  const optionsList = solanaPaymentOptions.options?.map((option) => {
11357
11373
  const titlePrice = isDepositFlow
11358
11374
  ? formatUsd(option.balance.usd)
@@ -11386,13 +11402,15 @@ const SelectSolanaToken = () => {
11386
11402
  disabled,
11387
11403
  };
11388
11404
  }) ?? [];
11405
+ // IsAnotherMethodButtonVisible is true when there are token options and we are in desktop mode or in mobile mode using a wallet connect connector or if we are connected to solana and evm in mobile mode
11406
+ const isAnotherMethodButtonVisible = optionsList.length > 0 && (!isMobileFormat || isEvmConnected);
11389
11407
  return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true, showSolana: true }), !solanaPaymentOptions.isLoading && optionsList.length === 0 && (jsxs(ModalContent, { style: {
11390
11408
  display: "flex",
11391
11409
  alignItems: "center",
11392
11410
  justifyContent: "center",
11393
11411
  paddingTop: 16,
11394
11412
  paddingBottom: 16,
11395
- }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(SelectAnotherMethodButton, {})] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: solanaPaymentOptions.isLoading, options: optionsList, scrollHeight: isMobileFormat ? 225 : 300, orDivider: optionsList.length != 0 }), optionsList.length != 0 && jsx(SelectAnotherMethodButton, {})] }));
11413
+ }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(SelectAnotherMethodButton, {})] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: solanaPaymentOptions.isLoading, options: optionsList, scrollHeight: isAnotherMethodButtonVisible && isMobileFormat ? 225 : 300, orDivider: isAnotherMethodButtonVisible }), isAnotherMethodButtonVisible && jsx(SelectAnotherMethodButton, {})] }));
11396
11414
  };
11397
11415
 
11398
11416
  const WaitingDepositAddress = () => {
@@ -11548,7 +11566,7 @@ const DaimoPayModal = ({ mode, theme, customTheme, lang, }) => {
11548
11566
  const paymentState = context.paymentState;
11549
11567
  const { payParams, generatePreviewOrder, isDepositFlow, showSolanaPaymentMethod, setPaymentWaitingMessage, setSelectedExternalOption, setSelectedTokenOption, setSelectedSolanaTokenOption, setSelectedDepositAddressOption, setSelectedWallet, } = paymentState;
11550
11568
  const { isConnected: isEthConnected, connector, chain, address, } = useAccount();
11551
- useWallet$1();
11569
+ const { connected: isSolanaConnected } = useWallet$1();
11552
11570
  const chainIsSupported = useChainIsSupported(chain?.id);
11553
11571
  //if chain is unsupported we enforce a "switch chain" prompt
11554
11572
  const closeable = !(context.options?.enforceSupportedChains &&
@@ -11691,12 +11709,10 @@ const DaimoPayModal = ({ mode, theme, customTheme, lang, }) => {
11691
11709
  return;
11692
11710
  if (context.route !== ROUTES.SELECT_METHOD)
11693
11711
  return;
11694
- const canPayEth = paymentState.walletPaymentOptions.options?.some((o) => o.disabledReason == null);
11695
- const canPaySolana = paymentState.solanaPaymentOptions.options?.some((o) => o.disabledReason == null);
11696
11712
  // Skip to token selection if exactly one wallet is connected. If both
11697
11713
  // wallets are connected, stay on the SELECT_METHOD screen to allow the
11698
11714
  // user to select which wallet to use
11699
- if (canPayEth && !canPaySolana) {
11715
+ if (isEthConnected && !isSolanaConnected) {
11700
11716
  context.setRoute(ROUTES.SELECT_TOKEN, {
11701
11717
  event: "eth_connected_on_open",
11702
11718
  walletId: connector?.id,
@@ -11704,7 +11720,7 @@ const DaimoPayModal = ({ mode, theme, customTheme, lang, }) => {
11704
11720
  address,
11705
11721
  });
11706
11722
  }
11707
- else if (canPaySolana && !canPayEth) {
11723
+ else if (isSolanaConnected && !isEthConnected) {
11708
11724
  context.setRoute(ROUTES.SOLANA_SELECT_TOKEN, {
11709
11725
  event: "solana_connected_on_open",
11710
11726
  });