Провайдер vs Сайнер — в чём разница
Чем Provider отличается от Signer в ethers.js: кто читает Ethereum, кто подписывает сообщения и кто отправляет транзакции.
Провайдер vs Сайнер
Provider читает Ethereum. Signer подписывает действия от имени аккаунта.
Если нужно узнать баланс, блок или состояние контракта — нужен provider. Если нужно отправить транзакцию или подписать сообщение — нужен signer.
Главное отличие
| Объект | Что умеет | Приватный ключ |
|---|---|---|
| Provider | читать сеть и отправлять RPC-запросы | Не нужен |
| Signer | подписывать сообщения и транзакции | Нужен |
Provider не доказывает личность пользователя. Он просто подключается к RPC-ноду.
Signer связан с аккаунтом. Он может создать цифровую подпись, которую Ethereum проверит перед изменением State.
Читаем через provider, подписываем через signer
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://ethereum.publicnode.com");
// Тестовый ключ из документации Hardhat. Не используйте его для реальных средств.
const wallet = new ethers.Wallet(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
provider,
);
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const blockNumber = await provider.getBlockNumber();
const vitalikBalance = await provider.getBalance(vitalikAddress);
const walletBalance = await provider.getBalance(wallet.address);
const message = "Я управляю этим адресом";
const signature = await wallet.signMessage(message);
const txRequest = {
to: vitalikAddress,
value: ethers.parseEther("0.001"),
nonce: await provider.getTransactionCount(wallet.address),
gasLimit: 21_000n,
maxFeePerGas: ethers.parseUnits("20", "gwei"),
maxPriorityFeePerGas: ethers.parseUnits("2", "gwei"),
chainId: 1,
type: 2,
};
const signedTx = await wallet.signTransaction(txRequest);
console.log("Latest block:", blockNumber);
console.log("Vitalik balance:", ethers.formatEther(vitalikBalance), "ETH");
console.log("Wallet address:", wallet.address);
console.log("Wallet balance:", ethers.formatEther(walletBalance), "ETH");
console.log("Message signature:", signature);
console.log("Signed transaction:", signedTx);Этот код ничего не отправляет в сеть. Provider читает данные, а signer локально создаёт подпись сообщения и raw transaction.
Как это работает
new ethers.JsonRpcProvider() — создаёт подключение к Ethereum RPC.
provider.getBlockNumber() — читает номер последнего блока. Для этого не нужен приватный ключ.
new ethers.Wallet(privateKey, provider) — создаёт signer и подключает его к provider.
wallet.signMessage(message) — подписывает текст приватным ключом. Это не транзакция и gas не тратится.
wallet.signTransaction(txRequest) — подписывает транзакцию и возвращает raw transaction. State всё ещё не изменился.
Чтобы реально отправить подписанную транзакцию, нужен отдельный вызов provider.broadcastTransaction(signedTx).
Provider нужен даже signer
Signer умеет подписывать, но сам по себе не знает состояние сети.
Чтобы подготовить транзакцию, ему часто нужны данные из provider:
nonce текущего аккаунта
chainId сети
актуальная цена gas
баланс отправителяПоэтому в ethers часто используют связку:
const provider = new ethers.JsonRpcProvider("https://ethereum.publicnode.com");
const signer = new ethers.Wallet(PRIVATE_KEY, provider);Signer подписывает, provider доставляет запросы в сеть.
Read и write в контрактах
Один и тот же контракт ведёт себя по-разному в зависимости от третьего аргумента:
const readOnlyContract = new ethers.Contract(address, abi, provider);
const writableContract = new ethers.Contract(address, abi, signer);readOnlyContract.balanceOf(user) сделает бесплатный eth_call.
writableContract.transfer(to, amount) создаст транзакцию, которую signer должен подписать и отправить.
Частые ошибки
Пытаться отправить транзакцию через provider → provider не владеет приватным ключом и не может подписать действие.
Использовать signer для простого чтения → для getBalance(), getBlockNumber() и view функций достаточно provider.
Путать подпись сообщения и транзакцию → signMessage() не меняет State и не стоит gas. Транзакция меняет State только после отправки и включения в блок.
Что дальше
- Как работает транзакция изнутри — что происходит после подписи транзакции
- Gas — как считается и кто платит — почему запись в State стоит ETH
- Что такое ABI — как provider и signer вызывают функции контракта