Переменные состояния, видимость и константы

Переменные состояния — это данные, которые контракт хранит между транзакциями. Их видимость и неизменяемость напрямую влияют на газ.
«private» в Solidity не значит «секретно». Это лишь правило для компилятора, а данные всё равно лежат в публичном блокчейне.

Переменная, объявленная на уровне контракта (а не внутри функции), — это переменная состояния. Она живёт в storage и сохраняется навсегда. У неё есть модификатор видимости: public (доступна снаружи, компилятор создаёт геттер), internal (доступна в этом контракте и наследниках, по умолчанию), private (только в этом контракте).

Видимость — это не приватность

Важно понять: private ограничивает доступ только на уровне Solidity-кода. Само значение хранится в открытом блокчейне, и любой может прочитать слот storage напрямую. Поэтому секреты в контракте хранить нельзя ни с каким модификатором.

   public    -> виден всем + авто-геттер  (снаружи и внутри)
   internal  -> этот контракт + наследники (по умолчанию)
   private   -> только этот контракт
   ----------------------------------------------------
   !!! Все они физически читаются из storage блокчейна

constant и immutable

Два особых модификатора экономят газ. constant — значение известно на этапе компиляции и зашивается прямо в байт-код (слот storage не занимается). immutable — задаётся один раз в конструкторе и потом не меняется; тоже не занимает обычный слот storage, читается из кода. Оба дают огромную экономию по сравнению с обычной переменной.

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

contract Config {
    uint256 public constant MAX_SUPPLY = 1_000_000; // в байт-коде
    address public immutable deployer;              // задаётся 1 раз
    uint256 private counter;                        // обычный storage

    constructor() {
        deployer = msg.sender; // immutable можно присвоить тут
    }

    function bump() external {
        counter += 1; // обычная переменная — дорогая запись в storage
    }
}

Как работает под капотом (EVM/газ)

Обычное чтение переменной состояния — это опкод SLOAD (~2100 газа на холодный слот). Запись — SSTORE (до 20000). А вот constant и immutable вообще не делают SLOAD: их значения подставлены в байт-код, чтение почти бесплатно (~3 газа, как обычная константа). Поэтому всё, что не меняется после деплоя, выгодно объявлять immutable или constant.

# Та же логика на Python: стоимость доступа к разным переменным
COST = {"constant": 3, "immutable": 3, "storage_read": 2100, "storage_write": 20000}

class Config:
    MAX_SUPPLY = 1_000_000          # constant — в коде
    def __init__(self, deployer):
        self.deployer = deployer    # immutable — один раз
        self.counter = 0            # обычный storage

cfg = Config("0xDEPLOYER")
gas = 0
gas += COST["constant"]      # читаем MAX_SUPPLY
gas += COST["immutable"]     # читаем deployer
gas += COST["storage_write"] # пишем counter
cfg.counter += 1
print("counter =", cfg.counter, "| суммарный газ:", gas)

«Та же логика на Python ▶». Видно, что обращение к constant/immutable почти бесплатно, а запись в обычный storage доминирует в счёте газа.

Частые ошибки

  • Считать private-переменную секретной — её значение читается из блокчейна напрямую.
  • Хранить неизменные параметры (адрес владельца, максимальный сапплай) в обычной переменной вместо immutable/constant — переплата газа на каждом чтении.
  • Пытаться присвоить immutable после конструктора — компилятор запретит.

Best practices

  • Всё, что фиксировано на этапе компиляции — в constant. Всё, что задаётся при деплое и не меняется — в immutable.
  • Не делайте переменные public «на всякий случай»: лишний геттер раздувает байт-код. Делайте публичными только то, что действительно читают снаружи.
  • Для дорогих чтений в горячих функциях кэшируйте storage в локальную переменную.

Итоги

Переменные состояния живут в storage, их видимость (public/internal/private) — правило компилятора, а не защита данных. Модификаторы constant и immutable убирают дорогие обращения к storage и серьёзно экономят газ. Дальше переходим к функциям и модификаторам.

Проверьте себя
1. Что верно про private-переменную состояния?
AЕё значение зашифровано и недоступно
BЕё нельзя прочитать никому, даже узлу
CЕё значение всё равно публично читается из storage блокчейна
DОна хранится только в памяти
2. Чем immutable выгоднее обычной переменной состояния?
AЕго можно менять каждую транзакцию
BЕго значение зашивается в байт-код и не требует дорогого SLOAD
CОн автоматически становится public
DОн хранится в памяти и исчезает