Inheritance¶
Contracts can inherit from other contracts to reuse code.
Basic Inheritance¶
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid address");
owner = newOwner;
}
}
contract Token is Ownable {
mapping(address => uint256) public balances;
// Inherits owner, onlyOwner, transferOwnership
function mint(address to, uint256 amount) public onlyOwner {
balances[to] += amount;
}
}
Constructor Inheritance¶
Calling Parent Constructor¶
contract Base {
uint256 public value;
constructor(uint256 _value) {
value = _value;
}
}
// Method 1: In inheritance list
contract Derived1 is Base(100) {
// Base constructor called with 100
}
// Method 2: In derived constructor
contract Derived2 is Base {
constructor(uint256 _value) Base(_value) {
// Additional initialization
}
}
// Method 3: With own parameters
contract Derived3 is Base {
string public name;
constructor(string memory _name, uint256 _value) Base(_value) {
name = _name;
}
}
Function Overriding¶
Virtual and Override¶
contract Base {
function getValue() public view virtual returns (uint256) {
return 10;
}
function getMessage() public pure virtual returns (string memory) {
return "Base";
}
}
contract Derived is Base {
function getValue() public view override returns (uint256) {
return 20;
}
function getMessage() public pure override returns (string memory) {
return "Derived";
}
}
Calling Parent Functions¶
contract Base {
uint256 public value;
function increment() public virtual {
value += 1;
}
}
contract Derived is Base {
function increment() public override {
// Call parent implementation
super.increment();
// Then add more
value += 1; // Total: +2
}
}
Explicit Parent Call¶
contract A {
function foo() public virtual returns (string memory) {
return "A";
}
}
contract B is A {
function foo() public virtual override returns (string memory) {
return "B";
}
}
contract C is A, B {
function foo() public override(A, B) returns (string memory) {
// Call specific parent
return A.foo(); // Returns "A"
// Or: return B.foo(); // Returns "B"
// Or: return super.foo(); // Returns "B" (most derived)
}
}
Multiple Inheritance¶
contract Ownable {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
}
contract Pausable {
bool public paused;
modifier whenNotPaused() {
require(!paused, "Paused");
_;
}
}
// Inherit from multiple contracts
contract Token is Ownable, Pausable {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount)
public
whenNotPaused
{
// Uses Pausable modifier
balances[msg.sender] -= amount;
balances[to] += amount;
}
function pause() public onlyOwner {
// Uses Ownable modifier
paused = true;
}
}
Inheritance Order¶
Order matters - list from most base to most derived:
// Correct order
contract D is A, B, C {
// A is most base, C is most derived
}
// If B and C both override A's function,
// super.foo() calls C's implementation
Abstract Contracts¶
abstract contract Base {
// Abstract function - no implementation
function getValue() public view virtual returns (uint256);
// Concrete function - has implementation
function getDoubleValue() public view returns (uint256) {
return getValue() * 2;
}
}
contract Derived is Base {
uint256 private value = 10;
// Must implement abstract function
function getValue() public view override returns (uint256) {
return value;
}
}
Visibility in Inheritance¶
Internal Access¶
contract Base {
uint256 private privateVar; // Only in Base
uint256 internal internalVar; // Base + derived
uint256 public publicVar; // Anyone
function _internalFunc() internal view returns (uint256) {
return internalVar;
}
function _privateFunc() private view returns (uint256) {
return privateVar;
}
}
contract Derived is Base {
function test() public view returns (uint256) {
// return privateVar; // Error - private
// return _privateFunc(); // Error - private
return internalVar; // OK - internal
// return _internalFunc(); // OK - internal
}
}
Protected Pattern¶
contract Base {
// Internal setter for derived contracts
function _setValue(uint256 value) internal {
_value = value;
}
// Public getter for everyone
function getValue() public view returns (uint256) {
return _value;
}
uint256 private _value;
}
contract Derived is Base {
function updateValue(uint256 newValue) public {
_setValue(newValue); // Can call internal
}
}
State Variable Inheritance¶
contract Base {
uint256 public baseValue;
mapping(address => uint256) internal balances;
}
contract Derived is Base {
// Additional state
uint256 public derivedValue;
function getBalance(address user) public view returns (uint256) {
return balances[user]; // Access inherited mapping
}
}
Shadowing Prevention¶
contract Base {
uint256 public value;
}
contract Derived is Base {
// This would shadow - not allowed
// uint256 public value; // Error!
// Use different name
uint256 public derivedValue;
}
Modifier Inheritance¶
contract Base {
bool internal locked;
modifier nonReentrant() virtual {
require(!locked, "Reentrant");
locked = true;
_;
locked = false;
}
}
contract Derived is Base {
// Use inherited modifier
function withdraw() public nonReentrant {
// ...
}
// Or override it
modifier nonReentrant() override {
require(!locked, "Derived: Reentrant");
locked = true;
_;
locked = false;
}
}
Interface Implementation¶
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
abstract contract ERC20Base is IERC20 {
mapping(address => uint256) internal _balances;
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
// Leave transfer abstract
function transfer(address to, uint256 amount) public virtual override returns (bool);
}
contract Token is ERC20Base {
function transfer(address to, uint256 amount) public override returns (bool) {
_balances[msg.sender] -= amount;
_balances[to] += amount;
return true;
}
}
Diamond Inheritance¶
contract A {
function foo() public pure virtual returns (string memory) {
return "A";
}
}
contract B is A {
function foo() public pure virtual override returns (string memory) {
return "B";
}
}
contract C is A {
function foo() public pure virtual override returns (string memory) {
return "C";
}
}
// Diamond: D inherits from both B and C, which both inherit from A
contract D is B, C {
// Must override and specify all parents
function foo() public pure override(B, C) returns (string memory) {
return super.foo(); // Calls C.foo() (rightmost)
}
}
Best Practices¶
1. Use Composition Over Deep Inheritance¶
// Good - shallow hierarchy
contract Token is Ownable, Pausable {
// Combines focused base contracts
}
// Avoid - deep hierarchy
contract A { }
contract B is A { }
contract C is B { }
contract D is C { }
contract E is D { } // Too deep
2. Mark Functions Virtual Explicitly¶
contract Base {
// Explicitly virtual - can be overridden
function getValue() public view virtual returns (uint256) {
return 10;
}
// Not virtual - cannot be overridden
function getFixed() public pure returns (uint256) {
return 100;
}
}
3. Use Abstract Contracts for Shared Logic¶
abstract contract TokenBase {
mapping(address => uint256) internal _balances;
function _transfer(address from, address to, uint256 amount) internal virtual {
_balances[from] -= amount;
_balances[to] += amount;
}
// Force derived to implement
function transfer(address to, uint256 amount) public virtual returns (bool);
}
4. Document Inheritance Requirements¶
/// @title Base token with ownership
/// @notice Derived contracts must implement _beforeTransfer hook
abstract contract TokenBase {
/// @dev Override to add transfer validation
function _beforeTransfer(address from, address to, uint256 amount) internal virtual;
}
5. Be Careful with Constructor Order¶
contract A {
constructor() { /* runs first */ }
}
contract B is A {
constructor() { /* runs second */ }
}
contract C is A, B {
constructor() { /* runs last */ }
}
// Order: A -> B -> C
Next Steps¶
- Interfaces - Contract interfaces
- Contracts - Contract fundamentals
- Modifiers - Reusable function conditions