3

I previously asked about converting uint to hex string. Now I want to store a HEX value 0x00ff08 in a bytes3 variable and to be able to convert it to a string in Solidity smart contract. Subsequently I intend to deploy it on RSK with Solidity compiler version at least 0.8.0 .

I tried this string(abi.encodePacked(bytes3(0x00ff08))) but it throws a runtime error

Failed to decode output: null: invalid codepoint at offset 1; bad codepoint prefix (argument="bytes", value={"0":0,"1":255,"2":8}, code=INVALID_ARGUMENT, version=strings/5.4.0)

A different argument string(abi.encodePacked(bytes3(0x443322))) doesn't cause an error, but returns a very strange D3" result. What could be the problem here? How do I convert bytes3 to a string with the same characters?

bguiz
  • 27,371
  • 47
  • 154
  • 243
Aleks Shenshin
  • 2,117
  • 5
  • 18

1 Answers1

4

Reason for the behaviour you've observed

abi.encodePacked(myBytes3) produces 3 bytes because every 2 hexadecimal characters results in 1 bytes. so when you wrap that in string(..) you do get a string with 1 character per 1 byte, which in this case, results in a string with 3 characters. For some inputs this results in a string that can be rendered as a human readable string. In other cases, it results in a string that cannot be, hence the parse error.

Solution

You cannot use abi.encodePacked(..) in this case, because "packing" is the opposite of what you're trying to accomplish - to render a bytes3 as a hexadecimal string - one that is human readable, ASCII encoded - you would need to do something similar to my answer to your previous question, where you use a combination of bit shifting with bit masking to extract one character per half byte.

Code

Utility function that converts a single uint8 into the ASCII character that would represent it in hexadecimal:

    function uint8tohexchar(uint8 i) public pure returns (uint8) {
        return (i > 9) ?
            (i + 87) : // ascii a-f
            (i + 48); // ascii 0-9
    }

Function that converts a uint24 to a 6 character hexadecimal string.

Note that since this time you're not looking for a "general purpose" solution, but rather one that is specific to bytes3, there is no need for any loops, but rather, a sequential set of statements will do. Probably nets some gas savings too .(to be confirmed!)

    function uint24tohexstr(uint24 i) public pure returns (string memory) {
        bytes memory o = new bytes(6);
        uint24 mask = 0x00000f;
        o[5] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[4] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[3] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[2] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[1] = bytes1(uint8tohexchar(uint8(i & mask)));
        i = i >> 4;
        o[0] = bytes1(uint8tohexchar(uint8(i & mask)));
        return string(o);
    }

And finally, you probably want a function that explicitly accepts bytes3 as a parameter.

Note that this is purely for convenience, as all it really does is a type cast. The bytes3 and uint24 can be thought of simply as "24 bits" being viewed/ interpreted in different ways.

    function bytes3tohexstr(bytes3 i) public pure returns (string memory) {
        uint24 n = uint24(i);
        return uint24tohexstr(n);
    }
bguiz
  • 27,371
  • 47
  • 154
  • 243