Inheritance

Solidity uses C3 linearization (like Python) to determine the order of base contracts.

19.1 Basics, virtual and override

A function that may be overridden must be virtual; the overriding function must say override.

Visibility can only change from external to public; mutability can only become stricter (nonpayable → view → pure; payable cannot change).

A public state variable can override an external function with matching signature.

State variables cannot be overridden or shadowed (shadowing is an error since 0.6).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Animal {
    function speak() public pure virtual returns (string memory) { return "..."; }
    function legs() public pure virtual returns (uint256) { return 4; }
}

contract Dog is Animal {
    function speak() public pure virtual override returns (string memory) { return "Woof"; }
}

contract Puppy is Dog {
    function speak() public pure override returns (string memory) {
        return string.concat(super.speak(), "!");   // "Woof!"
    }
}

19.2 Multiple inheritance and C3 linearization

Solidity uses C3 linearization (like Python) to determine the order of base contracts. List bases from "most base-like" to "most derived".

super.f() calls the next contract in the linearization, not necessarily the direct parent.

contract X is A, B where B is A works; contract X is B, A fails ("Linearization of inheritance graph impossible").

Base constructors run in linearization order (most base first), regardless of the order in which arguments are given.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract A {
    event Log(string who);
    function foo() public virtual { emit Log("A"); }
}

contract B is A {
    function foo() public virtual override { emit Log("B"); super.foo(); }
}

contract C is A {
    function foo() public virtual override { emit Log("C"); super.foo(); }
}

// Linearization of D: [D, C, B, A]  (rightmost base is "closest")
contract D is B, C {
    function foo() public override(B, C) {   // must list all bases that define foo
        super.foo();                          // calls C.foo → B.foo → A.foo
    }
}
// Emitted order when calling D.foo(): "C", "B", "A"

19.3 Calling a specific parent

B.foo() calls B's implementation directly (bypassing linearization). Prefer super for cooperative multiple inheritance.

19.4 Inheritance vs composition

Deep hierarchies make code hard to audit. Prefer shallow inheritance of small, well-tested mixins (e.g., OpenZeppelin's Ownable, Pausable, ReentrancyGuard) and composition (separate contracts called through interfaces).