1

I'm trying to write a dynamic nested struct but I’m not sure the pattern I'm following is right and cost-effective. It is just an practice example please ignore the string arguments which it can be replaced with bytes. Any help is greatly appreciated.

pragma solidity 0.8.9;

contract Test {
    struct Person {
        string name;
        string[] tokensHolding;
    }

    struct People {
        string country;
        string state;
        Person[] person;
    }

    mapping(address => People[]) private people;

    function addPeople(
        string memory _country,
        string memory _state,
        Person[] memory _person
    ) public {

       people[msg.sender].push(People(_country, _state, _person));
    }

}

The above contract throws an error on the compilation :

UnimplementedFeatureError: Copying of type struct Test.Person memory[] memory to storage not yet supported.

I also tried to add fields individually with for loop but the transactions are getting reverted with an error:

Note: The called function should be payable if you send value and the value you send should be less than your current balance.

Edited

For Loop Implementation:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

contract Test {
    struct Person {
        string name;
        string[] tokensHolding;
    }

    struct People {
        string country;
        string state;
        Person[] person;
    }

    mapping(address => People[]) private people;

    function addPeople(
        string memory _country,
        string memory _state,
        Person[] memory _person,
        uint _personLength
    ) public payable {
        uint _lengthOfPeople = people[msg.sender].length;

    //   people[msg.sender].push(People(_country, _state, _person));
    
        uint _lengthOfPersonInPeople = people[msg.sender][_lengthOfPeople].person.length;
        people[msg.sender][_lengthOfPeople].country = _country;
        people[msg.sender][_lengthOfPeople].state = _state;
        
        for (uint i = 0; i < _personLength; i++) {
            people[msg.sender][_lengthOfPeople].person[_lengthOfPersonInPeople].name = _person[i].name; 
            people[msg.sender][_lengthOfPeople].person[_lengthOfPersonInPeople].tokensHolding = _person[i].tokensHolding; 
        }
    }
    
    function getPeopleLength() view public returns(uint) {
        return people[msg.sender].length;
    }
    
    function getPeople(uint _index) view public returns(People memory) {
        return people[msg.sender][_index];
    }

}

Inputs - addPeople():

_country = IN
_state = TS
_person = [[ "NAME", [ "TOKEN1", "TOKEN2" ] ]]
_personLength = 1
6
  • U need a loop to iterate the array and add them one by one. I think u are trying to send ether (not fee) with ur transactions , how are u calling it?
    – Majd TL
    Commented Oct 15, 2021 at 21:51
  • ur code is not code efficient. Strings are bad :) Try to get rid of strings and use byte32 instead and address. To use bytes32 maybe u can hash the string or map it to byte32 outside the contract
    – Majd TL
    Commented Oct 15, 2021 at 21:54
  • @MajdTL I'm trying it in Remix and I didn't send any ether i just performed plain transaction with required parameters Commented Oct 15, 2021 at 21:54
  • @MajdTL Yeah i understand that I'm just trying the nested structs/array its just an example. I want to understand how exactly i can achieve that dynamic nesting Commented Oct 15, 2021 at 21:57
  • add payable as modifier to ur function and try again (the loop implementation) if it works then u are either sending ether without knowing to the function or the code in ur loop is not correct, try to add that loop to ur question
    – Majd TL
    Commented Oct 15, 2021 at 22:06

1 Answer 1

0

I changed my approach and decided to handle looping off-chain and add Person struct one by one by storing the IDs of those structs in People. If anyone has a more efficient way to approach such a deep nested data structure please share your thoughts.

New Approach:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

contract Test {
    struct Person {
        string name;
        string[] tokensHolding;
    }

    struct People {
        uint peopleId;
        string country;
        string state;
        uint[] personIds;
    }

    mapping(address => People[]) private people;
    mapping(address => mapping(uint => Person)) private person;

    function addPeople(
        uint _peopleId,
        string memory _country,
        string memory _state,
        uint[] calldata _personIds
    ) public {
        people[msg.sender].push(People(_peopleId, _country, _state, _personIds));
    }
    
    function addPersonInPeople(uint _personId, Person memory _person, uint[] calldata _personIds) public {
        for(uint i = 0; i < _personIds.length; i++) {
            person[msg.sender][_personId] = Person(_person.name, _person.tokensHolding);
        }
    }
    
    function getPeopleLength() view public returns(uint) {
        return people[msg.sender].length;
    }
    
    function getPeople(uint _index) view public returns(People memory) {
        return people[msg.sender][_index];
    }
    
    function getPerson(uint _personId) view public returns(Person memory) {
        return person[msg.sender][_personId];
    }
}

And I have a question, what if instead of storing deeply nested structs such a way, instead add hex of stringified JSON Object that can have deeply nested objects. Here an issue can be raised of having a defined schema for data, as functions are public anyone can add any arbitrary hex values, and the frontend fetching this hex and parsing them to JSON objects can fail and mess up the application. Is there any other way of handling this?

2
  • Is this a answer or a question? Commented Oct 23, 2021 at 2:18
  • @MadeInDreams The approach seems to be valid as per the question. And i think the better solution would be passing the struct data as input (so that there's an well defined schema). Compute the hash off-chain and pass it as argument. If required the data can be hashed again on-chain and check both are same, which might require more gas or just emit the struct data as event and store the hash value on-chain. Commented Oct 23, 2021 at 2:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.