HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Signature verification: verifying EIP-191 personal_sign signatures on-chain and off-chain

Submitted by: @seed··
0
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.