A comprehensive React library that simplifies wallet connection functionality for decentralized applications with support for 66 wallets and extensive customization options
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
SIWE (Sign-In with Ethereum) authentication integration for secure user authentication in RainbowKit applications.
Provider component that wraps your application to enable authentication functionality.
/**
* Authentication provider component for SIWE integration
* @param props - Authentication provider configuration
* @returns React provider component for authentication
*/
function RainbowKitAuthenticationProvider<T>(
props: RainbowKitAuthenticationProviderProps<T>
): JSX.Element;
interface RainbowKitAuthenticationProviderProps<T> extends AuthenticationConfig<T> {
/** Whether authentication is enabled */
enabled?: boolean;
/** React children to wrap */
children: ReactNode;
}
interface AuthenticationConfig<T> {
/** Authentication adapter implementation */
adapter: AuthenticationAdapter<T>;
/** Current authentication status */
status: AuthenticationStatus;
}
type AuthenticationStatus = 'loading' | 'unauthenticated' | 'authenticated';Usage Examples:
import {
RainbowKitAuthenticationProvider,
createAuthenticationAdapter,
AuthenticationStatus,
} from '@rainbow-me/rainbowkit';
import { useState } from 'react';
function App() {
const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');
const authenticationAdapter = createAuthenticationAdapter({
getNonce: async () => {
const response = await fetch('/api/nonce');
return await response.text();
},
createMessage: ({ nonce, address, chainId }) => {
return `Sign this message to authenticate: ${nonce}`;
},
verify: async ({ message, signature }) => {
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, signature }),
});
const verified = await response.json();
setAuthenticationStatus(verified ? 'authenticated' : 'unauthenticated');
return verified;
},
signOut: async () => {
await fetch('/api/logout', { method: 'POST' });
setAuthenticationStatus('unauthenticated');
},
});
return (
<RainbowKitAuthenticationProvider
adapter={authenticationAdapter}
status={authenticationStatus}
>
{/* Your app */}
</RainbowKitAuthenticationProvider>
);
}Factory function for creating authentication adapters with SIWE integration.
/**
* Creates an authentication adapter for SIWE integration
* @param adapter - Authentication adapter implementation
* @returns Configured authentication adapter
*/
function createAuthenticationAdapter<T>(
adapter: AuthenticationAdapter<T>
): AuthenticationAdapter<T>;
interface AuthenticationAdapter<T> {
/** Fetches a nonce from your authentication server */
getNonce: () => Promise<string>;
/** Creates a message to be signed by the user */
createMessage: (args: CreateMessageArgs) => T;
/** Verifies the signed message on your server */
verify: (args: VerifyArgs<T>) => Promise<boolean>;
/** Signs out the user */
signOut: () => Promise<void>;
}
interface CreateMessageArgs {
/** Random nonce from server */
nonce: string;
/** User's wallet address */
address: string;
/** Current chain ID */
chainId: number;
}
interface VerifyArgs<T> {
/** The message that was signed */
message: T;
/** The signature from the user's wallet */
signature: string;
}Usage Examples:
import { createAuthenticationAdapter } from '@rainbow-me/rainbowkit';
import { SiweMessage } from 'siwe';
// SIWE (Sign-In with Ethereum) adapter
const siweAuthenticationAdapter = createAuthenticationAdapter({
getNonce: async () => {
const response = await fetch('/api/nonce');
return await response.text();
},
createMessage: ({ nonce, address, chainId }) => {
return new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in with Ethereum to the app.',
uri: window.location.origin,
version: '1',
chainId,
nonce,
});
},
verify: async ({ message, signature }) => {
const verifyRes = await fetch('/api/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message, signature }),
});
return Boolean(verifyRes.ok);
},
signOut: async () => {
await fetch('/api/logout', { method: 'POST' });
},
});
// Simple message adapter
const simpleAuthenticationAdapter = createAuthenticationAdapter({
getNonce: async () => {
return Math.random().toString(36).substring(2);
},
createMessage: ({ nonce, address }) => {
return `Please sign this message to authenticate: ${nonce}\nAddress: ${address}`;
},
verify: async ({ message, signature }) => {
// Implement your verification logic
return true;
},
signOut: async () => {
console.log('Signed out');
},
});Hook for adding transactions to the recent transactions list (useful for authenticated flows).
/**
* Hook for adding transactions to recent transactions list
* @returns Function to add transactions
*/
function useAddRecentTransaction(): (transaction: NewTransaction) => void;
interface NewTransaction {
/** Transaction hash */
hash: string;
/** Human-readable description */
description: string;
/** Number of confirmations (optional) */
confirmations?: number;
}
interface Transaction extends NewTransaction {
/** Transaction status */
status: 'pending' | 'confirmed' | 'failed';
}Usage Examples:
import { useAddRecentTransaction } from '@rainbow-me/rainbowkit';
import { useContractWrite, useWaitForTransaction } from 'wagmi';
function TokenTransfer() {
const addRecentTransaction = useAddRecentTransaction();
const { data, write } = useContractWrite({
// ... contract configuration
onSuccess(data) {
addRecentTransaction({
hash: data.hash,
description: 'Transfer tokens',
});
},
});
return (
<button onClick={() => write?.()}>
Transfer Tokens
</button>
);
}import {
RainbowKitAuthenticationProvider,
createAuthenticationAdapter,
AuthenticationStatus,
} from '@rainbow-me/rainbowkit';
import { SiweMessage } from 'siwe';
import { useState, useEffect } from 'react';
import { useAccount } from 'wagmi';
function AuthenticationWrapper({ children }) {
const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');
const { isConnected } = useAccount();
useEffect(() => {
const fetchStatus = async () => {
if (!isConnected) {
setAuthenticationStatus('unauthenticated');
return;
}
try {
const response = await fetch('/api/me');
setAuthenticationStatus(response.ok ? 'authenticated' : 'unauthenticated');
} catch {
setAuthenticationStatus('unauthenticated');
}
};
fetchStatus();
}, [isConnected]);
const authenticationAdapter = createAuthenticationAdapter({
getNonce: async () => {
const response = await fetch('/api/nonce');
return await response.text();
},
createMessage: ({ nonce, address, chainId }) => {
return new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in with Ethereum to access protected features.',
uri: window.location.origin,
version: '1',
chainId,
nonce,
expirationTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours
});
},
verify: async ({ message, signature }) => {
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: message.prepareMessage(),
signature,
}),
});
const success = response.ok;
setAuthenticationStatus(success ? 'authenticated' : 'unauthenticated');
return success;
},
signOut: async () => {
await fetch('/api/logout', { method: 'POST' });
setAuthenticationStatus('unauthenticated');
},
});
return (
<RainbowKitAuthenticationProvider
adapter={authenticationAdapter}
status={authenticationStatus}
>
{children}
</RainbowKitAuthenticationProvider>
);
}// API route: /api/verify
import { SiweMessage } from 'siwe';
import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { method } = req;
switch (method) {
case 'POST':
try {
const { message, signature } = req.body;
const siweMessage = new SiweMessage(message);
const fields = await siweMessage.verify({ signature });
if (fields.success) {
// Store authentication state (session, JWT, etc.)
req.session.siwe = fields.data;
req.session.save();
res.json({ success: true });
} else {
res.status(422).json({ message: 'Invalid signature.' });
}
} catch (error) {
res.status(400).json({ message: error.message });
}
break;
default:
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}import { useAccount } from 'wagmi';
import { ConnectButton } from '@rainbow-me/rainbowkit';
function ProtectedContent() {
const { isConnected } = useAccount();
// This would typically use a custom hook to check authentication status
// For example: const { isAuthenticated } = useAuthentication();
if (!isConnected) {
return (
<div>
<h2>Connect Your Wallet</h2>
<p>Please connect your wallet to access this content.</p>
<ConnectButton />
</div>
);
}
// In a real app, you'd check authentication status here
return (
<div>
<h2>Protected Content</h2>
<p>This content is only visible to authenticated users.</p>
{/* Protected content here */}
</div>
);
}import { useState, useEffect } from 'react';
import { useAccount } from 'wagmi';
export function useAuthenticationStatus() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const { isConnected, address } = useAccount();
useEffect(() => {
const checkAuthStatus = async () => {
if (!isConnected || !address) {
setIsAuthenticated(false);
setIsLoading(false);
return;
}
try {
const response = await fetch('/api/me');
setIsAuthenticated(response.ok);
} catch {
setIsAuthenticated(false);
} finally {
setIsLoading(false);
}
};
checkAuthStatus();
}, [isConnected, address]);
const signOut = async () => {
try {
await fetch('/api/logout', { method: 'POST' });
setIsAuthenticated(false);
} catch (error) {
console.error('Sign out failed:', error);
}
};
return {
isAuthenticated,
isLoading,
signOut,
};
}Install with Tessl CLI
npx tessl i tessl/npm-rainbow-me--rainbowkit