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/zerodev — 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.