I'm using web3 to verify signed messages using the code below:

web3.eth.accounts.recover(message, signature);

I'm testing against the list of publicly verified signatures on EtherScan - https://etherscan.io/verifiedSignatures and this actually works for the vast majority of the signatures on there.

For example, by taking this publicly verified signature as an example I can see that the console output is 0x65f4667a96bbfd393f39b35b84145f2521bb359c which is as expected and verified by EtherScan

web3.eth.accounts.recover("test message", "0x290aa619d5e054c3de8b5f48e2a88e0ce410b2aea2ad3deb5c2ea7e270a10f3d27c85801ad48445a37fc27eecc86bbec92748af5fde22159a8bbe923278ec43a1b")

However, trying to do the same with this publicly verified signature returns an output of 0x31e7d77287169f0AF8aE63d276441f905A68b7aD as a result, which does not match the address published on etherscan (0x8F5d3Ad9A330009C751D76cAc7D27F929AC059CE)

web3.eth.accounts.recover("I have the private key for 0x8f5d3ad9a330009c751d76cac7d27f929ac059ce", "0xa45a8a4d35158f3610a7a8263c048b171fbf6ed9fe31a958e56be73323bf585d67a689dc361226512b04be308ccbe4e93b6c49c2253d2299dd243edd563299e701")

The exception seems to be those entries where EtherScan indicates in the results that there are multiple passes.

What does having 2 or even 3 passes to verify a signature mean, and what is the reason for doing so? I wasn't able to find documentation that goes into this


1 Answer 1


Ok, was finally able to figure this out. I noticed that EtherScan are thanking Nethereum for the module which I happened to be familiar with so I had a look at the MessageSignerTests in Nethereum.Signer.UnitTests and it appears that the differences lie in a mix of whether it is the raw message that's being passed to the recover function, or a hashed version of it. In addition to this we need to consider whether the Geth Prefix (\x19Ethereum Signed Message:\n<length of message>) is being applied once or twice.

This is what I was able to deduce:

Verified with Pass(1)

This is the "simplest" form where the message is expected to be the text as a string (together with the Geth Prefix)

web3.eth.accounts.recover(message, signature);

Verified with Pass(2)

This means that the signature's message was hashed and subsequently signed without the Geth Prefix, so in order to recover the address we also need to set the preFixed parameter to true, as otherwise the web3.eth.accounts.recover would append the Geth Prefix and validation would fail.

let hashedMessage = web3.utils.keccak256(message);
web3.eth.accounts.recover(hashedMessage, signature, true);

Verified with Pass(3)

This means that the message was hashed and signed, but unlike the previous example it is with the Geth Prefix, so to recover we need to

let hashedMessage = web3.utils.keccak256(message);
web3.eth.accounts.recover(hashedMessage, signature);

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.