I'm getting to grips with web3.js and have noticed an unusual issue with signing data.

When I sign a string with the web3.eth.sign() method, I am getting a different signature than if I use web3.eth.account.sign(). Note this latter method takes the private key as an argument.

I'm using the the latest web3.js beta and ganache. The test public address & private key I'm using in the examples below are:

address: 0x627306090abaB3A6e1400e9345bC60c78a8BEf57
pkey: 0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
//note 0x prefix


const signTest = async function(){

    // Using eth.sign()

    let accounts = await web3.eth.getAccounts();
    let msg = "Some data"

    let prefix = "\x19Ethereum Signed Message:\n" + msg.length
    let msgHash1 = web3.utils.sha3(prefix+msg)

    let sig1 = await web3.eth.sign(msg, accounts[0]);

    // Using eth.accounts.sign() - returns an object

    let privateKey = "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3"

    let sigObj = await web3.eth.accounts.sign(msg, privateKey)
    let msgHash2 = sigObj.messageHash;

    let sig2 = sigObj.signature;


sig 1 and sig 2 do not match - the last 4 chars are different.

sig1: 0xb3bd48482d2c13a1d1f3bba070fb9de9375ffc61347504fb4e3aed903fbc073e65588de1207ed716e0e439bfa527d44ea13a28ba08f79ac025a0e42b683967ec00
sig2: 0xb3bd48482d2c13a1d1f3bba070fb9de9375ffc61347504fb4e3aed903fbc073e65588de1207ed716e0e439bfa527d44ea13a28ba08f79ac025a0e42b683967ec1b

However, note both signatures recover the correct account:

    let whoSigned1 = await web3.eth.accounts.recover(msgHash1, sig1)
    let whoSigned2 = await web3.eth.accounts.recover(sigObj)

Is this behaviour normal or could it lead to problems?



Likely web3.eth.accounts sign method is encoding the chainId as the parameter V while web3.eth.sign (an older implementation) is not. This was an update wrt to EIP 155. More info: (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)


According to this page: https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethsign the signature of web3.eth.sign is:

web3.eth.sign(address, dataToSign, [, callback])

But on your example you use, web3.eth.sign(data, address), that's likely to be one of the reasons ( :


I discovered that web3.eth.accounts.sign(msg, privateKey) does automatic prefixing of the msg. The actual signed message is: "\x19Ethereum Signed Message:\n" + message.length + message. So if in your solidity contract (when recovering and validating), you are expecting a prefix like that, you should use web3.eth.accounts.sign. Checkout the documentation for more info the solidity documentation


you need to add prefix "0x" with your private key

  • even after adding "0x" before the private key, signatures are different. – Sachin Tomar Apr 13 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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