Ethereum Smart Contract Programming

I thinking about this owner modifier. The constructor is saving the address of the owner which is taken from the wallet (we can use our own or customer/employer), and then after deploy nobody will be able to change the value of the owner variable in the constructor, won’t they? That would mean if I as a owner will lose the account with the ownership address or I would die, without leaving my keys somewhere, nobody will be able to do modify the contract?

Yes, correct. The owner won’t be able to be changed. You could configure the contract to have multiple owners if you want that.

And just to confirm, code below means: // ownerToAnimals[msg.sender] is pointing which array of animals to use what is defined by msg.sender, then which animal to return by id, and the last returning string which the name of the animal; is my reasoning right?

Yes, that’s correct.

I have tried to return whole array, but it doesn’t work, it seems that I need to use some expansions to run it

Yeah, solidity doesn’t support returning entire arrays as of now. I haven’t played around with the experimental version of the encoder. But it looks like you got all the data from the array. But the AnimalType has been cast to an integer, since solidity doesn’t know how to return our ENUM. 0 means the first option in our enum and 1 the second, and so on.

1 Like

Hi @Darren, I’m so sorry to hear that. Was there something specific that was hard to grasp or that I didn’t explain? I would love to help you move forward.

I’m looking at re-recording these videos soon anyway. So if you can explain further what you thought was difficult when moving from Javascript and C++ to Solidity it would help me create better videos for the next release.

Hi Fillip,

Thanks for your response. I think it would be useful to maybe watch how Ivan explains things. He takes his time and everything he does he explains in a simple way to understand. In general I think it would be beneficial for students if you were to slow down and whenever you introduce a new concept to explain it more in depth and the relationship with other concepts. It’s nothing personal to you as you seem like someone I would enjoy learning from given the points above are addressed. When will the new content be available pls as I’m keen to come back to these tutorials but are currently investigating other sources to get a better understanding on these topics? Thanks in advance, Darren.

Thank you for your feedback Daniel, we really appreciate it. We will let you know when the new content is up :slight_smile:

Thanks a lot for answers!

Thanks again Fillip. To clarify my name is Darren not Daniel. Taking your time in future is key here in my opinion.

Regards, Darren.

1 Like

I have a problem with cost modifier, when adding dog by external contract I can’t to execute the function when value is below 1000, but when I would put more than that, it works, but doesn’t give refund like the genuine Dog contract.

I am not sure if I have done the task about getBalance properly, in the beginning I thought that I have to get balance of the external contract, but using return address(this).balance; didn’t work.

Can you help with it, @filip or anyone?

DogContract:

pragma solidity 0.5.1;
pragma experimental ABIEncoderV2;

import "./Animal.sol";

contract DogContract is AnimalContract {
    
    modifier cost(uint amount){
        require (msg.value >= amount);
        _;
        if (msg.value > amount){
            msg.sender.send(msg.value - amount);
        }
    }
    
    function addDog(string memory _name, uint _age) public payable cost(1000) returns (uint) {
        return _addAnimal(_name, _age, AnimalType.DOG);
    }
    
    // function getDog() public view returns (string memory) {
    //     address owner = msg.sender;
    //     return ownerToDog[owner].name;
    // }
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
}

ExternalContract:

pragma solidity 0.5.1;

contract DogContract{
    
    function addDog(string memory _name, uint _age) public payable returns (uint);
    
    function getBalance() public view returns(uint);
}

contract ExternalContractToUse{
    
    DogContract externalDog = DogContract(0xd1f1b7CCA23d135E2929Aa99ae06AcfBAeB412d3);
    
    function addDog(string memory _name, uint _age) public payable returns (uint){
        return externalDog.addDog.value(msg.value)(_name, _age);    
    }
    
    function getBalance() public view returns(uint){
        // return address(this).balance;
        // return address(0xCB38efEF50e2937060318Bf73cB194Ac65Ea2c79).balance; same as below but shorter
        return externalDog.getBalance();
    }
    
    // 0xd1f1b7CCA23d135E2929Aa99ae06AcfBAeB412d3 address of deployed DogContract;
    
}

I think the issue here is that the money is being sent to the ExternalContractToUse. Since you are not interacting with DogContract, you are not the msg.sender. ExternalContractToUse is the one calling DogContract, so that is the msg.sender in that case. I hope I mentioned that in the video.

If you want to refund the money back to the original sender, you can use tx.origin instead of msg.sender.

In this case you have a call chain of multiple contracts. You -> ExternalContractToUse -> DogContract.
From DogContracts perspective msg.sender will be ExternalContractToUse and tx.origin will be You.

So, that means I should get these surplus of wei back to the ExternalContractToUse?
Because DogContract takes over the whole amount of wei and when I would send transaction with 1500 wei, the balance of the DogContract would be 1500 wei instead 1000 wei with refund the rest wei either to me as original sender or either to the contract.

I think it is about implementing transfer instead send in the cost modifier, but when I use it there is an error, so probably first I should somehow send wei to the ExternalContractToUse and then use it within this “charged” contract.

Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.

    require (msg.value >= amount);
        _;
        if (msg.value > amount){
            msg.sender.transfer(msg.value - amount);
        }
    }
    

Alright, now I solved it.

My previous answer was still correct. If you change it to tx.origin, it will work as intended.

However, I was wrong in saying that your code would return the remaining balance to the ExternalContractToUse. It didn’t. And after some research, I realize why :smiley:

In order for a contract to receive funds by either send() or transfer() it needs to have a payable function to retrieve those funds. So this is solved by adding a payable fallback function to ExternalContractToUse. It can be empty. Like this:

function() external payable {
}

I found this while searching through the solidity docs.

Contracts that receive Ether directly (without a function call, i.e. using send or transfer ) but do not define a fallback function throw an exception, sending back the Ether (this was different before Solidity v0.4.0). So if you want your contract to receive Ether, you have to implement a payable fallback function.

The reason you didn’t get any error message was because we used send instead of transfer. Send() will not throw any error if it fails, while transfer() will. So if you use transfer the addDog call won’t work at all, unless you add the payable fallback function.

Good luck!

1 Like

Wow, man! You are amazing, I was searching in Google, but I didn’t know how to define this problem and found solutions were a bit confusing for me. Thank you for your effort!

Defenately I can’t be afraid of reading these Solidity docs, haha.

1 Like

Mappings Assignment:

After doing some more research on mappings and tinkering with the code for a while, I was able to add a counter feature to the addDog function which counts the number of dogs added to our dog structure/array. I also added a mapping that acts as a getter function to return the information about a specific dog using the counter to find its location within the structure/array.

pragma solidity 0.5.1;

contract DogContract{
    uint public dogCounter = 0;
    mapping(address => Dog) ownerToDog;
    mapping(uint => Dog) public dogNumber;

    struct Dog {
        uint id;
        string name;
        uint age;
    }
   
    function addDog(string memory _name, uint _age) public {
        dogCounter += 1;
        dogNumber[dogCounter] = Dog(dogCounter, _name, _age);
        ownerToDog[msg.sender] = Dog(dogCounter, _name, _age);

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

However, I was unable to make these functions and mappings work only for a specific address like the getDog function. In addition, I wanted to add the functionality of the dogNumber mapping, returning data about a specific dog, to the original getDog function, but I wasn’t sure how to do this while also retaining the function exclusive to a single address. I look forward to learning more on this though!

Any feedback for improvement on the code above is greatly appreciated!

Actually Filip I think I owe you an apology. You are a very good tutor & in hindsight the person who commented on my post was right I did panic when the tutor changed from Ivan. Also I done some other research and I’m starting to get my head round it now but I still have a huge amount to learn. I’ve got a month on leave from work now now and my target is to get @ least 200 hrs in programming. Anyway thanks for the great tutorials, I’m really enjoying it & sorry again.

Hmmmm, I’m not sure what you are looking to accomplish? This would give anyone the ability to query for dogs, and not just the owner. But it doesn’t solve the issue of owning multiple dogs in a good way, because I would have to remember my id. This would be a better solution imo.

pragma solidity 0.5.1;

contract DogContract{
    uint public dogCounter = 0;
    mapping(address => Dog[]) ownerToDogs;
    mapping(uint => Dog) public dogNumber;

    struct Dog {
        uint id;
        string name;
        uint age;
    }
   
    function addDog(string memory _name, uint _age) public {
        ownerToDogs[msg.sender].push(Dog(dogCounter, _name, _age));

    }
    
    function getDog(uint id) public view returns (string memory) {
        return ownerToDogs[msg.sender][id].name;
    }
}

But maybe I misunderstood your intention :slight_smile: Let me know!

No, don’t worry about it. I appreciate the feedback you gave me. But I’m happy to hear that you managed to continue :slight_smile: Let me know if you have any questions along the way.

Yes, the getDog function you have returns a particular dogs name like I was trying to achieve.

Well, I admit my intention might be a bit far-reaching for my current skill level this early in the course :sweat_smile:

However, my understanding of the problem is that one address can only hold one dog and that dog is replaced whenever we add a new dog. So my attempted solution was to enable one address to hold more than one dog, since some people have multiple dogs, without replacing the first one with a new one.

The problem I encountered was that I could not make these dogs accessible by only the address holder and nobody else. Could this simply be done by replacing public with private or maybe internal?

Also, I see the issue with using an arbitrary id to look up the dogs. Instead, could we enter the name of the dog as an input in the getDog function and have the output also be its name if the dog is indeed already included?

I know the attempt below to do is wrong because I keep getting an error saying member name is not found or visible. Is it because there is no initial name value for the function to compare? Or does the code just make no sense as it’s written? (I know both could be true :confused:)

function getDog(string memory name) public view returns (string memory) {
        require(ownerToDogs[msg.sender].name == ownerToDogs[msg.sender].name);
        
        return ownerToDogs[msg.sender].name;
    }

Thanks in advance. I don’t mean to be such a bother this early on.

Ok, thanks for clarifying. I’m very happy to see that you are exploring things on your own, you will learn a lot. But your previous code did not solve it in a very intuitive way. As I said, the owner needs to keep the dog Id in order to get their dogs. Then you could just replace the dog function so that the user inputs the dog id and it returns the dog with a particular id from the dogNumber mapping. Then you don’t need the ownerToDog mapping anymore.

If you want to be able to store multiple dogs connected to one address, you should put them in an array. You can’t get them from the mapping if it’s just a mapping to one dog.

Your last function doesn’t really work. You are comparing ownerToDogs[msg.sender].name to itself. It will always be true, regardless. It’s like comparing is 1 equal to 1. It will always be true.

I assume you wanted to do something like this.

function getDog(string memory name) public view returns (string memory) {
        require(ownerToDogs[msg.sender].name == name);
        
        return ownerToDogs[msg.sender].name;
    }

But that does nothing to solve the problem, because the mapping stil only holds one dog per owner. You need the array, as I wrote in my last response :slight_smile:

1 Like

Got it. So I rewrote most of the contract. Now, a user would be able to store multiple dogs in an array, search for them by an ID the user assigns, and check the number of dogs they own. It also includes a modifier so only the address owner can add dogs to the array.

However, when I try to add a function, searchByName, to search for a specific dog by its name, it just returns an empty string.

pragma solidity 0.5.1;

contract DogContract{
    
    Dog[] dogs;
    address internal owner;
    
    uint public numberOfDogs = 0;
    mapping(address => Dog) ownerToDog;
    mapping(uint => Dog) public searchById;
    
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
    
    constructor() public {
        owner = msg.sender;
    }
 
    struct Dog {
         uint id;
         string name;
         uint age;
     }
     
    function addDogs(uint _id, string memory _name, uint _age) public onlyOwner {
        numberOfDogs += 1;
        dogs.push(Dog(_id, _name, _age)) - 1;   
        searchById[numberOfDogs] = Dog(_id, _name, _age);
    }
    
    function searchByName(string memory _name) public view returns (string memory) {

        return ownerToDog[msg.sender].name;
    }
}

I kept the ownerToDog mapping solely for this function because I was having a hard time figuring out how to return a dog’s name by searching for its name in the array.

Also, when I include the requirement require(ownerToDog[msg.sender].name == name);, it says the compare operator, ==, is not compatible with types string storage ref and string memory ref. I’m unsure how to fix this since function calls cannot be saved in storage.

Again, thanks for the feedback and support!

Good job! And sorry about my spontaneous code not working :smiley: I’ll comment that at the end. The array you created should be part of the mapping, as the value, like I showed you. Otherwise you will have a hard time to know who’s dog is who, or you will store an unnecessary large amount of data.

You can’t really search through an array in solidity. Your require statement will unfortunately not help you with that. That’s why we have mappings, so that you can instantly look up data based on address or other properties.

You could, if you want, create a mapping that maps dog names to dogs.

Regarding the error you got with the code. I forgot that we have two different data locations on the strings, which means we can’t compare them using ==. If we’re gong to do that, we need to use something called Stringutils (https://ethereum.stackexchange.com/a/4562). But I don’t recommend you to continue on this path, because it won’t help you creating a search function.

Thank you, and no worries. That’s good to know. The concept of mappings is still fairly new to me, but I’ve seen examples of mappings holding other mappings in addition to arrays, so I’m excited to learn the limits of their usage. I’m also aware that gas costs and data size will be an important consideration when creating contracts for production. I just haven’t reached that point of testing yet.

That’s very interesting. I just recently watched a video that made a comment suggesting that same point about arrays, but it did not go into great detail. They continued to explain that a loop would be required in order to output all the data within an array. They did not clarify that was why mappings are so necessary in Solidity, but I guess that was implied.

I’ll probably return to this code to attempt a mapping of dog names to dogs after another Solidity deep dive. Based on the link you provided about StringUtils, they don’t seem to be a very popular solution unless they are absolutely required (like Oracalize queries) because of higher gas costs. But it’s interesting to know there is a way to compare each character between string literals.

For now, I’m eager to expose myself to more of the basic capabilities of the language. I don’t want tinkering with this code to distract me from learning more useful concepts, as I’ve noticed many of my questions being answered simply by continuing with the curriculum :sweat_smile:. I am grateful for your feedback and patience thus far though.