patternjavascriptModerate
Signature verification: verifying EIP-191 personal_sign signatures on-chain and off-chain
Viewed 0 times
ethers.js v6.x
signature verificationecrecoverEIP-191verifyMessagepersonal_signauthentication
Problem
You need to verify that a message was signed by a specific Ethereum address, either in Solidity or in JavaScript.
Solution
Off-chain: use ethers.verifyMessage(). On-chain: hash the message with the EIP-191 prefix and use ecrecover.
// Off-chain
const signer = ethers.verifyMessage(message, signature);
// On-chain (Solidity)
bytes32 hash = keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', messageHash));
address recovered = ecrecover(hash, v, r, s);Why
The EIP-191 prefix prevents signatures meant for one context from being replayed in another (e.g., a login signature being used as a transaction).
Gotchas
- personal_sign in MetaMask prepends the Ethereum Signed Message prefix automatically
- ecrecover returns address(0) for invalid signatures — always check the recovered address is not zero
- OpenZeppelin's ECDSA library handles malleability protection and safe ecrecover wrapping
Code Snippets
Sign a message and verify it off-chain
import { ethers } from 'ethers';
async function signAndVerify(signer, message) {
// Sign
const signature = await signer.signMessage(message);
// Verify (off-chain)
const recoveredAddress = ethers.verifyMessage(message, signature);
const signerAddress = await signer.getAddress();
return recoveredAddress.toLowerCase() === signerAddress.toLowerCase();
}Context
Implementing sign-in with Ethereum or off-chain authorization that is verified on-chain
Revisions (0)
No revisions yet.