# Sign typed data

> Sign EIP-712 typed data with your Smart Wallet

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

This guide covers how to sign EIP-712 typed data with your Smart Wallet. Typed data signing provides a more structured and secure way to sign complex data compared to plain text messages.

## Prerequisites

* API key from your [dashboard](https://dashboard.alchemy.com/apps)
* A Smart Wallet with an associated owner

<Note>
  When using [EIP-7702](/docs/wallets/transactions/using-eip-7702), your account must be delegated before signatures will be valid. Send at least one transaction to delegate your account first.
</Note>

## What is typed data signing?

EIP-712 typed data signing allows users to:

* **Sign structured data** with clear type definitions
* **Improve user experience** with readable signature requests
* **Enhance security** through type safety and domain separation

Typed data follows the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) standard, which provides a way to encode structured data for signing.

## Implementation

<Tabs>
  <Tab title="JavaScript" language="typescript">
    <Info>`@alchemy/wallet-apis` (v5.x.x) is currently in beta but is the recommended replacement for `@account-kit/wallet-client` (v4.x.x). If you run into any issues, please [reach out](mailto:support@alchemy.com).</Info>

See the [`signTypedData` SDK reference](/docs/wallets/reference/wallet-apis/functions/signTypedData) for full parameter descriptions.

<CodeBlocks>

```ts title="signTypedData.ts"
import { createPublicClient, http } from "viem";
import { arbitrumSepolia } from "viem/chains";
import { client } from "./client";

const signerAddress = client.account.address;

// Sign typed data
const typedData = {
  domain: {
    name: "MyDApp",
    version: "1",
    chainId: 1,
    verifyingContract: "0x...",
  },
  types: {
    Auth: [
      { name: "user", type: "address" },
      { name: "nonce", type: "uint256" },
      { name: "timestamp", type: "uint256" },
    ],
  },
  primaryType: "Auth",
  message: {
    user: signerAddress,
    nonce: 12345,
    timestamp: Date.now(),
  },
} as const;

const signature = await client.signTypedData({
  ...typedData,
});

console.log("Signature:", signature);

// Verify the signature
const publicClient = createPublicClient({
  chain: arbitrumSepolia,
  transport: http(),
});

const isValid = await publicClient.verifyTypedData({
  address: client.account.address,
  ...typedData,
  signature,
});

console.log("Valid:", isValid); // true
```

```ts title="client.ts"
import type { Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { arbitrumSepolia } from "viem/chains";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";

export const client = createSmartWalletClient({
  transport: alchemyWalletTransport({
    apiKey: "YOUR_API_KEY",
  }),
  chain: arbitrumSepolia,
  signer: privateKeyToAccount("0xYOUR_PRIVATE_KEY" as const),
});
```

</CodeBlocks>

  </Tab>
</Tabs>

<Accordion title="Using @account-kit/wallet-client (v4.x.x)?">
The examples on this page use `@alchemy/wallet-apis` (v5.x.x). If you're using `@account-kit/wallet-client` (v4.x.x), the client setup looks like this:

```ts title="client.ts (v4.x.x)"
import { LocalAccountSigner } from "@aa-sdk/core";
import { createSmartWalletClient } from "@account-kit/wallet-client";
import { alchemy, sepolia } from "@account-kit/infra";

const signer = LocalAccountSigner.privateKeyToAccountSigner("0xYOUR_PRIVATE_KEY" as const);

export const client = createSmartWalletClient({
  transport: alchemy({ apiKey: "YOUR_API_KEY" }),
  chain: sepolia,
  signer,
  account: signer.address, // can also be passed per action as `from` or `account`
  // Optional: sponsor gas for your users (see "Sponsor gas" guide)
  policyId: "YOUR_POLICY_ID", 
});
```

Key v4.x.x differences:
* **Account address** must be specified on the client or per action (`from` or `account`). In v5.x.x, the client automatically uses the owner's address as the account address via [EIP-7702](/docs/wallets/transactions/using-eip-7702).
* **Chain imports** come directly from `@account-kit/infra` instead of `viem/chains`.
* **Numeric values** use hex strings: `value: "0x0"` instead of `value: BigInt(0)`.
* In v4.x.x, the **paymaster capability** on `prepareCalls` or `sendCalls` is called `paymasterService` instead of `paymaster`, or you can set the `policyId` directly on the client.
* **Owners** use `LocalAccountSigner` / `WalletClientSigner` from `@aa-sdk/core`. In v5.x.x, a viem `LocalAccount` or `WalletClient` is used directly.

See the [full migration guide](/docs/wallets/resources/migration-v5) for a complete cheat sheet.
</Accordion>


## Typed data structure

EIP-712 typed data consists of four main components:

### Domain

The domain provides context and prevents signature reuse across different applications:

```ts
const domain = {
  name: "Example DApp",
  version: "1",
  chainId: 1,
  verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
};
```

### Types

Define the structure of your data:

```ts
const types = {
  Person: [
    { name: "name", type: "string" },
    { name: "wallet", type: "address" },
  ],
  Mail: [
    { name: "from", type: "Person" },
    { name: "to", type: "Person" },
    { name: "contents", type: "string" },
  ],
};
```

### Primary type

Specify which type is the main type being signed:

```ts
const primaryType = "Mail";
```

### Message

The actual data to sign:

```ts
const message = {
  from: {
    name: "Alice",
    wallet: "0xAaAaAaAaAaAaAaAaAaAAAAAAAAaaaAaAaAaaAaAa",
  },
  to: {
    name: "Bob",
    wallet: "0xBbBbBbBbBbBbBbBbBbBBBBBBBBbbBbBbBbbBbBb",
  },
  contents: "Hello, Bob!",
};
```

## Next steps

Build more:

* [Sign messages](/docs/wallets/transactions/signing/sign-messages)
* [Send transactions](/docs/wallets/transactions/send-transactions)

Troubleshooting:

* [Common errors](/docs/wallets/resources/faqs#common-errors)