5

I'd like to convert bytes into any other type. I.e. "0x57D80C61128d608857d5310BB514223Bb6011CAB" into the address 0x57D80C61128d608857d5310BB514223Bb6011CAB, "5" into the uint256 5 and so on. I have followed all the snippets I could find but I can't get any of them working post 0.5.0. I believe the culprit is the change outlined here:

Conversions between bytesX and uintY of different size are now disallowed due to bytesX padding on the right and uintY padding on the left which may cause unexpected conversion results. The size must now be adjusted within the type before the conversion. For example, you can convert a bytes4 (4 bytes) to a uint64 (8 bytes) by first converting the bytes4 variable to bytes8 and then to uint64. You get the opposite padding when converting through uint32.

Taking the uint example, here is some code that would have worked prior to this version of the compiler:

function bytesToUInt(string memory _b) public returns (uint256){
  bytes memory b = bytes(_b);
  uint256 number;
  for(uint i=0;i<b.length;i++){
    number = number + uint(b[i])*(2**(8*(b.length-(i+1))));
  }
  return number;
}

But this now results in a TypeError: Explicit type conversion not allowed, I've tried moving things around and adding extra conversions but nothing has worked.

For bytes to address, I've tried this:

function bytesToAddress(string memory _b) public returns (address) {
    bytes20 b = bytes20(bytes(_b));
    return address(uint160(b));
}

But you can't explicitly convert from bytes memory to bytes20 and I couldn't get that working either.

Can someone show me how this is done properly? Thanks.

2

3 Answers 3

0

As far as I know, you will need inline assembly. Someone already posted some code applicable to previous solidity versions at least, although generally short and correct inline assembly should be pretty universal.

Here's some code that converts some bytes from memory to bytes20 (only the first 20, it gets truncated if over that of course).

contract t {
    function tb20(bytes memory _b) 
    public
    pure
    returns (bytes20 _result) {
        assembly {
            _result := mload(add(_b, 0x20))
        }
    }

    function bytesToAddress(bytes memory _b) 
    public 
    returns (address) {
        return address(tb20(_b));
    }
}

No guarantees or warranties on the code but it appears to work. Can of course change up tb20 to return an address instead, although you'll need shifts then.

2
  • This doesn't work for me either, I get back a normal looking address, but it's not the one I use as input. To be clear, I'm running this on remix with the 0.5.2 solidity version. Maybe it's a problem with remix?
    – FlashyQpt
    Jan 7, 2019 at 2:56
  • Not a remix problem: rinkeby.etherscan.io/address/…
    – FlashyQpt
    Jan 7, 2019 at 3:43
0

My earlier answer has to do with converting a bytes type from memory into bytes20, because that is how I understood the question from the title, converting dynamic bytes, to statically sized 1-32 bytes, and therafter into any other derivatives.

Regarding the actual question in your body, this answer is to confirm that you can in fact use your existing bytesToUInt function for solc 0.5.x. You are still able to do explicit conversions between other static bytes, as long as their corresponding lengths are equal. Thereby if you have something bytes32 and want it to be uint128, you first have to explicitly convert it to bytes16, and therafter can convert it to uint128, via regular solidity. Of course, you'll wish to debug/test to see which parts of that bytes32 may get truncated yourself before proceeding with this, as there's converse cases where it may make more sense to convert it to uint256 first and then cut it down to uint128.

Simply said, you can get your existing function working, by converting the bytes1 to uint8 first.

function bytesToUInt(string memory _b) public returns (uint256){
  bytes memory b = bytes(_b);
  uint256 number;
  for(uint i=0;i<b.length;i++){
    number = number + uint256(uint8(b[i]))*(2**(8*(b.length-(i+1))));
  }
  return number;
}

the uint256 was added for explicitness, which is probably always a good idea, but not needed

4
  • Converting bytes1 to uint8 was my first thought as well but no. "5" returns 53, "0" returns 48, "124" returns 3224068 etc It's off by 48 from 0 to 9 and then gets more complex. I thought it might be the string to bytes conversion but apparently not.
    – FlashyQpt
    Jan 7, 2019 at 2:51
  • But you posted that function, and the result the above function I posted for solc 0.5 produces is the same as the one you posted and said was working??? My answers are answering your questions, but I don't think you've asked the right questions for what you're actually looking for. I think you may wish to re-post a new proper question. I believe what you are in fact looking for is a base 10 string converter into uint, which exist. Check the Oraclize API for some working helpers.
    – DenisM
    Jan 7, 2019 at 16:22
  • 1
    @FlashyQpt The function bytesToAddress will convert from a bytes sequence to uint256, it the string has hexadecimal digits you have to convert them to their value first. Somethink like this ethereum.stackexchange.com/a/40247/2124 first.
    – Ismael
    Jan 7, 2019 at 16:57
  • I thought it would work in 0.4.x, I foolishly didn't bother testing this. I misinterpreted what the input was supposed to be. @Ismael Thank you, I believe you solved my problem
    – FlashyQpt
    Jan 7, 2019 at 17:31
0

The mistake I was making came from the initial conversion to bytes. Because you can pass anything into them with bytes(), I assumed that this was giving me something that I could into the other functions, it wasn't.

My issue wasn't with 0.5.x at all, if you convert to bytes properly then the rest of the code works as expected. You only have a couple extra conversions to do compared to 0.4.x. The Seriality library works great.

Here's an example of a conversion from an int to bytes and back to that int:

function intToBytesToInt(int256 _input) public pure returns (int256) {
  bytes memory buffer = new bytes(32);
  uint offset = 32;

  assembly{
    mstore(add(buffer, offset), _input)
  }

  //b is the converted input
  bytes memory b = buffer;

  //Post 0.5.0 you must remember to convert to the appropriate size. For int256, that's bytes32.

  bytes32 preI;

  assembly {
    preI := mload(add(buffer, offset))
  }

  //i is b converted back to an int256
  int256 i = int256(preI);

  return i;
}

Your Answer

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

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