0

I have a mapping of structs and a counter variable to keep track of how many structs are stored in the mapping.

For front-end development would you recommend to query each struct using the auto-generated getter mymapping[mystruct_index]. Or is it better to create a specific getter function to loop through X structs and retrieve structs in arrays of 50 for instance. Also if I use the getter function to get just few rows of the struct instead of the whole struct, is it going to lead to better efficiency than simply making one query for one struct ? Do you have some experience regarding front-end interaction with smart contracts, what do you think is the best design for efficiency and long term viability of the smart contract ? Best regards

1 Answer 1

2

Solidity doesn't currently support returning a mapping or a variable-sized list, so you would need to implement a getter function as you described that takes an index.

The approach I think you're describing is keeping a second list next to the mapping, and using it to return the mapping in chunks like the source code below.

contract SomeContract {

  mapping(address => uint256) public someMapping;
  address[] public addresses;

  function addValue(address _newAddress, uint256 _newValue) public {
    someMapping[_newAddress] = _newValue;
    addresses.push(_newAddress);
  }

  function getAddressCount() public view returns (uint256 _count) {
    return addresses.length;
  }

  function getValueByIndex(uint256 _index) public view returns (uint256 _val) {
    return someMapping[addresses[_index]];
  }

  // Get the values from mapping in chunks of size 10
  // This isn't a feasible solution in my opinion
  function getValuesChunk(uint256 _index) public view returns (uint256[10] memory _chunk) {
    uint256[10] memory vals;
    require(_index < 2^256 - 10, 'Index would wrap around unsafely');
    for (uint256 i = _index; i < _index+10; i++) {
        vals[i] = someMapping[addresses[i]];
    }
    return vals;
  }
}

The view methods are read-only and don't cost gas, so you can technically call them as many times as you like in order to display values to your front-end. However, there are a few reasons not to do this. * It would still take CPU power on your Ethereum node. If it's a large enough list, your node might consider it a DoS attack and throttle or ban you. If you run your own node, that's a lot of work or AWS credits to burn on blockchain operations. * The mapping won't change much in between calls to addValue. You would be re-doing the work of retrieving mostly the same values each time, even if you do some caching in your front-end.

Instead, I would recommend using Solidity events (EVM logs), a facility which lets you emit an event, which is retrieved by a database that allows one-pass indexing to be read multiple times off-chain, like Subgraph https://thegraph.com/

This lets you keep your solidity code minimal and clean, not spending gas on a complicated contract just to support front-end functionality that you can easily offload onto systems that are designed for it (GraphQL + React + Typescript)

contract SomeContract {

  event NewValue (
      address _address,
      uint256 _value
      );
  mapping(address => uint256) public someMapping;


  function addValue(address _newAddress, uint256 _newValue) public {
    someMapping[_newAddress] = _newValue;
    emit NewValue(_newAddress, _newValue);
  }

}

Hope this helps.

5
  • Ok thank you. Just I don't understand why you created getValueByAddress getter as someMapping is public and I should be able to do someMapping(adress) ? I can do it from my truffle testing environment, but are you saying that it is not possible on mainet ? Best regards
    – Kevin Wad
    Oct 7, 2019 at 10:14
  • @paul-ham Plus my mapping is sligthly different: the key is a uint index linked to the counter: mapping(uint => struct) mymapping; uint mappingCounter; //keeps track of how many structs in the mapping. I was thinking about making a getter function that would return a memory made array by looping through mapping structs
    – Kevin Wad
    Oct 7, 2019 at 10:18
  • @KevinWad you're welcome. lol my name is @paul-pham, not @paul-ham, that is a different breakfast food. You're right, the public mapping doesn't need a separate getter, I'll update my answer to remove it. Also, I forgot that your value is a struct, but the general principle of my answer still applies. Instead of returning uint256 _value you would return your struct type.
    – Paul Pham
    Oct 7, 2019 at 10:22
  • Yeah my apologize. Ok thank you very much. So to resume if I rely on the public mapping getter without an additional getter function, as the mapping becomes bigger I will have to use off chain methods because otherwise I will face Dos issues, is it right ? And what does this mean "If you run your own node, that's a lot of work or AWS credits to burn on blockchain operations" ? Best regards
    – Kevin Wad
    Oct 7, 2019 at 10:33
  • No worries. Calling the public getter for the mapping has a fixed cost for individual calls. If you want to get all the mapping values each time, it will require more and more calls as the mapping grows bigger, as you said. Rest of my answer doesn't fit here, I moved it to a gist: gist.github.com/cryptogoth/edbacf61517bb9100b742b69ccd5fbc1 Hope it helps
    – Paul Pham
    Oct 7, 2019 at 10:47

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.