This guide covers creating a new Sign-In with Ethereum template project that uses Smart Wallet.

This simple implementation is for demo purposes only and is not meant to be an example of a production app. A production app should: 1. Generate a random nonce for each message on the backend. 2. Check the nonce and verify the message signature on the backend as specified in EIP-4361. 3. Invalidate nonces on logout to prevent replay attacks through session duplication.

1

Create a project

Follow the Wagmi guide to set up a template project and connect a Smart Wallet.

2

Install dependencies

pnpm install siwe
3

Create a new SIWE component

import React from 'react';

export function SignInWithEthereum() {
  return (
    <div>
      <h2>SIWE Example</h2>
    </div>
  );
}
4

Prompt to sign and store signature

Wagmi’s signMessage function will open the Smart Wallet popup to sign the message. The signature is stored in the component’s state.

src/SignInWithEthereum.tsx
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useAccount, usePublicClient, useSignMessage } from 'wagmi';
import { SiweMessage } from 'siwe';
import type { Hex } from 'viem';

export function SignInWithEthereum() {
  const [signature, setSignature] = useState<Hex | undefined>(undefined);
  const { signMessage } = useSignMessage({ mutation: { onSuccess: (sig) => setSignature(sig) } });
  const account = useAccount();

  const siweMessage = useMemo(() => {
    return new SiweMessage({
      domain: document.location.host,
      address: account.address,
      chainId: account.chainId,
      uri: document.location.origin,
      version: '1',
      statement: 'Smart Wallet SIWE Example',
      nonce: '12345678', // replace with nonce generated by your backend
    });
  }, []);

  const promptToSign = () => {
    signMessage({ message: siweMessage.prepareMessage() });
  };

  return (
    <div>
      <h2>SIWE Example</h2>
      <button onClick={promptToSign}>Sign In with Ethereum</button>
      {signature && <p>Signature: {signature}</p>}
    </div>
  );
}
5

Verify the message signature

For Smart Wallet, ERC-1271 should be used to verify the SIWE message signature.

This simple example does not check the nonce during verification, all production implementations should. Furthermore, nonces should be invalidated on logout to prevent replay attacks through session duplication (e.g. store expired nonce and make sure they can’t be used again). In production apps, SIWE message verification is generally handled on the backend.

src/SignInWithEthereum.tsx
// @noErrors: 2339
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { Hex } from 'viem';
import { useAccount, usePublicClient, useSignMessage } from 'wagmi';
import { SiweMessage } from 'siwe';
export function SignInWithEthereum() {
  const [signature, setSignature] = useState<Hex | undefined>(undefined);
  const [valid, setValid] = useState<boolean | undefined>(undefined); 
  const client = usePublicClient(); 
  const { signMessage } = useSignMessage({ mutation: { onSuccess: (sig) => setSignature(sig) } });
  const account = useAccount();

  const message = useMemo(() => {
    return new SiweMessage({
      domain: document.location.host,
      address: account.address,
      chainId: account.chainId,
      uri: document.location.origin,
      version: '1',
      statement: 'Smart Wallet SIWE Example',
      nonce: '12345678', // replace with nonce generated by your backend
    });
  }, []);

  const checkValid = useCallback(async () => { 
    if (!signature || !account.address || !client) return; 
    const isValid = await client.verifyMessage({  
      address: account.address, 
      message: message.prepareMessage(), 
      signature, 
    }); 
    setValid(isValid); 
  }, [signature, account]); 

  useEffect(() => { 
    checkValid(); 
  }, [signature, account]); 

  const promptToSign = () => {
    signMessage({ message: message.prepareMessage() });
  };
  return (
    <div>
      <h2>SIWE Example</h2>
      <button onClick={promptToSign}>Sign In with Ethereum</button>
      {signature && <p>Signature: {signature}</p>}
      {valid !== undefined && <p>Is valid: {valid.toString()}</p>}{/* // [!code focus] */}
    </div>
  );
}
6

Sign in with Ethereum

Visit your local server and click “Sign In with Ethereum”