13

Is there an easy way to convert a uint to bytes in Solidity?

17

The alternative to @eth's answer is to use assembly:

function toBytes(uint256 x) returns (bytes b) {
    b = new bytes(32);
    assembly { mstore(add(b, 32), x) }
}

This is significantly more gas-efficient, but depends on the internal memory layout used by the Solidity compiler. In theory, this can change in future, but in practice it should be fairly stable.

8

Here's a comparison of the gas used in the three methods by @NickJohnson, @Eth and @k26dr. I've added a constant to the function modifiers as these functions do not alter the blockchain:

pragma solidity ^0.4.2;

contract Test {
    function toBytesNickJohnson(uint256 x) constant returns (bytes b) {
        b = new bytes(32);
        assembly { mstore(add(b, 32), x) }
    }

    function toBytesEth(uint256 x) constant returns (bytes b) {
        b = new bytes(32);
        for (uint i = 0; i < 32; i++) {
            b[i] = byte(uint8(x / (2**(8*(31 - i))))); 
        }
    }

    function toBytesNicolasMassart(uint256 x) constant returns (bytes c) {
        bytes32 b = bytes32(x);
        c = new bytes(32);
        for (uint i=0; i < 32; i++) {
            c[i] = b[i];
        }
    }    
}

You can see the gas cost of running these functions in Remix (Solidity Browser):

enter image description here

4

There are no easy ways to convert anything to bytes. Here's a function:

function toBytes(uint256 x) returns (bytes b) {
    b = new bytes(32);
    for (uint i = 0; i < 32; i++) {
        b[i] = byte(uint8(x / (2**(8*(31 - i))))); 
    }
}

Based on Solidity Gitter chat.

3

If you're overly concerned about gas we can improve on @NickJohnson answer too.

function toBytes(uint _num) returns (bytes _ret) {
    assembly {
        _ret := mload(0x10)
        mstore(_ret, 0x20)
        mstore(add(_ret, 0x20), _num)
    }
}

This will shave approximately a further 15% off the gas cost, just be careful that you're not using that memory for something else as 0x10 is a direct reference to memory.

Also, if you want to copy an 8bit int to byte here's an alternative which only uses half the gas cost:

function toByte(uint8 _num) returns (byte _ret) {
    assembly {
        mstore8(0x20, _num)
        _ret := mload(0x20)
    }
}

Again, be mindful that 0x20 is another direct memory reference and in all honesty with this one I would stick with

function toByte(uint8 _num) returns (byte _ret) {
    return byte(_num);
}

The gas price between the two was almost identical, but the functional ASM was undercutting it by about 70 wei

Edit: If you're concerned about overwriting memory you could use:

let m_alloc := add(msize(),0x1)

Instead of referencing memory yourself

  • I didn't know msize() can be used to increase the memory allocation by adding to it. The solidity assembly document (solidity.readthedocs.io/en/v0.4.24/…) is very brief. Is there a better reference document out there? – Thomas Jul 20 '18 at 23:04
  • msize doesn't, it returns the highest point in memory and you simply state that you want to write to msize() plus 1. Here's another stack page where I explain it in more detail ethereum.stackexchange.com/questions/9537/… – James Lockhart Jul 23 '18 at 16:20
1

You can convert to bytes32 then convert to bytes:

uint u = 200;
bytes32 b = bytes32(u);
bytes memory c = new bytes(32);
for (uint i=0; i < 32; i++) {
    c[i] = b[i];
}
1

You can avoid padding the bytes array with 0's with the toBytes implementations by determining the scriptNumSize

function scriptNumSize(uint256 i) public view returns (uint256) {
    if      (i > 0x7fffffff) { return 5; }
    else if (i > 0x7fffff  ) { return 4; }
    else if (i > 0x7fff    ) { return 3; }
    else if (i > 0x7f      ) { return 2; }
    else if (i > 0x00      ) { return 1; }
    else                     { return 0; }
}

function toBytes(uint256 x) public view returns (bytes memory b) {
    uint a = scriptNumSize(x);
    b = new bytes(a);
    for (uint i = 0; i < a; i++) {
        b[i] = byte(uint8(x / (2**(8*(a - 1 - i))))); 
    }
}

For example uint 1563384765 would return 0x5d2f5bbd not 0x000000000000000000000000000000000000000000000000000000005d2f5bbd

This was written specifically for timestamps. If you want larger numbers you can add more if cases.

New contributor
user3562257 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.