Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. ООП: наследование и интерфейсы
oop_solidity

ООП: наследование и интерфейсы

Наследование, множественное наследование, интерфейсы, абстрактные контракты, virtual/override

ООП: наследование и интерфейсы

Solidity поддерживает множественное наследование с разрешением коллизий через C3-линеаризацию — понимание этого механизма критически важно при работе с OpenZeppelin

#Наследование

Ключевое слово is создаёт наследование. Дочерний контракт получает все public и internal переменные и функции родителя.

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; 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), "Zero address"); owner = newOwner; } } contract Pausable is Ownable { bool public paused; modifier whenNotPaused() { require(!paused, "Paused"); _; } function pause() external onlyOwner { paused = true; } function unpause() external onlyOwner { paused = false; } } // Множественное наследование contract MyToken is Pausable { mapping(address => uint256) public balances; function transfer(address to, uint256 amount) external whenNotPaused { require(balances[msg.sender] >= amount, "Insufficient balance"); balances[msg.sender] -= amount; balances[to] += amount; } }

MyToken наследует и Pausable, и (транзитивно) Ownable. Это позволяет вызывать myToken.pause(), myToken.transferOwnership() и использовать модификаторы onlyOwner и whenNotPaused.

#Virtual и Override

Чтобы функция могла быть переопределена в дочернем контракте, она должна быть помечена virtual. Переопределение помечается override.

contract Base { function greet() public virtual returns (string memory) { return "Hello from Base"; } } contract Child is Base { function greet() public virtual override returns (string memory) { return "Hello from Child"; } } contract GrandChild is Child { function greet() public override returns (string memory) { // super вызывает ближайшего родителя в цепочке MRO string memory parentGreet = super.greet(); return string.concat(parentGreet, " and GrandChild"); } }

Если дочерний контракт хочет и сам быть переопределяемым, нужно virtual override вместе.

#Множественное наследование и C3-линеаризация

Solidity разрешает множественное наследование, но порядок перечисления родителей важен. Используется алгоритм C3-линеаризации, который определяет MRO (Method Resolution Order).

Правило Solidity: родителей нужно перечислять от «более базового» к «более производному».

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 { function foo() public virtual override returns (string memory) { return "C"; } } // Порядок важен: B и C — оба наследуют A, B указан первым contract D is B, C { function foo() public override(B, C) returns (string memory) { return super.foo(); // вызовет C.foo() по MRO: D → C → B → A } }

При конфликте (две функции с одним именем в разных родителях) Solidity требует явного override(B, C) — это явное объявление, что вы знаете о конфликте и намеренно его разрешаете.

#Интерфейсы

Интерфейс определяет контракт без реализации. Все функции в интерфейсе external и не имеют тела. Интерфейс не может иметь state variables, constructor или не-external функции.

Интерфейс используется для: определения стандарта (ERC-20, ERC-721), типизации переменных других контрактов, взаимодействия с известными внешними контрактами.

// Стандарт ERC-20 как интерфейс interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } // Использование интерфейса для взаимодействия с любым ERC-20 токеном contract Staking { IERC20 public immutable stakingToken; constructor(address tokenAddress) { stakingToken = IERC20(tokenAddress); } function deposit(uint256 amount) external { // Вызываем transferFrom на произвольном ERC-20 токене require(stakingToken.transferFrom(msg.sender, address(this), amount), "Transfer failed"); } }

#Абстрактные контракты

Абстрактный контракт — промежуточное звено между интерфейсом и обычным контрактом. Может иметь реализацию части функций, но содержит хотя бы одну virtual функцию без реализации (или объявленную без тела).

abstract contract BaseAuction { address public highestBidder; uint256 public highestBid; // Абстрактная функция — наследники ОБЯЗАНЫ реализовать function calculateMinIncrement() public virtual returns (uint256); // Конкретная функция с общей логикой function bid() external payable { uint256 minBid = highestBid + calculateMinIncrement(); require(msg.value >= minBid, "Bid too low"); highestBidder = msg.sender; highestBid = msg.value; } } contract EnglishAuction is BaseAuction { function calculateMinIncrement() public pure override returns (uint256) { return highestBid / 10; // Каждая ставка минимум на 10% выше } }

Контракт, помеченный abstract, нельзя задеплоить напрямую — только через наследника, который реализует все абстрактные функции.

Выбор между interface и abstract contract: если нужно задать только API (как в ERC стандартах) — интерфейс. Если нужна общая логика с возможностью переопределения части — абстрактный контракт.

Далее: Токен ERC-20