UNPKG

@daimo/pay

Version:

Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.

212 lines (209 loc) 10.1 kB
import { jsxs, jsx } from 'react/jsx-runtime'; import { ROUTES } from '../../../constants/routes.js'; import { usePayContext } from '../../../hooks/usePayContext.js'; import { PageContent } from '../../Common/Modal/styles.js'; import { getAddressContraction } from '@daimo/pay-common'; import { useWallet } from '@solana/wallet-adapter-react'; import { useAccount, useDisconnect } from 'wagmi'; import { Ethereum, Solana, Tron, Base } from '../../../assets/chains.js'; import { WalletIcon, Trust, Rainbow, Phantom, Rabby, MetaMask } from '../../../assets/logos.js'; import useIsMobile from '../../../hooks/useIsMobile.js'; import { walletConfigs } from '../../../wallets/walletConfigs.js'; import { OptionsList } from '../../Common/OptionsList/index.js'; import { OrderHeader } from '../../Common/OrderHeader/index.js'; import PoweredByFooter from '../../Common/PoweredByFooter/index.js'; import WalletChainLogo from '../../Common/WalletChainLogo/index.js'; function SelectMethod() { const { isMobile, isIOS, isAndroid } = useIsMobile(); const { address, chain, isConnected: isEthConnected, connector, } = useAccount(); const { connected: isSolanaConnected, wallet: solanaWallet, wallets: solanaWallets, disconnect: disconnectSolana, publicKey, } = useWallet(); const { setRoute, paymentState, log, disableMobileInjector } = usePayContext(); const { showSolanaPaymentMethod } = paymentState; const { disconnectAsync } = useDisconnect(); const { externalPaymentOptions, senderEnsName } = paymentState; // Decide whether to show the connected eth account, solana account, or both. // Desktop: Always show connected wallets when available // Mobile: Only show connected wallets when mobile injector is enabled (!disableMobileInjector) const showConnectedEth = isEthConnected && (!isMobile || !disableMobileInjector); const showConnectedSolana = isSolanaConnected && (!isMobile || !disableMobileInjector); const getConnectedWalletOptions = () => { const showChainLogo = isEthConnected && isSolanaConnected && showSolanaPaymentMethod; const connectedOptions = []; if (showConnectedEth) { const ethWalletDisplayName = senderEnsName ?? (address ? getAddressContraction(address) : "wallet"); // Prefer icon from walletConfigs if there's a name match, otherwise fall back // to the connector-provided icon, and finally to the generic WalletIcon. let walletIcon; const matchedConfig = Object.values(walletConfigs).find((cfg) => { if (!cfg.name || !connector?.name) return false; const cfgName = cfg.name.toLowerCase(); const connName = connector.name.toLowerCase(); return cfgName.includes(connName) || connName.includes(cfgName); }); if (matchedConfig?.icon) { walletIcon = typeof matchedConfig.icon === "string" ? (jsx("img", { src: matchedConfig.icon, alt: matchedConfig.name })) : matchedConfig.icon; } else if (connector?.icon) { walletIcon = (jsx("div", { style: { borderRadius: "22.5%", overflow: "hidden" }, children: jsx("img", { src: connector.icon, alt: connector.name }) })); } else { walletIcon = jsx(WalletIcon, {}); } const connectedEthWalletOption = { id: "connectedWallet", title: `Pay with ${ethWalletDisplayName}`, icons: [ jsx(WalletChainLogo, { walletIcon: walletIcon, walletName: connector?.name || "Wallet", chainLogo: showChainLogo ? jsx(Ethereum, {}) : null }, "eth"), ], onClick: () => { paymentState.setTokenMode("evm"); setRoute(ROUTES.SELECT_TOKEN, { event: "click-wallet", walletId: connector?.id, chainId: chain?.id, address: address, }); }, }; connectedOptions.push(connectedEthWalletOption); } if (showConnectedSolana && showSolanaPaymentMethod) { const solWalletDisplayName = getAddressContraction(publicKey?.toBase58() ?? ""); // Prefer icon from walletConfigs if available let solWalletIcon; const solMatchedConfig = Object.values(walletConfigs).find((cfg) => { if (!cfg.name) return false; const cfgName = cfg.name.toLowerCase(); const solName = solanaWallet?.adapter.name.toLowerCase() || ""; return cfgName.includes(solName) || solName.includes(cfgName); }); if (solMatchedConfig?.icon) { solWalletIcon = typeof solMatchedConfig.icon === "string" ? (jsx("img", { src: solMatchedConfig.icon, alt: solMatchedConfig.name })) : solMatchedConfig.icon; } else if (solanaWallet?.adapter.icon) { solWalletIcon = solanaWallet.adapter.icon; } else { solWalletIcon = jsx(Solana, {}); } const connectedSolWalletOption = { id: "connectedSolanaWallet", title: `Pay with ${solWalletDisplayName}`, icons: [ jsx(WalletChainLogo, { walletIcon: solWalletIcon, walletName: solanaWallet?.adapter.name || "Wallet", chainLogo: showChainLogo && jsx(Solana, {}) }, "sol-wallet"), ], onClick: () => { paymentState.setTokenMode("solana"); setRoute(ROUTES.SELECT_TOKEN, { event: "click-wallet", walletId: solanaWallet?.adapter.name, chainId: "solana", address: publicKey?.toBase58(), }); }, }; connectedOptions.push(connectedSolWalletOption); } return connectedOptions; }; const connectedWalletOptions = getConnectedWalletOptions(); const unconnectedWalletOption = { id: "unconnectedWallet", title: isEthConnected || isSolanaConnected ? `Pay with another wallet` : `Pay with wallet`, icons: getBestUnconnectedWalletIcons(connector, isMobile), onClick: async () => { await disconnectAsync(); await disconnectSolana(); setRoute(ROUTES.CONNECTORS); }, }; const options = []; options.push(...connectedWalletOptions); options.push(unconnectedWalletOption); log(`[SELECT_METHOD] loading: ${externalPaymentOptions.loading}, options: ${JSON.stringify(externalPaymentOptions.options)}`); // Pay with Exchange const exchangeOptions = externalPaymentOptions.options.get("exchange") ?? []; const showExchangePaymentMethod = exchangeOptions.length > 0; if (showExchangePaymentMethod) { options.push({ id: "exchange", title: "Pay with exchange", icons: exchangeOptions.slice(0, 3).map((option) => option.logoURI), onClick: () => { setRoute(ROUTES.SELECT_EXCHANGE, { event: "click-option", option: "exchange", }); }, }); } const depositAddressOption = getDepositAddressOption(setRoute); options.push(depositAddressOption); // ZKP2P is currently only available on desktop. Check if the user is on // desktop and if any ZKP2P options are available. const zkp2pOptions = externalPaymentOptions.options.get("zkp2p") ?? []; const showZkp2pPaymentMethod = !isMobile && zkp2pOptions.length > 0; if (showZkp2pPaymentMethod) { options.push({ id: "ZKP2P", title: "Pay via payment app", icons: zkp2pOptions.slice(0, 2).map((option) => option.logoURI), onClick: () => { setRoute(ROUTES.SELECT_ZKP2P); }, }); } // Order disabled to bottom options.sort((a, b) => (a.disabled ? 1 : 0) - (b.disabled ? 1 : 0)); return (jsxs(PageContent, { children: [jsx(OrderHeader, {}), jsx(OptionsList, { requiredSkeletons: isMobile ? 4 : 3, isLoading: externalPaymentOptions.loading, options: externalPaymentOptions.loading ? [] : options }), jsx(PoweredByFooter, {})] })); } // Get 3 icons, skipping the one that is already connected function getBestUnconnectedWalletIcons(connector, isMobile) { const icons = []; const strippedId = connector?.id.toLowerCase(); // some connector ids can have weird casing and or suffixes and prefixes const [isRainbow, isTrust, isPhantom, isCoinbase, isMetamask, isRabby] = [ strippedId?.includes("rainbow"), strippedId?.includes("trust"), strippedId?.includes("phantom"), strippedId?.includes("coinbase"), strippedId?.includes("metamask"), strippedId?.includes("rabby"), ]; if (isMobile) { if (!isTrust) icons.push(jsx(Trust, { background: true })); if (!isRainbow) icons.push(jsx(Rainbow, {})); if (!isPhantom) icons.push(jsx(Phantom, {})); } else { if (!isRainbow) icons.push(jsx(Rainbow, {})); if (!isPhantom) icons.push(jsx(Phantom, {})); if (!isRabby) icons.push(jsx(Rabby, {})); if (!isMetamask && icons.length < 3) icons.push(jsx(MetaMask, {})); } return icons; } function getDepositAddressOption(setRoute) { return { id: "depositAddress", title: "Pay to address", icons: [jsx(Ethereum, {}, "eth"), jsx(Tron, {}, "tron"), jsx(Base, {}, "base")], onClick: () => { setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN); }, }; } export { SelectMethod as default }; //# sourceMappingURL=index.js.map