> ## Documentation Index
> Fetch the complete documentation index at: https://docs.story.foundation/llms.txt
> Use this file to discover all available pages before exploring further.

# Email Login & Sponsored Transactions with Privy

> Learn how to implement email logins and sponsored transactions with Privy & Pimlico.

<Card title="Completed Code" href="https://github.com/jacob-tucker/story-privy-tutorial" icon="thumbs-up" color="#51af51">
  View the completed code for this tutorial.
</Card>

You are reading this tutorial because you probably want to do one or both of these things:

1. Enable users who don't have a wallet to login with email to your app ("Embedded Wallets")
2. Sponsor transactions for your users so they don't have to pay gas ("Smart Wallets")

Here is how Privy describes both of these things:

> Embedded wallets are self-custodial wallets provisioned by Privy itself for a wallet experience that is directly embedded in your application. Embedded wallets do not require a separate wallet client, like a browser extension or a mobile app, and can be accessed directly from your product. These are primarily designed for users of your app who may not already have an external wallet, or don't want to connect their external wallet.
>
> Smart wallets are programmable, onchain accounts that incorporate the features of account abstraction. With just a few lines of code, you can create smart wallets for your users to sponsor gas payments, send batched transactions, and more.

We will be implementing both using [Privy](https://www.privy.io/) + [Pimlico](https://www.pimlico.io/).

### ⚠️ Prerequisites

There are a few steps you have to complete before you can start the tutorial.

1. Create a new project on [Privy's Dashboard](https://dashboard.privy.io)
2. Copy your **"App ID"** under **"App settings > API keys"**. In your local project, make a `.env` file and add your App ID:

```Text .env theme={null}
NEXT_PUBLIC_PRIVY_APP_ID=
```

3. On your project dashboard, enable Smart Wallets under "**Wallet Configuration > Smart wallets**" and select "**Kernel (ZeroDev)**" as shown below:

<Frame>
  <img src="https://mintcdn.com/story/KwuxPVrS3UIa7CU1/images/tutorials/privy-tutorial-1.png?fit=max&auto=format&n=KwuxPVrS3UIa7CU1&q=85&s=d68057863ec71a8731d3ec777fe5c432" alt="Privy Dashboard" width="3024" height="1530" data-path="images/tutorials/privy-tutorial-1.png" />
</Frame>

4. Once you enable Smart wallets, right underneath make sure to put a "Custom chain" with the following values:
   1. Name: `Story Aeneid Testnet`
   2. ID number: `1315`
   3. RPC URL: `https://aeneid.storyrpc.io`
   4. For the Bundler URL and Paymaster URL, go to [Pimlico's Dashboard](https://dashboard.pimlico.io) and create a new app. Then click on "API Keys", create a new API Key, click "RPC URLs" as shown below, and then select "Story Aeneid Testnet" as the network:

<Warning>
  This is for testing. In a real scenario, you would have to set up proper sponsorship policies and billing info on Pimlico to automatically sponsor the transactions on behalf of your app. We don't have to do this on testnet.
</Warning>

<Frame>
  <img src="https://mintcdn.com/story/KwuxPVrS3UIa7CU1/images/tutorials/pimlico-dashboard.png?fit=max&auto=format&n=KwuxPVrS3UIa7CU1&q=85&s=ae0e505de6591cb2b0c8e05167a7b8a6" alt="Pimlico Dashboard" width="3024" height="1530" data-path="images/tutorials/pimlico-dashboard.png" />
</Frame>

5. Install the dependencies:

```Text Terminal theme={null}
npm install @story-protocol/core-sdk permissionless viem @privy-io/react-auth
```

## 1. Set up Embedded Wallets

<CardGroup cols={1}>
  <Card title="Official Privy Tutoral" href="https://docs.privy.io/wallets/using-wallets/evm-smart-wallets/setup/configuring-dashboard" icon="house">
    Follow Privy's official tutorial for setup instead of reading this step.
  </Card>
</CardGroup>

<Note>
  This part of the Privy documentation
  [here](https://docs.privy.io/basics/react/advanced/automatic-wallet-creation#automatic-wallet-creation)
  describes setting up Embedded Wallets automatically, which is a fancy way of
  saying it supports email login, such that when a user logs in with email it
  creates a wallet for them. In the below example, we simply create an embedded
  wallet for every user, but you may want more customization by reading their
  tutorial.
</Note>

You must wrap any component that will be using embedded/smart wallets with the `PrivyProvider` and `SmartWalletsProvider`. In a `providers.tsx` (or whatever you want to call it) file, add the following code:

```jsx providers.tsx theme={null}
"use client";

import { PrivyProvider } from "@privy-io/react-auth";
import { SmartWalletsProvider } from "@privy-io/react-auth/smart-wallets";
import { aeneid } from "@story-protocol/core-sdk";

export default function Providers({ children }: { children: React.ReactNode }) {
  return (
    <PrivyProvider
      appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID as string}
      config={{
        // Customize Privy's appearance in your app
        appearance: {
          theme: "light",
          accentColor: "#676FFF",
          logo: "/story-logo.jpg",
        },
        // Create embedded wallets for users who don't have a wallet
        // when they sign in with email
        embeddedWallets: {
          createOnLogin: "all-users",
        },
        defaultChain: aeneid,
        supportedChains: [aeneid],
      }}
    >
      <SmartWalletsProvider>{children}</SmartWalletsProvider>
    </PrivyProvider>
  );
}
```

Then you can simply add it to your`layout.tsx` like so:

```jsx layout.tsx theme={null}
import Providers from "@/providers/providers";

/* other code here... */

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode,
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
```

## 2. Login & Logout

You can add email login to your app like so:

```jsx page.tsx theme={null}
import { usePrivy } from "@privy-io/react-auth";

export default function Home() {
  const { login, logout, user } = usePrivy();

  useEffect(() => {
    if (user) {
      const smartWallet = user.linkedAccounts.find(
        (account) => account.type === "smart_wallet"
      );
      // Logs the smart wallet's address
      console.log(smartWallet.address);
      // Logs the smart wallet type (e.g. 'safe', 'kernel', 'light_account', 'biconomy', 'thirdweb', 'coinbase_smart_wallet')
      console.log(smartWallet.type);
    }
  }, [user]);

  return (
    <div>
      <button onClick={user ? logout : login}>
        {user ? "Logout" : "Login with Privy"}
      </button>
    </div>
  );
}
```

## 3. Sign a Message with Privy

<CardGroup cols={1}>
  <Card title="Official Privy Tutoral" href="https://docs.privy.io/wallets/using-wallets/evm-smart-wallets/usage#sign-a-message" icon="house">
    Follow Privy's official tutorial for signing messages instead of reading
    this step.
  </Card>
</CardGroup>

We can use the generated smart wallet to sign messages:

```jsx page.tsx theme={null}
import { useSmartWallets } from "@privy-io/react-auth/smart-wallets";

export default function Home() {
  const { client: smartWalletClient } = useSmartWallets();

  /* previous code here */

  async function sign() {
    const uiOptions = {
      title: "Example Sign",
      description: "This is an example for a user to sign.",
      buttonText: "Sign",
    };
    const request = {
      message: "IP is cool",
    };
    const signature = await smartWalletClient?.signMessage(request, {
      uiOptions,
    });
  }

  return (
    <div>
      {/* previous code here */}
      <button onClick={sign}>Sign</button>
    </div>
  );
}
```

## 4. Send an Arbitrary Transaction

<CardGroup cols={1}>
  <Card title="Official Privy Tutoral" href="https://docs.privy.io/wallets/using-wallets/evm-smart-wallets/usage#send-a-transaction" icon="house">
    Follow Privy's official tutorial for sending transactions instead of reading
    this step.
  </Card>
</CardGroup>

We can also use the generated smart wallet to sponsor transactions for our users:

<CodeGroup>
  ```jsx page.tsx theme={null}
  import { useSmartWallets } from "@privy-io/react-auth/smart-wallets";
  import { encodeFunctionData } from "viem";
  import { defaultNftContractAbi } from "./defaultNftContractAbi";

  export default function Home() {
    const { client: smartWalletClient } = useSmartWallets();

    /* previous code here */

    async function mintNFT() {
      const uiOptions = {
        title: "Mint NFT",
        description: "This is an example transaction that mints an NFT.",
        buttonText: "Mint",
      };

      const transactionRequest = {
        to: "0x937bef10ba6fb941ed84b8d249abc76031429a9a", // example nft contract
        data: encodeFunctionData({
          abi: defaultNftContractAbi, // abi from another file
          functionName: "mintNFT",
          args: ["0x6B86B39F03558A8a4E9252d73F2bDeBfBedf5b68", "test-uri"],
        }),
      } as const;

      const txHash = await smartWalletClient?.sendTransaction(
        transactionRequest,
        { uiOptions }
      );
      console.log(`View Tx: https://aeneid.storyscan.io/tx/${txHash}`);
    }

    return (
      <div>
        {/* previous code here */}
        <button onClick={mintNFT}>Mint NFT</button>
      </div>
    )
  }
  ```

  ```Text defaultNftContractAbi.ts theme={null}
  export const defaultNftContractAbi = [
    {
      inputs: [],
      stateMutability: "nonpayable",
      type: "constructor",
    },
    {
      inputs: [
        {
          internalType: "address",
          name: "recipient",
          type: "address",
        },
        {
          internalType: "string",
          name: "tokenURI",
          type: "string",
        },
      ],
      name: "mintNFT",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256",
        },
      ],
      stateMutability: "nonpayable",
      type: "function",
    },
    {
      inputs: [
        {
          internalType: "uint256",
          name: "tokenId",
          type: "uint256",
        },
      ],
      name: "tokenURI",
      outputs: [
        {
          internalType: "string",
          name: "",
          type: "string",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
    {
      inputs: [
        {
          internalType: "uint256",
          name: "tokenId",
          type: "uint256",
        },
      ],
      name: "ownerOf",
      outputs: [
        {
          internalType: "address",
          name: "",
          type: "address",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
    {
      inputs: [],
      name: "symbol",
      outputs: [
        {
          internalType: "string",
          name: "",
          type: "string",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
    {
      inputs: [],
      name: "name",
      outputs: [
        {
          internalType: "string",
          name: "",
          type: "string",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
    {
      inputs: [],
      name: "totalSupply",
      outputs: [
        {
          internalType: "uint256",
          name: "",
          type: "uint256",
        },
      ],
      stateMutability: "view",
      type: "function",
    },
  ];

  ```
</CodeGroup>

## 5. Send a Transaction from Story SDK

We can also use the generated smart wallet to send transactions from the [🛠️ TypeScript SDK](/developers/typescript-sdk). Some of the functions have an option to return the `encodedTxData`, which we can use to pass into Privy's smart wallet. You can see which functions support this in the [SDK Reference](/sdk-reference).

```jsx page.tsx theme={null}
import { useSmartWallets } from "@privy-io/react-auth/smart-wallets";
import {
  EncodedTxData,
  StoryClient,
  StoryConfig,
} from "@story-protocol/core-sdk";
import { http } from "viem";

export default function Home() {
  const { client: smartWalletClient } = useSmartWallets();

  /* previous code here */

  async function setupStoryClient() {
    const config: StoryConfig = {
      account: smartWalletClient!.account,
      transport: http("https://aeneid.storyrpc.io"),
      chainId: "aeneid",
    };
    const client = StoryClient.newClient(config);
    return client;
  }

  async function registerIp() {
    const storyClient = await setupStoryClient();

    const response = await storyClient.ipAsset.registerIpAsset({
      nft: {
        type: "mint",
        spgNftContract: "0xc32A8a0FF3beDDDa58393d022aF433e78739FAbc",
      },
    });

    const uiOptions = {
      title: "Register IP",
      description: "This is an example transaction that registers an IP.",
      buttonText: "Register",
    };

    const txHash = await smartWalletClient?.sendTransaction(
      response.encodedTxData as EncodedTxData,
      { uiOptions }
    );
    console.log(`View Tx: https://aeneid.storyscan.io/tx/${txHash}`);
  }

  return (
    <div>
      {/* previous code here */}
      <button onClick={registerIp}>Register IP</button>
    </div>
  );
}
```

## 6. Done!

<CardGroup cols={2}>
  <Card title="Completed Code" href="https://github.com/jacob-tucker/story-privy-tutorial" icon="thumbs-up" iconColor="#51af51">
    View the completed code for this tutorial.
  </Card>

  <Card title="Learn More" href="/developers/tutorials" icon="book-open">
    Explore more tutorials in our documentation
  </Card>
</CardGroup>
