Search code examples
solidity

Solidity Compilations: UnimplementedFeature


I am getting an Error in the IDE:

UnimplementedFeatureError: Copying of type struct ChatGroups.Message memory[] memory to storage not yet supported.

And I really don't know what to do with everything that I made ( It's commented in French ) There is the full code, and I really tried to find how can I implement what do I want with a different approach if someone could help for that and if I can preserv this code.

Thanks a lot for the people that are going to help !

pragma solidity ^0.6.12;

contract ChatGroups {
    struct Group {
        string name;
        address[] members;
        Message[] messages;
    }

    struct Message {
        uint id;
        string text;
        address author;
        bool deleted;
    }

    Group[] groups;

    address[] users;

    event GroupCreated(uint groupId, string groupName);

    event MessagePublished(uint groupId, uint messageId, string messageText, address messageAuthor);

    event MessageDeleted(uint groupId, uint messageId);

    event UserAddedToGroup(uint groupId, address user);

    event UserRemovedFromGroup(uint groupId, address user);

    function createGroup(string memory groupName) public {

        require(isUser(msg.sender), "Unauthorized user");


        groups.push(Group({
            name: groupName,
            members: new address[](1),
            messages: new Message[](0)
        }));
        groups[groups.length - 1].members[0] = msg.sender;


        emit GroupCreated(groups.length, groupName);
    }

    function publishMessage(uint groupId, string memory messageText) public {

        require(isUser(msg.sender), "Unauthorized user");


        Group storage group = groups[groupId - 1];


        require(isMember(group, msg.sender), "Unauthorized user");


        group.messages.push(Message({
            id: group.messages.length + 1,
            text: messageText,
            author: msg.sender,
            deleted: false
        }));


        emit MessagePublished(groupId, group.messages[group.messages.length - 1].id, messageText, msg.sender);
    }

    function deleteMessage(uint groupId, uint messageId) public {

        require(isUser(msg.sender), "Unauthorized user");


        Group storage group = groups[groupId - 1];


        require(isMember(group, msg.sender) && group.messages[messageId - 1].author == msg.sender, "Unauthorized user");


        group.messages[messageId - 1].deleted = true;


        emit MessageDeleted(groupId, messageId);
    }

    function removeUser(uint groupId, address user) public {

        require(isUser(msg.sender), "Unauthorized user");


        Group storage group = groups[groupId - 1];


        require(isAdmin(group, msg.sender), "Unauthorized user");


        require(isMember(group, user), "Unauthorized user");


        uint index = getMemberIndex(group, user);
        group.members[index] = group.members[group.members.length - 1];
        group.members.length--;


        emit UserRemovedFromGroup(groupId, user);
    }

    function addUser(uint groupId, address user) public {

        require(isUser(msg.sender), "Unauthorized user");


        Group storage group = groups[groupId - 1];
        require(isAdmin(group, msg.sender), "Unauthorized user");


        require(!isMember(group, user), "User already member of group");


        group.members.push(user);


        emit UserAddedToGroup(groupId, user);
    }

    function isUser(address user) private view returns (bool) {
        for (uint i = 0; i < users.length; i++) {
            if (users[i] == user) {
                return true;
            }
        }
        return false;
    }

    function isMember(Group memory group, address user) public view returns (bool) {

        if (group.members[0] == user) {
            return true;
        }

        for (uint i = 1; i < group.members.length; i++) {
            if (group.members[i] == user) {
                return true;
            }
        }
        return false;
    }

    function isAdmin(Group memory group, address user) public view returns (bool) {
        if (group.members[0] == user) {
            return true;
        }

        for (uint i = 1; i < group.members.length; i++) {
            if (group.members[i] == user) {
                return true;
            }
        }
        return false;
    }

    function getMemberIndex(Group storage group, address user) private view returns (uint) {
        for (uint i = 0; i < group.members.length; i++) {
            if (group.members[i] == user) {
                return i;
            }
        }
        return group.members.length;
    }
}

While compiling it, I'm getting this error, and I really have no solutions in my mind...


Solution

  • In your smart contract, there are some issues related to your logic in different operation. Thought, the main issue that you shared in this thread refers to a struct inside another struct (in this case, I say about Message[] struct). The error say that you cannot pass a struct array initializing at runtime because this object is memorized inside storage space.

    In details, you have to change the implementation for valorized attribute related to group struct (method: createGroup(string memory groupName)). The change to make is:

    Group storage group = groups.push();
    group.name = groupName;
    group.members = new address[](1);
    group.members[0] = msg.sender;
    

    In this way, you create before a space into storage memory array, and then you fill it with the variable (if them has been valorized). At this point, you have to change the method called publishMessage() changing this line:

    Group storage group = groups[groupId - 1]; 
    

    with:

    Group storage group = groups[groupId];
    

    This same improvement you have to do in: deleteMessage() and addUser().

    In following lines, I put your smart contract with my changes:

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.6.12;
    pragma experimental ABIEncoderV2;
    
    contract ChatGroups {
        struct Group {
            string name;
            address[] members;
            Message[] messages;
        }
    
        struct Message {
            uint id;
            string text;
            address author;
            bool deleted;
        }
    
        Group[] groups;
    
        address[] users;
    
        event GroupCreated(uint groupId, string groupName);
    
        event MessagePublished(uint groupId, uint messageId, string messageText, address messageAuthor);
    
        event MessageDeleted(uint groupId, uint messageId);
    
        event UserAddedToGroup(uint groupId, address user);
    
        event UserRemovedFromGroup(uint groupId, address user);
    
        function createGroup(string memory groupName) public {
            require(isUser(msg.sender), "Unauthorized user");
    
            // NOTE: I changed at this point your original implementation 
            Group storage group = groups.push();
            group.name = groupName;
            group.members = new address[](1);
            group.members[0] = msg.sender;
            emit GroupCreated(groups.length, groupName);
        }
    
        function publishMessage(uint groupId, string memory messageText) public {
            require(isUser(msg.sender), "Unauthorized user");
    
            Group storage group = groups[groupId];
            require(isMember(group, msg.sender), "Unauthorized user");
            group.messages.push(Message({
                id: group.messages.length + 1,
                text: messageText,
                author: msg.sender,
                deleted: false
            }));
            emit MessagePublished(groupId, group.messages[group.messages.length - 1].id, messageText, msg.sender);
        }
    
        function deleteMessage(uint groupId, uint messageId) public {
    
            require(isUser(msg.sender), "Unauthorized user");
    
    
            Group storage group = groups[groupId];
    
    
            require(isMember(group, msg.sender) && group.messages[messageId - 1].author == msg.sender, "Unauthorized user");
    
    
            group.messages[messageId - 1].deleted = true;
    
    
            emit MessageDeleted(groupId, messageId);
        }
    
        function removeUser(uint groupId, address user) public {
    
            require(isUser(msg.sender), "Unauthorized user");
    
    
            Group storage group = groups[groupId - 1];
    
    
            require(isAdmin(group, msg.sender), "Unauthorized user");
    
    
            require(isMember(group, user), "Unauthorized user");
    
    
            uint index = getMemberIndex(group, user);
            group.members[index] = group.members[group.members.length - 1];
            
            // NOTE: The attribute length() is only read-only, you cannot modify or handle the length of array using in this way! 
            group.members.length;
    
    
            emit UserRemovedFromGroup(groupId, user);
        }
    
        function addUser(uint groupId, address user) public {
    
            require(isUser(msg.sender), "Unauthorized user");
    
    
            Group storage group = groups[groupId];
            require(isAdmin(group, msg.sender), "Unauthorized user");
    
    
            require(!isMember(group, user), "User already member of group");
    
    
            group.members.push(user);
    
    
            emit UserAddedToGroup(groupId, user);
        }
    
        function isUser(address user) private view returns (bool) {
            for (uint i = 0; i < users.length; i++) {
                if (users[i] == user) {
                    return true;
                }
            }
            return false;
        }
    
        function isMember(Group memory group, address user) public view returns (bool) {
    
            if (group.members[0] == user) {
                return true;
            }
    
            for (uint i = 1; i < group.members.length; i++) {
                if (group.members[i] == user) {
                    return true;
                }
            }
            return false;
        }
    
        function isAdmin(Group memory group, address user) public view returns (bool) {
            if (group.members[0] == user) {
                return true;
            }
    
            for (uint i = 1; i < group.members.length; i++) {
                if (group.members[i] == user) {
                    return true;
                }
            }
            return false;
        }
    
        function getMemberIndex(Group storage group, address user) private view returns (uint) {
            for (uint i = 0; i < group.members.length; i++) {
                if (group.members[i] == user) {
                    return i;
                }
            }
            return group.members.length;
        }
    }
    

    P.S.: I think that you have to integrate users before call the various method inside users array. Although, the smart contract logic give you this error: "Unauthorized user". I think you should think better about this aspect