Is there an easy way to convert a uint
to bytes
in Solidity?
7 Answers
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);
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.
-
1Would you mind explaining what each line does? What is the purpose of this add(b, 32). Couldn't we simply do mstore(b, x)?– ppolianiCommented 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).– skgbangaCommented May 31, 2021 at 19:12
-
Actually it is explained here: ethereum.stackexchange.com/questions/98750/…– skgbangaCommented May 31, 2021 at 19:23
-
1@ppoliani the
bytes
type in Solidity is stored in memory as: 1) first 32 bytes = length of thebytes
value, 2) then thebytes
value itself. Themstore
opcode stores 32 bytes, starting from a certain offset (in the bytes value to grab) to a specified location in memory. So the instructionmstore(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 theb
" (so to skip the length, and just storeb
itself).– CJ42Commented Dec 13, 2021 at 0:01
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):
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.
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?– ThomasCommented 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
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];
}
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.