Search code examples
assemblyblockchainethereumsolidityerc20

How to use BytesToUint function in Solidity (the one with assembly)?


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!


Solution

  • 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.