Оценка газа — estimateGas
Как заранее оценить gas для Ethereum-транзакции через ethers v6 и использовать результат как gasLimit.
Что делает estimateGas
estimateGas симулирует транзакцию через RPC и возвращает, сколько gas ей примерно понадобится.
Это не отправка транзакции. State не меняется, ETH не переводится, комиссия не списывается.
Оцениваем gas перед отправкой ETH
Запустите локальную сеть Hardhat в отдельном терминале:
npx hardhat nodeЗатем выполните TypeScript-код:
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545");
// Первый тестовый аккаунт Hardhat. Не используйте этот ключ в mainnet.
const sender = new ethers.Wallet(
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
provider,
);
// Второй тестовый аккаунт Hardhat.
const recipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
const txRequest = {
to: recipient,
value: ethers.parseEther("0.1"),
};
const estimatedGas = await sender.estimateGas(txRequest);
const feeData = await provider.getFeeData();
if (!feeData.maxFeePerGas) {
throw new Error("RPC не вернул maxFeePerGas");
}
const estimatedMaxFee = estimatedGas * feeData.maxFeePerGas;
console.log("Sender:", sender.address);
console.log("Recipient:", recipient);
console.log("Estimated gas:", estimatedGas.toString());
console.log("Max fee per gas:", ethers.formatUnits(feeData.maxFeePerGas, "gwei"), "gwei");
console.log("Estimated max fee:", ethers.formatEther(estimatedMaxFee), "ETH");
const tx = await sender.sendTransaction({
...txRequest,
gasLimit: estimatedGas,
});
console.log("Transaction hash:", tx.hash);
const receipt = await tx.wait();
if (!receipt || receipt.status !== 1) {
throw new Error("Транзакция не была успешно выполнена");
}
const actualFee = receipt.gasUsed * receipt.gasPrice;
console.log("Block:", receipt.blockNumber);
console.log("Gas used:", receipt.gasUsed.toString());
console.log("Actual fee:", ethers.formatEther(actualFee), "ETH");Для обычного перевода ETH оценка обычно равна 21 000 gas. Для вызова контракта значение зависит от кода контракта и текущего State.
Как это работает
sender.estimateGas(txRequest) — симулирует транзакцию от адреса sender и возвращает ожидаемый расход gas.
txRequest — объект будущей транзакции: получатель и сумма перевода.
provider.getFeeData() — читает текущие параметры комиссии из сети.
feeData.maxFeePerGas — максимальная цена одного gas в EIP-1559 транзакции.
estimatedGas * feeData.maxFeePerGas — верхняя оценка комиссии, а не точная сумма списания.
gasLimit: estimatedGas — задаёт максимум gas, который транзакция может потратить.
receipt.gasUsed — фактический gas, который транзакция потратила после включения в блок.
Estimate gas vs комиссия
estimateGas отвечает на вопрос:
Сколько единиц gas нужно для выполнения?Комиссия отвечает на другой вопрос:
Сколько ETH спишется за эти единицы gas?Формула остаётся той же:
fee = gasUsed × effectiveGasPriceПоэтому 21 000 gas само по себе не говорит, сколько ETH вы заплатите. Нужно знать цену одного gas.
Почему оценка может ошибиться
estimateGas смотрит на State в момент симуляции.
Если State изменится до включения транзакции в блок, реальное выполнение может потребовать больше gas или вообще упасть.
Чаще всего это заметно в контрактах: swap, mint, claim, liquidation и любые функции, где результат зависит от балансов, allowance, цены или времени.
Частые ошибки
Считать estimateGas точной комиссией → это оценка gas units, а не сумма ETH.
Не указывать правильный from → оценка может отличаться, потому что контракт часто проверяет msg.sender, баланс или allowance.
Использовать оценку без запаса для сложных контрактов → если State изменится, транзакция может упасть с out of gas.
Что дальше
- Отправка ETH — отправить транзакцию и дождаться receipt
- EIP-1559: maxFeePerGas vs gasPrice — понять, как цена gas превращается в комиссию
- Статусы TX: pending / confirmed / failed — проверить, что произошло после отправки