0

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

0

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

0

You want a mapped struct with index, and you want to store inside a mapped struct with index.

Mapped, so you can resolve keyExists(key) or getDetails(key) in one move. Indexed, so you can count the keys and enumerate the list.

Each time you insert a mapped struct you should also append a key to the index, taking care to avoid duplicate keys.

struct MemberStruct {
   bool isMember; 
   // ... carry on
}

mapping(address => MemberStruct) public memberStructs;
address[] public memberList;

That is a basic structure that covers most bases. If you don't need to delete, life is easier. Have a look over here for better examples: Are there well-solved and simple storage patterns for Solidity?

You can use that pattern to maintain two lists in a single contract. What you're describing is a one-to-many join. You probably need the CRUD operations for both tables, plus a way to enforce referential integrity.

To accomplish that, you'll have to manage bi-directional pointers in both tables. https://medium.com/@robhitchens/enforcing-referential-integrity-in-ethereum-smart-contracts-a9ab1427ff42

As Mike quite rightly pointed out. It's vital that you maintain internal organisation so you can complete all operations without unbounded for loops or recursion. If you find yourself looping through records, it's a sign things are off track. You have to arrange things so the needed row, for example, is known immediately.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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