0
votes

I was using the following function to convert bytes to uint:

function bytesToUint(bytes b) public pure returns (uint){
    uint number;

    for(uint i=0;i<b.length;i++){
        number = number + uint(b[b.length-1-i])*(10**i);
    }

    return number;
}

Since explicit byte1 to uint conversion is no longer supported I found the following alternative:

function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
    require(_bytes.length >= (_start + 32), "Read out of bounds");
    uint256 tempUint;

    assembly {
        tempUint := mload(add(add(_bytes, 0x20), _start))
    }

    return tempUint;
}

The bytes is the input in the ApproveAndCall function of the ERC20 Token

function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) {
    allowed[msg.sender][spender] = tokens;
    emit Approval(msg.sender, spender, tokens);
    ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data);
    return true;
}

which is sent over to the receiveApproval of my smart contract.

function receiveApproval(address _from, uint _token, address _tokenContract, bytes memory _data) public {
    
    if(!ERC20Interface(_tokenContract).transferFrom(_from, address(this), _token)) {
        revert();
    }
    
    _0xChangeLib.place_sell_order(exchange, _from, _tokenContract, _token, _0xChangeLib.toUint256(_data, 0));

}

Could someone explain how this new BytesToUint256 works? I can't get my head around the assembly code and how to use this function. I don't understand the uint256 _start argument. I am also not sure if I could use the same format as input as I was using. As argument I was converting a wei amount as a bytes, e.g. 100 wei = 0x100, with a simple function in javascript and sent over to the token address with Web3.js.

I would like within ReceiveApproval function of the smart contract to call the BytesToUint function to further process the data.

Thank you very much in advance for your help!

1
I don't know this assembly language, but it looks like _start is just the initial value to add to, instead of uint number = 0;. But I don't see it multiplying by 10, so it looks like it's just adding all the digits ignoring their place values. Or maybe it's turning an array of integer digits into an ASCII string as a uint256? But ASCII '0' is 0x30, not 0x20, so that's not it.Peter Cordes
Where I'm lost is that if I enter 0x0 as input for the bytes data in the ApproveAndCall and put 0 as _start, it returns an out of bound error caught in the ToUint256 functionGilles Walther

1 Answers

2
votes

The _start basically points to the byte index in bytes array where the integer value starts. The first 32 (or 0x20 in hex) bytes contain the length of the bytes array and then starts the integer value which is stored in the next 32 bytes. The _start value is zero because second set of 32 bytes contain the integer value you need. You can essentially convert this function to this.

function toUint256(bytes memory _bytes)   
  internal
  pure
  returns (uint256 value) {

    assembly {
      value := mload(add(_bytes, 0x20))
    }
}

With regards to your comment 0x0 would mean bytes array has length 1 i.e. the second zero and the require statement expects the length of at least 32.