Skip to main content

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.

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 and Sign Typed Data (EIP-712).

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. You can build the PublicClient directly from the active network with createPublicClientFromNetworkData so you don’t have to hardcode RPC URLs.

EIP-191 (message / personal_sign)

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)

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

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

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 wraps the signature with a deploy-and-verify payload so verifiers can check it before the account exists on chain.
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/zerodevverifyMessage 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.