Адреса, булевы значения и байты

Адрес — это идентичность в Ethereum: 20 байт, по которым находят кошельки и контракты.
Половина логики контракта — это ответ на вопрос «кто меня вызвал?». А отвечает на него тип address и переменная msg.sender.

Тип address хранит 20-байтовый адрес — кошелька или контракта. У него есть свойства: balance (баланс в wei) и методы для отправки ETH. Особый подтип address payable — это адрес, которому разрешено отправлять эфир методами transfer, send, call. Обычный address деньги получать «напрямую» через эти методы не может — нужно явное приведение.

Булевы и байты

bool — стандартные true/false. bytes32 — фиксированный массив из 32 байт, часто используется для хэшей и коротких идентификаторов; он дешевле, чем динамический. bytes и string — динамические: длина не фиксирована, хранятся дороже. string — это по сути UTF-8 байты без удобной индексации.

   address  : 0x71C7...d8976F   (20 байт)  -> кошелёк/контракт
   bytes32  : 0xabcd...0000     (32 байта, фикс.) -> хэш, ключ
   bytes    : 0x48656c6c6f      (динамич.)  -> произвольные данные
   string   : "Привет"          (UTF-8 байты) -> текст
   bool     : true / false
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Identity {
    address public owner;
    bool public active;
    bytes32 public secretHash;

    constructor() {
        owner = msg.sender;     // кто задеплоил — тот владелец
        active = true;
    }

    function balanceOfOwner() public view returns (uint256) {
        return owner.balance;   // баланс адреса в wei
    }

    function setHash(string memory word) public {
        secretHash = keccak256(abi.encodePacked(word)); // хэш в bytes32
    }
}

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

Адрес внутри EVM хранится в тех же 256-битных словах, просто старшие байты нулевые. bytes32 идеально ложится в один слот storage — отсюда его дешевизна. А string/bytes хранят длину и данные отдельно, для длинных значений это несколько слотов. Хэш keccak256 — основной криптографический примитив Ethereum: он детерминирован и помещается ровно в bytes32.

# Та же логика на Python: фикс. байты дешевле динамических
import hashlib

def keccak_like(word):
    # имитация: 32-байтовый хэш как ключ фиксированного размера
    return hashlib.sha256(word.encode("utf-8")).hexdigest()[:64]

owner = "0x71C7656EC7 ... d8976F"  # 20 байт-адрес
active = True
secret_hash = keccak_like("Привет")  # всегда 64 hex = 32 байта

print("owner:", owner)
print("active:", active)
print("hash(32 байта):", secret_hash)
print("длина строки фиксирована? ", len(secret_hash) == 64)

«Та же логика на Python ▶». Хэш всегда занимает фиксированный размер независимо от длины входа — поэтому в storage его держать предсказуемо дёшево.

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

  • Пытаться отправить ETH на обычный address вместо address payable — компилятор не пропустит.
  • Сравнивать строки через == — так нельзя; сравнивают хэши: keccak256(a) == keccak256(b).
  • Хранить длинные строки в storage без нужды — это дорого; часто достаточно хранить хэш или вынести текст в события/метаданные.

Best practices

  • Для идентификаторов и хэшей используйте bytes32 вместо string — один слот, дёшево и предсказуемо.
  • Проверяйте адреса на address(0) перед важными операциями: нулевой адрес часто означает ошибку или сжигание.
  • Не доверяйте tx.origin для авторизации — используйте msg.sender (подробнее в разделе безопасности).

Единицы эфира и глобальные переменные

Раз уж мы говорим про адреса и деньги, важно знать единицы. Самая мелкая единица эфира — wei; в одном ETH ровно 10^18 wei. Все суммы внутри EVM считаются в wei, потому что дробей нет: «полэфира» — это 5 * 10^17 wei. Solidity даёт удобные суффиксы wei, gwei и ether, так что можно написать 1 ether вместо длинного числа. Рядом с адресами почти всегда фигурируют глобальные переменные контекста: msg.sender (кто вызвал), msg.value (сколько wei прислано с вызовом), block.timestamp (время блока) и address(this) (адрес самого контракта). Эти величины — основной «вход» данных в контракт: именно из них логика узнаёт, кто и с какими деньгами к ней обратился, и на них же строятся все проверки доступа.

Итоги

address описывает идентичность, address payable умеет принимать ETH, bool — флаги, bytes32 — дешёвые фиксированные данные и хэши, а string/bytes — дорогие динамические. Дальше разберём область видимости и константы переменных состояния.

Проверьте себя
1. Зачем нужен тип address payable, если есть address?
AОн хранит больше байт
BТолько на него можно отправлять ETH методами transfer/send
CОн приватный
DОн не нуждается в газе
2. Как правильно сравнить две строки на равенство в Solidity?
Aa == b
Bkeccak256(bytes(a)) == keccak256(bytes(b))
Ca.equals(b)
Dstrcmp(a, b)