Array of structs inside a struct

arrayssoliditystruct

Im trying to set a mapping to a struct that contains an array of structs inside. Like so:

struct ParentTokenDetails{
    TokenType _tokenType;
    address _tokenOwner;
    ChildTokenDetails[] children;
}

struct ChildTokenDetails{
    TokenType _tokenType;
    address _tokenOwner;
    bool _locked;
}

mapping(uint256 => ParentTokenDetails) public TokenEnums;

I have a function where I try to do this but so far no luck:

function test()
    public
    returns (uint256)
{
    uint256 newItemId = 1;
    ParentTokenDetails._tokenType = TokenType.NON_FUNGIBLE;
    ParentTokenDetails._tokenOwner = msg.sender;
    ParentTokenDetails.children = ChildTokenDetails([TokenType.NULL, msg.sender, false]);
    TokenEnums[newItemId] = ParentTokenDetails;

    return newItemId;
}

Which prompts the error:

TypeError: Type struct EndLabsEnum.ChildTokenDetails memory is not implicitly convertible to expected type struct EndLabsEnum.ChildTokenDetails storage ref[] storage ref.

Ideally I would be able to create a struct for a new Id with an empty struct array I can add information to later.

Any pointers would help!

Full contract:

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

contract TestEnum {
    enum TokenType{ FUNGIBLE, NON_FUNGIBLE, NULL }
    address public owner;

    struct ParentTokenDetails{
        TokenType _tokenType;
        address _tokenOwner;
        ChildTokenDetails[] children;
    }
    struct ChildTokenDetails{
        TokenType _tokenType;
        address _tokenOwner;
        bool _locked;
    }
    mapping(uint256 => ParentTokenDetails) public TokenEnums;

    constructor(){
        owner = msg.sender;
    }

    // Ensure minting of assets with duplicated Ids fail
    function mintNonFungibleTokenEnum(uint256 tokenID)
        public
        returns (uint256)
    {
        uint256 newItemId = 1;
        ParentTokenDetails._tokenType = TokenType.NON_FUNGIBLE;
        ParentTokenDetails._tokenOwner = msg.sender;
        ParentTokenDetails.children = ChildTokenDetails([TokenType.NULL, msg.sender, false]);
        TokenEnums[newItemId] = ParentTokenDetails;

        return newItemId;
}

Best Answer

For work around this issue you can modify your ChildTokenDetails[] inside ParentTokenDetails using a mapping. In your case, I thougth that you can use the tokenId for key in this second mapping and ChildTokensDetails[] for values.

At this point, you must change your ParentTokenDetails struct in this way:

struct ParentTokenDetails{
   TokenType _tokenType;
   address _tokenOwner;
   mapping(uint => ChildTokenDetails[]) children;
}

And then, you must update your mintNonFungibleTokenEnum function in this way:

function mintNonFungibleTokenEnum(uint256 tokenID)
    public
    returns (uint256)
{
    uint256 newItemId = 1;
    ParentTokenDetails storage ptd = tokenEnums[tokenID];
    ptd._tokenType = TokenType.NON_FUNGIBLE;
    ptd._tokenOwner = msg.sender;
    ptd.children[tokenID].push(ChildTokenDetails(TokenType.NULL, msg.sender, false));
    ptd.children[tokenID].push(ChildTokenDetails(TokenType.FUNGIBLE, msg.sender, false));
    return newItemId;
}

In the following line, I put your smart contract modified:

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

import "hardhat/console.sol";

contract TestEnum {
    enum TokenType { FUNGIBLE, NON_FUNGIBLE, NULL }
    address public owner;

    struct ParentTokenDetails{
        TokenType _tokenType;
        address _tokenOwner;
        mapping(uint => ChildTokenDetails[]) children;
    }

    struct ChildTokenDetails{
        TokenType _tokenType;
        address _tokenOwner;
        bool _locked;
    }

    mapping(uint256 => ParentTokenDetails) public tokenEnums;

    constructor(){
        owner = msg.sender;
    }
    
    function mintNonFungibleTokenEnum(uint256 tokenID)
        public
        returns (uint256)
    {
        uint256 newItemId = 1;
        ParentTokenDetails storage ptd = tokenEnums[tokenID];
        ptd._tokenType = TokenType.NON_FUNGIBLE;
        ptd._tokenOwner = msg.sender;
        ptd.children[tokenID].push(ChildTokenDetails(TokenType.NULL, msg.sender, false));
        ptd.children[tokenID].push(ChildTokenDetails(TokenType.FUNGIBLE, msg.sender, false));
        return newItemId;
    }

    // Getter Child tokens 
    function getChildTokens(uint tokenId) external view returns(ChildTokenDetails[] memory _tokens) {
        ChildTokenDetails[] storage ctdS = tokenEnums[tokenId].children[tokenId];
        return ctdS;
    }
} 
Related Topic