Solidity questions

@filip Hi I was looking into the mapping example you gave in the DogContract, I modified without storing the id just the Dog struct in the mapping like this, is it still correct?

`pragma solidity^0.4.0;

contract DogContract{

struct Dog {
    string name;
    uint age;
}

Dog[] dogs;
mapping(address => Dog) ownerToDog;

event addedDog(address owner, string name);

function addDog(string _name, uint _age) public {
    address owner = msg.sender;
    var newDog = Dog(_name, _age);
    dogs.push(newDog);
    ownerToDog[owner] = newDog;
    addedDog(owner, _name);
}

function getDog() returns (string) {
    address owner = msg.sender;
    return ownerToDog[owner].name;
}

}`

Yes and no… Your code will execute fine, however;

  • Your storing two instances of the Dog struct, one in the dogs array and the other in the ownerToDog mapping.
  • There’s no link between the two stores. You can’t get a dog from your dogs array and find it’s owner, the same way that you can’t get a dog for address and find it in the array.
2 Likes

Thanks! you are right it makes sense

 function addDog(string _name, uint _age) internal {
    address owner = msg.sender;
    uint id = dogs.push(Dog(_name, _age));
    ownerToDog[owner] = id;
    addedDog(owner, _name, id);
}

what happen if the user calls this function multiple times? can a owner own multiple dogs? if not how can this be impemented?

If the user calls this function multiple times, multiple dogs will be created and stored in the dogs array. But the reference id will be overwritten. So your ownerToDog mapping will only point to the latest Dog in the dogs array, and all other instances will be stored indefinitely.

There’s many solutions, depending on what kind of data you want to collect. For example, I like arrays for getting total counts of dogs, something you can’t do with a struct.

Presuming your users can only have one dog, like it seems like you want? My solution would look like this;

pragma solidity ^0.4.22; // Revision 22 allows require to emit messages

contract DogContract {
	
	struct Dog {
		string name = "";
		uint age = 0;
	}

	mapping(address => Dog) ownerToDog;

	event addedDog(address owner, string name);

	function addDog(string _name, uint _age) public {
		// Generate hash (no owner can have two identical named dogs)
		bytes32 dogHash = keccak256(msg.sender, _name);
		// Ensure dog doesn't exist
		require(ownerToDog[msg.sender].name != "", "You already own a dog");
		// Create our dog
		ownerToDog[Dog(_name, _age)];
		// Notify
		emit addedDog(owner, _name, id);
	}

	function getDog(string _name) returns (string) {
		string dogName = ownerToDog[msg.sender];
		require(dogName != "", "You don't own a dog");
		return dogName;
	}
}

Limitations:

  • User can only have one dog (You could make a transfer function)
  • Can’t count total dogs
  • Probably more, this is fairly limited

Here’s a way to think of mappings, they can be imagined as objects in javascript;
Take the ownerToDogs mapping for example, it would look like this in javascript;

// Create an object of dogs and store in ownerToDogs variable
var ownerToDogs = {
          "abc1": {
              name:"a",
              age:1
          },
          "cba2": {
              name:"b",
              age:2
          },
          "xyz3": {
              name:"c",
              age:3
          }
}

Then in Javascript we can get a dog like so

var dog = ownerToDog["xyz3"];

So it functions exactly the same, you also can’t count how many dogs are in the program storing this way in Javascript, just like solidity.

thanks, I was asking about a solution to allow a user to have many dogs, like cryptokitties, you can have multiple kitties and list them. Can you map and array of ids on an address like this?

mapping (address => uint[]) private ownerDogs; 

and everytime you add a dog just do

ownerDogs[msg.sender].push(dogId);

Yes you 100% can have arrays as mapping values. This was going to be my next solution, but requires more work checking if things exist already.

You can even have mapping(address => Dog[]) private ownerDogs;, which would be an array of Dog objects mapped to an address.

I should add a third level as well, you can have a mapping inside a mapping!

mapping(address => mapping(bool => Dog[])) private ownerDogs

where the bool could be dogs alive, allowing you to collect dogs by alive or dead for an address :joy:

Solidity visibility question:

Hi,
This is my smart contract and I set visibility as internal to one of the functions below. Why the function ‘addDog’ is not displayed in Remix when I deployed the contract? Shouldn’t it be displayed/accessed in the same contract? How do I access this in same contract then?

pragma solidity ^0.4.2;

contract DogContract {
    struct Dog {
        string name;
        uint age;
    }
    
    Dog[] dogs;
    mapping (address => uint) OwnerToDog;
    
    function addDog(string _name, uint _age) internal {
        address owner = msg.sender;
        uint id = dogs.push(Dog(_name, _age));
        OwnerToDog[owner] = id;
        
    }
    
    function getDog() returns (string) {
        address owner = msg.sender;
        uint id = OwnerToDog[owner];
        return dogs[id-1].name;     
    }
}

Hi Raghu,

Your internal means the function is only visible to itself (defining contract) and contracts that extend it like BigDogContract is DogContract. The function is not callable by the outside world or contracts that have an instance of it. For example, if our BigDogContract contained an instance of the dog contract like DogContract myDog = DogContract(DOG_CONTACT_ADDRESS);, then it wouldn’t be able to call the internal function either. To make this visible in remix, you wold need to make it public.

From the solidity documentation on visibility and security;

Functions can be specified as being external, public, internal or private, where the default is public. For state variables, external is not possible and the default is internal.

external:
External functions are part of the contract interface, which means they can be called from other contracts and via transactions. An external function f cannot be called internally (i.e. f() does not work, but this.f() works). External functions are sometimes more efficient when they receive large arrays of data.
public:
Public functions are part of the contract interface and can be either called internally or via messages. For public state variables, an automatic getter function (see below) is generated.
internal:
Those functions and state variables can only be accessed internally (i.e. from within the current contract or contracts deriving from it), without using this.
private:
Private functions and state variables are only visible for the contract they are defined in and not in derived contracts.

1 Like

Thank you Mitch. It helps.