# Smart Wallets on Base: A Developer’s Guide to Better Onchain UX

## What is a Smart Wallet?

A smart wallet is a non-custodial crypto wallet powered by smart contracts on the blockchain. It makes it possible for your users to create an onchain account in seconds, without any app or extension. One of the best and most friendly ways to enable users to interact with onchain applications.

## How Does Smart Wallet Work?

Smart wallets rely on passkeys, stored on users’ devices which can be used on the Coinbase website (keys.coinbase.com) and mobile app. Developers get to integrate an SDK that uses keys.coinbase.com popups to allow users to see requests and sign with their passkey. The SDK and the popup use cross-domain messaging to share information back to the app.

Coinbase SDK and client support the following networks:

1. Base
    
2. Optimism
    
3. Polygon
    
4. BNB Chain
    
5. Arbitrum
    
6. Avalanche
    
7. Zora
    
8. Ethereum Mainnet (very expensive)
    
9. Ethereum Sepolia (Testnet)
    
10. Base Sepolia (Testnet)
    
11. Optimism Sepolia (Testnet)
    

### Why Smart Wallet?

1. **Users can get free sponsored transactions**: Use Paymaster to sponsor transactions and you could qualify for up to $15k in gas credits.
    
2. **Support Sub-Accounts:** Let your app create and manage extra onchain accounts for users, all controlled by their main smart wallet — no more constant transaction pop-ups or signatures for every small action.
    
3. **Batch Operations:** You can now perform multiple operations in a single transaction with the help of a smart wallet
    
4. **ERC-20 Paymaster**: Let Users Pay Gas with Your App Token.  
    Instead of forcing users to top up ETH just to use your app, **ERC-20 paymasters** let them pay gas fees using *your* app's native token (e.g., $VIBES, $AFRICOIN).
    

### Adding Smart Wallet To An Existing Next.js Project

This guide would help you add smart wallet to an existing [Next.js](https://nextjs.org) application using [Wagmi](https://wagmi.sh). Github Demo [Here](https://github.com/Base-West-Africa/Demo-examples/tree/main/smart-wallet-demo)

**Step 1: Install Dependencies**

First, navigate to your project directory and install the required dependencies

```plaintext
npm install @coinbase/wallet-sdk wagmi viem @tanstack/react-query
```

**Step 2: Create Wagmi Config**

Your project should have a `wagmi.ts` file, if it does not have one then you should proceed to create one in the root directory of your project.

```typescript
import { http, createConfig } from "wagmi";
import { baseSepolia } from "wagmi/chains";
import { coinbaseWallet } from "wagmi/connectors";

export const cbWalletConnector = coinbaseWallet({
  appName: "Wagmi Smart Wallet",
  preference: "smartWalletOnly",
});

export const config = createConfig({
  chains: [baseSepolia],
  // turn off injected provider discovery
  multiInjectedProviderDiscovery: false,
  connectors: [cbWalletConnector],
  ssr: true,
  transports: {
    [baseSepolia.id]: http(),
  },
});

declare module "wagmi" {
  interface Register {
    config: typeof config;
  }
}
```

We just had to modify the wagmi config to include the Smart Wallet connector `cbWalletConnector`

**Step 3: Create Providers Component**

Create a file called `providers.tsx` in the `app/` directory:

```typescript
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState, type ReactNode } from "react";
import { WagmiProvider } from "wagmi";

import { config } from "@/wagmi";

export function Providers(props: { children: ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());

  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {props.children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}
```

**Step 4: Add Providers to the Root Layout**

Update the root layout file (app/layout.tsx):

```typescript
import "./globals.css";
import type { Metadata } from "next";
import type { ReactNode } from "react";

import { Providers } from "./providers";

export const metadata: Metadata = {
  title: "Smart Wallet App",
  description: "Smart Wallet Next.js integration",
};

export default function RootLayout(props: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{props.children}</Providers>
      </body>
    </html>
  );
}
```

**Step 5: Set Up Wallet Connection + SIWE (Sign-In With Ethereum)**

Now let’s create a component that handles:

* Connecting the Smart Wallet using Coinbase’s connector
    
* Triggering **Sign-In With Ethereum (SIWE)** for authentication
    

Create a file called `ConnectAndSIWE.tsx` in your `components/` directory and set up the logic to:

* Connect to the smart wallet
    
* Prepare and sign the SIWE message
    
* Verify the signature
    

```typescript
import { useCallback, useEffect, useState } from "react";
import type { Hex } from "viem";
import {
  useAccount,
  useConnect,
  usePublicClient,
  useSignMessage,
} from "wagmi";
import { SiweMessage } from "siwe";
import { cbWalletConnector } from "@/wagmi";

export function ConnectAndSIWE() {
  const { connect } = useConnect({
    mutation: {
      onSuccess: (data) => {
        const address = data.accounts[0];
        const chainId = data.chainId;
        const m = new SiweMessage({
          domain: document.location.host,
          address,
          chainId,
          uri: document.location.origin,
          version: "1",
          statement: "Smart Wallet SIWE Example",
          nonce: "12345678", // Replace with a real nonce in production
        });
        setMessage(m);
        signMessage({ message: m.prepareMessage() });
      },
    },
  });

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

  const [message, setMessage] = useState<SiweMessage | undefined>();
  const [valid, setValid] = useState<boolean | undefined>();

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

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

  return (
    <div>
      <button onClick={() => connect({ connector: cbWalletConnector })}>
        Connect + SIWE
      </button>
      {valid !== undefined && <p>Is valid: {valid.toString()}</p>}
    </div>
  );
}
```

**Step 6: Use the Component in a Page**

Add the component to `app/page.tsx`:

```typescript
import { ConnectAndSIWE } from '../components/ConnectAndSIWE'

export default function Home() {
  return (
    <main>
      <h1>Smart Wallet Integration</h1>
      <ConnectAndSIWE />
    </main>
  )
}
```
