Provider против Signer: читать или писать
Главное различие ethers.js, которое путает новичков: Provider только читает, Signer ещё и подписывает.
Provider — соединение для чтения сети. Signer — провайдер плюс способность подписывать: он привязан к конкретному аккаунту и может отправлять транзакции.
Это самое практичное различие во всём ethers.js. Перепутаете — получите ошибку «cannot estimate gas» или «unknown account» в самый ответственный момент. Разберёмся раз и навсегда.
Provider: глаза, но не руки
Provider умеет всё, что не требует ключа: номер блока, баланс, чтение view-функций, история событий, оценка газа. Он анонимен — не знает «от чьего имени» действовать, потому что для чтения это и не нужно.
Signer: руки с ключом
Signer представляет конкретный аккаунт (адрес) и умеет подписывать: отправлять транзакции, подписывать сообщения. В браузерном dApp Signer получают из кошелька — это аккаунт пользователя. Кошелёк не отдаёт ключ, но даёт объект Signer, который при подписи покажет окно подтверждения.
import { BrowserProvider } from "ethers";
const provider = new BrowserProvider(window.ethereum);
// getSigner() запросит у кошелька активный аккаунт
const signer = await provider.getSigner();
const me = await signer.getAddress(); // адрес пользователя
console.log("я:", me);
// signer теперь можно передать в контракт для записиПравило выбора
| Задача | Что нужно |
| Прочитать баланс / view-функцию | Provider |
| Подписаться на события | Provider (лучше WebSocket) |
| Отправить транзакцию | Signer |
| Подписать сообщение (вход) | Signer |
Как работает под капотом
Контракт в ethers.js создаётся с «runner» — это либо Provider, либо Signer. От него зависит, что контракт умеет. Если контракт подключён к Provider, у него доступны только view/pure-методы (чтение). Если к Signer — доступны и методы записи, потому что есть кому подписать. Поэтому частый паттерн: один экземпляр контракта на чтение (через Provider) и второй на запись (через Signer), либо переподключение методом contract.connect(signer).
Частые ошибки
- Звать функцию записи на контракте с Provider. Будет ошибка — некому подписать. Нужен Signer.
- Создавать Signer до подключения кошелька.
getSigner()запрашивает аккаунт; без подключения вернёт ошибку. - Думать, что Signer «знает приватный ключ». В браузере Signer — это прокси к кошельку; ключ остаётся в MetaMask.
Итоги
- Provider читает, Signer читает и подписывает (привязан к аккаунту).
- Для записи и подписи сообщений нужен Signer из кошелька.
- Контракт с Provider умеет только читать; с Signer — ещё и писать.