> For the complete documentation index, see [llms.txt](/llms.txt).

# Upgrade an EOA to a smart account

`MetaMask Connect` `wagmi` `EOA` `smart account` `EIP-7702` `EIP-5792`MetaMask Developer Relations | Aug 22, 2025

Open in Claude

This tutorial walks you through upgrading a MetaMask externally owned account (EOA) to a [MetaMask smart account](/smart-accounts-kit/concepts/smart-accounts/) via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702), and sending an [atomic batch transaction](/metamask-connect/evm/guides/send-transactions/batch-transactions/) via [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792). You will use a provided template, which sets up MetaMask SDK with a [Next.js](https://nextjs.org/docs) and [Wagmi](https://wagmi.sh/) dapp.

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

- [Node.js](https://nodejs.org/) version 18 or later installed
- An [Infura API key](/developer-tools/dashboard/get-started/create-api/) from the MetaMask Developer dashboard
- [MetaMask](https://metamask.io/) installed, with an EOA that has Sepolia ETH  
note  
You can use the [MetaMask faucet](/developer-tools/faucet/) to get Sepolia ETH.

## Steps[​](#steps "Direct link to Steps")

### 1\. Set up the project[​](#1-set-up-the-project "Direct link to 1. Set up the project")

1. Clone the [MetaMask/7702-livestream-demo](https://github.com/MetaMask/7702-livestream-demo) repository:  
```  
git clone git@github.com:MetaMask/7702-livestream-demo.git  
```
2. Switch to the `feat/mm-sdk` branch:  
```  
cd 7702-livestream-demo && git switch feat/mm-sdk  
```
3. Install dependencies:  
```  
npm install  
```
4. Run the development server and navigate to [http://localhost:3000](http://localhost:3000/):  
```  
npm run dev  
```  
The initial template displays with non-functional buttons:

![SDK 7702 initial template](/assets/images/sdk-7702-initial-0a03070568c25b65e222343c272b94d7.png)

### 2\. Configure the MetaMask connector[​](#2-configure-the-metamask-connector "Direct link to 2. Configure the MetaMask connector")

In the root directory, create a `.env.local` file. Add a `NEXT_PUBLIC_INFURA_API_KEY` environment variable, replacing `<YOUR-API-KEY>` with your Infura API key:

.env.local

```
NEXT_PUBLIC_INFURA_API_KEY=<YOUR-API-KEY>

```

In `src/providers/AppProvider.tsx`, configure the Wagmi [MetaMask connector](https://wagmi.sh/react/api/connectors/metaMask) using your Infura API key:

AppProvider.tsx

```
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createConfig, http, WagmiProvider } from "wagmi";
import { sepolia } from "viem/chains";
import { ReactNode } from "react";
import { metaMask } from "wagmi/connectors";

- export const connectors = []
+ export const connectors = [
+   metaMask({
+     infuraAPIKey: process.env.NEXT_PUBLIC_INFURA_API_KEY,
+   }),
+ ];
// ...

```

### 3\. Create a connect and disconnect button[​](#3-create-a-connect-and-disconnect-button "Direct link to 3. Create a connect and disconnect button")

In `src/app/page.tsx`, use the `useAccount`, `useConnect`, and `useDisconnect` hooks from Wagmi, along with the MetaMask connector, to create a button to connect and disconnect your MetaMask wallet, and display the connection status:

page.tsx

```
"use client";

import Image from "next/image";
+ import { metaMask } from "wagmi/connectors";
+ import { useAccount, useConnect, useDisconnect } from "wagmi";

export default function Home() {
+ const { connect } = useConnect();
+ const { disconnect } = useDisconnect();
+ const { address, isConnected } = useAccount();

+ const formatAddress = (addr: string) => {
+  return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
+ };

  return (
  // ...
-   <ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
-     <li className="mb-2 tracking-[-.01em]">
-       Get started by editing{" "}
-       <code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
-         src/app/page.tsx
-       </code>
-       .
-     </li>
-     <li className="tracking-[-.01em]">
-       Save and see your changes instantly.
-     </li>
-   </ol>

-   <div className="flex gap-4 items-center flex-col sm:flex-row">
+   {/* Wallet connection section */}
+   <div className="bg-gray-50 dark:bg-gray-900 p-6 rounded-lg w-full">
+     <h2 className="text-xl font-semibold mb-4">Wallet Connection</h2>

+     {/* Connection status */}
+     <div className="mb-6">
+       {isConnected ? (
+         <div className="flex items-center gap-2 text-green-600">
+           <div className="w-2 h-2 bg-green-500 rounded-full"></div>
+           <span>Connected to {formatAddress(address!)}</span>
+         </div>
+       ) : (
+         <div className="flex items-center gap-2 text-red-600">
+           <div className="w-2 h-2 bg-red-500 rounded-full"></div>
+           <span>Not connected</span>
+         </div>
+       )}
+     </div>

-     <button
-       className="rounded-full border border-solid px-4 py-2 cursor-pointer hover:bg-gray-100"
-       onClick={() => {
-         console.log("clicked");
-       }}
-     >
-       Connect Wallet
-     </button>
+     {/* Connect/disconnect button */}
+     <button
+       className={`w-full rounded-lg border border-solid px-6 py-3 font-medium transition-colors ${
+         isConnected
+           ? "bg-red-50 hover:bg-red-100 text-red-700 border-red-300 cursor-pointer"
+           : "bg-blue-50 hover:bg-blue-100 text-blue-700 border-blue-300 cursor-pointer"
+       }`}
+       onClick={() => {
+         if (isConnected) {
+           disconnect();
+         } else {
+           connect({ connector: metaMask() });
+         }
+       }}
+     >
+       {isConnected ? "Disconnect Wallet" : "Connect with MetaMask"}
+     </button>
      // ...

```

In the development server, test that the button works to connect and disconnect from your MetaMask wallet. When connected, the interface displays your connected wallet address:

![SDK disconnected](/assets/images/sdk-7702-disconnected-4cb41404c58996b88d4ec78e7935c3d4.png)

![SDK connected](/assets/images/sdk-7702-connected-aa8f5121dea668ee4acf9571d785ce9d.png)

### 4\. Handle and send batch transactions[​](#4-handle-and-send-batch-transactions "Direct link to 4. Handle and send batch transactions")

In `src/app/page.tsx`, use the [useSendCalls](https://wagmi.sh/react/api/hooks/useSendCalls) hook from Wagmi to handle and send [atomic batch transactions](/metamask-connect/evm/guides/send-transactions/batch-transactions/). Also use React's `useState` hook to handle the transaction state. The following example sends 0.001 and 0.0001 ETH in a batch transaction. Replace `<YOUR-RECIPIENT-ADDRESS>` with recipient addresses of your choice:

page.tsx

```
"use client";

import Image from "next/image";
import { metaMask } from "wagmi/connectors";
- import { useAccount, useConnect, useDisconnect } from "wagmi";
+ import { useAccount, useConnect, useDisconnect, useSendCalls } from "wagmi";
+ import { useState } from "react";
+ import { parseEther } from "viem";

export default function Home() {
  const { connect } = useConnect();
  const { disconnect } = useDisconnect();
  const { address, isConnected } = useAccount();
+ const { sendCalls, error, isPending, isSuccess, data, reset } = useSendCalls();
+ const [transactionHash, setTransactionHash] = useState<string | null>(null);
+ const [statusError, setStatusError] = useState<string | null>(null);

+ const handleSendTransaction = () => {
+   if (!isConnected) return;
+
+   // Reset previous states
+   setTransactionHash(null);
+   setStatusError(null);
+   reset();
+
+   sendCalls({
+     calls: [
+       {
+         to: "<YOUR-RECIPIENT-ADDRESS>",
+         value: parseEther("0.001"),
+       },
+       {
+         to: "<YOUR-RECIPIENT-ADDRESS>",
+         value: parseEther("0.0001"),
+       },
+     ],
+   });
+ };
  // ...

```

Then, create a button to send batch transactions, and display the transaction state (pending, success, or error). Also, update the connect/disconnect button to reset states when disconnected:

page.tsx

```
// ...
  {/* Connect/disconnect button */}
    <button
      // ...
      onClick={() => {
        if (isConnected) {
          disconnect();
+         // Reset previous states
+         setTransactionHash(null);
+         setStatusError(null);
+         reset();
        // ...
    >
      {isConnected ? "Disconnect Wallet" : "Connect with MetaMask"}
    </button>
-   <button
-     className="rounded-full border border-solid px-4 py-2 cursor-pointer hover:bg-gray-100"
-     onClick={() => {
-       console.log("clicked");
-     }}
-   >
-     Send Batch Transaction
-   </button>
  </div>

+ {/* Batch transaction section */}
+ <div className="bg-gray-50 dark:bg-gray-900 p-6 rounded-lg w-full">
+   <h2 className="text-xl font-semibold mb-4">Send Batch Transaction</h2>

+   {/* Send batch transaction button */}
+   <button
+     className={`w-full rounded-lg border border-solid px-6 py-3 font-medium transition-colors mb-4 ${
+       !isConnected || isPending
+         ? "bg-gray-100 text-gray-400 border-gray-300 cursor-not-allowed"
+         : "bg-green-50 hover:bg-green-100 text-green-700 border-green-300 cursor-pointer"
+     }`}
+     onClick={handleSendTransaction}
+     disabled={!isConnected || isPending}
+   >
+     {isPending ? "Sending Transaction..." : "Send Batch Transaction"}
+   </button>

+   {/* Transaction state */}
+   {isPending && (
+     <div className="flex items-center gap-2 text-blue-600 mb-4">
+       <div className="w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin"></div>
+       <span>Transaction pending...</span>
+     </div>
+   )}
+
+   {isSuccess && data && (
+     <div className="bg-green-50 border border-green-200 rounded-lg p-4 mb-4">
+       <div className="flex items-center gap-2 text-green-700 mb-2">
+         <div className="w-2 h-2 bg-green-500 rounded-full"></div>
+         <span className="font-medium">
+           Transaction submitted successfully!
+         </span>
+       </div>
+       <div className="text-sm text-gray-600">
+         <p>
+           Data ID:{" "}
+           <code className="bg-gray-100 px-1 rounded">{data.id}</code>
+         </p>
+       </div>
+     </div>
+   )}
+
+   {error && (
+     <div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
+       <div className="text-red-700 font-medium">Transaction Error</div>
+       <div className="text-sm text-red-600 mt-1">{error.message}</div>
+     </div>
+   )}
+ </div>
// ...

```

In the development server, test that the button works to send batch transactions from your wallet. Ensure you are connected to the Sepolia network in MetaMask. MetaMask prompts you to upgrade your EOA to a smart account in order to send a batch transaction:

![SDK send batch transactions button](/assets/images/sdk-7702-send-batch-txns-5d9c6e0acb37251a6c24ff4d4af38ab6.png)

![SDK upgrade EOA to smart account](/assets/images/sdk-7702-upgrade-eoa-6396f50f24fc374f6b6dcf92bd8b0468.png)

### 5\. Get the status of batch transactions[​](#5-get-the-status-of-batch-transactions "Direct link to 5. Get the status of batch transactions")

In `src/app/page.tsx`, use the [getCallsStatus](https://wagmi.sh/core/api/actions/getCallsStatus) action from Wagmi to get the status of sent batch transactions:

page.tsx

```
"use client";

import Image from "next/image";
import { metaMask } from "wagmi/connectors";
import { useAccount, useConnect, useDisconnect, useSendCalls } from "wagmi";
import { useState } from "react";
import { parseEther } from "viem";
+ import { getCallsStatus } from "@wagmi/core";
+ import { wagmiConfig as config } from "@/providers/AppProvider";

export default function Home() {
  const { connect } = useConnect();
  const { disconnect } = useDisconnect();
  const { address, isConnected } = useAccount();
  const { sendCalls, error, isPending, isSuccess, data, reset } = useSendCalls();
  const [transactionHash, setTransactionHash] = useState<string | null>(null);
  const [statusError, setStatusError] = useState<string | null>(null);
+ const [statusLoading, setStatusLoading] = useState(false);

+ const handleGetCallsStatus = async () => {
+   if (!data?.id) return;
+
+   setStatusLoading(true);
+   setStatusError(null);
+
+   try {
+     const status = await getCallsStatus(config, { id: data.id });
+     console.log("Transaction status:", status);
+
+     if (
+       status.status === "success" &&
+       status.receipts?.[0]?.transactionHash
+     ) {
+       setTransactionHash(status.receipts[0].transactionHash);
+     } else if (status.status === "failure") {
+       setStatusError("Transaction failed");
+     }
+   } catch (err) {
+     console.error("Error getting call status:", err);
+     setStatusError(
+       err instanceof Error ? err.message : "Failed to get transaction status"
+     );
+   } finally {
+     setStatusLoading(false);
+   }
+ };
  // ...

```

Then, create a button to check the batch transaction status:

page.tsx

```
// ...

  {/* Transaction state */}
  // ...

+ {/* Check transaction status button */}
+ {data && (
+   <button
+     className={`w-full rounded-lg border border-solid px-6 py-3 font-medium transition-colors ${
+       statusLoading
+         ? "bg-gray-100 text-gray-400 border-gray-300 cursor-not-allowed"
+         : "bg-purple-50 hover:bg-purple-100 text-purple-700 border-purple-300 cursor-pointer"
+     }`}
+     onClick={handleGetCallsStatus}
+     disabled={statusLoading || !data.id}
+   >
+     {statusLoading
+       ? "Checking Status..."
+       : "Check Transaction Status"}
+   </button>
+ )}

+ {/* Status error */}
+ {statusError && (
+   <div className="bg-red-50 border border-red-200 rounded-lg p-4 mt-4">
+     <div className="text-red-700 font-medium">Status Check Error</div>
+     <div className="text-sm text-red-600 mt-1">{statusError}</div>
+   </div>
+ )}

+ {/* Transaction hash */}
+ {transactionHash && (
+   <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mt-4">
+     <div className="text-blue-700 font-medium mb-2">
+       Transaction Confirmed!
+     </div>
+     <div className="text-sm">
+       <a
+         href={`https://sepolia.etherscan.io/tx/${transactionHash}`}
+         target="_blank"
+         rel="noopener noreferrer"
+         className="text-blue-600 hover:text-blue-800 hover:underline break-all"
+       >
+         View on Etherscan: {transactionHash}
+       </a>
+     </div>
+   </div>
+ )}
// ...

```

In the development server, when you send a successful batch transaction, the success state and the **Check Transaction Status** button appear. When you select the **Check Transaction Status** button, if the transaction is confirmed, a link to Etherscan with your transaction hash appears:

![SDK successful 7702 transaction](/assets/images/sdk-7702-successful-txn-34393ae8b24d7bfd0c1cea8ac004e451.png)

![SDK check transaction status](/assets/images/sdk-7702-check-txn-status-f3de585bc796ba9d962e0965eef920f1.png)

You have successfully used the SDK to upgrade a MetaMask EOA to a MetaMask smart account, send an atomic batch transaction, and check its status!

## Resources[​](#resources "Direct link to Resources")

- View the `feat-mm-sdk-final` branch of the [MetaMask/7702-livestream-demo](https://github.com/MetaMask/7702-livestream-demo/tree/feat-mm-sdk-final) repository for the completed implementation of this tutorial.
- Watch the [live coding session](https://www.youtube.com/watch?v=crMqCb8RPEE) on YouTube, in which the MetaMask DevRel team walks through setting up EIP-7702 functionality from the initial template.
- See the [Smart Accounts Kit EIP-7702 quickstart](/smart-accounts-kit/get-started/smart-account-quickstart/eip7702/) to learn how to use the Smart Accounts Kit to upgrade an EOA to a MetaMask smart account.

[Share](https://www.facebook.com/sharer/sharer.php?https://metamask.io/tutorials/upgrade-eoa-to-smart-account)[Tweet](http://twitter.com/share?text=Checkout Upgrade an EOA to a smart account published by @MetaMask&url=https://metamask.io/tutorials/upgrade-eoa-to-smart-account)Copy

On this page
- [Prerequisites](#prerequisites)
- [Steps](#steps)
  - [1\. Set up the project](#1-set-up-the-project)
  - [2\. Configure the MetaMask connector](#2-configure-the-metamask-connector)
  - [3\. Create a connect and disconnect button](#3-create-a-connect-and-disconnect-button)
  - [4\. Handle and send batch transactions](#4-handle-and-send-batch-transactions)
  - [5\. Get the status of batch transactions](#5-get-the-status-of-batch-transactions)
- [Resources](#resources)
