Cryptographic signing of messages and typed data with EIP-712 support for secure authentication. This module provides comprehensive message signing and verification functionality for secure user authentication and data integrity.
Hook to sign arbitrary messages with the connected wallet for authentication and verification.
/**
* Hook to sign a message
* @param parameters - Message signing configuration with mutation callbacks
* @returns Sign message mutation with signature state
*/
function useSignMessage<config = Config, context = unknown>(
parameters?: UseSignMessageParameters<config, context>
): UseSignMessageReturnType<config, context>;
interface UseSignMessageParameters<config = Config, context = unknown> {
config?: Config | config;
mutation?: {
onMutate?: (variables: SignMessageVariables) => Promise<context> | context;
onError?: (error: SignMessageErrorType, variables: SignMessageVariables, context?: context) => Promise<void> | void;
onSuccess?: (data: SignMessageData, variables: SignMessageVariables, context?: context) => Promise<void> | void;
onSettled?: (data?: SignMessageData, error?: SignMessageErrorType, variables?: SignMessageVariables, context?: context) => Promise<void> | void;
};
}
interface UseSignMessageReturnType<config = Config, context = unknown> {
/** Sign a message */
signMessage: (variables: SignMessageVariables, options?: SignMessageMutateOptions) => void;
/** Async version of signMessage */
signMessageAsync: (variables: SignMessageVariables, options?: SignMessageMutateAsyncOptions) => Promise<SignMessageData>;
/** Signature data */
data?: SignMessageData;
/** Signing error */
error: SignMessageErrorType | null;
/** Signing status flags */
isError: boolean;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
/** Reset signing state */
reset: () => void;
/** Current status */
status: 'error' | 'idle' | 'pending' | 'success';
/** Additional variables */
variables?: SignMessageVariables;
}
interface SignMessageVariables {
/** Message to sign */
message: string | Uint8Array;
/** Account to sign with */
account?: Address;
}
type SignMessageData = Hex; // The signatureUsage Example:
import { useSignMessage, useAccount } from "wagmi";
function MessageSigner() {
const { address } = useAccount();
const { signMessage, data: signature, isPending } = useSignMessage();
const handleSign = () => {
signMessage({
message: 'Hello, this is a signed message!',
});
};
return (
<div>
{address ? (
<div>
<button onClick={handleSign} disabled={isPending}>
{isPending ? 'Signing...' : 'Sign Message'}
</button>
{signature && (
<div>
<h4>Signature:</h4>
<p style={{ wordBreak: 'break-all' }}>{signature}</p>
</div>
)}
</div>
) : (
<p>Please connect your wallet</p>
)}
</div>
);
}Hook to sign structured data according to EIP-712 standard for secure typed data signing.
/**
* Hook to sign typed data (EIP-712)
* @param parameters - Typed data signing configuration
* @returns Sign typed data mutation with signature state
*/
function useSignTypedData<config = Config, context = unknown>(
parameters?: UseSignTypedDataParameters<config, context>
): UseSignTypedDataReturnType<config, context>;
interface UseSignTypedDataParameters<config = Config, context = unknown> {
config?: Config | config;
mutation?: {
onMutate?: (variables: SignTypedDataVariables) => Promise<context> | context;
onError?: (error: SignTypedDataErrorType, variables: SignTypedDataVariables, context?: context) => Promise<void> | void;
onSuccess?: (data: SignTypedDataData, variables: SignTypedDataVariables, context?: context) => Promise<void> | void;
onSettled?: (data?: SignTypedDataData, error?: SignTypedDataErrorType, variables?: SignTypedDataVariables, context?: context) => Promise<void> | void;
};
}
interface UseSignTypedDataReturnType<config = Config, context = unknown> {
/** Sign typed data */
signTypedData: (variables: SignTypedDataVariables, options?: SignTypedDataMutateOptions) => void;
/** Async version of signTypedData */
signTypedDataAsync: (variables: SignTypedDataVariables, options?: SignTypedDataMutateAsyncOptions) => Promise<SignTypedDataData>;
/** Signature data */
data?: SignTypedDataData;
/** Signing error */
error: SignTypedDataErrorType | null;
/** Signing status flags */
isError: boolean;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
/** Reset signing state */
reset: () => void;
/** Current status */
status: 'error' | 'idle' | 'pending' | 'success';
/** Additional variables */
variables?: SignTypedDataVariables;
}
interface SignTypedDataVariables {
/** EIP-712 domain */
domain: TypedDataDomain;
/** Type definitions */
types: Record<string, TypedDataField[]>;
/** Primary type name */
primaryType: string;
/** Message data */
message: Record<string, unknown>;
/** Account to sign with */
account?: Address;
}
type SignTypedDataData = Hex; // The signature
interface TypedDataDomain {
name?: string;
version?: string;
chainId?: number;
verifyingContract?: Address;
salt?: Hex;
}
interface TypedDataField {
name: string;
type: string;
}Usage Example:
import { useSignTypedData } from "wagmi";
function TypedDataSigner() {
const { signTypedData, data: signature, isPending } = useSignTypedData();
const handleSignTypedData = () => {
signTypedData({
domain: {
name: 'My App',
version: '1.0.0',
chainId: 1,
verifyingContract: '0x742d35Cc6634C0532925a3b8D',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person' },
{ name: 'contents', type: 'string' },
],
},
primaryType: 'Mail',
message: {
from: {
name: 'Alice',
wallet: '0x742d35Cc6634C0532925a3b8D',
},
to: {
name: 'Bob',
wallet: '0x8ba1f109551bD432803012645Hac136c',
},
contents: 'Hello Bob!',
},
});
};
return (
<div>
<button onClick={handleSignTypedData} disabled={isPending}>
{isPending ? 'Signing...' : 'Sign Typed Data'}
</button>
{signature && (
<div>
<h4>EIP-712 Signature:</h4>
<p style={{ wordBreak: 'break-all' }}>{signature}</p>
</div>
)}
</div>
);
}Hook to verify a signed message against an address and signature.
/**
* Hook to verify a signed message
* @param parameters - Message verification parameters
* @returns Verification result indicating if signature is valid
*/
function useVerifyMessage<config = Config, selectData = UseVerifyMessageReturnType>(
parameters: UseVerifyMessageParameters<config, selectData>
): UseVerifyMessageReturnType<selectData>;
interface UseVerifyMessageParameters<config = Config, selectData = UseVerifyMessageReturnType> {
/** Original message that was signed */
message: string | Uint8Array;
/** Signature to verify */
signature: Hex;
/** Address that allegedly signed the message */
address: Address;
/** Block number to verify at */
blockNumber?: bigint;
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
/** Chain to use */
chainId?: config['chains'][number]['id'];
config?: Config | config;
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
select?: (data: UseVerifyMessageReturnType) => selectData;
};
}
type UseVerifyMessageReturnType = boolean;Hook to verify a signed typed data (EIP-712) signature.
/**
* Hook to verify signed typed data
* @param parameters - Typed data verification parameters
* @returns Verification result indicating if signature is valid
*/
function useVerifyTypedData<config = Config, selectData = UseVerifyTypedDataReturnType>(
parameters: UseVerifyTypedDataParameters<config, selectData>
): UseVerifyTypedDataReturnType<selectData>;
interface UseVerifyTypedDataParameters<config = Config, selectData = UseVerifyTypedDataReturnType> {
/** EIP-712 domain */
domain: TypedDataDomain;
/** Type definitions */
types: Record<string, TypedDataField[]>;
/** Primary type name */
primaryType: string;
/** Message data */
message: Record<string, unknown>;
/** Signature to verify */
signature: Hex;
/** Address that allegedly signed */
address: Address;
/** Block number to verify at */
blockNumber?: bigint;
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
/** Chain to use */
chainId?: config['chains'][number]['id'];
config?: Config | config;
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
select?: (data: UseVerifyTypedDataReturnType) => selectData;
};
}
type UseVerifyTypedDataReturnType = boolean;Usage Example:
import { useVerifyMessage, useVerifyTypedData } from "wagmi";
function SignatureVerifier() {
// Verify simple message signature
const { data: isMessageValid } = useVerifyMessage({
message: 'Hello, this is a signed message!',
signature: '0x1234...', // signature from useSignMessage
address: '0x742d35Cc6634C0532925a3b8D',
});
// Verify EIP-712 typed data signature
const { data: isTypedDataValid } = useVerifyTypedData({
domain: {
name: 'My App',
version: '1.0.0',
chainId: 1,
verifyingContract: '0x742d35Cc6634C0532925a3b8D',
},
types: {
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallet', type: 'address' },
],
},
primaryType: 'Person',
message: {
name: 'Alice',
wallet: '0x742d35Cc6634C0532925a3b8D',
},
signature: '0x5678...', // signature from useSignTypedData
address: '0x742d35Cc6634C0532925a3b8D',
});
return (
<div>
<p>Message signature valid: {isMessageValid ? '✅' : '❌'}</p>
<p>Typed data signature valid: {isTypedDataValid ? '✅' : '❌'}</p>
</div>
);
}import { useSignMessage, useAccount } from "wagmi";
import { SiweMessage } from "siwe";
function SiweAuth() {
const { address } = useAccount();
const { signMessage, data: signature, isPending } = useSignMessage();
const handleSiweSign = async () => {
if (!address) return;
// Create SIWE message
const siweMessage = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in to My App',
uri: window.location.origin,
version: '1',
chainId: 1,
nonce: Math.random().toString(36), // Generate proper nonce
issuedAt: new Date().toISOString(),
});
// Sign the formatted message
signMessage({
message: siweMessage.prepareMessage(),
});
};
const handleVerify = async () => {
if (!signature || !address) return;
try {
// Verify the signature
const siweMessage = new SiweMessage({
domain: window.location.host,
address,
statement: 'Sign in to My App',
uri: window.location.origin,
version: '1',
chainId: 1,
nonce: Math.random().toString(36),
issuedAt: new Date().toISOString(),
});
await siweMessage.verify({ signature });
console.log('SIWE verification successful!');
} catch (error) {
console.error('SIWE verification failed:', error);
}
};
return (
<div>
<button onClick={handleSiweSign} disabled={!address || isPending}>
Sign In with Ethereum
</button>
{signature && (
<button onClick={handleVerify}>
Verify SIWE Signature
</button>
)}
</div>
);
}import { useSignTypedData, useVerifyTypedData } from "wagmi";
interface AuthToken {
user: Address;
expires: number;
permissions: string[];
}
function MultiSigAuth() {
const { signTypedData, data: signature } = useSignTypedData();
const authDomain = {
name: 'AuthService',
version: '1.0.0',
chainId: 1,
verifyingContract: '0x742d35Cc6634C0532925a3b8D' as Address,
};
const authTypes = {
AuthToken: [
{ name: 'user', type: 'address' },
{ name: 'expires', type: 'uint256' },
{ name: 'permissions', type: 'string[]' },
],
};
const createAuthToken = (userAddress: Address): AuthToken => ({
user: userAddress,
expires: Math.floor(Date.now() / 1000) + 3600, // 1 hour
permissions: ['read', 'write'],
});
const signAuth = (userAddress: Address) => {
const token = createAuthToken(userAddress);
signTypedData({
domain: authDomain,
types: authTypes,
primaryType: 'AuthToken',
message: token,
});
};
const { data: isValid } = useVerifyTypedData({
domain: authDomain,
types: authTypes,
primaryType: 'AuthToken',
message: signature ? createAuthToken('0x742d35Cc6634C0532925a3b8D') : {},
signature: signature || '0x',
address: '0x742d35Cc6634C0532925a3b8D',
query: { enabled: !!signature }
});
return (
<div>
<button onClick={() => signAuth('0x742d35Cc6634C0532925a3b8D')}>
Sign Auth Token
</button>
{signature && (
<div>
<p>Signature: {signature}</p>
<p>Valid: {isValid ? '✅' : '❌'}</p>
</div>
)}
</div>
);
}import { useSignTypedData } from "wagmi";
function PermitSigner() {
const { signTypedData, data: signature } = useSignTypedData();
const signPermit = () => {
signTypedData({
domain: {
name: 'USD Coin',
version: '2',
chainId: 1,
verifyingContract: '0xA0b86a33E6417C90CC5F6d2c4a29f9D7e5D8ecf0', // USDC
},
types: {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
primaryType: 'Permit',
message: {
owner: '0x742d35Cc6634C0532925a3b8D',
spender: '0x8ba1f109551bD432803012645Hac136c',
value: '1000000000000000000', // 1 token
nonce: 0,
deadline: Math.floor(Date.now() / 1000) + 3600, // 1 hour
},
});
};
return (
<div>
<button onClick={signPermit}>
Sign Permit
</button>
{signature && (
<p>Permit signature: {signature}</p>
)}
</div>
);
}type Address = `0x${string}`;
type Hex = `0x${string}`;
interface SignMessageMutateOptions {
onError?: (error: Error, variables: SignMessageVariables, context?: unknown) => void;
onSuccess?: (data: Hex, variables: SignMessageVariables, context?: unknown) => void;
onSettled?: (data?: Hex, error?: Error, variables?: SignMessageVariables, context?: unknown) => void;
}
interface SignTypedDataMutateOptions {
onError?: (error: Error, variables: SignTypedDataVariables, context?: unknown) => void;
onSuccess?: (data: Hex, variables: SignTypedDataVariables, context?: unknown) => void;
onSettled?: (data?: Hex, error?: Error, variables?: SignTypedDataVariables, context?: unknown) => void;
}
type SignMessageErrorType = Error;
type SignTypedDataErrorType = Error;
// Common EIP-712 domain types
interface StandardTypedDataDomain {
name?: string;
version?: string;
chainId?: number;
verifyingContract?: Address;
salt?: Hex;
}
// Common type definitions for structured data
type CommonTypedDataTypes = {
EIP712Domain: TypedDataField[];
[key: string]: TypedDataField[];
};