1

So I have to write on-chain about 15 string key-value pairs in one call. Of course, I took the naive approach first by putting all those strings as arguments of my contract, but then I run onto the 16 local variables limit.

I came up with the following solution : I convert all those strings as bytes and supply a data layout array of uint with the length of each string. It's working fine, but I'm not that confident about it being the best, cleanest and cheapest of the solutions.

Javascript side (preparing the data) :

exports.stringsToBytes = (stringArray) => {
  var res = [];
  var layout = [];
  for(var i = 0; i < stringArray.length; i++) {
    var bytesArray = this.toUtf8Bytes(stringArray[i]);
    res = res.concat(bytesArray);
    layout.push(bytesArray.length);
  }

  return {data: res, dataLayout: layout};
}

Solidity code :

  function recordStatement(
    uint[] memory statementDataLayout,
    bytes memory statementData
  ) public onlyPrimaryOrOwner returns(bytes32) {
    bytes32 statementId = generateStatementId();

    recordStatementStrings(statementId, statementDataLayout, statementData);
    dataStore.createBool(keccak256(abi.encodePacked(statementId)), true);

    emit NewStatementEvent(buildingPermitId, statementId);
  }

  function recordStatementStrings(
    bytes32 statementId,
    uint[] memory statementDataLayout,
    bytes memory statementData) internal {
    string[] memory infos = parseStatementStrings(statementDataLayout, statementData);

    recordStatementString(statementId, "pcId", infos[0]);
    recordStatementString(statementId, "acquisitionDate", infos[1]);
    [...]
  }

  function parseStatementStrings(
    uint[] memory statementDataLayout,
    bytes memory statementData) internal pure returns(string[] memory) {
    string[] memory res = new string[](statementDataLayout.length - 1);
    uint bytePos = statementDataLayout[0] * 32;
    uint resLength = res.length;
    for(uint i = 0; i < resLength; i++) {
      bytes memory strBytes = new bytes(statementDataLayout[i+1]);
      uint strBytesLength = strBytes.length;
      for(uint j = 0; j < strBytesLength; j++) {
        strBytes[j] = statementData[bytePos];
        bytePos++;
      }
      res[i] = string(strBytes);
    }

    return res;
  }

Could you please share any potential issue or threat you can think of with this method ? Any other, 'cleaner' or 'cheaper' gas-wise alternative you know of ? I remind sending a string array is supported as an experimental method, but that wouldn't be suitable for production, would it ?

Many thanks,

Thibaut

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.