Три кита ethers.js: Provider, Signer, Contract

Три главных класса ethers.js — Provider, Signer, Contract — и чем отличаются версии v5 и v6.

ethers.js — библиотека-«переводчик» между JavaScript и блокчейном: кодирует вызовы, подписывает транзакции, разбирает ответы и события.

Вся работа с ethers.js держится на трёх классах. Поймёте их роли — поймёте библиотеку целиком.

Три кита

  • Provider — соединение с сетью для чтения (мы разбирали его в разделе про провайдеры).
  • Signer — аккаунт, умеющий подписывать (для записи и подписи сообщений).
  • Contract — типобезопасная обёртка над контрактом: вы зовёте методы как обычные JS-функции, а ethers под капотом кодирует/декодирует данные по ABI.

Связь простая: Contract создаётся из адреса, ABI и runner'а (Provider или Signer). Runner определяет, что контракт умеет: с Provider — только читать, с Signer — ещё и писать.

import { Contract, BrowserProvider } from "ethers";

const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

const abi = [ /* описание функций контракта */ ];
const address = "0xКонтракт";

// для чтения
const reader = new Contract(address, abi, provider);
// для записи
const writer = new Contract(address, abi, signer);

v5 против v6: что поменялось

В 2023 году вышла ethers v6 с несовместимыми изменениями. Туториалы в сети часто на v5 — важно различать:

v5v6
ethers.providers.Web3ProviderBrowserProvider
ethers.utils.parseEtherparseEther (из корня)
числа — BigNumber (свой класс)числа — нативный bigint
contract.functions...прямые методы
provider.getSigner() синхронныйgetSigner() возвращает Promise

Самое заметное для фронта — числа. В v5 был громоздкий BigNumber со своими методами .add(), .mul(). В v6 ethers перешёл на встроенный в JS bigint — это упрощает арифметику, но требует понимать его правила (об этом отдельный урок).

Как работает под капотом

Когда вы вызываете contract.balanceOf(addr), ethers находит в ABI функцию balanceOf, вычисляет её селектор (первые 4 байта от keccak256 сигнатуры), кодирует аргумент по правилам ABI-кодирования и отправляет это как eth_call через runner. Ответ-hex декодируется обратно в JS-значение по типу из ABI. Вся «магия» Contract — это автоматический ABI-кодек поверх Provider/Signer.

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

  • Мешать примеры v5 и v6. Скопированный код из старого туториала не запустится; сверяйте версию.
  • Забывать про Promise у getSigner() в v6. Нужен await.
  • Считать BigNumber и bigint одним и тем же. В v6 это нативный bigint с другим API.

Итоги

  • Три кита: Provider (чтение), Signer (подпись), Contract (обёртка по ABI).
  • Contract = адрес + ABI + runner; runner решает, читать или писать.
  • v6 использует нативный bigint и убрал namespace utils/providers — следите за версией примеров.
Проверьте себя
1. Из чего создаётся объект Contract в ethers.js?
AТолько из адреса
BИз адреса, ABI и runner (Provider или Signer)
CИз приватного ключа
DИз chainId
2. Чем в ethers v6 представлены большие числа вместо BigNumber из v5?
AОбычным number
BНативным bigint
CСтрокой
DFloat64
3. Если контракт создан с Provider (а не Signer), что он умеет?
AТолько читать (view/pure)
BТолько писать
CИ читать, и писать
DНичего