китаев.tech

Оценка газа — estimateGas

Как заранее оценить gas для Ethereum-транзакции через ethers v6 и использовать результат как gasLimit.

Что делает estimateGas

estimateGas симулирует транзакцию через RPC и возвращает, сколько gas ей примерно понадобится.

Это не отправка транзакции. State не меняется, ETH не переводится, комиссия не списывается.


Оцениваем gas перед отправкой ETH

Запустите локальную сеть Hardhat в отдельном терминале:

bash
npx hardhat node

Затем выполните TypeScript-код:

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 отвечает на вопрос:

text
Сколько единиц gas нужно для выполнения?

Комиссия отвечает на другой вопрос:

text
Сколько ETH спишется за эти единицы gas?

Формула остаётся той же:

text
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.


Что дальше

Материалы китаev.tech публикуются в образовательных целях и не являются инвестиционной рекомендацией. Примеры кода и описания протоколов — для обучения; использование в продакшне на ваш собственный риск. Дисклеймер и политика конфиденциальности.