Solidity

Solidity Inheritance

Solidity Inheritance Main Tips

  • Solidity supports multiple inheritance in the form of copying copying code, which includes polymorphism.
  • When inheriting from multiple contracts, only one contract gets generated on the blockchain, the code from all base contracts gets copied into the final contract that is created,

Solidity Inheritance

Solidity supports multiple inheritance in the form of copying copying code, which includes polymorphism.

Every function call is virtual, meaning that the function that is most derived gets called, with the exception of when the contract name is given explicitly.

When inheriting from multiple contracts, only one contract gets generated on the blockchain, the code from all base contracts gets copied into the final contract that is created,

The inheritance system generally is greatly similar to Python’s, mostly when it comes to multiple inheritance.

See the example below for more details.

Example

pragma solidity ^0.4.0;

contract ownedContract {
    function ownedContract() { ownerAddress = msg.sender; }
    address ownerAddress;
}

// The keyword "is" is used to for deriving from another contract. Derived
// contracts are able to access every non-private member which includes
// internal functions as well as state variables. However, those members 
// cannot be accessed from outside using this.
contract mortalContract is ownedContract {
    function killOwner() {
        if (msg.sender == ownerAddress) selfdestruct(ownerAddress);
    }
}

// These abstract contracts are only provided to make the
// interface known to the compiler. Note the function
// without body. If a contract does not implement all
// functions it can only be used as an interface.
contract Config {
    function lookup(uint id) returns (address adr);
}

contract RegisterName {
    function register(bytes32 name);
    function unregister();
 }

// Multiple inheritance is viable too. Keep in mind that "ownedContract" is
// a base class of "mortalContract", but there is only one
// instance of "ownedContract" (in the case of virtual inheritance in C++).
contract named is ownedContract, mortalContract {
    function named(bytes32 name) {
        Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
        RegisterName(config.lookup(1)).register(name);
    }

    // It is possible to override a function by another function with the same name and
    // the same types/number of inputs.  Wheb the overriding function has any
    // differing types of output parameters, which will cause an error.
    // Message-based and local function calls both take overrides like this
    // into account.
    function killOwner() {
        if (msg.sender == ownerAddress) {
            Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
            RegisterName(config.lookup(1)).unregister();
            // It is still possible to call a specific
            // overridden function.
            mortalContract.killOwner();
        }
    }
}

// When an argument is taken by a constructor, it would have to be
// inside the header (or  at the derived contract's
//  constructor in modifier-invocation-style (as shown below)).
contract FeedPrice is ownedContract, mortalContract, named("GoldenFeed") {
   function infoUpdate(uint infoNew) {
      if (msg.sender == ownerAddress) info = infoNew;
   }

   function getInfo() constant returns(uint r) { return info; }

   uint info;
}

 

Try on Remix Try live on Hosting

Notice how in the code snippet above, we call mortalContract.killOwner() in order to “forward” the destruction request. This is done in a way that is questionable though, as the following example shows:

Example

pragma solidity ^0.4.0;

contract ownedContract {
    function ownedContract() { ownerAddress = msg.sender; }
    address ownerAddress;
}

contract mortalContract is ownedContract {
    function killOwner() {
        if (msg.sender == ownerAddress) selfdestruct(ownerAddress);
    }
}

contract BaseNo1 is mortalContract {
    function killOwner() { /* do cleanup 1 */ mortalContract.killOwner(); }
}

contract BaseNo2 is mortalContract {
    function killOwner() { /* do cleanup 2 */ mortalContract.killOwner(); }
}

contract Final is BaseNo1, BaseNo2 {
}

 

Try on Remix Try live on Hosting

Calling Final.killOwner() will cause BaseNo2.killOwner to be called as the override that is most derrived, however, this function would bypass BaseNo1.killOwner, since it cannot even know of BaseNo1. To get around this, we can use super:

Example

pragma solidity ^0.4.0;

contract ownedContract {
    function ownedContract() { ownerAddress = msg.sender; }
    address ownerAddress;
}

contract mortalContract is ownedContract {
    function killOwner() {
        if (msg.sender == ownerAddress) selfdestruct(ownerAddress);
    }
}

contract BaseNo1 is mortalContract {
    function killOwner() { /* do cleanup 1 */ super.killOwner(); }
}


contract BaseNo2 is mortalContract {
    function killOwner() { /* do cleanup 2 */ super.killOwner(); }
}

contract Final is BaseNo2, BaseNo1 {
}

 

Try on Remix Try live on Hosting

When BaseNo1 calls a function of super, it does not merely call that function on one of its base contracts. Instead, it would call this function on the following base contract inside the final inheritance graph, therefore it would call BaseNo2 (keep in mind that the final sequence of inheritance is – starting with the contract that is derivedthe most: Final, then BaseNo1, BaseNo2, mortalContract, ownedContract). The function itself which gets called when using super is not known inside the context of the class it is used in, however, its type is known. This is a lot like the ordinary virtual method lookup.


Solidity Inheritance Arguments For Base Constructors

contracts that are derived have to provide every argument the base constructors need, to do which there are two ways:

Example

pragma solidity ^0.4.0;

contract BaseContract {
    uint x;
    function BaseContract(uint _x) { x = _x; }
}

contract DerivedContract is BaseContract(7) {
    function DerivedContract(uint _y) BaseContract(_y * _y) {
    }
}

 

Try on Remix Try live on Hosting

The first ways is straight right in the inheritance list(is BaseContract(7)).

The second one is in the way a modifier is invoked as a part of the derived constructor’s (BaseContract(_y * _y)) header.

Using the first way is more convenient when the constructor argument is a constant, which defines or describes the contract’s behaviour. Using the second way is a must when the arguments of the constructor of the base depend on the ones that belong to the derived contract. If, like in this example, both of these places are used, the argument in modifier-style would take precedence.


Solidity Inheritance Multiple and Linearization

Languages which allow multiple inheritance must deal with a few problems. One of them is called the Diamond Problem. Solidity, following the path of Python and using “C3 Linearization” for forcing a certain kind of order in the base classes’ DAG. This would result in the desirable property of monotonicity, howeverm it also disallows certain inheritance graphs. Even more so, the order in which the classes that are used as bases for the derived class are given in the directive is, is essential.

In the code snippet below, Solidity would give an error stating “Linearization of inheritance graph impossible”.

Example

// This is not going to compile

pragma solidity ^0.4.0;

contract Cont1 {}
contract Cont2 is Cont1 {}
contract Cont3 is Cont2, Cont1 {}

 

Try on Remix Try live on Hosting

This is because Cont3 is requesting Cont1 to override the  Cont2 (This is because of the order in the is directive being Cont2, Cont1), however, Cont2 itself would request to override Cont1, causing a contradiction which cannot be resolved.

A basic rule to remember is to always specify the base classes in an order going from “most base-like” to “most derived”.


Solidity Inheritance Different Members Of The Same Name

When due to inheritance, you have a contract that has a modifier and a function of a similar same name, it is seen to be an error.

This error also is produced when there is a modifier of a similar name, in addition to an event and function of that exact name.

There exists an exception though, allowing state variable getter functions to override other public functions.

Read previous post:
Solidity Events

Solidity Events Main Tips In Solidity, events allow convenient use of EVM logging facilities, that can be used for "calling" JavaScript...

Close