39

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

7 Answers 7

24

It seems there now is, since solidity version 0.4.24 you can use abi.encodePacked

E.G:

uint i = 0;
i_bytes = abi.encodePacked(i);
32

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.

4
  • 1
    Would you mind explaining what each line does? What is the purpose of this add(b, 32). Couldn't we simply do mstore(b, x)?
    – ppoliani
    Commented Nov 24, 2020 at 21:46
  • @ppoliani did you get an answer to your question? I am also curious why we add 32 to b. (when b should be at the start of memory address anyway).
    – skgbanga
    Commented May 31, 2021 at 19:12
  • Actually it is explained here: ethereum.stackexchange.com/questions/98750/…
    – skgbanga
    Commented May 31, 2021 at 19:23
  • 1
    @ppoliani the bytes type in Solidity is stored in memory as: 1) first 32 bytes = length of the bytes value, 2) then the bytes value itself. The mstore opcode stores 32 bytes, starting from a certain offset (in the bytes value to grab) to a specified location in memory. So the instruction mstore(add(b, 32), x) can be translated in plain words as "stores 32 bytes at position x in memory, starting from the 32nd bytes offset in the b" (so to skip the length, and just store b itself).
    – CJ42
    Commented Dec 13, 2021 at 0:01
18

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

7

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.

5

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

2
  • 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
    Commented Jul 20, 2018 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/… Commented Jul 23, 2018 at 16:20
2

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];
}
2

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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