There is a function web3.eth.accounts.sign in the implementation of web3js. But I cannot find the exact equivalent in web3py I don't see this. I found something similar to this — the signHash function (link https://web3py.readthedocs.io/en/stable/web3.eth.account.html#sign-a-message). This documentation says that the same hashing mechanism, but as a result, execution returns not the same as in web3js (web3.eth.accounts.sign).

I used this function in web3js to get signature (and then using ECDSA library (on Solidity) to get signer. On web3js works correctly (but on web3py - no). I connect to test network (ropsten) via Infura.

On web3js (where msg - what is signed):

let signature = await web3.eth.accounts.sign(msgHash, '0x' + privateKeyUser);
console.log('signature ='+signature.signature);

On web3py (I try, but doesn't work):

signature = web3.eth.account.signHash(msgHash, '0x' + privateKeyUser)
print('signature ='+signature.signature.hex())

And for example in variable "msgHash" value: 0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230 And at the output of web3js function (web3.eth.accounts.sign), we get signature: 0xd19241ed816026e846c8511e03a468aa6e038165ecb3c2229eee38b04c64b235408389dfad2b65d776dba236e7de1b63fdb6b2579e70c4ce9ad1b09a35da96521c And this is the correct value. Web3py has other value.

What is exact analogue in web3py of how this web3.eth.accounts.sign function works in web3js? Thank you!

  • It would be helpful if you provided an example message that you are trying to sign, as well as the raw (hex-encoded) data that web3.js produces. Commented Jul 9, 2019 at 13:14
  • It would also be good to know if you are using a remote node to sign / handle keys, or if the private key to sign with is available in-script to web3.js. (There is this distinction in web3.py.) Commented Jul 9, 2019 at 13:15
  • In general, convenience methods for EIP-712 structured signing were added to web3.py in v5; the stable docs you link to are v4. Do clarify if you specifically need v4, or if v5 is also acceptable. Commented Jul 9, 2019 at 13:17
  • ...And since you're new to SE: these clarifications should go as edits to your original question. (Welcome! :D) Commented Jul 9, 2019 at 13:19
  • Thanks for help! Yes, I'm new here :) I added more information.
    – Alex
    Commented Jul 9, 2019 at 15:49

3 Answers 3


Since you mentioned in comments that you're open to using web3.py v5, here is the v5 approach to sign a message:

from eth_account import Account, messages

msg_hash_hex = "058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230"
private_key_hex = "b25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"

#// This part prepares "version E" messages, using the EIP-191 standard
message = messages.encode_defunct(hexstr=msg_hash_hex)

#// This part signs any EIP-191-valid message
signed_message = Account.sign_message(message, private_key=private_key_hex)
print("signature =", signed_message.signature.hex())

If you'd like to know more about EIP-191, check out the original EIP-191 writeup. It's short and readable.

That snippet is equivalent to the javascript:

msgHash = "0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230"
privateKeyUser = "b25c7db31feed9122727bf0939dc769a96564b2de4c4726d035b36ecf1e5b364"

// web3.js sign() *only* supports "version E" EIP-191 signing
// so it adds the preamble for you.
let signature = await web3.eth.accounts.sign(msgHash, '0x' + privateKeyUser);
console.log('signature ='+signature.signature);

Note that the javascript sign() method "signs arbitrary data" rather than a message hash. So although you appear to be hashing it first, you could sign the original message instead.

In the python version, prepare the original message for signing with: encode_defunct(text=original_message).

  • Thank you for answer! But I have not succeeded on your advice:
    – Alex
    Commented Jul 11, 2019 at 23:24
  • 1) In version with web3py, I have error: "AttributeError: 'bytes' object has no attribute 'encode'" I converted "msg_hash" variable to text. Line with "encode_defunct" has become: "message = encode_defunct(text="0x058c3b4c8e5dc4632b5c6b861b2c1861d53e426dc673c907ddf2651942b0f230")"
    – Alex
    Commented Jul 11, 2019 at 23:25
  • And after that it started without errors. and result is: 0x4ef46c84d3b33927369c69da0aa46328a80c88f747ee33e9a597e76e8cf8a723642302bf793c1b80b8979a97416ff64423f0b4cad8a1f0e5e141b3f2a219b3881b
    – Alex
    Commented Jul 11, 2019 at 23:26
  • 2) In version with web3js, I don't have errors. And result: 0xdccea59712c525f67783bc2c6bb5add55447d3f2122f30eccb602c3e629a609f2a13c74c32fcb15799c94d3e00753c7264aee93339b327c2130e2f02ce4abb5c1b As you can see, results are not same.
    – Alex
    Commented Jul 11, 2019 at 23:26
  • As you can see, results are not same. For information: I have version of web3py - 5.0.0b2, and version of web3js - 1.0.0-beta.55
    – Alex
    Commented Jul 11, 2019 at 23:27

It is hard to tell without seeing the content of all variables, including keys.

But my gut feeling is that you are mixing raw bytes and hexadecimal ASCII strings - some of the variables are not what you think they are.

If possible can you edit your question with a lot of prints and output to compare?

  • Thank you for answer! I need to write a variable where is private key? I think this is only one thing that missing here. Data mixing - in my opinion, here I wrote them as strings explicitly). Please correct me if I misunderstood you.
    – Alex
    Commented Jul 11, 2019 at 23:38

For anyone came here with web3.py < v5:

Use defunct_hash_message on the message before using signHash.

from eth_account.messages import defunct_hash_message
prepared_message = defunct_hash_message(primitive=msgHash)
signature = web3.eth.account.signHash(prepared_message, '0x' + privateKeyUser)
print('signature =' + signature.signature.hex())

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.