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

# Validate Signatures

> Verify EIP-191, EIP-712, ERC-1271, and EIP-6492 signatures produced by Dynamic wallets.

There are three ways to validate EVM signatures: using Viem's verification helpers, manually recovering the signer, and EIP-6492 (required if the signer is a counterfactual smart account that hasn't been deployed yet).

For producing signatures, see [Sign a Message](/javascript/reference/wallets/sign-message) and [Sign Typed Data (EIP-712)](/javascript/reference/evm/sign-typed-data).

## Validate with Viem

Viem provides `verifyMessage` (EIP-191) and `verifyTypedData` (EIP-712). When you pass a `PublicClient`, Viem automatically validates contract wallet signatures via [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271).

You can build the `PublicClient` directly from the active network with [`createPublicClientFromNetworkData`](/javascript/reference/evm/getting-viem-public-client) so you don't have to hardcode RPC URLs.

### EIP-191 (message / personal\_sign)

```typescript theme={"system"}
import { verifyMessage } from 'viem';
import { getActiveNetworkData } from '@dynamic-labs-sdk/client';
import { createPublicClientFromNetworkData } from '@dynamic-labs-sdk/evm/viem';

type VerifyInput = {
  walletAccount: WalletAccount;
  address: `0x${string}`;
  message: string;
  signature: `0x${string}`;
};

export async function verifyEip191({ walletAccount, address, message, signature }: VerifyInput) {
  const { networkData } = await getActiveNetworkData({ walletAccount });
  const publicClient = createPublicClientFromNetworkData({ networkData });

  // Returns true for EOAs and ERC-1271 contracts (if the contract implements isValidSignature).
  return verifyMessage(publicClient, { address, message, signature });
}
```

### EIP-712 (typed data)

```typescript theme={"system"}
import { verifyTypedData } from 'viem';
import { getActiveNetworkData } from '@dynamic-labs-sdk/client';
import { createPublicClientFromNetworkData } from '@dynamic-labs-sdk/evm/viem';

const domain = {
  name: 'Example DApp',
  version: '1',
  chainId: 1,
  verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
} as const;

const types = {
  Mail: [
    { name: 'from', type: 'address' },
    { name: 'to', type: 'address' },
    { name: 'contents', type: 'string' },
  ],
} as const;

export async function verifyEip712({ walletAccount, address, message, signature }) {
  const { networkData } = await getActiveNetworkData({ walletAccount });
  const publicClient = createPublicClientFromNetworkData({ networkData });

  return verifyTypedData(publicClient, {
    address,
    domain,
    types,
    primaryType: 'Mail',
    message,
    signature,
  });
}
```

## Manual validation

If you only need to compare against a known signer (no contract wallets), you can recover the address yourself.

### EIP-191

```typescript theme={"system"}
import { recoverMessageAddress } from 'viem';

export async function isValidMessageSignature({
  expectedAddress,
  message,
  signature,
}: {
  expectedAddress: `0x${string}`;
  message: string;
  signature: `0x${string}`;
}) {
  const recovered = await recoverMessageAddress({ message, signature });
  return recovered.toLowerCase() === expectedAddress.toLowerCase();
}
```

### EIP-712

```typescript theme={"system"}
import { recoverTypedDataAddress } from 'viem';

export async function isValidTypedDataSignature({
  expectedAddress,
  domain,
  types,
  primaryType,
  message,
  signature,
}) {
  const recovered = await recoverTypedDataAddress({
    domain,
    types,
    primaryType,
    message,
    signature,
  });
  return recovered.toLowerCase() === expectedAddress.toLowerCase();
}
```

## EIP-6492 (counterfactual smart accounts)

Smart accounts (e.g. Kernel via ZeroDev) may sign before the wrapper contract is deployed. [EIP-6492](https://eips.ethereum.org/EIPS/eip-6492) wraps the signature with a deploy-and-verify payload so verifiers can check it before the account exists on chain.

```typescript theme={"system"}
import { verifyEIP6492Signature } from '@zerodev/sdk';
import { hashMessage } from 'viem';
import { getActiveNetworkData } from '@dynamic-labs-sdk/client';
import { createPublicClientFromNetworkData } from '@dynamic-labs-sdk/evm/viem';

export async function verifyKernelSignature({ walletAccount, signerAddress, message, signature }) {
  const { networkData } = await getActiveNetworkData({ walletAccount });
  const publicClient = createPublicClientFromNetworkData({ networkData });

  return verifyEIP6492Signature({
    signer: signerAddress,
    hash: hashMessage(message),
    signature,
    client: publicClient,
  });
}
```

Use this path if you sign with a Kernel client from [`@dynamic-labs-sdk/zerodev`](/javascript/reference/zerodev/adding-zerodev-extension) — `verifyMessage` alone won't recognize the wrapped payload until the account is deployed.

## Server-side validation

The verification helpers above are pure functions — you can run them server-side as long as the server has an RPC URL for the relevant chain. Build the `PublicClient` directly with viem's `createPublicClient` rather than `createPublicClientFromNetworkData` if the server doesn't have a Dynamic client instance.

## Related functions

* [Sign a Message](/javascript/reference/wallets/sign-message)
* [Sign Typed Data (EIP-712)](/javascript/reference/evm/sign-typed-data)
* [Getting a Viem PublicClient](/javascript/reference/evm/getting-viem-public-client)
* [Adding ZeroDev Extension](/javascript/reference/zerodev/adding-zerodev-extension)
