28

I know I can return dynamic byte arrays in Solidity, such as in the following code:

function createMemoryArray(uint size) returns (bytes) {
    // Dynamic memory arrays are created using `new`:
    uint[2][] memory arrayOfPairs = new uint[2][](size);
    // Create a dynamic byte array:
    bytes memory b = new bytes(200);
    for (uint i = 0; i < b.length; i++)
        b[i] = byte(i);
    return b;
}

But is there a way to return something like a dynamic array of strings? I feel like this is just an implementation detail in Solidity, but it would be great if it just serialized everything nicely for me.

4 Answers 4

12

Is it possible to return an array of strings ( string[] ) from a Solidity function?

Not yet, as this requires two levels of dynamic arrays (string is a dynamic array itself). Doc

However you can return an Array of Bytes32 ( fixed size of 32 byte) So you can try to do something like this ( you can copy paste on Remix to test it )

pragma solidity ^0.4.11;
contract ArrayOfBytes32 {
    address creator;
    bytes32[10] bytesArray; // size must be fixed
  
    function ArrayRR() 
    {
        creator = msg.sender;
        uint8 x = 0;
        while(x < bytesArray.length)
        {
            bytesArray[x] = "myString"; 
            x++;
        }
    }
   
    function getArray() constant returns (bytes32[10])
    {
        return bytesArray;
    }
    
    function getValue(uint8 x) constant returns (bytes32)
    {
        return bytesArray[x];
    }
}

Note that you will have to use web3.toAcsii() doc to convert the result if you use web3 to interact with your contract

6
  • 1
    Huh, I never would've thought to check the doc FAQs. Nice find and interesting solution!
    – user9402
    Jun 6, 2017 at 13:35
  • 1
    Why I can't return a dynamic array of of bytes32 (bytes32[])? Aug 21, 2017 at 14:41
  • 2
    You can return a dynamic array of of bytes32 (bytes32[]). What you can't is returning an object which combines two levels of dynamic arrays. Aug 21, 2017 at 18:02
  • @RobertZaremba But isn't bytes32 an array itself? Then bytes 32[] is a two dimensional array
    – Andrey
    Mar 21, 2018 at 14:16
  • 1
    @Andrey Problem is with two levels of dynamic arrays, not a two dimensional arrays. You could have function return two dimensional array of 10 uints each without problem - but you cannot have two dimensional array where both arrays are dynamic. Type "bytes32" just indicate that it allocates 32 bytes in the memory, so it is not dynamic variable - you always know that it will take 32 bytes of memory, therefore it is static variable (cannot change size during runtime). That's why it works :) Dec 10, 2018 at 17:38
4

It is an old issue... But, for newcomers and for completeness sake, there is no problems in Solidity to return dynamic arrays of pairs, or dynamic arrays of strings, if one uses the modern compiler (tested with 0.5.6) and experimental ABI pragma:

    pragma experimental ABIEncoderV2;

    ...

    function createMemoryArray(uint size) public pure returns (uint[2][] memory) {
        uint[2][] memory b = new uint[2][](size);
        for (uint i=0; i < b.length; i++) {
            b[i][0] = i;
            b[i][1] = i * 2;
        }
        return b;
    }

    function createStringArray(uint size) public pure returns (string[] memory) {
        string[] memory b = new string[](size);
        for (uint i=0; i < b.length; i++) {
            b[i] = "test";
        }
        return b;
    }
1
  • Thanks, this should be the accepted answer
    – charelf
    May 28, 2020 at 14:28
0

Return dynamic arrays of structure in a function

pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
contract Money {
  struct People{
    uint id;
    string name;
    uint amount;
  }
  mapping (uint => People) public peoples;
  event votedEvent(uint indexed _candidateId);
  uint public candidateConut;

  constructor() public {
    candidateConut = 0;
    addCandidate("Holder 1");
    addCandidate("Holder 2");
  }
  function addCandidate(string memory _name) public {
    peoples[candidateConut] = People(candidateConut,_name,0);
    candidateConut++;
  }
  //return Single structure
  function get(uint _candidateId) public view returns(People memory) {
    return peoples[_candidateId];
  }
  //return Array of structure Value
  function getPeople() public view returns (uint[] memory, string[] memory,uint[] memory){
      uint[]    memory id = new uint[](candidateConut);
      string[]  memory name = new string[](candidateConut);
      uint[]    memory amount = new uint[](candidateConut);
      for (uint i = 0; i < candidateConut; i++) {
          People storage people = peoples[i];
          id[i] = people.id;
          name[i] = people.name;
          amount[i] = people.amount;
      }

      return (id, name,amount);

  }
  //return Array of structure
  function getPeoples() public view returns (People[] memory){
      People[]    memory id = new People[](candidateConut);
      for (uint i = 0; i < candidateConut; i++) {
          People storage people = peoples[i];
          id[i] = people;
      }
      return id;
  }
}
1
  • I don't encourage you to use this in main net, cause it is experimental and a lately article has shown a bug in it.
    – Majd TL
    Aug 28, 2019 at 14:26
0

Is it possible to return a dynamic array of strings ( string[] ) from a Solidity function?

Yes, you can serialize it to bytes and deserialize it back to string[].

In your smart contract:

function toBytes(string[] strArray)
private
pure
returns(bytes serialized) {
    uint startindex = 0;
    uint endindex = strArray.length - 1;

    require(endindex >= startindex);

    if (endindex > (strArray.length - 1)) {
        endindex = strArray.length - 1;
    }

    //64 byte is needed for safe storage of a single string.
    //((endindex - startindex) + 1) is the number of strings we want to pull out.
    uint offset = 64 * ((endindex - startindex) + 1);

    bytes memory buffer = new bytes(offset);
    string memory out1 = new string(32);


    for (uint i = startindex; i <= endindex; i++) {
        out1 = strArray[i];

        stringToBytes(offset, bytes(out1), buffer);
        offset -= sizeOfString(out1);
    }

    return (buffer);
}

function stringToBytes(uint _offst, bytes memory _input, bytes memory _output)
private
pure {
    uint256 stack_size = _input.length / 32;
    if (_input.length % 32 > 0) stack_size++;

    assembly {
        stack_size: = add(stack_size, 1) //adding because of 32 first bytes memory as the length
        for {
            let index: = 0
        }
        lt(index, stack_size) {
            index: = add(index, 1)
        } {
            mstore(add(_output, _offst), mload(add(_input, mul(index, 32))))
            _offst: = sub(_offst, 32)
        }
    }
}


function sizeOfString(string memory _in)
private
pure
returns(uint _size) {
    _size = bytes(_in).length / 32;
    if (bytes(_in).length % 32 != 0)
        _size++;

    _size++; // first 32 bytes is reserved for the size of the string
    _size *= 32;
}

}

Then to deserialize it back to string[] (using js) use the below function:

function hexBytesToStr(hex) {
    let str = '';
    for (let i = 0; i < hex.length; i += 2) {
        let v = parseInt(hex.substr(i, 2), 16);
        if (v) str += String.fromCharCode(v);
    }

    let params = [];
    let res = "";
    for (let i = 0; i <= str.length; i++) {
        if (str.charCodeAt(i) > 31) {
            res = res + str[i];
        }
        else {
            params.push(res);
            res = "";
        }
    }
    params.pop();

    return params;
}

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.