Запись через wagmi и состояния транзакции
Запись через wagmi: useWriteContract плюс ожидание подтверждения, и корректные состояния pending/success/error в UI.
useWriteContract — хук wagmi для отправки транзакций; в паре с useWaitForTransactionReceipt он покрывает весь путь от подписи до подтверждения.
Запись сложнее чтения: тут две асинхронные стадии (подписать и дождаться блока) и больше состояний. wagmi разбивает это на два хука, что удобно мапится на UI.
Отправка транзакции
import { useWriteContract, useWaitForTransactionReceipt } from "wagmi";
function Send() {
const { writeContract, data: hash, isPending, error } = useWriteContract();
const onClick = () => {
writeContract({
address: tokenAddress,
abi,
functionName: "transfer",
args: [recipient, amount],
});
};
// вторая стадия: ждём включения в блок
const { isLoading: confirming, isSuccess } =
useWaitForTransactionReceipt({ hash });
return (
<div>
<button onClick={onClick} disabled={isPending}>
{isPending ? "Подтвердите в кошельке…" : "Отправить"}
</button>
{confirming && <p>Ожидаем подтверждения…</p>}
{isSuccess && <p>Готово!</p>}
{error && <p>Ошибка или отклонено</p>}
</div>
);
}Карта состояний UI
| Состояние | Что показать пользователю |
isPending | «Подтвердите в кошельке» — ждём подпись |
есть hash, confirming | «Транзакция отправлена, ждём блок» (+ссылка на explorer) |
isSuccess | «Готово» — обновить данные |
error | различить отказ (4001) и реальную ошибку |
Главное правило: блокируйте кнопку на время isPending/confirming, иначе пользователь нажмёт дважды и отправит две транзакции.
Обновление данных после успеха
После isSuccess старые прочитанные данные устарели (баланс изменился). В wagmi их обновляют, инвалидируя кэш TanStack Query — тогда useReadContract перечитает свежее значение. Часто это делают в обработчике успеха транзакции.
Как работает под капотом
writeContract через viem Wallet Client просит кошелёк подписать и рассылает транзакцию, отдавая hash. useWaitForTransactionReceipt опрашивает ноду (или слушает через WebSocket), пока транзакция не попадёт в блок, и тогда выставляет isSuccess. Разделение на два хука повторяет два await из «голого» ethers (tx и tx.wait()), но в реактивной форме, удобной для рендера.
Частые ошибки
- Не блокировать кнопку. Двойной клик = двойная транзакция и двойной газ.
- Не дожидаться receipt перед обновлением UI. Покажете «успех», когда транзакция ещё в мемпуле.
- Не инвалидировать кэш чтения. Баланс на экране останется старым после успешной записи.
Итоги
useWriteContractотправляет,useWaitForTransactionReceiptждёт блок.- Мапьте pending/confirming/success/error на понятный UI и блокируйте кнопку.
- После успеха инвалидируйте кэш чтений, чтобы показать свежие данные.