0

I'm trying to create a contract with a struct (Category) containing an array of another structs (Item). Item struct has a boolean variable minted with a default value of false. I'd like to change that variable to true after a token is minted.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Test {
    uint256 mintPrice = 50000000 gwei;

    struct Item {
        uint256 code;
        bool minted;
    }

    struct Category {
        string name;
        Item[] items;
    }

    mapping (string => Category) public categories;

    constructor() {
        // Create test categories with custom `Item` codes
        uint[] memory i = new uint[](3);
        i[0] = 4;
        i[1] = 5;
        i[2] = 9;
        createCategory("Test", i);

        uint[] memory j = new uint[](4);
        j[0] = 121;
        j[1] = 131;
        j[2] = 132;
        j[3] = 141;
        createCategory("Test2", j);
    }

    function createCategory(string memory categoryName, uint[] memory itemIds) public {
        Category storage c = categories[categoryName];
        c.name = categoryName;

        for (uint i = 0; i < itemIds.length; i++) {
            Item memory item = Item(itemIds[i], false);
            c.items.push(item);
        }
    }

    function getCategory(string memory categoryName) public view returns (string memory name, Item[] memory items) {
        Category storage c = categories[categoryName];
        return (c.name, c.items);
    }

    function getUnminted(string memory categoryName) public view returns (uint256 itemId) {
        // Find the first available "code" in current category
        uint256 unmintedItemId;
        Category storage c = categories[categoryName];
        for (uint i = 0; i < c.items.length; i++) {
            if (c.items[i].minted == false) {
                unmintedItemId = c.items[i].code;
                break;
            }
        }
        return unmintedItemId;
    }

    function mint(string memory categoryName) public payable {
        Category storage c = categories[categoryName];
        uint256 unmintedID = getUnminted(categoryName);

        // Category must be valid (must have items)
        require(c.items.length > 0, "Invalid category");

        // Category must have available items to mint
        require(unmintedID > 0, "No tokens available");

        // Amount must be valid
        require(msg.value == mintPrice, "Invalid amount");

        // mint the actual token (by using safeMint() from OpenZeppelin or similar function)

        // Change Item's "minted" variable to "true"
        Item storage currentItem = c.items[unmintedID];  // Obviously not working
        currentItem.minted = true;
    }
}

As commented in my code, in mint() function, after the token gets minted, I would like to change variable minted in Item with specific code to true. How could I accomplish this?

2 Answers 2

1

You could modify your getUnminted so it returns the index of the first unminted Item instead of its code. With that you then use this index to access the Item in the items array of the Category.

updated getUnminted function:

function getUnminted(string memory categoryName) public view returns (uint256 itemIndex) {
    // Find the first available "code" in current category
    Category storage c = categories[categoryName];
    for (uint i = 0; i < c.items.length; i++) {
        if (c.items[i].minted == false) {
            return i;
        }
    }
    revert("No tokens available");
}

updated mint function:

function mint(string memory categoryName) public payable {
    Category storage c = categories[categoryName];
    uint256 unmintedIndex = getUnminted(categoryName);

    // Category must be valid (must have items)
    require(c.items.length > 0, "Invalid category");

    // Amount must be valid
    require(msg.value == mintPrice, "Invalid amount");

    // Mint the actual token (by using safeMint() from OpenZeppelin or similar function)

    // Change Item's "minted" variable to "true"
    Item storage currentItem = c.items[unmintedIndex];
    currentItem.minted = true;
}
2
  • That's a very good idea, I actually changed it a bit more, returning the whole Item struct in my getUnminted() function, I think it will be more useful in other places in the contract. I just had to change getUnminted() to internal so I can return Item storage unmintedItem and then in mint() function changed the second line to Item storage unmintedItem = getUnminted(categoryName);. In the end of mint(), I modify it with unmintedItem.minted = true;. Thanks for help!
    – errata
    Feb 19, 2023 at 12:15
  • sounds good! you're welcome
    – timsntech
    Feb 19, 2023 at 12:49
0

If you ask how to access the bool from Category you can do something like this:

    function mint()public {
    categories["one"].items[1].minted = true;
}

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.