Skip to content

useAuth

The useAuth hook handles Sign-In with Ethereum (SIWE) authentication for Silent Swap. It manages the full authentication flow including nonce generation, message signing, and auth caching.

Import

import { useAuth, useSilentClient } from '@silentswap/react';
import { useAccount, useWalletClient } from 'wagmi';

Basic Usage

import { useAuth, useSilentClient } from '@silentswap/react';
import { useAccount, useWalletClient } from 'wagmi';
import { ENVIRONMENT } from '@silentswap/sdk';
 
function AuthComponent() {
  const { address } = useAccount();
  const { data: walletClient } = useWalletClient();
  const { client } = useSilentClient({
    config: {
      environment: ENVIRONMENT.MAINNET,
    },
  });
 
  const {
    auth,
    isAuthenticated,
    signIn,
    signOut,
    isLoading,
    error,
  } = useAuth({
    client,
    address: address!,
    walletClient: walletClient as any,
    domain: 'example.com',
    autoAuthenticate: true, // Automatically authenticate when ready
  });
 
  if (isLoading) {
    return <div>Authenticating...</div>;
  }
 
  if (error) {
    return <div>Error: {error.message}</div>;
  }
 
  if (isAuthenticated()) {
    return (
      <div>
        <p>Authenticated as {auth?.address}</p>
        <button onClick={signOut}>Sign Out</button>
      </div>
    );
  }
 
  return (
    <button onClick={signIn} disabled={isLoading}>
      Sign In with Ethereum
    </button>
  );
}

API Reference

Options

interface useAuthOptions {
  /** SilentSwap client instance */
  client?: SilentSwapClient;
  /** User's EVM address */
  address?: `0x${string}`;
  /** Wallet client for signing operations */
  walletClient?: WalletClient;
  /** Wagmi connector */
  connector?: Connector;
  /** Domain for SIWE message (defaults to window.location.host) */
  domain?: string;
  /** Whether to auto-authenticate when dependencies are available */
  autoAuthenticate?: boolean;
}

Return Value

interface useAuthReturn {
  // State
  auth: AuthResponse | null;
  nonce: string | null;
  isLoading: boolean;
  error: Error | null;
 
  // Sign-in message creation
  createSignInMessage: (
    address: `0x${string}`,
    nonce: string,
    domain?: string
  ) => SignInMessage;
 
  // EIP-712 document creation
  createEip712DocForOrder: (
    quoteResponse: QuoteResponse
  ) => ReturnType<typeof createEip712DocForOrder>;
  
  createEip712DocForWalletGeneration: (
    scope: string,
    token: string
  ) => ReturnType<typeof createEip712DocForWalletGeneration>;
 
  // Authentication methods
  getNonce: () => Promise<string>;
  authenticate: () => Promise<AuthResponse | null>;
  signIn: () => Promise<AuthResponse | null>;
  signOut: () => void;
  isAuthenticated: () => boolean;
}

Authentication Flow

  1. Get Nonce - Request a nonce from the server
  2. Create SIWE Message - Generate the sign-in message
  3. Sign Message - User signs the message with their wallet
  4. Authenticate - Send signed message to server
  5. Cache Auth - Store authentication response locally

Auto-Authentication

Enable automatic authentication when all dependencies are ready:

const { auth, isAuthenticated } = useAuth({
  client,
  address: address!,
  walletClient,
  autoAuthenticate: true, // Automatically authenticate
});

The hook will:

  • Check for cached authentication first
  • Automatically authenticate if cache is expired or missing
  • Handle errors gracefully

Manual Authentication

You can also manually control the authentication flow:

const {
  getNonce,
  authenticate,
  createSignInMessage,
} = useAuth({
  client,
  address: address!,
  walletClient,
  autoAuthenticate: false,
});
 
const handleManualAuth = async () => {
  try {
    // Get nonce
    const nonce = await getNonce();
    
    // Create message
    const message = createSignInMessage(
      address!,
      nonce,
      'example.com'
    );
    
    // Sign message (you would do this with walletClient)
    // const signature = await walletClient.signMessage({ message: message.message });
    
    // Authenticate
    // await authenticate();
  } catch (error) {
    console.error('Authentication failed:', error);
  }
};

Auth Caching

The hook automatically caches authentication responses:

  • Storage: LocalStorage (per address)
  • Expiration: Based on authExpires from server
  • Auto-load: Cached auth is loaded on mount

EIP-712 Document Creation

The hook provides helpers for creating EIP-712 documents:

const {
  createEip712DocForOrder,
  createEip712DocForWalletGeneration,
} = useAuth({
  client,
  address: address!,
  walletClient,
});
 
// Create EIP-712 doc for order
const orderDoc = createEip712DocForOrder(quoteResponse);
 
// Create EIP-712 doc for wallet generation
const walletDoc = createEip712DocForWalletGeneration(
  `eip155:43114:${address}`, // Scope
  auth.secretToken // Token
);

Error Handling

const { auth, error, signIn } = useAuth({
  client,
  address: address!,
  walletClient,
});
 
const handleSignIn = async () => {
  try {
    const result = await signIn();
    if (!result) {
      console.error('Sign in returned null');
    }
  } catch (err) {
    console.error('Sign in error:', err);
  }
};
 
// Display errors
{error && (
  <div className="error">
    Authentication failed: {error.message}
  </div>
)}

Best Practices

  1. Enable Auto-Authentication - Use autoAuthenticate: true for better UX
  2. Handle Loading States - Show loading indicators during authentication
  3. Check Authentication - Use isAuthenticated() before protected operations
  4. Error Recovery - Provide retry mechanisms for failed authentication
  5. Sign Out - Clear auth state when user disconnects wallet

Next Steps