diff --git a/landing/siwe.react.js b/landing/siwe.react.js index 9d062f58f..b0c8fcb2c 100644 --- a/landing/siwe.react.js +++ b/landing/siwe.react.js @@ -1,199 +1,204 @@ // @flow import { useConnectModal, RainbowKitProvider, darkTheme, } from '@rainbow-me/rainbowkit'; import '@rainbow-me/rainbowkit/styles.css'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import invariant from 'invariant'; import _merge from 'lodash/fp/merge.js'; import * as React from 'react'; import { useAccount, useWalletClient, WagmiProvider } from 'wagmi'; import ConnectedWalletInfo from 'lib/components/connected-wallet-info.react.js'; import { type SIWEWebViewMessage } from 'lib/types/siwe-types.js'; import { getSIWEStatementForPublicKey, userTextsForSIWEMessageTypes, createSIWEMessage, } from 'lib/utils/siwe-utils.js'; import { AlchemyENSCacheProvider, getWagmiConfig, } from 'lib/utils/wagmi-utils.js'; import { SIWEContext } from './siwe-context.js'; import css from './siwe.css'; import { useMonitorForWalletConnectModal, type WalletConnectModalUpdate, } from './walletconnect-hooks.js'; function postMessageToNativeWebView(message: SIWEWebViewMessage) { window.ReactNativeWebView?.postMessage?.(JSON.stringify(message)); } -const wagmiConfig = getWagmiConfig(['rainbow', 'metamask', 'walletconnect']); +const wagmiConfig = getWagmiConfig([ + 'rainbow', + 'metamask', + 'coinbase', + 'walletconnect', +]); type Signer = { +signMessage: ({ +message: string, ... }) => Promise, ... }; async function signInWithEthereum( address: string, signer: Signer, nonce: string, statement: string, ) { invariant(nonce, 'nonce must be present in signInWithEthereum'); const message = createSIWEMessage(address, statement, nonce); const signature = await signer.signMessage({ message }); postMessageToNativeWebView({ type: 'siwe_success', address, message, signature, }); } const queryClient = new QueryClient(); function SIWE(): React.Node { const { address } = useAccount(); const { data: signer } = useWalletClient(); const { siweNonce, siwePrimaryIdentityPublicKey, siweMessageType } = React.useContext(SIWEContext); const onClick = React.useCallback(() => { invariant(siweNonce, 'nonce must be present during SIWE attempt'); invariant(siweMessageType, 'message type must be set during SIWE attempt'); invariant( siwePrimaryIdentityPublicKey, 'primaryIdentityPublicKey must be present during SIWE attempt', ); const statement = getSIWEStatementForPublicKey( siwePrimaryIdentityPublicKey, siweMessageType, ); void signInWithEthereum(address, signer, siweNonce, statement); }, [ address, signer, siweNonce, siwePrimaryIdentityPublicKey, siweMessageType, ]); const { openConnectModal, connectModalOpen } = useConnectModal(); const hasNonce = siweNonce !== null && siweNonce !== undefined; React.useEffect(() => { if (hasNonce && openConnectModal) { openConnectModal(); } }, [hasNonce, openConnectModal]); const [wcModalOpen, setWCModalOpen] = React.useState(false); const prevModalOpen = React.useRef(false); const modalOpen = connectModalOpen || wcModalOpen; React.useEffect(() => { if (!modalOpen && prevModalOpen.current && !signer) { postMessageToNativeWebView({ type: 'siwe_closed' }); } prevModalOpen.current = modalOpen; }, [modalOpen, signer]); const onWalletConnectModalUpdate = React.useCallback( (update: WalletConnectModalUpdate) => { if (update.state === 'closed') { setWCModalOpen(false); postMessageToNativeWebView({ type: 'walletconnect_modal_update', ...update, }); } else { setWCModalOpen(true); postMessageToNativeWebView({ type: 'walletconnect_modal_update', ...update, }); } }, [], ); useMonitorForWalletConnectModal(onWalletConnectModalUpdate); if (!siweMessageType) { return (

Unable to proceed: message type not found

); } if (!hasNonce) { return (

Unable to proceed: nonce not found.

); } else if (!signer) { return null; } else { const { explanationStatement, buttonStatement, showTermsAgreement } = userTextsForSIWEMessageTypes[siweMessageType]; let termsOfUseAndPolicyInfo = null; if (showTermsAgreement) { termsOfUseAndPolicyInfo = (

By signing up, you agree to our{' '} Terms of Use &{' '} Privacy Policy.

); } return (

Wallet Connected

{explanationStatement}

{termsOfUseAndPolicyInfo}
{buttonStatement}
); } } function SIWEWrapper(): React.Node { const theme = React.useMemo(() => { return _merge(darkTheme())({ radii: { modal: 0, modalMobile: 0, }, colors: { modalBackdrop: '#242529', }, }); }, []); return ( ); } export default SIWEWrapper; diff --git a/lib/utils/wagmi-utils.js b/lib/utils/wagmi-utils.js index 109f92812..1158821a9 100644 --- a/lib/utils/wagmi-utils.js +++ b/lib/utils/wagmi-utils.js @@ -1,75 +1,76 @@ // @flow import { connectorsForWallets } from '@rainbow-me/rainbowkit'; import { injectedWallet, metaMaskWallet, rainbowWallet, coinbaseWallet, walletConnectWallet, // eslint-disable-next-line import/extensions } from '@rainbow-me/rainbowkit/wallets'; import { AlchemyProvider } from 'ethers'; import * as React from 'react'; import { http } from 'viem'; import { createConfig } from 'wagmi'; // eslint-disable-next-line import/extensions import { mainnet } from 'wagmi/chains'; import { ENSCacheProvider } from '../components/ens-cache-provider.react.js'; const projectId = process.env.COMM_WALLETCONNECT_KEY; const alchemyKey = process.env.COMM_ALCHEMY_KEY; const transport = alchemyKey ? http(`https://eth-mainnet.g.alchemy.com/v2/${alchemyKey}`) : http(); const walletMap = { injected: injectedWallet, rainbow: rainbowWallet, metamask: metaMaskWallet, coinbase: coinbaseWallet, walletconnect: walletConnectWallet, }; type Wallet = $Keys; function getWagmiConfig(walletArr: $ReadOnlyArray): mixed { let wallets: Array; if (!projectId) { wallets = walletArr.includes('injected') ? [injectedWallet] : []; } else { wallets = walletArr.map(wallet => walletMap[wallet]); } const connectors = connectorsForWallets( [{ groupName: 'Recommended', wallets }], { appName: 'Comm', projectId, }, ); return createConfig({ connectors, chains: [mainnet], transports: { [mainnet.id]: transport, }, + ssr: !process.env.BROWSER, }); } const ethersAlchemyProvider = new AlchemyProvider('mainnet', alchemyKey); type Props = { +children: React.Node, }; function AlchemyENSCacheProvider(props: Props): React.Node { const { children } = props; return ( {children} ); } export { getWagmiConfig, AlchemyENSCacheProvider };