1

I'm doing oraclize calls on the json rpc of a chain to get this function:

  function getTransfer(uint _transferId) public view returns(uint,address,uint){
    Details memory _locked = transferDetails[_transferId];
    return(_locked.amount,_locked.owner,_locked.transferId);
  }

It's really not that important what it does, but when I call this function from oraclize, I get back a string with all three variables...for instance:

"0x0000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000c69c64c226fea62234afe4f5832a051ebc8605400000000000000000000000000000000000000000000000000000000000000001"

I'm working on a callback to parse this into a uint, an address, and a uint. This is what I have so far:

function __callback(string result) public returns(uint,address,uint){
    emit Print(result);
    string memory _int = Strings.substring(result,60,90);
    emit Print(_int);
    emit P2(stringToUint(_int));
    uint _amount= parseInt(_int);
    _int =strConcat('0x',Strings.substring(result,91,130)) ;
    address _owner =  parseAddr(_int);
    _int =Strings.substring(result,131,194) ;
    uint _transId = parseInt(_int);
    return (_amount,_transId,_transId);
}

This sort of works, but the problem is that the uint's are actually in hex value (this string should return .8e18,0x,1), but I need to convert a literal string to a hex to a decimal..if you have any ideas. Also, can I use substring, or will the length of my string change with the values?

The code of the contract we're calling is here: https://ropsten.etherscan.io/address/0xd29d27cfacf7b77a16edac7c2cddf07dc4a603b8#readContract

p.s. It can break remix doing this kind of string manipulation

1 Answer 1

2

This code works if anyone else is curious..had to write a string uint hex to uint converters:

  pragma solidity ^0.4.21;



/**
 * Set of utilities to parse results of JSON-RPC call from oraclize.
 */
contract HexUtils {

    //constants for comparison and conversions below
    byte constant a = byte('a');
    byte constant f = byte('f');
    byte constant A = byte('A');
    byte constant F = byte('F');
    byte constant zero = byte('0');
    byte constant nine = byte('9');

    /**
     * Convert a character to its hex value as a byte. This is NOT
     * very efficient but is a brute-force way of getting the job done.
     * It's possible to optimize this with assembly in solidity but
     * that would require a lot more time.
     */
    function hexCharToByte(uint c) pure internal returns(uint) {
        byte b = byte(c);

        //convert ascii char to hex value
        if(b >= zero && b <= nine) {
            return c - uint(zero);
        } else if(b >= a && b <= f) {
            return 10 + (c - uint(a));
        } else if(b >= A && b <= F) {
            return 10 + (c - uint(A));
        }
    }

    /**
     * Check whether a string has hex prefix.
     */
    function hasZeroXPrefix(string s) pure internal returns(bool) {
        bytes memory b = bytes(s);
        if(b.length < 2) {
            return false;
        }
        return b[1] == 'x';
    }

    /**
     * Convert a hex string to a uint. This is NOT very efficient but
     * gets the job done. Could probably optimize with assembly but would
     * require a lot more time.
     */
    function hexToUint(string s) pure public returns(uint) {
        //convert string to bytes
        bytes memory b = bytes(s);

        //make sure zero-padded
        require(b.length % 2 == 0, "String must have an even number of characters");

        //starting index to parse from
        uint i = 0;
        //strip 0x if present
        if(hasZeroXPrefix(s)) {
            i = 2;
        }
        uint r = 0;
        for(;i<b.length;i++) {
            //convert each ascii char in string to its hex/byte value.
            uint b1 = hexCharToByte(uint(b[i]));

            //shift over a nibble for each char since hex has 2 chars per byte
            //OR the result to fill in lower 4 bits with hex byte value.
            r = (r << 4) | b1;
        }
        //result is hex-shifted value of all bytes in input string.
        return r;
    }

    /**
     * Extract a substring from an input string.
     */
    function substr(string s, uint start, uint end) pure public returns(string) {
        require(end > start, "End must be more than start");
        bytes memory res = new bytes(end-start);
        bytes memory bts = bytes(s);
        require(end <= bts.length, "End must be less than or equal to the length of string");
        require(start >= 0 && start < bts.length, "Start must be between 0 and length of string");

        uint idx = 0;
        for(uint i=start;i<end;++i) {
          //just copy bytes over
            res[idx] = bts[i];
            ++idx;
        }
        return string(res);
    }

    /**
     * Parse a hex string into an address.
     */
    function parseAddr(string _a) internal pure returns (address){
        //address is really a uint160...
        uint iaddr = hexToUint(_a);
        return address(iaddr);
    }

    /**
     * Parse oraclize result into uint, address, uint values.
     */
    function parseResults(string _hexData) pure public returns(uint, address, uint) {
        //start at zero or 2 depending on 0x prefix in string
        uint startIdx = 0;
        if(hasZeroXPrefix(_hexData)) {
            startIdx = 2;
        }
        bytes memory bts = bytes(_hexData);
        //take the first 64 bytes and convert to uint
        uint ethVal = hexToUint(substr(_hexData, startIdx,64+startIdx));

        //id is at the end and will be 64 bytes. So grab its starting idx first.
        uint idStart = bts.length - 64;

        //the address portion will end where the id starts.
        uint addrEnd = idStart-1;

        //parse the last 40 bytes of the address hex.
        address addr = parseAddr(substr(_hexData, addrEnd-40, addrEnd));

        //then extract the id
        uint id = hexToUint(substr(_hexData, idStart, bts.length));

        //done
        return (ethVal, addr, id);
    }
}

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.