Inheritance & External Contracts - Discussion

Filip & Ivan thanks for the videos as a companion to the programming on Remix. At the moment, I’ll be honest I didn’t have programming in high school or college. I went to trade school. However, I like that I can redefine “for my own mind” what is exactly going on inside the smart contract. Just being exposed to programming I know there is something for me in this awesome field.

2 Likes

Hello sir, does not matter if you have not see any programming lessons in the past, you will find everything you need to know in the academy in order to jump right into the action of programming! So yes, there is something for you in this awesome field! Just keep yourself learning to achieve the goal that you are looking for!

I advice you to start in this order:
Javascript Programming for Blockchain Developers

Then you can take the programming course for Ethereum in order to build your own Dapps or Smart Contracts.

C++ Programming for Blockchain Developers

That one will give you all the knowledge need it to start building Smart contracts and Dapps on EOS programming courses!

Hope you find this useful!

Carlos Z.

1 Like

@Filip.

All of the things I’ve been bumbling through you cover so clearly
and so well.

I’ve searched structs and addresses and visibility and read the docs
and read the docs more and searched more
and just fiddled around looking at different contracts
for the last yearl

All of that frustration has helped your lectures really stick.
It’s one AHA! moment after another.
Loving it.

I can’t wait to finish this course, do the next level,
and FINALLY build out the projects I’ve been planning.

Thank-you so much.

2 Likes

Hey

I have a question, and heads up i am still new, so maybe a stupid question
if

contract HelloWorld is Ownable {
....
}

and

contract Ownable is Destroyable{
..
}

Would HelloWorld then also have Destroyable then? or should I write it like this?

contract HelloWorld is Ownable, Destroyable{
..
}

or aint that nessary because HelloWorld is granddad to Destroyable?

Yes you are right @Jacob_M a lot of people are doing this ‘error’
I think the best way is to do

contract Destroyable is Ownable{
..
}

Because only the owner is allow to destroy the contract
then

contract HelloWorld is Destroyable {
....
}

Because Destroyable inherit of Ownable, so the methods and variables of Ownable will be present in HelloWorld

2 Likes

Hi @mx_krzak,

I know you asked this question quite a while ago, and I’m not sure if you got an answer or resolved it yourself. Anyway, for what it’s worth (to you or anyone else who may try the same thing) I implemented exactly what you tried to…

The only thing your code is missing is the return keyword in the externalGetPerson function body, as follows:

return instance.getPerson();

Mine works like this. I’m sure it was just a slip, as you included returns correctly in the function header :wink:

If you (or anyone else) gets this working, you need to bear in mind:

  • Each new person created with ExternalContract will have the same msg.sender address. This is because ExternalContract is now the msg.sender calling createPerson() in HelloWorld and actually creating the new person, NOT the caller of externalCreatePerson() even though they pay the ETH.
  • So, when the new person is stored in HelloWorld's mapping, it is mapped to ExternalContract's address. As ExternalContract's address is a single unique key, this means that at any one time there can only be one person stored in HelloWorld's mapping which has been created with ExternalContract.
  • Each new person created with this same address will overwrite the previous one, meaning that the externalGetPerson function can only ever retrieve the most recent person created with ExternalContract.

So, after implementing this, I basically realised it was pretty pointless. But it was good practice, and an interesting experiment! :smiley:

@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