Inheritance & External Contracts - Discussion

@gabba

I have the same question as @Brian_Lenertz

The Quiz Question is:
What are some of the benefits of using inheritance as part of your code?
We both agree with the two benefits given in the correct answer. However, by reducing code duplication, wouldn’t that also mean that the gas cost is reduced? Or could the code duplication just be factored out by enabling optimization, thus reducing any associated additional gas cost even if we didn’t use inheritance?

I also included Decreased compilation time in my answer as another possible benefit of using inheritance. My thinking was that if there is less code, it will be quicker to compile — is there a reason why that might not be the case?

@jon_m when your contract inherit from an other contract the code gets injected in the contract to be deployed, so it will not affect the gas cost.

I think by saying Reduces code duplication we should take it as:

  • Instead of declaring an onlyOwner modifier in every contract you create, you just make your helloWorldX.sol inherit of the Ownable contract, so you don’t have to write again your modifier, it makes you saved time.

And not as:

  • It will help you to not write the sames functionality multiples times in your contract, because you can use a modifier for this purpose you don’t need to inherit from an other contract.

To make it short

contract HelloWorld1 is Ownable {
....
}

Is cleaner and it reduces code duplication because you will not write your modifier again and again in every contract

contract HelloWorld1 {
    address public owner;

    modifier onlyOwner(){
        require(msg.sender == owner);
        _; 
    }

    constructor() public{
        owner = msg.sender;
    }
}

contract HelloWorld2 {
    address public owner;
    ......
}

Yes it makes sense


Enabling optimization is not always the best thing to do.
For example let’s say i have two functions using this code where senior is in the storage and age is in memory.

if (_age > 90){
   senior++;
}

If i don’t use the compile optimization, my contract will be more expensive to deploy because there is code duplication, but it ll be cheaper to execute.

Why cheaper to execute ? Because with compiler optimization, the compiler will find the same OP code in two different location, so it will store it at an unique address in the contract.

  • During compilation: The compiler will store this OP code at a specific address
  • During the execution: ADD the value to the stack, and add a JUMP to an address where this code is located.

So during the execution we will execute more op code. But during the deployment our contract will be smaller to deploy.

https://solidity.readthedocs.io/en/v0.6.0/using-the-compiler.html

ref:

If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, set it to --optimize-runs=1

1 Like

Wow! Some really interesting points there with optimization. I’ve got the basic concept but definitely something to keep thinking about and gradually understand in more depth as I continue to learn and experiment. I think it will become a lot clearer when I start to understand more about what the different Op Codes actually do.

So, if we have 3 contracts (A, B and C) and we want…

Example 1

  • B to inherit from A
  • C to inherit from both A and B

… we can code:

contract A {}
contract B is A {}
contract C is B {}
/*
contract C is A, B {}
No need to add A here because C automatically inherits A from B
*/

Is the correct term for this type of contract composition multi-level inheritance?

Example 2

Now let’s say we want both B and C

  • to inherit from A (as in Example 1)
  • but not from each other (different to Example 1)

In other words, instead of 3 “generations” with C being A’s “grandchild” and B’s child,
we now have just 2 “generations”, with C being A’s child (as well as B).

I assume we would code this as follows:

contract A {}
contract B is A {}
contract C is A {}   // only difference to Example 1 where:  C is B 

Is the correct term for this type of contract composition hierarchical inheritance?

Example 3

Now let’s say we want to keep the same contract composition as in Example 2, but also add an additional contract (contract D) which will inherit from contract A, B and C.

We now have 3 “generations” again, with contract D being A’s “grandchild” and child of both B an C.

For our additional contract D, would we add the following to our Example 2 code?

contract D is B, C {}
/*
contract D is A, B, C {}
No need to add A here because D automatically inherits A from B and C.
Is that correct?
*/

Is the correct term for this type of contract composition multiple inheritance?

2 Likes

Yes exactly @jon_m :+1:
There is also the single Inheritance which is obvious.

contract A {}
contract B is A {}
2 Likes

Hi @filip ,
I am having trouble deploying the external contract, it keeps failing.
Here is my code:

pragma solidity 0.5.12;

//interface
contract HelloWorld{
function createPerson(string memory name, uint age, uint height) public payable;
}

//contract
contract ExternalContract{

HelloWorld instance = HelloWorld(0x70371a21552E8868df8E31520d9689a7b47c7C0a);

function externalCreatePerson(string memory name, uint age, uint height) public payable{
    //CALL createPerson in HelloWorld contract
    // Forward any ether to HelloWorld contract
    instance.createPerson.value(msg.value)(name, age, height);
}

}

This is the error i keep getting:
[vm]

from:0x703…c7c0a

to:ExternalContract.externalCreatePerson(string,uint256,uint256) 0xeef…be804

value:1000000000000000000 wei

data:0x7f2…00000

logs:0

hash:0x28a…8dc56

Debug

transact to ExternalContract.externalCreatePerson errored: VM error: revert. revert The transaction has been reverted to the initial state. 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.

Hi @mjwatson10,

Sorry for the delay in responding.

This error will occur if you have just loaded Remix before you deploy HelloWorld and External, but then don’t update your External contract for the new HelloWorld address (which changes each time you deploy it) here:

Payable instance = Payable(0x4***WhateverYourPreviousHelloWorldAddressWas***87);
/* This needs to be changed to the new address of the Hello World contract you
   you have just deployed, otherwise the HelloWorld instance created in External
   contract isn't actually interfacing with the HelloWorld contract deployed */  

We should do this each time we redeploy our HelloWorld contract, but for some reason it doesn’t throw an error if we redeploy, don’t update, and we are still using the same “session” in Remix before closing it in our browser. I’ll ask @gabba if he can shed any further light on this. Maybe it’s because the current session somehow stores our previous deployment of HelloWorld in memory… but I’m not 100% sure.

Anyway, having said all this, maybe you’ve already solved it yourself, in which case, great! If not, I hope this helps. Either way, let us know if you are still having any problems.

1 Like

Hi Filip,

i was thinking about what happens when the external contract does not exist anymore.
If the external contract has been destroyed all ether is getting lost (as far as i understood from the reading assignment).

Assumably a contract using any external contract, that has been destroyed, is broken.
Even if i could handle to make the link to the external contract dynamic / changeable
i would surely face the issue that the interface is not valid any more.

Are there any best practices how this can be handled ?
(or is this even explained in any further topics or course ?)


Started the Contract Security Couse now i think most of my open questions (like the updatatbility issue and unit testing) are covered there. !

1 Like

Hi @matren!

Great question!

My understanding is that our external contract is like a customer of the HelloWorld contract, using the services of its createPerson function instead of having to build and run its own. The price for doing this is the Ether that the external contract sends to HelloWorld. So, if the external contract was destroyed, then surely this would not affect HelloWorld, as it would just continue to be deployed and executed as normal, but just with one less customer. The interface with HelloWorld exists within the external contract, and so would be destroyed along with the external contract, if this were to happen — so I don’t think this would “break” anything in HelloWorld.

I hope I haven’t misunderstood your question. I’m tagging @gabba, as I’d like to get his input on this as well, and he would be the best person to comment on any best practices that may exist to mitigate any associated risks in this area.

1 Like

Hi!

When trying to instantiate the HelloWorld contract from External.sol I had this compilation error:

browser/External.sol:12:38: SyntaxError: This looks like an address but has an invalid checksum. Correct checksummed address: "0x692a70D2e424a56D2C6C27aA97D1a86395877b3A". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals
    HelloWorld instance = HelloWorld(0x692a70d2e424a56d2c6c27aa97d1a86395877b3a);
                                     ^----------------------------------------^

I entered my HelloWorld contract address in Etherscan:

0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

https://etherscan.io/address/0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

And I noticed that Etherscan formats it in a slightly different way (note the upper/lower case differences):

0x692a70D2e424a56D2C6C27aA97D1a86395877b3A

Using the Etherscan formatted address fixed the problem for me.

Maybe this is helpful for someone else.

Best!

1 Like

Hi @mjwatson10 ! Have you specified an ether value for the transaction?

1 Like

Hi @dAnijboned,

I wondered that at first, but if you look at the transaction error detail he’s provided in his post, you’ll see that he has, because it logs a value of the wei equivalent to 1 ether. I’m pretty sure the problem/solution is as I replied here.

Also, thanks for your post about the contract address format issue you experienced. I’m not sure what’s going on there to be honest, so I’m tagging my colleague @gabba to see if he knows anything about it. Glad you managed to sort it though :+1: Was it only with that one contract address that you experienced this format error, or has it happened with several? It’s the first I’ve heard of this problem, to be honest, but thanks for sharing how you solved it as, like you say, it may well help others out.

Hi @jon_m , yes you’re right (I didn’t notice the wei value). Thanks!

Regarding the address format, it happened to me with 2 different addresses.

Best!
//Dani

1 Like

Hi @dAnijboned

This address on etherscan is correct :

0x692a70D2e424a56D2C6C27aA97D1a86395877b3A

this one is not :

0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

An EIP has been implemented to verify the address checksum and avoid sending funds to the wrong contract

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md

All wallets are supposed to accept Ethereum addresses expressed in capital or lowercase characters, without any difference in interpretation since EIP-55, UpperCase addresses are used to validate a checksum.

Remix is just using this checksum validation to make sure you are not using a wrong address in your contract

1 Like

In the quiz about inheritance there is this question:

What are some benefits of using inheritance as part of your code?

Your Answer

check_box Reduces code duplication

Correct Answer

check_box Reduces code duplication

check_box Creates a clear code structure

I disagree that inheritance creates a clear code structure. In a lot of cases (not all) it makes it harder to read and easier to miss things because you have to dig into the parent contracts. I come from C# for

1 Like

Thanks for the explanation @gabba !

1 Like

Hey Iris,
I had the same problem and solved the issue by creating a new account/wallet by clicking on the ‘plus icon’ next to the “account” Title.

1 Like

Hi @chadrickm,

Sorry for the delay in responding to you on this.

There are always many aspects in any field of work that are subjective and experienced differently by different individuals — after all, we all work, organise our thoughts, and structure processes in different ways. So, I can appreciate what you’re saying here. I guess the whole question comes down to whether you as an individual find it easier to process a large amount of detailed information in less separate and compartmentalised locations, or prefer more “bite-sized” packages of information (i.e. less detail in just one place) but with a higher number of different (yet interconnected) locations where you need to go to access that information. Do you prefer a bag with lots of different pockets, compartments, zips and fastenings, to carry around your personal items; or do you prefer one with just one large internal space to hold everything all together (without having to deal with all the zips and fastenings)?

If, as individual programmers, we were the only people who ever had to read, modify, debug and continue developing the code that we ourselves have written, then I think you could argue that each person could structure their code in either of the two ways I’ve described above, depending on our own personal view of what constitutes clarity. However, this isn’t the case, and we always need to structure our code in a way that is more developer-friendly in terms of the general consensus across the whole community of developers using a particular programming language.

In terms of Solidity (and indeed other programming languages as well) it is more widely considered that, by using inheritance, we can create smart contracts that are structured, compartmentalised and interconnected in a way that makes them easier to read, build, adapt, manage and debug by the wide majority of developers, and in the wide majority of use cases.

Obviously, each particular project is different, and so either approach could be considered as providing more clarity on an individual case-by-case basis. However, in terms of this quiz question, the answer has been based on what is more generally considered best practice.

2 Likes

Filip,

I noticed that the modifier in the parent contract was not declared to be public or internal. Does that mean that all modifiers are always inherited by the child contacts as long as they are not marked as ‘private’?

1 Like

Hi @FrankB,

Apologies for the late reponse, and for leaving you hanging on this one!

YES

Modifiers are always visible/available to derived contracts, and in fact their syntax does not allow visibility to be set anyway i.e. they cannot be marked private, public, internal or external.