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:

    { 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:

// Tried encoding
web3.utils.sha3('test1', {encoding: 'String'})
web3.utils.sha3('test1', {encoding: 'hex'})
web3.utils.sha3('test1', {encoding: 'string'})
// Tried converting to hex
// 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)

The abi.encode function encode its parameters for a contract call using solidity's ABI specs. For fixed size types like uintXXX, bool, address is 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'})

If you are curious 'test1' ABI encoded is

0000000000000000000000000000000000000000000000000000000000000020   // offset
0000000000000000000000000000000000000000000000000000000000000005   // length
7465737431000000000000000000000000000000000000000000000000000000   // data

In your case the equivalente to makeHash in js is:

const encoded = web3.eth.abi.encodeParameters(['uint256', 'string', 'address'],[1234, 'country roads', 0x44444..])
const hash = web3.utils.sha3(encoded, {encoding: 'hex'})

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.