Based on my Debug observation:
I have tested that:
- Firstly I insert data into my map data-structure as I explained in my question.
- Secondly, I have returned the address of first mapped index, which is 0, this could be done for other indexes as well (0:10).
- Without a delete operation I can retrieve the data perfectly fine from the address of mapped Foo[]. But if I did the delete operation, Solidity gives an error.
This debug result: leads me to conclude that memory is freed or address does not contain any valid information.
Test code:
set_txn_hash = my_contract.transact().map_insert();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
for x in range(0, 9):
for y in range(0, 9):
output = my_contract.call().get_(x, y);
print(output); //returns correct value.
set_txn_hash = my_contract.transact().map_remove_map();
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
set_txn_hash = my_contract.transact().get_map_address(0);
contract_address = unmigrated_chain.wait.for_receipt(set_txn_hash)
output = my_contract.call().get_map();
print(output);
output = my_contract.call().try_();
print(output); //Solidity gives error if delete map_remove_map() is called
Additional functions inside my Contract:
function map_insert(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
add(i, i*10 + j);
}
function map_remove_map(){
for(uint i = 0; i < 10; i++)
delete_(i);
}
function map_remove_map_item(){
for(uint i = 0; i < 10; i++)
for(uint j = 0; j < 10; j++)
delete_items(i, j);
}
function get_map_address(uint id) {
uint addr;
Foo[] a = foo[id];
assembly{
addr := a
}
map_address = addr;
}
function get_map() constant returns (uint){
return map_address;
}
function try_() constant returns (uint ){
uint addr = map_address;
Foo[] a;
assembly{
a := addr
}
return a[1].x;
}
This can be achieved by the following:
Declare storage variables at the contract level
mapping(address => uint256[]) public userOwnedTokens;
mapping(uint256 => int256) public tokenIsAtIndex;
mint(tokenId) {
// Prior minting logic from OpenZeppelin
userOwnedTokens[msg.sender].push(tokenId);
uint256 arrayLength = userOwnedTokens[msg.sender].length;
tokenIsAtIndex[tokenId] = arrayLength;
}
To get all user tokens without loop, all you have to do is, this is possible as the access specifier for userOwnedTokens is public
contractInstance.methods.userOwnedTokens.call(address) // THIS WILL RETURN AN ARRAY OF TOKEN IDs
In case of transfer of tokens, change tokenIsAtIndex[tokenId] to -1
transfer(from, to, tokenId) {
// Transfer logic
uint256 tokenIndex = tokenIsAtIndex[tokenId];
userOwnedTokens[from][tokenIndex] = 000; // TO DENOTE THAT THE TOKEN HAS BEEN TRANSFERRED, YOU CAN USE ANY OTHER NUMBER
}
Next time when userOwnedTokens
is called you can keep a check wherever the tokenId is 000, those tokens have already been transferred and you can decide not to show it to the user on the frontend.
I hope it helps!!!!!
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)
So basically you would have to call getUserItems function once, and getItemInfo for every item that this user owns