@daimo/pay
Version:
Seamless crypto payments. Onboard users from any chain, any coin into your app with one click.
463 lines (460 loc) • 20.5 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { useWallet } from '@solana/wallet-adapter-react';
import { useEffect } from 'react';
import { useAccount } from 'wagmi';
import { ExternalPaymentOptions, DepositAddressPaymentOptions } from '@daimo/pay-common';
import { ROUTES } from '../../constants/routes.js';
import { getAppName } from '../../defaultConfig.js';
import { useChainIsSupported } from '../../hooks/useChainIsSupported.js';
import { useDaimoPay } from '../../hooks/useDaimoPay.js';
import useIsMobile from '../../hooks/useIsMobile.js';
import { usePayContext } from '../../hooks/usePayContext.js';
import Modal from '../Common/Modal/index.js';
import { DaimoPayThemeProvider } from '../DaimoPayThemeProvider/DaimoPayThemeProvider.js';
import About from '../Pages/About/index.js';
import Confirmation from '../Pages/Confirmation/index.js';
import Wallets from '../Pages/Connectors/index.js';
import DownloadApp from '../Pages/DownloadApp/index.js';
import ErrorPage from '../Pages/Error/index.js';
import MobileConnectors from '../Pages/MobileConnectors/index.js';
import Introduction from '../Pages/Onboarding/index.js';
import PayWithToken from '../Pages/PayWithToken/index.js';
import SelectAmount from '../Pages/SelectAmount/index.js';
import SelectDepositAddressAmount from '../Pages/SelectDepositAddressAmount/index.js';
import SelectDepositAddressChain from '../Pages/SelectDepositAddressChain/index.js';
import SelectExchange from '../Pages/SelectExchange/index.js';
import SelectExternalAmount from '../Pages/SelectExternalAmount/index.js';
import SelectMethod from '../Pages/SelectMethod/index.js';
import SelectToken from '../Pages/SelectToken/index.js';
import SelectWalletAmount from '../Pages/SelectWalletAmount/index.js';
import SelectWalletChain from '../Pages/SelectWalletChain/index.js';
import SelectZKP from '../Pages/SelectZKP/index.js';
import ConnectSolana from '../Pages/Solana/ConnectorSolana/index.js';
import PayWithSolanaToken from '../Pages/Solana/PayWithSolanaToken/index.js';
import SelectSolanaAmount from '../Pages/Solana/SelectSolanaAmount/index.js';
import SwitchNetworks from '../Pages/SwitchNetworks/index.js';
import WaitingDepositAddress from '../Pages/WaitingDepositAddress/index.js';
import WaitingExternal from '../Pages/WaitingExternal/index.js';
import WaitingWallet from '../Pages/WaitingWallet/index.js';
import ConnectUsing from './ConnectUsing.js';
const DaimoPayModal = ({
mode,
theme,
customTheme,
lang,
disableMobileInjector
}) => {
const context = usePayContext();
const {
setMode,
setTheme,
setCustomTheme,
setLang,
setDisableMobileInjector
} = context;
const paymentState = context.paymentState;
const {
generatePreviewOrder,
isDepositFlow,
showSolanaPaymentMethod,
setPaymentWaitingMessage,
setSelectedExternalOption,
setSelectedTokenOption,
setSelectedSolanaTokenOption,
setSelectedDepositAddressOption,
setSelectedWallet
} = paymentState;
const { paymentState: paymentFsmState } = useDaimoPay();
const {
isConnected: isEthConnected,
connector,
chain,
address
} = useAccount();
const { connected: isSolanaConnected } = useWallet();
const chainIsSupported = useChainIsSupported(chain?.id);
const closeable = !(context.options?.enforceSupportedChains && isEthConnected && !chainIsSupported);
const showBackButton = closeable && context.route !== context.uniquePaymentMethodPage && context.route !== ROUTES.SELECT_METHOD && context.route !== ROUTES.CONFIRMATION && context.route !== ROUTES.SELECT_TOKEN && context.route !== ROUTES.ERROR && paymentFsmState !== "error";
const onBack = () => {
const meta = { event: "click-back" };
if (context.route === ROUTES.DOWNLOAD) {
context.setRoute(ROUTES.CONNECT, meta);
} else if (context.route === ROUTES.CONNECTORS) {
context.setRoute(ROUTES.SELECT_METHOD, meta);
} else if (context.route === ROUTES.SELECT_AMOUNT) {
setSelectedTokenOption(void 0);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
} else if (context.route === ROUTES.SELECT_EXTERNAL_AMOUNT) {
setSelectedExternalOption(void 0);
context.setRoute(context.uniquePaymentMethodPage, meta);
} else if (context.route === ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT) {
setSelectedDepositAddressOption(void 0);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
} else if (context.route === ROUTES.WAITING_EXTERNAL) {
setPaymentWaitingMessage(void 0);
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_EXTERNAL_AMOUNT, meta);
} else {
setSelectedExternalOption(void 0);
context.setRoute(context.uniquePaymentMethodPage, meta);
}
} else if (context.route === ROUTES.PAY_WITH_TOKEN) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_AMOUNT, meta);
} else {
setSelectedTokenOption(void 0);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
} else if (context.route === ROUTES.ONBOARDING) {
context.setRoute(ROUTES.CONNECTORS, meta);
} else if (context.route === ROUTES.WAITING_DEPOSIT_ADDRESS) {
if (isDepositFlow) {
if (paymentState.selectedDepositAddressOption === void 0) {
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
} else {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT, meta);
}
} else {
setSelectedDepositAddressOption(void 0);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, meta);
}
} else if (context.route === ROUTES.WAITING_WALLET) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SELECT_WALLET_AMOUNT, meta);
} else {
setSelectedWallet(void 0);
context.setRoute(ROUTES.CONNECTORS, meta);
}
} else if (context.route === ROUTES.SOLANA_SELECT_AMOUNT) {
setSelectedSolanaTokenOption(void 0);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
} else if (context.route === ROUTES.SOLANA_PAY_WITH_TOKEN) {
if (isDepositFlow) {
generatePreviewOrder();
context.setRoute(ROUTES.SOLANA_SELECT_AMOUNT, meta);
} else {
setSelectedSolanaTokenOption(void 0);
context.setRoute(ROUTES.SELECT_TOKEN, meta);
}
} else {
context.setRoute(context.uniquePaymentMethodPage, meta);
}
};
const pages = {
[ROUTES.SELECT_METHOD]: /* @__PURE__ */ jsx(SelectMethod, {}),
[ROUTES.SELECT_TOKEN]: /* @__PURE__ */ jsx(SelectToken, {}),
[ROUTES.SELECT_AMOUNT]: /* @__PURE__ */ jsx(SelectAmount, {}),
[ROUTES.SELECT_EXTERNAL_AMOUNT]: /* @__PURE__ */ jsx(SelectExternalAmount, {}),
[ROUTES.SELECT_EXCHANGE]: /* @__PURE__ */ jsx(SelectExchange, {}),
[ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT]: /* @__PURE__ */ jsx(SelectDepositAddressAmount, {}),
[ROUTES.SELECT_WALLET_AMOUNT]: /* @__PURE__ */ jsx(SelectWalletAmount, {}),
[ROUTES.SELECT_WALLET_CHAIN]: /* @__PURE__ */ jsx(SelectWalletChain, {}),
[ROUTES.WAITING_EXTERNAL]: /* @__PURE__ */ jsx(WaitingExternal, {}),
[ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN]: /* @__PURE__ */ jsx(SelectDepositAddressChain, {}),
[ROUTES.WAITING_DEPOSIT_ADDRESS]: /* @__PURE__ */ jsx(WaitingDepositAddress, {}),
[ROUTES.SELECT_ZKP2P]: /* @__PURE__ */ jsx(SelectZKP, {}),
[ROUTES.WAITING_WALLET]: /* @__PURE__ */ jsx(WaitingWallet, {}),
[ROUTES.CONFIRMATION]: /* @__PURE__ */ jsx(Confirmation, {}),
[ROUTES.ERROR]: /* @__PURE__ */ jsx(ErrorPage, {}),
[ROUTES.PAY_WITH_TOKEN]: /* @__PURE__ */ jsx(PayWithToken, {}),
[ROUTES.SOLANA_CONNECTOR]: /* @__PURE__ */ jsx(ConnectSolana, {}),
[ROUTES.SOLANA_SELECT_AMOUNT]: /* @__PURE__ */ jsx(SelectSolanaAmount, {}),
[ROUTES.SOLANA_PAY_WITH_TOKEN]: /* @__PURE__ */ jsx(PayWithSolanaToken, {}),
// Unused routes. Kept to minimize connectkit merge conflicts.
[ROUTES.ONBOARDING]: /* @__PURE__ */ jsx(Introduction, {}),
[ROUTES.ABOUT]: /* @__PURE__ */ jsx(About, {}),
[ROUTES.DOWNLOAD]: /* @__PURE__ */ jsx(DownloadApp, {}),
[ROUTES.CONNECTORS]: /* @__PURE__ */ jsx(Wallets, {}),
[ROUTES.MOBILECONNECTORS]: /* @__PURE__ */ jsx(MobileConnectors, {}),
[ROUTES.CONNECT]: /* @__PURE__ */ jsx(ConnectUsing, {}),
[ROUTES.SWITCHNETWORKS]: /* @__PURE__ */ jsx(SwitchNetworks, {})
};
function hide() {
if (isDepositFlow) {
generatePreviewOrder();
}
context.setOpen(false, { event: "click-close" });
}
const goToManualAddressScreen = (eventSuffix) => {
if (paymentState.isDepositFlow) {
context.setUniquePaymentMethodPage(ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT, {
event: `unique_payment_option_deposit_${eventSuffix}`
});
} else {
context.setUniquePaymentMethodPage(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN);
context.setRoute(ROUTES.WAITING_DEPOSIT_ADDRESS, {
event: `unique_payment_option_${eventSuffix}`
});
}
};
const { isMobile } = useIsMobile();
useEffect(() => {
if (!context.open) return;
if (context.route !== ROUTES.SELECT_METHOD) return;
if (paymentState.buttonProps && "uniquePaymentOption" in paymentState.buttonProps && paymentState.buttonProps.uniquePaymentOption) {
switch (paymentState.buttonProps.uniquePaymentOption) {
case "Tron":
const tronOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.TRON_USDT
);
if (tronOption) {
setSelectedDepositAddressOption(tronOption);
goToManualAddressScreen("tron");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_tron_fallback"
});
}
break;
case "AllExchanges":
context.setUniquePaymentMethodPage(ROUTES.SELECT_EXCHANGE);
context.setRoute(ROUTES.SELECT_EXCHANGE, {
event: "unique_payment_option_all_exchanges"
});
break;
case "ManualAddress":
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_manual_address"
});
break;
case "Base":
const baseOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.BASE
);
if (baseOption) {
setSelectedDepositAddressOption(baseOption);
goToManualAddressScreen("base");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_base_fallback"
});
}
break;
case "Arbitrum":
const arbitrumOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.ARBITRUM
);
if (arbitrumOption) {
setSelectedDepositAddressOption(arbitrumOption);
goToManualAddressScreen("arbitrum");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_arbitrum_fallback"
});
}
break;
case "Optimism":
const optimismOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.OP_MAINNET
);
if (optimismOption) {
setSelectedDepositAddressOption(optimismOption);
goToManualAddressScreen("optimism");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_optimism_fallback"
});
}
break;
case "Polygon":
const polygonOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.POLYGON
);
if (polygonOption) {
setSelectedDepositAddressOption(polygonOption);
goToManualAddressScreen("polygon");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_polygon_fallback"
});
}
break;
case "Ethereum":
const ethereumOption = paymentState.depositAddressOptions.options?.find(
(option) => option.id === DepositAddressPaymentOptions.ETH_L1
);
if (ethereumOption) {
setSelectedDepositAddressOption(ethereumOption);
goToManualAddressScreen("ethereum");
} else if (!paymentState.depositAddressOptions.loading) {
context.setUniquePaymentMethodPage(
ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN
);
context.setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN, {
event: "unique_payment_option_ethereum_fallback"
});
}
break;
case "Binance":
const exchangeOptions = paymentState.externalPaymentOptions.options.get("exchange");
const binanceOption = exchangeOptions?.find(
(option) => option.id === ExternalPaymentOptions.Binance
);
if (binanceOption) {
setSelectedExternalOption(binanceOption);
context.setUniquePaymentMethodPage(ROUTES.WAITING_EXTERNAL);
context.setRoute(ROUTES.WAITING_EXTERNAL, {
event: "unique_payment_option_binance"
});
} else if (!paymentState.externalPaymentOptions.loading) {
context.setUniquePaymentMethodPage(ROUTES.SELECT_EXCHANGE);
context.setRoute(ROUTES.SELECT_EXCHANGE, {
event: "unique_payment_option_binance_fallback"
});
}
break;
case "Coinbase":
const coinbaseExchangeOptions = paymentState.externalPaymentOptions.options.get("exchange");
const coinbaseOption = coinbaseExchangeOptions?.find(
(option) => option.id === ExternalPaymentOptions.Coinbase
);
if (coinbaseOption) {
setSelectedExternalOption(coinbaseOption);
context.setUniquePaymentMethodPage(ROUTES.WAITING_EXTERNAL);
context.setRoute(ROUTES.WAITING_EXTERNAL, {
event: "unique_payment_option_coinbase"
});
} else if (!paymentState.externalPaymentOptions.loading) {
context.setUniquePaymentMethodPage(ROUTES.SELECT_EXCHANGE);
context.setRoute(ROUTES.SELECT_EXCHANGE, {
event: "unique_payment_option_coinbase_fallback"
});
}
break;
case "Lemon":
const lemonExternalOptions = paymentState.externalPaymentOptions.options.get("external");
const lemonOption = lemonExternalOptions?.find(
(option) => option.id === ExternalPaymentOptions.Lemon
);
if (lemonOption) {
setSelectedExternalOption(lemonOption);
context.setUniquePaymentMethodPage(ROUTES.WAITING_EXTERNAL);
context.setRoute(ROUTES.WAITING_EXTERNAL, {
event: "unique_payment_option_lemon"
});
} else if (!paymentState.externalPaymentOptions.loading) {
context.setUniquePaymentMethodPage(ROUTES.SELECT_METHOD);
context.setRoute(ROUTES.SELECT_METHOD, {
event: "unique_payment_option_lemon_fallback"
});
}
break;
case "Wallets":
context.setUniquePaymentMethodPage(ROUTES.CONNECTORS);
context.setRoute(ROUTES.CONNECTORS, {
event: "unique_payment_option_wallets"
});
break;
default:
context.setUniquePaymentMethodPage(ROUTES.SELECT_METHOD);
break;
}
}
const hasUniquePaymentOption = paymentState.buttonProps && "uniquePaymentOption" in paymentState.buttonProps && paymentState.buttonProps.uniquePaymentOption;
const isWalletsUniquePaymentOption = paymentState.buttonProps?.uniquePaymentOption === "Wallets";
const evmOptionsCount = paymentState.walletPaymentOptions.options?.length ?? 0;
const isEvmLoading = paymentState.walletPaymentOptions.isLoading;
const solanaOptionsCount = paymentState.solanaPaymentOptions.options?.length ?? 0;
const isSolanaLoading = paymentState.solanaPaymentOptions.isLoading;
if ((!hasUniquePaymentOption || isWalletsUniquePaymentOption) && isEthConnected && !isSolanaConnected && (!isMobile || !disableMobileInjector) && !isEvmLoading && evmOptionsCount > 0) {
paymentState.setTokenMode("evm");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "eth_connected_on_open",
walletId: connector?.id,
chainId: chain?.id,
address
});
} else if ((!hasUniquePaymentOption || isWalletsUniquePaymentOption) && isSolanaConnected && !isEthConnected && showSolanaPaymentMethod && !disableMobileInjector && !isSolanaLoading && solanaOptionsCount > 0) {
paymentState.setTokenMode("solana");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "solana_connected_on_open"
});
}
}, [
context.open,
paymentState.walletPaymentOptions.options,
paymentState.walletPaymentOptions.isLoading,
paymentState.solanaPaymentOptions.options,
paymentState.solanaPaymentOptions.isLoading,
paymentState.externalPaymentOptions.options,
paymentState.externalPaymentOptions.loading,
paymentState.depositAddressOptions.options,
paymentState.depositAddressOptions.loading,
showSolanaPaymentMethod,
address,
chain?.id,
connector?.id,
context.uniquePaymentMethodPage
]);
useEffect(() => {
if (context.route === ROUTES.CONNECT || context.route === ROUTES.CONNECTORS || context.route === ROUTES.MOBILECONNECTORS) {
if (isEthConnected) {
paymentState.setTokenMode("evm");
context.setRoute(ROUTES.SELECT_TOKEN, {
event: "connected",
walletId: connector?.id,
chainId: chain?.id,
address
});
}
}
}, [isEthConnected, context.route, connector?.id, chain?.id, address]);
useEffect(() => setMode(mode), [mode, setMode]);
useEffect(() => setTheme(theme), [theme, setTheme]);
useEffect(() => setCustomTheme(customTheme), [customTheme, setCustomTheme]);
useEffect(() => setLang(lang), [lang, setLang]);
useEffect(
() => setDisableMobileInjector(disableMobileInjector),
[disableMobileInjector, setDisableMobileInjector]
);
useEffect(() => {
const appName = getAppName();
if (!appName || !context.open) return;
const title = document.createElement("meta");
title.setAttribute("property", "og:title");
title.setAttribute("content", appName);
document.head.prepend(title);
return () => {
try {
document.head.removeChild(title);
} catch {
}
};
}, [context.open]);
return /* @__PURE__ */ jsx(DaimoPayThemeProvider, { theme, customTheme, mode, children: /* @__PURE__ */ jsx(
Modal,
{
open: context.open,
pages,
pageId: context.route,
onClose: closeable ? hide : void 0,
onInfo: void 0,
onBack: showBackButton ? onBack : void 0
}
) });
};
export { DaimoPayModal };
//# sourceMappingURL=index.js.map