[Ethereum] Abi encode / decode using strings instead of bytes

abisolidity

For some reason beyond my control, I need to store encoded data in a string variable.

I know abi.encode and abi.decode usually work on bytes memory but I also believe that string memory can be casted to bytes memory.

Here is an example contract I use to debug in remix:

pragma solidity ^0.5.0;

contract Test
{
    function encode(uint256 a, string calldata b) external pure returns(string memory)
    {
        return string(abi.encode(a, b));
    }
    function decode(string calldata a) external pure returns(uint256, string memory)
    {
        return abi.decode(bytes(a), (uint256, string));
    }
}

It works great with some values. For example encode(42, "test") returns

\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000*\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000@\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004test\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000

which can then be decoded back to 42, "test".

However, for some values, such as 255, "test", then I get a revert when trying to decode the encoded string.

Any idea how to solve that ? Is their no other way then to use bytes instead of string ?

Best Answer

I think I would focus on the testing methodology.

The contract looks okay to me.

I fiddled with it a little to test the original two functions and ended up with something that seems to work. It works for both examples, e.g. 255, "test".

I didn't find a case where it doesn't work. Admittedly, did not test very much.

pragma solidity ^0.5.0;

contract Test
{
    function encode(uint a, string memory b) internal pure returns(string memory)
    {
        return string(abi.encode(a, b));
    }
    function decode(string memory a) internal pure returns(uint, string memory)
    {
        return abi.decode(bytes(a), (uint, string));
    }

    function test(uint a, string calldata b) external pure returns(uint, string memory) {
        string memory encoded = encode(a, b);
        return decode(encoded);
    }    
}

Hope it helps.