17

Is there any convenient way to convert (e.g. cast) a struct type to bytes?

I'm trying to call a method like the one below (taken from here) where you can pass some (arbitrary) contextual info that get returned to you later in a "callback" method.

function approveAndCall(address _spender, uint256 _value, bytes _extraData)
    returns (bool success) {    
    tokenRecipient spender = tokenRecipient(_spender);
    if (approve(_spender, _value)) {
        spender.receiveApproval(msg.sender, _value, this, _extraData);
        return true;
    }
}

Suppose I want to call this method passing a User struct as its _extraData argument, what would be a proper usage?

Thanks in advance,

q9f
  • 32,913
  • 47
  • 156
  • 395
jlpiedrahita
  • 275
  • 2
  • 6
  • 6
    Solidity does not have serialization format for structs. You need to take out parameters one by one and concatenate them to bytes array one by one. (Though I don't know details how to append to bytes array). Here are some more details http://ethereum.stackexchange.com/questions/11016/copy-a-struct-from-contract-a-into-a-struct-in-contract-b-using-contract-c/11020#11020 – Mikko Ohtamaa Jan 09 '17 at 16:31

2 Answers2

5

You can do something like this. Note, for every custom struct you'll have to write custom serialization and deserialization methods.

pragma solidity ^0.4.0;

contract StructSerialization
{
    function StructSerialization()
    {
    }

    event exactUserStructEvent(uint32 id, string name);

    //Use only fixed size simple (uint,int) types!
    struct ExactUserStruct
    {
        uint32 id;
        string name;
    }

    function showStruct(ExactUserStruct u) private
    {
        exactUserStructEvent(u.id, u.name);
    }


    function exactUserStructToBytes(ExactUserStruct u) private
    returns (bytes data)
    {
        // _size = "sizeof" u.id + "sizeof" u.name
        uint _size = 4 + bytes(u.name).length;
        bytes memory _data = new bytes(_size);

        uint counter=0;
        for (uint i=0;i<4;i++)
        {
            _data[counter]=byte(u.id>>(8*i)&uint32(255));
            counter++;
        }

        for (i=0;i<bytes(u.name).length;i++)
        {
            _data[counter]=bytes(u.name)[i];
            counter++;
        }

        return (_data);
    }


    function exactUserStructFromBytes(bytes data) private
    returns (ExactUserStruct u)
    {
        for (uint i=0;i<4;i++)
        {
            uint32 temp = uint32(data[i]);
            temp<<=8*i;
            u.id^=temp;
        }

        bytes memory str = new bytes(data.length-4);

        for (i=0;i<data.length-4;i++)
        {
            str[i]=data[i+4];
        }

        u.name=string(str);
     }

    function test()
    {
        //Create and  show struct
        ExactUserStruct memory struct_1=ExactUserStruct(1234567,"abcdef");
        showStruct(struct_1);

        //Serializing struct
        bytes memory serialized_struct_1 = exactUserStructToBytes(struct_1);

        //Deserializing struct
        ExactUserStruct memory struct_2 = exactUserStructFromBytes(serialized_struct_1);

        //Show deserealized struct
        showStruct(struct_2);
    }
}
jlpiedrahita
  • 275
  • 2
  • 6
Alexey Barsuk
  • 2,259
  • 2
  • 17
  • 25
2

You can use Seriality library.

1- By means of Seriality you can easily serialize and deserialize your variables, structs, arrays, tuples, ... and pass them through the contracts and libraries.

2- You can decouple your contract from libraries by serializing parameters into a byte array.

3- It also can be used as an alternative for RLP protocol in Solidity.

Here is a sample :

pragma solidity ^0.4.16;

import "./Seriality.sol";

contract SerialitySample is Seriality {

   function testSample1() public returns(int n1, int8 n2, uint24 n3,  string n4,string n5) {

    bytes memory buffer = new  bytes(200);
    string memory out4  = new string(32);        
    string memory out5  = new string(32);
    n4 = new string(32);
    n5 = new string(32);
    int     out1 = 34444445;
    int8    out2 = 87;
    uint24  out3 = 76545;
    out4 = "Copy kon lashi";
    out5 = "Bia inja dahan service";

    // Serializing
    uint offset = 200;

    intToBytes(offset, out2, buffer);
    offset -= sizeOfInt(8);

    uintToBytes(offset, out3, buffer);
    offset -= sizeOfUint(24);

    stringToBytes(offset, bytes(out5), buffer);
    offset -= sizeOfString(out5);

    stringToBytes(offset, bytes(out4), buffer);
    offset -= sizeOfString(out4);       

    intToBytes(offset, out1, buffer);
    offset -= sizeOfInt(256);

    // Deserializing
    offset = 200; 

    n2 = bytesToInt8(offset, buffer);
    offset -= sizeOfInt(8);

    n3 = bytesToUint24(offset, buffer);
    offset -= sizeOfUint(24);

    bytesToString(offset, buffer, bytes(n5));
    offset -= sizeOfString(out5);

    bytesToString(offset, buffer, bytes(n4));
    offset -= sizeOfString(out4);

    n1 = bytesToInt256(offset, buffer);
}

}

output buffer :

00000000000000000000000000000000000000000000000000000000020d949d 436f7079206b6f6e206c61736869000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000e 42696120696e6a6120646168616e207365727669636500000000000000000000 0000000000000000000000000000000000000000000000000000000000000016 012b0157

"1": int256: n1 34444445

"2": int8: n2 87

"3": uint24: n3 76545

"4": string: n4 Copy kon lashi

"5": string: n5 Bia inja dahan service

Pouladzade
  • 71
  • 3