1

I'm creating a message hash with Viem in typescript and in Solidity, however, I can't seem to get the hash in Solidity to match the one created with Viem.

My viem code:

const hashedMessage = hashMessage("test");
console.log("hashed message:", hashedMessage);
// hashed message: 0x4a5c5d454721bbbb25540c3317521e71c373ae36458f960d2ad46ef088110e95 

But when I try to do the same thing in Solidity, I can't figure out the correct syntax to get the same hash.

function returnHashedMsg() public pure returns (bytes32) {
        string memory test = "test";
        bytes32 hash = keccak256(
            abi.encodePacked(test)
        );
        bytes32 messageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n", hash.length, hash)
        );
        return messageHash;
    }

hash - 0xada625be259fe1e28a18f93e8b9721ea032a2e1b96677d7fc3eaad5fd8b49387

 function returnHashedMsg() public pure returns (bytes32) {
        string memory test = "test";
        bytes32 messageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", test)
        );
        return messageHash;
    }

hash - 0x45f373debb8d5a53c7df499f79affafa8497c0a91018f7f82fbb3666c8c5563f

function returnHashedMsg() public pure returns (bytes32) {
        string memory test = "test";
        bytes32 messageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n", bytes(test).length, test)
        );
        return messageHash;
    }

hash - 0x6b949bebd615f903fc43643801c27334884c09c74a0e471669a5263baf25be38

function returnHashedMsg() public pure returns (bytes32) {
        string memory test = "test";
        bytes32 hash = keccak256(
            abi.encodePacked(test)
        );
        bytes32 messageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n", uint256(bytes(test).length), hash)
        );
        return messageHash;
    }

hash - 0x8e23f6de1e62fecf3011ff54e8d44ec446f89506a0cf1a33cb99aff27d3287e4

1 Answer 1

2

For EIP-191, len(message) is the string representation of the decimal number of bytes the message takes when encoded in UTF-8, not a literal byte value1.

You were closest in the third example you gave. Instead of bytes(test).length (which becomes a literal byte value 0x0000000000000000000000000000000000000000000000000000000000000004), you need to pass in the string "4" (which becomes a literal byte value 0x34 when encoded in UTF-8).

Unfortunately there's not currently any built-in way in Solidity to convert a uint256 to its string equivalent. However, OpenZeppelin's utils/Strings provides a Strings.toString function you can use.

Full example:

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol";

// ...

function returnHashedMsg() public pure returns (bytes32) {
    string memory test = "test";
    string memory len = Strings.toString(bytes(test).length); // "4"
    bytes32 messageHash = keccak256(
        abi.encodePacked("\x19Ethereum Signed Message:\n", len, test)
    );
    return messageHash;
}

This updated function returns 0x4a5c5d454721bbbb25540c3317521e71c373ae36458f960d2ad46ef088110e95.


1 EIP-191 isn't precise about this and this decision has been questioned before. However, the subsequent EIP-712 adopted the same convention and used more precise language to clear this up.

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.