use mappings inside structs in solidity - ethereum

After including a mapping inside a struct, when trying to pass the struct into a function, it has to be "storage" instead of "memory". (the "checkBalance" function is just an example here)
Is it a good practice to store mapping inside struct?
Any alternatives to the mapping type inside the struct? Thought of using array but it has to do the iteration to find the item.
library MappingLib {
struct Balance {
uint256 value;
mapping(string => uint256) positions;
}
function checkBalance(Balance storage balance, string memory key) public returns (uint256) {
return balance.positions[key];
}
}

First of all mappings and any other data structures that need to be persisted in-between contract calls are always stored in storage, whether they are part of any struct or not. Now answering your questions:
Yes, it's perfectly fine to have mappings inside structs
In your particular case mapping seems to be the most natural option as you want to access values by keys

You could put the mapping outside the struct
`mapping(string => uint256) positions;`
Pass in the string and get the value or you could just create the key in the mapping
`mapping(uint256 => Balance) positions;`

Related

Is there a a way to overload 'is' operator?

I designed a class which wrap an internal json object.
My class acts like a real json, returning a Map or a List depending of the current intermediate node (a json is made of Maps, Lists and values).
This class overrides runtimeType to use it's current json type :
#override
get runtimeType{
return (_json.runtimeType);
}
This indeed overrides the type to an _InternalLinkedHashMap<String, dynamic> which is what the json uses for a Map or to an List<dynamic> for a List.
But this is not enough to override tests like (result is Map) or (result is List) which works for json but not for my class.
I try to overload operator is but i don't find how to.
is there a way to do that ?

Solidity : submit string array, key value pair or an object as parameter to a function

In order to change the state of the smart contract from front end inputs, wanted to submit string array to a smart contract , key value pair or objects.
Is it possible to use string array as parameter?
No solidity doesn't support arrays of strings as parameter. You would have to serialize and deserialize it in a string yourself to have the desired result but that would be expensive to do in solidity. You can test that on remix if you want. However, on remix the error message says that this function is supported in the experimental ABI encoder but I have never tested that, or how well it works with other libraries, and it is experimental after all.
As seen in below example from solidity document we can send bytes array to constructor
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
If you are trying to send string/Objects data specifically then it's better to separate out the methods and call each methods separately or within each other as currently solidity does not support that (using ABIencodere v2 is exceptional as it is only recommended for development purpose- as per on the date of this answer written)
struct A{
uint date,
B[] b
}
You can separate this out to
struct A{
uint date
}
struct B{
string goods,
uint quantity
}
so now for 1 A you can call N B from your service. Use mapping for binding both(if dependent).
In current situation it's better to design a contract which does not take bulk inputs or give out bulk outputs. However contracts are not for storage of huge data it's for storage of related data which fulfills agreement between parties

Retrieving multidimensional balance

I am developing an Ethereum based Card Game. A user can collect n amount of individual/unique Cards. Everything is up and running, I am using the following balance mapping:
mapping (address => mapping (uint256 => uint256)) balances;
The first uint is the Card ID, the second uint is the Card count. I will have up to 1000 Cards, right now I am testing with 700 Cards.
I retrieve the balances on DApp Start by calling:
function balanceOf(address _owner, uint256 _id) view external returns(uint256) {
return balances[_owner][_id];
}
for every single ID. On balance changes I do partial balance updates. This generally works. It is free, but it is also extremely slow as the initial retrieval call has to be done 640 times. I have researched a lot and also tried various implementations, but the main problem is that I need to retrieve an address mapped array holding the Card ID and Count information. Currently you can not easily retrieve dynamic sized Arrays or Structs.
What would be the proposal to resolve the issue? Am I stuck with up to 1000 balanceOf calls on DApp Start until Solidity introduces simple Array calls?
I thought about caching data on my WebServer, but for this to work I would need to run a node on the WebServer which I would like to avoid.
A Client based caching, where the Client posts the balance to the WebServer may also run into an inconsistent state because of the asynchronous nature of the Blockchain.
You can also use struct for managing data more easily. I made one contract using struct and retained data too. Please let me know if this approach didnt work for you.
pragma solidity ^0.4.18;
contract Test{
struct User{
uint cardId;
uint cardCount;
}
address user_address;
mapping (address => User) public Users;
function add_user() public {
user_address = msg.sender ;
var new_user = Users[user_address];
new_user.cardId =2;
new_user.cardCount = 50;
}
function get() public returns(uint,uint)
{
return(Users[user_address].cardId,Users[user_address].cardCount);
}
}
Your best chance is to use a secondary mapping to store the card IDs some user has, query it first, and than, for each ID, check the count for that user and that card ID.
Here is the code, tested on Remix:
pragma solidity 0.4.24;
contract Test {
mapping (address => uint[]) cardsOwned;
mapping (address => mapping (uint => uint)) cardsCounter;
function cardsOwnedBy(address _owner) view public returns (uint[]) {
return (cardsOwned[_owner]);
}
function cardsCounterFor(address _owner, uint _id) view public returns (uint) {
return cardsCounter[_owner][_id];
}
}
I kept on trying various different solutions but couldn't find any good way to handle this. I found a setup which will work for me for now, until Solidity is updated to be more functional when it comes to array handling and especially dynamically sized variables.
Fastest solution for my requirements:
mapping (address => uint256[1000]) public balances;
The mapping now assigns addresses to a fixed size array. I can now retrieve the full list on DApp Start by using:
function balancesOf(address _owner) view external returns (uint256[1000]) {
return balances[_owner];
}
The main advantage is that it is extremely fast, compared to any other solution. The main disadvantage is, that I lost my dynamically sized array and I have to know the maximum Card Count in advance - which I do not. I used a safety buffer now but if I hit the 1000 Cards mark I will have to update the contract. Hopefully there will be better solutions in the future.

Return a whole struct in solidity?

Let's say I have a struct & a mapping like this:
struct myStruct {
address addr;
uint256 price;
bool approved;
}
mapping(string => myStruct) mappy;
How can I retrieve all the keys?
I know solidity will generate getters so if I have the key I can retrieve the info from inside the struct. But the keys are unknown to me and I need to retrieve the complete struct.
Maybe a better solution would be to have a public variable which would be the size of the struct and an index has key and store the key in the struct?
That way I will know the size and I suppose I can iterate it
First thing is that you shouldn't use string as key, better declare bytes32 as string is just an alias for it.
you can make a call to the mapping giving the string key as reference.
However it will retrieve only an iterative array without the internal keynames.
let's say for example you have :
mappy['a'] that contains the following information
{
'0x000...000',
1,
true
}
the call will retrieve an simple array like this : ['0x000...000',1,true]
If you want to make it an object you'll have to rebuild it on your own.

Solidity storage struct not compiling

I'm new to solidity and I'm just experiencing with some simple code.
Basically I just want to create a struct that will store data permenantly inside a contract. According to the doc I need storage for that.
But the following code is not compiling except if I use memory instead. Why?
pragma solidity ^0.4.11;
contract test {
struct Selling {
address addr;
string name;
uint price;
}
mapping(string => Selling) selling;
function sellName() constant returns (bool ok)
{
address a = 0x4c3032756d5884D4cAeb2F1eba52cDb79235C2CA;
Selling storage myStruct = Selling(a,"hey",12);
}
}
The error I get is this:
ERROR:
browser/test.sol:16:9: TypeError: Type struct test.Selling memory is not implicitly convertible to expected type struct test.Selling storage pointer.
Selling storage myStruct = Selling(a,"hey",12);
^--------------------------------------------^
When you first create the myStruct instance, it will be created in memory and then written out to storage (assuming you put the object into your selling map and don't declare your method constant) when the function returns. If you were to retrieve the item from your map in another function, then you would declare it as a storage variable.
See this explanation on Ethereum StackExchange for more details. The Solidity documentation also has a very good explanation of how variables are stored and when to use memory vs storage.
I had similiar situation, the fix for this was:
function sellName() constant returns (bool ok)
{
address a = 0x4c3032756d5884D4cAeb2F1eba52cDb79235C2CA;
Selling memory myStruct = Selling(a,"hey",12);
// insert 'myStruct' to the mapping:
selling[a] = myStruct;
}