Not able to convert keccak256 hash into a string type

Hi there does anyone how to cast a result of a keccak256/sha256, which returns a bytes32 into a string in Solidity? This doesn't seem to work in comparison to normal bytes32 objects and throws a failed to decode output error every time.

Here is the code below:

	function getHash(uint256 _id) external view returns (string memory) {
        return string(abi.encodePacked(keccak256(abi.encodePacked(_id, "value"))));
	}

The error:

	"error": "Failed to decode output: null: invalid codepoint at offset 0; 
unexpected continuation byte (argument=\"bytes\",
value=Uint8Array(0x8bb170e451ca7e22248c3383ea823c551dfb51469997174dd0ab7034be4877bb),
code=INVALID_ARGUMENT, version=strings/5.4.0)"
1 Like

Found the solution, pass the result of the hash into this function toHex(bytes32 _hash)
Source: https://stackoverflow.com/questions/67893318/solidity-how-to-represent-bytes32-as-string

function toHex(bytes32 data) public pure returns (string memory) {
		return string(abi.encodePacked("0x", toHex16(bytes16(data)), toHex16(bytes16(data << 128))));
	}

	function toHex16(bytes16 data) internal pure returns (bytes32 result) {
		result =
			(bytes32(data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000) |
			((bytes32(data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64);
		result =
			(result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000) |
			((result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32);
		result =
			(result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000) |
			((result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16);
		result =
			(result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000) |
			((result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8);
		result =
			((result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4) |
			((result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8);
		result = bytes32(
			0x3030303030303030303030303030303030303030303030303030303030303030 +
				uint256(result) +
				(((uint256(result) + 0x0606060606060606060606060606060606060606060606060606060606060606) >> 4) &
					0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) *
				7
		);
	}
1 Like

Note that OpenZeppelin Contracts includes a Strings.toHex utility function as well.

Right but that does not take a bytes as an input, only uint256 which is not what the result of a keccak256/sha256 returns.

You can try this:

function bytes32ToString(bytes32 _b) external pure returns (string memory) {
    return string(abi.encodePacked(_b));
}

This won't work for a result of a hashing function.

You're right. My bad!

For the record, it's still possible to use our library with bytes32 with a little workaround, by casting bytes32 to uint256:

Strings.toHexString(uint256(keccak256(...)), 32);
1 Like

if only that worked. there would be no need for this question :joy:

what's the reason this doesn't work for a hash?

Because it is "one-way". Keccak creates a unique hash out of an input... if you could decode it just back there would be no "private" keys.

well, not quite ... hashing isn't the same as encryption, but the question was never about trying to reverse a hash. It was about turning the hash output from bytes32 to a string representation of the same, and so you could do something like print it out with hardhat's console.log() or some other thing that you can do with a string but can't with bytes32

1 Like

Yes exactly. The solution I posted was the only way to resolve this.