I need help with Ethereum address verification. I'm following the tutorial on signing and validating Ethereum address. GitHub code. I'm confused by my actual result of the address after I ran loginContractInstance.recoverAddr.call(addr, fixed_msg_sha, v_decimal, r,s, function(err, actual) { because it returns null.

Here is my backend code below,

// Unlock account before the login attempt
web3.personal.unlockAccount(req.body.address, req.body.password, 100, function(error, unlocked) {
    console.log(`>>>>> Login - User is unlocked: ${unlocked}`);
    if (unlocked) {

        // Signing/ Encryption
        const addr = req.body.address;
        const msg = req.body.password;
        const hex_msg = '0x' + toHex(msg);
        let signature = web3.eth.sign(addr, hex_msg);

        console.log(`address -----> ${addr}`);
        console.log(`msg ---------> ${msg}`);
        console.log(`hex(msg) ----> ${hex_msg}`);
        console.log(`sig ---------> ${signature}`);

        const r = signature.slice(0, 66)
        const s = '0x' + signature.slice(66, 130)
        const v = '0x' + signature.slice(130, 132)
        const v_decimal = web3.toDecimal(v);

        console.log(`r -----------> ${r}`);
        console.log(`s -----------> ${s}`);
        console.log(`v -----------> ${v}`);
        console.log(`vd ----------> ${v_decimal}`);

        // Validation/Decryption
        const fixed_msg = `\x19Ethereum Signed Message:\n${msg.length}${msg}`
        const fixed_msg_sha = '0x' + web3.sha3(fixed_msg)

        loginContractInstance.isSigned.call(addr, fixed_msg_sha, v_decimal, r, s, function (err, signed) {
            console.log(`>>>>> Login - Signature: ${signed}`);

            if (signed) {
                // Saving login attempt
                loginContractInstance.successfulLogin.sendTransaction(req.body.address, req.body.password,
                    {from:'6ded1c5b448819a6cde4293e33fbe54583ef5c52', gas:200000},
                    function(err, transactionHash) {
                        if (!err) {
                            console.log(`>>>>> Login - login details (${addr}, ${msg}) successfully saved @ ${transactionHash}`);
                            console.log(`>>>>> Login - Successfully login`);

                        } else {
                            console.log(`>>>>> Login - login transaction failed: ${err}`);

                        // set primary account to mine
                        web3.eth.coinbase = req.body.address;

                        console.log(`>>>>> Login - Current User: ${web3.eth.coinbase} is logged in`);

                        var balanceWei = web3.eth.getBalance(req.body.address).toNumber();
                        var balance = web3.fromWei(balanceWei, 'ether');
                            allowedLogin: true,
                            address: req.body.address,
                            balance: balance,
                            token: generateToken(addr)

            } else {
                loginContractInstance.recoverAddr.call(fixed_msg_sha, v_decimal, r,s, function(err, actual) {
                    console.log(`>>>>> Login - Failed login: Signature not matched:${addr} = ${actual}`);
                    saveFailedLoginAttempts(loginContractInstance, req, res);
    } else {
        console.log(`>>>>> Login - Failed login: User is locked`);
        saveFailedLoginAttempts(loginContractInstance, req, res);


And my smart contract,

contract Login {

event LoginAttempt(address sender, string password);

address private sender;
string private password;

function successfulLogin (address _sender, string _password) public {
    LoginAttempt(_sender, _password);
    sender = _sender;
    password = _password;

function failedLogin (address _sender, string _password) public {
    LoginAttempt(_sender, _password);

function recoverAddr(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
    return ecrecover(msgHash, v, r, s);

function isSigned(address _addr, bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) public pure returns (bool) {
    return ecrecover(msgHash, v, r, s) == _addr;

Here is the result of that function call in my console,

enter image description here

Can anyone help what I'm missing on?


I have corrected the recoverAddr.call function to use take the correct input. I have found out as well that my fixed_msg_sha is the issue because I added 0x in const fixed_msg_sha = '0x' + web3.sha3(fixed_msg). It was printed like this 0x0xf348f6a3d221a5e7e851292474330ca6e6cf6ad54572ee114233ab9b9225ab4f

Now the problem has changed to returning a 0x address, enter image description here


your smart contract function:

function recoverAddr(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s)

your backend code:

loginContractInstance.recoverAddr.call(addr, fixed_msg_sha, v_decimal, r,s, function(err, actual) {

Don't think you intended to pass addr as first parameter?

Not sure why it's not complaining about "invalid number of arguments to solidity function" though

  • Thanks for correcting my carelessness. But the issue still remain after I corrected it. It still returns null. Apr 3 '18 at 23:29

I fyou're using this to reconstruct the preimages: \x19Ethereum Signed Message:\n30` which is what is given in the medium post, It is incorrect the proper bytes string is:

\x19Ethereum Signed Message:\n32

I looked at the same example as you did and was extremely stuck when first experimenting with signature verification, as it is incorrect (maybe it was correct when the medium post was written, but not anymore.)

I have a contract written that uses ecrecover, and geth signed messages, you can take a look at the signature prefix declaration here: https://github.com/postables/Postables-Payment-Channel/blob/7d2f91bb060f80b139cab72b5fdff79d116f6210/solidity/ChannelsV4.sol#L12 and notice that it doesn't match what the author of the medium post gave.

The actual signature verification takes place here:


My personal preference for signing messages is using python web3 + a local geth node as is demonstrated by this python script:


  • In my case I didn't follow what the tutorial said, because I'm using const fixed_msg = \x19Ethereum Signed Message:\n${msg.length}${msg} as it is more adjustable. Apr 4 '18 at 3:46
  • That is the mistake you sign the hash of the message not the message. The hash is always 32 bytes.
    – Ismael
    Apr 4 '18 at 4:39

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