RainbowKit
Integrate social logins within RainbowKit through Particle Auth.
Leveraging Particle Auth within RainbowKit
One of the most widely used methods of implementing Particle Auth is through RainbowKit. By leveraging RainbowKit as the underlying connection mechanism within your application, you can facilitate both Web2-based social logins with Particle Auth (through a custom wagmi connector powered by @particle-network/auth-core-modal
) or Web3-native wallet connections. This unification of social logins and external wallet connections is also possible through Particle Connect, although if you’d like to use RainbowKit, including Particle Auth within the list of connection options is simple.
Implementing particleWagmiWallet
If you’re already using RainbowKit within your application, you can set up a custom wagmi connector, particleWagmiWallet
alongside its respective derivatives, including particleGoogleWallet
, particleTwitterWallet
, and any other social login implementations that you’d like to include within your RainbowKit instance (through wallets
within RainbowKit’s connectorsForWallets
).
To build custom Particle Auth connectors of this nature, follow the walkthrough below.
Walkthrough
-
First, set up the
particleWagmiWallet
connector. You’ll first need to create an additional file within your project to hold the custom connector, such asparticleWagmiWallet.ts
. This file should include the below snippet:particleWagmiWallet.tsimport { type ConnectParam, type EIP1193Provider } from '@particle-network/auth-core'; import type { EVMProvider } from '@particle-network/auth-core-modal/dist/context/evmProvider'; import { ChainNotConfiguredError, createConnector, normalizeChainId } from '@wagmi/core'; import { SwitchChainError, UserRejectedRequestError, getAddress, numberToHex, type ProviderRpcError } from 'viem'; particleWagmiWallet.type = 'particleWallet' as const; export function particleWagmiWallet(param?: ConnectParam) { type Provider = EIP1193Provider; type Properties = any; return createConnector<Provider, Properties>((config) => ({ id: 'particleWalletSDK', name: 'Particle Wallet', type: particleWagmiWallet.type, async connect({ chainId }: { chainId: number }) { try { const provider = await this.getProvider(); const accounts = (await (provider as EVMProvider).connect(param)).map((x) => getAddress(x)); provider.on('accountsChanged', this.onAccountsChanged); provider.on('chainChanged', this.onChainChanged); provider.on('disconnect', this.onDisconnect.bind(this)); // Switch to chain if provided let currentChainId = await this.getChainId(); if (chainId && currentChainId !== chainId) { const chain = await this.switchChain!({ chainId }).catch((error: any) => { if (error.code === UserRejectedRequestError.code) throw error; return { id: currentChainId }; }); currentChainId = chain?.id ?? currentChainId; } return { accounts, chainId: currentChainId }; } catch (error: any) { if (error.code == 4011) throw new UserRejectedRequestError(error as Error); throw error; } }, async disconnect() { const provider = await this.getProvider(); provider.removeListener('accountsChanged', this.onAccountsChanged); provider.removeListener('chainChanged', this.onChainChanged); provider.removeListener('disconnect', this.onDisconnect.bind(this)); await (provider as any)?.disconnect?.(); }, async getAccounts() { const provider = await this.getProvider(); return ( await provider.request({ method: 'eth_accounts', }) ).map((x: string) => getAddress(x)); }, async getChainId() { const provider = await this.getProvider(); const chainId = await provider.request({ method: 'eth_chainId' }); return normalizeChainId(chainId); }, async getProvider() { if (typeof window === 'undefined') { return; } while (!(window as any).particle?.ethereum) { await new Promise((resolve) => setTimeout(() => resolve(true), 100)); } return (window as any).particle?.ethereum; }, async isAuthorized() { try { const provider = await this.getProvider(); return (provider as any).isConnected(); } catch { return false; } }, async switchChain({ chainId }: { chainId: number }) { const chain = config.chains.find((chain) => chain.id === chainId); if (!chain) throw new SwitchChainError(new ChainNotConfiguredError()); const provider = await this.getProvider(); const chainId_ = numberToHex(chain.id); try { await provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: chainId_ }], }); return chain; } catch (error) { // Indicates chain is not added to provider if ((error as ProviderRpcError).code === 4902) { try { await provider.request({ method: 'wallet_addEthereumChain', params: [ { chainId: chainId_, chainName: chain.name, nativeCurrency: chain.nativeCurrency, rpcUrls: [chain.rpcUrls.default?.http[0] ?? ''], blockExplorerUrls: [chain.blockExplorers?.default.url], }, ], }); return chain; } catch (error) { throw new UserRejectedRequestError(error as Error); } } throw new SwitchChainError(error as Error); } }, onAccountsChanged(accounts: string[]) { if (accounts.length === 0) config.emitter.emit('disconnect'); else config.emitter.emit('change', { accounts: accounts.map((x) => getAddress(x)), }); }, onChainChanged(chain: string) { const chainId = normalizeChainId(chain); config.emitter.emit('change', { chainId }); }, async onDisconnect(_error: any) { config.emitter.emit('disconnect'); const provider = await this.getProvider(); provider.removeListener('accountsChanged', this.onAccountsChanged); provider.removeListener('chainChanged', this.onChainChanged); provider.removeListener('disconnect', this.onDisconnect.bind(this)); }, })); }
-
Initializing connector instances (such as
particleGoogleWallet
). With theparticleWagmiWallet
connector set up, you’ll need to establish a number of configured (derivative) connector instances to include specific social logins within your RainbowKit instance (these should be placed in thewallets
array while configuringconnectorsForWallets
). An example of this is included below:
-
Wrapping your
ConnectButton
instance with a social login patch. When using Particle Auth Core with RainbowKit, you’ll need to add a short snippet that fixes a few common issues that may arise when using social logins with wagmi. In our demo, we place this within theApp.tsx
file, which is then rendered in place ofConnectButton
directly. Below is the snippet in question:import React, { useEffect } from 'react'; import { useConnect as useParticleConnect } from '@particle-network/auth-core-modal'; import { AuthCoreEvent, getLatestAuthType, isSocialAuthType, particleAuth } from '@particle-network/auth-core'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import { useConnect, useDisconnect } from 'wagmi'; import { particleWagmiWallet } from './particleWallet/particleWagmiWallet'; function App() { const { connect } = useConnect(); const { connectionStatus } = useParticleConnect(); const { disconnect } = useDisconnect(); useEffect(() => { if (connectionStatus === 'connected' && isSocialAuthType(getLatestAuthType())) { connect({ connector: particleWagmiWallet({ socialType: getLatestAuthType() }), }); } const onDisconnect = () => disconnect(); particleAuth.on(AuthCoreEvent.ParticleAuthDisconnect, onDisconnect); return () => particleAuth.off(AuthCoreEvent.ParticleAuthDisconnect, onDisconnect); }, [connect, connectionStatus, disconnect]); return <ConnectButton />; } export default App;
Demo repository available
To dive further into this process and view a practical example of implementation, take a look at the following demo repository:
1. Next.js example.
2. (create-react-app example coming soon)
Common issues
The following issues might be encountered in this process:
- Minification for Vercel deployments, generally seen as
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'sqrt')
.- Occasionally, Vercel deployments are known to throw errors relating to the minifier you’re using, crashing the application upon startup. This is due to the usage of
swcMinify
within yournext.config.js
file. You’ll need to switchswcMinify
fromtrue
tofalse
, thus using Terser as the minifier rather than SWC.
- Occasionally, Vercel deployments are known to throw errors relating to the minifier you’re using, crashing the application upon startup. This is due to the usage of
📹 A full walkthrough video on leveraging Particle Auth within RainbowKit is available.
For an extended explanation and tutorial regarding the integration of Particle Auth within RainbowKit, we’ve created a video and GitHub template repository.
Was this page helpful?