8

TL;DR: I have a method in a contract to calculate a keccak256 hash, and I'm trying to replicate it in Web3, but the hashes don't match. My problem is very similar to the one in this question, but the answers there didn't help me. It could be an encoding issue, but I'm not sure how to fix it.


I was trying to hash three parameters like this:

pragma solidity >=0.7.0 <0.9.0;
contract Test {
    function makeHash(uint intValue, string calldata strValue) public view returns (bytes32) {
        bytes32 myHash = keccak256(abi.encode(intValue, strValue, msg.sender));
        return myHash;
    }
}

Calling makeHash(1, 'test1') for this address 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 returns 0x94822408407fb535100a4152f8709ed58507e1b3dfd32798d25051065f680d64

In Web3, I tried to concatenate the values in a string (I realize this is wrong now, but also not sure how to do it in web3): web3.utils.keccak256("1test10x5B38Da6a701c568545dCfcB03FcB875f56beddC4") returns 0xcd347147b43e04263a58ca2b6d0f0aa8a3e0b00bd02c0e9bbee008aad68b1bf0

From this answer I also tried this method:

web3.utils.soliditySha3(
    { type: 'uint', value: 1 },
    { type: 'string', value: 'test1' },
    { type: 'address', value: '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4' }
)

it returns 0x0aca86dcfc0a652e35268737960c05e51eb03dbfc4c175fd3ca094a2e99e180c


Since all the methods returned different hashes I decided to test to hash just the string in both contract and web3, but even then they generate different hashes.

bytes32 myHash = keccak256(abi.encode(strValue));

Using the line aboce in the contract, calling makeHash('test1') returns 0xf5e2ed7011d2249ef10becf6f745e4b900ab43c0dc529b97d308f25bb36416d0

However in web3 all those methods returned the same hash:

web3.utils.keccak256("test1")
web3.utils.sha3Raw('test1')
web3.utils.sha3('test1')
web3.utils.soliditySha3('test1')
web3.utils.soliditySha3Raw('test1')
// Tried encoding
web3.utils.sha3('test1', {encoding: 'String'})
web3.utils.sha3('test1', {encoding: 'hex'})
web3.utils.sha3('test1', {encoding: 'string'})
// Tried converting to hex
web3.utils.soliditySha3Raw(web3.utils.asciiToHex('test1'))
web3.utils.keccak256(web3.utils.asciiToHex('test1'))
// Tried from this answer https://ethereum.stackexchange.com/a/74082/68459
web3.utils.soliditySha3({ type: 'string', value: 'test1' })

They all return 0x6d255fc3390ee6b41191da315958b7d6a1e5b17904cc7683558f98acc57977b4


Also, decided to try to replicate abi.encode in web3 by using web3.eth.abi.encodeParameter(String, 'test1') but it always returns an error:

    if (type.match(paramTypeBytesArray) || type.match(paramTypeNumberArray)) {
             ^
Uncaught TypeError: type.match is not a function
    at ABICoder.formatParam (..\truffle\build\webpack:\node_modules\web3-eth-abi\src\index.js:255:1)
    at ..\truffle\build\webpack:\node_modules\web3-eth-abi\src\index.js:117:1
    at Array.map (<anonymous>)
    at ABICoder.encodeParameters (..\truffle\build\webpack:\node_modules\web3-eth-abi\src\index.js:105:1)
    at ABICoder.encodeParameter (..\truffle\build\webpack:\node_modules\web3-eth-abi\src\index.js:88:1)
    at evalmachine.<anonymous>:0:14
    at sigintHandlersWrap (vm.js:274:15)
    at Script.runInContext (vm.js:128:14)
    at runScript (..\truffle\build\webpack:\packages\core\lib\console.js:270:1)
    at Console.interpret (..\truffle\build\webpack:\packages\core\lib\console.js:285:1)
    at bound (domain.js:426:14)
    at REPLServer.runBound [as eval] (domain.js:439:12)
    at REPLServer.onLine (repl.js:760:10)
    at REPLServer.emit (events.js:315:20)
    at REPLServer.EventEmitter.emit (domain.js:482:12)
    at REPLServer.Interface._onLine (readline.js:329:10)
    at REPLServer.Interface._line (readline.js:658:8)
    at REPLServer.Interface._ttyWrite (readline.js:999:14)
    at REPLServer.self._ttyWrite (repl.js:851:9)
    at ReadStream.onkeypress (readline.js:205:10)
    at ReadStream.emit (events.js:315:20)
    at ReadStream.EventEmitter.emit (domain.js:482:12)
2

2 Answers 2

6

The abi.encode function encode its parameters for a contract call using Solidity's ABI specs. For fixed size types like uintXXX, bool, address, it's quite easy, just pad them to 32 bytes; for dynamic types it's complicated.

The equivalent to this Solidity code:

bytes32 myHash = keccak256(abi.encode('test1'));

in JavaScript would be:

const encoded = web3.eth.abi.encodeParameters(['string'],['test1'])
const hash = web3.utils.sha3(encoded, {encoding: 'hex'})
console.log(hash)

If you are curious, 'test1' ABI-encoded is:

0x
0000000000000000000000000000000000000000000000000000000000000020   // offset
0000000000000000000000000000000000000000000000000000000000000005   // length
7465737431000000000000000000000000000000000000000000000000000000   // data

In your case, the equivalent to makeHash in JavaScript is:

const encoded = web3.eth.abi.encodeParameters(['uint256', 'string', 'address'],[1234, 'country roads', 0x44444..])
const hash = web3.utils.sha3(encoded, {encoding: 'hex'})
2
  • So I fed this binary of the required 256-bit hash-input into SpeedCrunch and it is 2.00000000000000000000e+129. I guess that's why better to use encodePacked unless you have things in 256-bit format already.
    – Tomachi
    Commented Aug 27, 2021 at 14:39
  • @Tomachi Sorry, but I don't understand your question.
    – Ismael
    Commented Aug 27, 2021 at 14:52
1

I found a way in Python to do this in case you are still looking for the answer. Here is the code below. Here is how you can do this in Python. I'm using the web3.keccak method of brownie which is similar to web3.py.

#!/usr/bin/python3
from brownie import web3
from eth_abi import encode

encoded_data = encode(
    ["uint", "string", "address"],
    [1, "test1", "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"],
)


print(web3.keccak(encoded_data).hex())
print(web3.sha3(encoded_data).hex())
  • Here is the output
0x94822408407fb535100a4152f8709ed58507e1b3dfd32798d25051065f680d64
0x94822408407fb535100a4152f8709ed58507e1b3dfd32798d25051065f680d64

enter image description here

Edit

  • ape is gaining popularity so here is how you can do this ape.
#!/usr/bin/python3
from ape import networks
from eth_abi import encode

web3 = networks.provider._web3

encoded_data = encode(
    ["uint", "string", "address"],
    [1, "test1", "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"],
)


print(web3.keccak(encoded_data).hex())
>>> 0x94822408407fb535100a4152f8709ed58507e1b3dfd32798d25051065f680d64

1

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.