китаев.tech

Что такое nonce и зачем он нужен

Nonce в Ethereum: как он задаёт порядок транзакций, защищает от повторной отправки и почему зависшая транзакция блокирует следующие.

Что такое nonce

Nonce — это порядковый номер транзакции от конкретного EOA.

Ethereum использует nonce, чтобы выполнять транзакции одного аккаунта строго по порядку и не принимать одну и ту же подписанную транзакцию дважды.


Как nonce растёт

text
Адрес 0xabc...

nonce 0 → первая транзакция
nonce 1 → вторая транзакция
nonce 2 → третья транзакция

Каждый EOA имеет свой счётчик. Если у Alice nonce 7, это ничего не говорит о nonce Bob.

Контракты тоже имеют nonce, но для разработчика приложений чаще важен nonce отправителя транзакции: EOA, который подписывает действие.


Читаем nonce через RPC

typescript
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://ethereum.publicnode.com");

const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";

const latestNonce = await provider.getTransactionCount(address, "latest");
const pendingNonce = await provider.getTransactionCount(address, "pending");
const balance = await provider.getBalance(address);

console.log("Address:", address);
console.log("Balance:", ethers.formatEther(balance), "ETH");
console.log("Latest nonce:", latestNonce);
console.log("Pending nonce:", pendingNonce);
console.log("Pending transactions:", pendingNonce - latestNonce);

latest показывает nonce по уже подтверждённым блокам. pending учитывает транзакции, которые уже отправлены в mempool, но ещё не попали в блок.


Как это работает

provider.getTransactionCount(address, "latest") — возвращает количество подтверждённых транзакций от адреса.

provider.getTransactionCount(address, "pending") — возвращает следующий nonce с учётом pending-транзакций.

pendingNonce - latestNonce — показывает, сколько транзакций от адреса уже ждут включения в блок.

Если разницы нет, у адреса нет pending-транзакций по данным этой RPC-ноды.


Nonce в подписанной транзакции

Nonce входит в данные, которые подписывает кошелёк.

typescript
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://ethereum.publicnode.com");

// Тестовый ключ из документации Hardhat. Не используйте его для реальных средств.
const wallet = new ethers.Wallet(
  "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
  provider,
);

const nonce = await provider.getTransactionCount(wallet.address, "pending");

const txRequest = {
  to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  value: ethers.parseEther("0.001"),
  nonce,
  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("Wallet:", wallet.address);
console.log("Nonce:", nonce);
console.log("Signed transaction:", signedTx);

Этот код подписывает транзакцию локально и не отправляет её в сеть. Если изменить nonce, изменится и подпись.


Зачем nonce нужен

Nonce решает две задачи.

Первая — порядок. Ethereum не выполнит транзакцию с nonce 8, пока не выполнена транзакция с nonce 7.

Вторая — защита от replay внутри сети. Если уже выполнена транзакция с nonce 7, эту же signed transaction нельзя выполнить ещё раз.


Как заменить зависшую транзакцию

Если транзакция застряла в mempool, новые транзакции с большим nonce будут ждать.

Чтобы заменить зависшую транзакцию, отправляют новую транзакцию:

text
тот же from
тот же nonce
более высокая комиссия

Когда новая транзакция с тем же nonce попадёт в блок, старая станет недействительной. Две транзакции с одним nonce от одного адреса не могут выполниться обе.


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

Брать nonce только через latest → если у адреса уже есть pending-транзакция, вы можете случайно подписать новую транзакцию с тем же nonce.

Думать, что nonce глобальный для сети → nonce считается отдельно для каждого аккаунта.

Отправлять несколько транзакций параллельно без управления nonce → две транзакции могут получить один nonce, и одна из них заменит другую или будет отклонена RPC.


Что дальше

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