I'm trying to implement a 'Login with Metamask' button on my dapp.

On the frontend I'm using ethers.js code:

 const provider = new ethers.providers.Web3Provider(window.ethereum);
    var [account] = await ethereum.request({
        method: 'eth_requestAccounts'

 const signer = provider.getSigner(account);
 signature = await signer.signMessage("Hello World");

This as expected, generates a signed message hash (lets assume its: 0xec325c188547379c92df21225c624206ba68bb2a87994c7e2937375ad6fbe609109780a5ed6e9633c132708fd66d6edf2bd993e9ce346797e7e33448639f751d1c)

On the backend, I want to use web3.py to verify this address.

message_hash = web3.keccak(text="Hello World")
signature = 0xec325c188547379c92df21225c624206ba68bb2a87994c7e2937375ad6fbe609109780a5ed6e9633c132708fd66d6edf2bd993e9ce346797e7e33448639f751d1c
web3.eth.account.recoverHash(message_hash, signature=signature)

However, this doesnt quite work, and the address I receive back is different to the address that signed the original message.

>>> web3.eth.account.recoverHash(message, signature=signature)

How do I correctly verify the signature in web3.py and return the correct address that signed 'Hello World' in the frontend ethers.js code?

  • 1
    Thanks for also sharing your findings helped me in my search! Rep to low comment or +1
    – AG23
    Apr 3, 2022 at 21:57

1 Answer 1


Thank you to the wonderful chinese website that explained this well and I figured it out.

from web3 import Web3, HTTPProvider
import sys
from eth_account.messages import defunct_hash_message

w3 = Web3() 

signature = sys.argv[1]

original_message = "Hello World"
message_hash = defunct_hash_message(text=original_message)

signer = w3.eth.account.recoverHash(message_hash, signature=signature)
return signer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.