Inheritance in Solidity
A comprehensive guide to understanding how contracts can inherit from other contracts in Solidity, including direct and multiple inheritance patterns, and the keywords that make it work.
Published on 04 November, 2025
Categories:
SolidityUnderstanding Contract Inheritance
Inheritance lets you build new contracts on top of existing ones. Think of it like building with LEGO blocks - you can take a base contract and extend it with new features without rewriting everything from scratch.
When Contract B inherits from Contract A, Contract B gets all of Contract A's functions and state variables. This keeps your code clean and reusable.
Direct Inheritance
Direct inheritance means one contract inherits from another single contract. You use the
is keyword to create this relationship.// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Animal { string public name; function makeSound() public pure virtual returns (string memory) { return "Some sound"; } } contract Dog is Animal { function makeSound() public pure override returns (string memory) { return "Woof!"; } }
Here,
Dog inherits from Animal. The Dog contract automatically gets the name variable from Animal. Notice how we're using three important keywords here: virtual, override, and is.The is Keyword
The
is keyword establishes the inheritance relationship. When you write contract Dog is Animal, you're telling Solidity that Dog should inherit everything from Animal.contract Parent { uint public value = 100; } contract Child is Parent { function getValue() public view returns (uint) { return value; // Can access parent's state variable } }
The virtual Keyword
When you mark a function as
virtual in the parent contract, you're saying "child contracts can replace this function if they want to." Without virtual, the function is locked and cannot be changed by child contracts.contract Base { function calculate() public pure virtual returns (uint) { return 5; } function fixedFunction() public pure returns (uint) { return 10; // Child contracts cannot override this } }
The override Keyword
When a child contract wants to replace a
virtual function from its parent, it must use the override keyword. This makes your intentions clear and prevents accidents.contract Calculator { function add(uint a, uint b) public pure virtual returns (uint) { return a + b; } } contract AdvancedCalculator is Calculator { function add(uint a, uint b) public pure override returns (uint) { return a + b + 1; // Modified behavior } }
If you forget to write
override when replacing a virtual function, Solidity will give you an error. This safety feature prevents you from accidentally replacing functions.Multiple Inheritance
Multiple inheritance means a contract inherits from more than one parent contract. Solidity handles this using C3 linearization, which determines the order in which parent contracts are processed.
contract A { function foo() public pure virtual returns (string memory) { return "A"; } } contract B { function bar() public pure virtual returns (string memory) { return "B"; } } contract C is A, B { function getValues() public pure returns (string memory, string memory) { return (foo(), bar()); // Can use functions from both parents } }
The order you list parent contracts matters when they have functions with the same name.
contract X { function getValue() public pure virtual returns (uint) { return 1; } } contract Y { function getValue() public pure virtual returns (uint) { return 2; } } contract Z is X, Y { function getValue() public pure override(X, Y) returns (uint) { return 3; } }
When overriding a function that exists in multiple parent contracts, you must list all parent contracts in the
override keyword like override(X, Y).Inheritance Order
Solidity processes parent contracts from right to left, most derived to most base. This matters when multiple parents have the same function.
contract Base1 { function greet() public pure virtual returns (string memory) { return "Hello from Base1"; } } contract Base2 { function greet() public pure virtual returns (string memory) { return "Hello from Base2"; } } // Order matters here contract Derived is Base1, Base2 { function greet() public pure override(Base1, Base2) returns (string memory) { return "Hello from Derived"; } }
If
Derived didn't override greet(), you'd get a compiler error because Solidity wouldn't know which parent's version to use.The super Keyword
The
super keyword calls the function from the parent contract. This is useful when you want to extend a parent's behavior rather than completely replace it.contract Counter { uint public count; function increment() public virtual { count += 1; } } contract DoubleCounter is Counter { function increment() public override { super.increment(); // Call parent's increment super.increment(); // Call it again } }
With multiple inheritance,
super follows the linearization order.contract A { event Log(string message); function process() public virtual { emit Log("A processed"); } } contract B is A { function process() public virtual override { emit Log("B processed"); super.process(); } } contract C is A { function process() public virtual override { emit Log("C processed"); super.process(); } } contract D is B, C { function process() public override(B, C) { super.process(); } }
When
D calls super.process(), Solidity follows this chain: D calls C, C calls B, B calls A. The order is determined by the linearization: D, C, B, A.Practical Example with All Concepts
Here's a real-world example that brings everything together:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Ownable { address public owner; constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; } } contract Pausable { bool public paused; function pause() public virtual { paused = true; } function unpause() public virtual { paused = false; } } contract Token is Ownable, Pausable { mapping(address => uint) public balances; function transfer(address to, uint amount) public { require(!paused, "Contract is paused"); balances[msg.sender] -= amount; balances[to] += amount; } function pause() public override onlyOwner { super.pause(); } function unpause() public override onlyOwner { super.unpause(); } }
The
Token contract inherits from both Ownable and Pausable. It uses override to modify the pause functions, adding the onlyOwner modifier. It uses super to call the parent implementation, and uses is to establish the inheritance relationships.Common Patterns and Mistakes
When working with inheritance, watch out for these issues:
State Variable Shadowing: You cannot declare a state variable in a child contract with the same name as one in the parent.
contract Parent { uint public value = 10; } contract Child is Parent { // This will cause an error // uint public value = 20; }
Constructor Execution: Parent constructors execute before child constructors. You can pass arguments to parent constructors.
contract Parent { uint public value; constructor(uint _value) { value = _value; } } contract Child is Parent(100) { // Parent constructor runs first with value 100 constructor() { // Child constructor runs after } }
Function Visibility: Child contracts can only override functions that are
public or external. You cannot override private functions.Inheritance in Solidity provides a powerful way to structure your smart contracts by reusing code and building hierarchies. The combination of
virtual, override, super, and is keywords gives you precise control over how contracts extend and modify behavior from their parents, whether using direct or multiple inheritance patterns.