Solidez: ¿Puede devolver matrices dinámicas en una función?

Sé que puedo devolver matrices de bytes dinámicas en Solidity, como en el siguiente código:

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;
}

Pero, ¿hay alguna forma de devolver algo como una matriz dinámica de cadenas? Siento que esto es solo un detalle de implementación en Solidity, pero sería genial si simplemente serializara todo bien para mí.

Respuestas (4)

¿Es posible devolver una matriz de cadenas ( string[] ) desde una función de Solidity?

Todavía no, ya que esto requiere dos niveles de matrices dinámicas (la cadena es una matriz dinámica en sí misma). Doc

Sin embargo, puede devolver una matriz de Bytes32 (tamaño fijo de 32 bytes), por lo que puede intentar hacer algo como esto (puede copiar y pegar en Remix para probarlo)

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];
    }
}

Tenga en cuenta que tendrá que usar web3.toAcsii() doc para convertir el resultado si usa web3 para interactuar con su contrato

Eh, nunca se me hubiera ocurrido consultar las preguntas frecuentes del documento. ¡Buen hallazgo e interesante solución!
¿Por qué no puedo devolver una matriz dinámica de bytes32 ( bytes32[])?
Puede devolver una matriz dinámica de bytes32 ( bytes32[]). Lo que no puede es devolver un objeto que combina dos niveles de matrices dinámicas.
@RobertZaremba ¿Pero no es bytes32 una matriz en sí misma? Entonces los bytes 32[] son ​​una matriz bidimensional
@Andrey El problema es con dos niveles de matrices dinámicas, no con matrices bidimensionales. Podría hacer que la función devuelva una matriz bidimensional de 10 uints cada una sin problema, pero no puede tener una matriz bidimensional donde ambas matrices sean dinámicas. Escriba "bytes32" simplemente indique que asigna 32 bytes en la memoria, por lo que no es una variable dinámica; siempre sabe que tomará 32 bytes de memoria, por lo tanto, es una variable estática (no puede cambiar el tamaño durante el tiempo de ejecución). Por eso funciona :)
ahora está volviendo 0x00000000.... ,0x0000000....., ....en solidez. Además, en la versión más nueva, tenemos que agregar una memorypalabra clave en el tipo de devolución, lo que me está creando el problema, creo.

Es un problema antiguo... Pero, para los recién llegados y para completar, no hay problemas en Solidity para devolver matrices dinámicas de pares o matrices dinámicas de cadenas, si se usa el compilador moderno (probado con 0.5.6) y pragma ABI experimental :

    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;
    }
Gracias, esta debería ser la respuesta aceptada.

Devolver matrices dinámicas de estructura en una función.

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;
  }
}
No te animo a usar esto en la red principal, porque es experimental y un artículo reciente ha mostrado un error.

¿Es posible devolver una matriz dinámica de cadenas ( string[] ) desde una función de Solidity?

Sí, puede serializarlo en bytes y deserializarlo nuevamente en string[].

En tu contrato inteligente:

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;
}

}

Luego, para deserializarlo de nuevo a string[] (usando js), use la siguiente función:

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;
}