[Ethereum] List of items per address

arrayscontract-designcontract-developmentsoliditystorage

I'm working on a dApp and I am trying to figure the following issue. I want to map an address to a dynamic list of items in one of my contracts. In this way when a user logs, he/she has their own list of items (a bit like cryptokittens work, for each address you can have the list of kittens for that address). My question is: can I do this mapping? Is it bad/not efficient? I have been told it is, and I couldn't really figure how to do it efficiently. Some people I work with suggest to put this info offchain. Is this the right approach? I would rather have everything onchain.

Thank you

Best Answer

This is not efficient in a way that you would have to call for every single item that user owns because currently you cannot return list of structs in solidity. But you can easily return list of ids of items that belong to the user and then get information about this items (unfortunately also one by one). But as this function returning information about items are "view" functions, they do not cost any gas. If user does not have dozens or hundreds of items, I don't think this is the problem.

The below code is just an example, it lacks security logic (anyone can call any function)

pragma solidity ^0.4.21;

    contract ItemsAndUsers {
    struct Item {
        uint id;
        string nameOfItem;
        string typeofItem;
        uint value;
    }

    //In this form user struct doesn't make much sense as it has only one field, 
    //but we can imagine it being more complex
    struct User {
        address userAddress;
    }

    Item[] public allItems;
    User[] public allUsers;

    mapping (address => uint[]) public userItemsIds;
    mapping (uint => address) public itemIdToUser;

    function createUser() public {
        uint[] memory items;
        User memory user = User({
          userAddress: msg.sender
        });
        allUsers.push(user);
    }

    function createItem(string _name, string _type, uint _value) public {
        Item memory item = Item({
           id: allItems.length,
           nameOfItem: _name,
           typeofItem: _type,
           value: _value
        });
        allItems.push(item);
    }

    function assignItemToUser(address _userAddress, uint _itemId) public {
        itemIdToUser[_itemId] = _userAddress;
        userItemsIds[_userAddress].push(_itemId);
    }

    //This function will return list of ids of all items belonging to the _userAddress
    function getUserItems(address _userAddress) public view returns (uint[] items){
        return userItemsIds[_userAddress];
    }

    //This function returns all information about single item
    function getItemInfo(uint _itemId) public view returns (uint id, string nameOfItem, string typeofItem, uint value) {
        Item memory item = allItems[_itemId];
        return (item.id, item.nameOfItem, item.typeofItem, item.value);
    }
}

So basically you would have to call getUserItems function once, and getItemInfo for every item that this user owns

Related Topic