Реализация покупки NFT в мобильном приложении
Купить NFT — это значит вызвать функцию смарт-контракта маркетплейса, передав нужную сумму ETH или ERC-20. Звучит просто, но между нажатием «Купить» и подтверждённой транзакцией — несколько шагов, где всё может пойти не так: газ иссяк, NFT уже купили раньше, кошелёк не подключён. UX должен обрабатывать каждый случай.
Проверка доступности перед покупкой
Перед показом кнопки «Купить» — проверить актуальный статус листинга. NFT мог быть продан секунду назад, а кэшированный экран этого не знает.
// iOS — проверка активности листинга перед покупкой
func checkListingActive(contractAddress: String, tokenId: BigUInt) async throws -> Bool {
let listing = try await marketplaceContract.getListing(
nftAddress: EthereumAddress(contractAddress)!,
tokenId: tokenId
)
return listing.price > 0 && listing.seller != EthereumAddress.zero
}
Если листинг неактивен — кнопка меняется на «Нет в продаже», без диалога покупки.
Approve + Buy: одна или две транзакции
При покупке за ETH — одна транзакция: buyItem(nftContract, tokenId, { value: price }).
При покупке за ERC-20 (например, USDC) — две:
-
usdc.approve(marketplaceAddress, price)— разрешить маркетплейсу тратить токены -
marketplace.buyItem(nftContract, tokenId)— непосредственно покупка
Пользователь должен видеть это как единый flow: «Шаг 1 из 2: Разрешить списание USDC» → «Шаг 2 из 2: Подтвердить покупку». Прогресс-индикатор, объяснение каждого шага.
Оптимизация: использовать permit (EIP-2612) вместо approve, если токен поддерживает. Это объединяет approve и buy в одну транзакцию через подпись off-chain.
Ожидание подтверждения
После отправки транзакции — не блокируй экран. Показывай:
- TransactionHash в виде ссылки на Explorer (Etherscan, Polygonscan)
- Индикатор «Ожидание подтверждения» с возможностью уйти
- Push при получении N подтверждений (обычно 1–3)
// Android — ожидание подтверждения с таймаутом
suspend fun waitForReceipt(txHash: String, timeoutMs: Long = 120_000): TransactionReceipt? {
val deadline = System.currentTimeMillis() + timeoutMs
while (System.currentTimeMillis() < deadline) {
val receipt = web3j.ethGetTransactionReceipt(txHash).send().transactionReceipt
if (receipt.isPresent) return receipt.get()
delay(3_000)
}
return null
}
Если receipt.status == "0x0" — транзакция завершилась с ошибкой (revert). Нужно декодировать причину через debug_traceTransaction или проверить известные ошибки смарт-контракта.
Типичные ошибки и их обработка
| Ошибка | Причина | Реакция UI |
|---|---|---|
execution reverted: Not listed |
NFT снят с продажи | «NFT больше не продаётся» |
execution reverted: Price mismatch |
Цена изменилась | Показать актуальную цену, предложить обновить |
insufficient funds |
Не хватает ETH на газ | «Пополните кошелёк для оплаты комиссии» |
| Transaction timeout | Congestion сети | Предложить ускорить транзакцию (increase gas price) |
Сроки: 3–5 дней: проверка листинга, flow approve+buy, ожидание подтверждения с push, обработка reverts.







